source: sandbox/2.4-expresso-api/prototype/api/datalayer.js @ 5888

Revision 5888, 125.3 KB checked in by cristiano, 12 years ago (diff)

Ticket #2598 - implementação base REST + oauth

  • 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 = /^:/;
6isIframe = /^iframe.*/;
7FILE = 'files';
8// cached_urls = {};
9
10$.ajaxPrefilter(function( options, originalOptions, jqXHR ){
11
12      if( options.url != 'undefined' && internalUrl.test( options.url ) ){
13
14//        if( !cached_urls[options.url] )
15//            return;
16//        alert( options.url + " dentro" );
17
18           var callback = ( options.success || options.complete || $.noop );
19
20          if( isIframe.test( options.dataType ) || options.data instanceof FormData )
21          {
22              options.url = options.fileInput.parents( 'form' ).attr( 'action' );
23           
24              res = internalUrl.exec( options.url );
25
26              var data = {};
27
28              data[ res[1] ] = DataLayer.form( options.fileInput.parents( 'form' ), options.fileInput );
29
30              options.formData = DataLayer.serializeForm( data );
31
32              callback = function( data ){
33
34                    //coutinho, escreva seu codigo aqui.
35
36                    return callback( DataLayer.encode( res[2], data ) );
37
38              }
39
40              options.url = DataLayer.dispatchPath + 'post.php';
41
42              if( options.data instanceof FormData )
43              {
44                  options.data = new FormData();
45
46                  $.each(options.formData, function (index, field) {
47                            options.data.append(field.name, field.value);
48                        });
49                 
50                  $.each(options.files, function (index, file) {
51                            options.data.append(options.paramName, file);
52                        });
53              }
54
55              return( true );
56          }
57
58          jqXHR.abort();
59
60          switch( options.type.toUpperCase() )
61          {
62            case 'GET':
63                  return callback( DataLayer.get( options.url, /*false,*/ options.data ) );
64
65            case 'POST':
66                  return callback( DataLayer.put( options.url, options.data ) );
67          }
68
69          //return( false );
70
71//        options.url = params[1];
72//        options.data = ( options.data || "" ) + "&" + params[2];
73      }
74
75});
76
77// $("a").live("click", function( event ){
78//
79//     event.preventDefault();
80//
81//     $.ajax({
82//
83//     
84//
85//     });
86//
87// });
88
89$("form").live( "submit", function( event ){
90
91    var $this = $(this), action = $this.attr('action'), res = false,
92   
93    method = $this.attr( 'method' ) || 'POST',
94   
95    fileInputs = $this.find('input[type="file"]');
96   
97    if( fileInputs.length && !$this.is('[enctype="multipart/form-data"]') )
98    {
99        event.preventDefault();
100
101        DataLayer.send( action,
102                        [ method, 'iframe json' ], {},
103                        //TODO: check the type for conversion
104                        DataLayer.receive,
105                        false, { 'formData': $this.serializeArray(),  'fileInput': fileInputs, 'paramName': FILE + '[]' } );
106
107        return( false );
108    }
109   
110    if( res = internalUrl.exec( action ) )
111    {
112        event.preventDefault();
113
114        var data = DataLayer.form( this );
115       
116        switch( method.toUpperCase() )
117        {
118          case 'GET':
119                DataLayer.get( res[0], data );
120
121          case 'POST':
122                DataLayer.put( res[1], data );
123        }
124
125        return( false );
126    }
127
128    return( true );
129});
130
131this.storage = new $.store();
132
133DataLayer = {
134
135    links: {},
136    nestedLinks: {},
137    concepts: {},
138    notArray: {},
139    listeners: {},
140    encoders: {},
141    decoders: {},
142    templates: {},
143    criterias: {},
144    tasks: {},
145
146    render: function( templateName, data, filter, formatter, force ){
147
148        if( $.isFunction( filter ) )
149        {
150            force = formatter;
151            formatter = filter;
152            filter = false;
153        }
154
155        if( typeof data === "string" )
156        {
157            data = this.get( data, filter, force ) || {};
158        }
159       
160        var formatting = function( template ){
161
162              if( template === false ) return( false );
163
164              if( template )
165                  DataLayer.templates[ templateName ] = new EJS({ text: template, cache: false });
166
167              var html = DataLayer.templates[ templateName ].render( { data: data } );
168
169              if( !formatter )
170                  return( html );
171
172              return formatter( html );
173        }
174
175        if( this.templates[ templateName ] )
176        {
177            return formatting();
178        }
179
180        return this.send( DataLayer.templatePath + templateName, 'get', false, formatting, !!!formatter );
181    },
182   
183    send: function( url, type, data, callback, sync, extraOptions ){
184     
185          var result = false, fired = false;
186     
187          var envelope = {
188
189              'async': ( typeof sync !== "undefined" ? !sync : !!callback ),
190              'url': url,
191              'success': function( dt, textStatus, jqXHR ){
192
193                    if( callback )
194                    {
195                        fired = true;
196                        result = callback( dt, textStatus, jqXHR );
197                    }
198                    else
199                        result = dt;
200
201                },
202              'complete': function( jqXHR, textStatus ){
203
204                  if( !fired && callback )
205                      result = callback( false, textStatus, jqXHR );
206
207              },
208
209              'type': $.isArray( type ) ? type[0] : type,
210              'data': data
211
212            };
213
214          if( $.isArray( type ) && type[1] )
215              envelope['dataType'] = type[1];
216
217          if( extraOptions )
218              envelope = $.extend( envelope, extraOptions );
219
220          $.ajax( envelope );
221     
222          return( result );
223    },
224   
225    /**
226    * This method is used to read resources from the server.
227    *
228    * @param uri Uri of the resource that gonna be readed.
229    * @param callback A function that is called when the resource is loaded successfully. When the parameter is ignored the request is made synchrounsly.
230    * @param accept The attribute accept that is used to ask the target format to the server
231    * @return The target resource when the callback is ommitted ( made it synchronous )
232    */
233
234    read: function( uri, callback, accept ){
235        //return this.send( this.dispatchPath + 'rest' + uri, [ 'get', accept || 'json' ], false, callback );
236        //http://expressodev.prognus.com.br/cristiano/expresso-api/rest/maillast
237       
238        return this.send( ('rest' + uri), [ 'get', accept || 'json' ], true, callback, !!!callback,
239        {'beforeSend' : function (xhr){
240            xhr.setRequestHeader('Authorization', "OAUTH Bearer " + DataLayer.me.token)}
241        });
242     
243    },
244
245    /**
246    * This method is used to create resources from the server.
247    *
248    * @param uri Uri of the resource that gonna be readed.
249    * @param callback A function that is called when the resource is created on the server successfully. When the parameter is ignored the request is made synchrounsly.
250    * @param accept The attribute accept that is used to ask the target format to the server.
251    * @return The result response of the create from the server when the callback is ommitted ( made it synchronous )
252    */
253   
254    create: function( uri, data, callback, accept ){
255     
256        return this.send( this.dispatchPath + 'rest' + uri, [ 'post', accept || 'json' ], data, callback, !!!callback,
257        {'beforeSend' : function (xhr){
258            xhr.setRequestHeader('Authorization', "OAUTH Bearer " + DataLayer.me.token)}
259        });
260     
261    },
262
263    /**
264    * This method is used to update resources from the server.
265    *
266    * @param uri Uri of the resource that gonna be readed.
267    * @param callback A function that is called when the resource is update on the server successfully. When the parameter is ignored the request is made synchrounsly.
268    * @param accept The attribute accept that is used to ask the target format to the server
269    * @return The result response of the update from the server when the callback is ommitted ( made it synchronous )
270    */
271
272    update: function( uri, data, callback, accept ){
273     
274        return this.send( this.dispatchPath + 'rest' + uri, [ 'put', accept || 'json' ], data, callback, !!!callback,
275        {'beforeSend' : function (xhr){
276            xhr.setRequestHeader('Authorization', "OAUTH Bearer " + DataLayer.me.token)}
277        });
278     
279    },
280
281    /**
282    * This method is used to delete resources from the server.
283    *
284    * @param uri Uri of the resource that gonna be readed.
285    * @param callback A function that is called when the resource is deleted successfully in the server. When the parameter is ignored the request is made synchrounsly.
286    * @param accept The attribute accept that is used to ask the target format to the server
287    * @return The result response of the delete from the server when the callback is ommitted ( made it synchronous )
288    */
289
290    "delete": function( uri, callback, accept ){
291     
292        return this.send( this.dispatchPath + 'rest' + uri, [ 'delete', accept || 'json' ], false, callback, !!!callback,
293        {'beforeSend' : function (xhr){
294            xhr.setRequestHeader('Authorization', "OAUTH Bearer " + DataLayer.me.token)}
295        });
296     
297    },
298   
299    dispatch: function( dispatcher, data, callback, isPost, dataType ){
300     
301      return this.send( this.dispatchPath + dispatcher + ".php",
302                        [ ( isPost ? 'post' : 'get' ), dataType || 'json' ],
303                        data,
304                        callback );
305
306//       $.ajax({
307//            'async': !!callback,
308//            'url': this.dispatchPath + dispatcher + ".php",
309//            'type': ( isPost ? 'post' : 'get' ),
310//            'dataType': 'json',
311//            'data': data,
312//            'success': function( dt, textStatus, jqXHR ){
313//
314//                  if( callback )
315//                  {
316//                      fired = true;
317//                      callback( dt, textStatus, jqXHR );
318//                  }
319//                  else
320//                      result = dt;
321//
322//              },
323//            'complete': function( jqXHR, textStatus ){
324//
325//                if( !fired && callback )
326//                    callback( false, textStatus, jqXHR );
327//
328//            }/*,
329//            'processData': false*/
330//        });
331
332      //return( result );
333    },
334
335    form: function( target, fileInputs ){
336
337        var params = {}, $this = $(target), inputArray = $this.serializeArray();
338
339        if( !$this.is( "form" ) )
340            $this = $this.parents( "form" );
341               
342        if( fileInputs )
343                fileInputs.each( function( i, el ){
344
345              inputArray[ inputArray.length ] = { name: $(this).prop("name"), value: FILE + i };
346
347                });
348
349        $.each( inputArray, function( i, el ){
350
351            if( newName = arrayName.exec( el.name ) )
352                el.name = newName[1];
353            else if( !params[ el.name ] )
354                return( params[ el.name ] = el.value );
355
356            params[ el.name ] = params[ el.name ] || [];
357
358            if( $.type(params[ el.name ]) !== "array" )
359                params[ el.name ] = [ params[ el.name ] ];
360
361            params[ el.name ].push( el.value );
362        });
363
364//      alert(dump(params));
365
366        return this.decode( $this.attr( "action" ), params );
367    },
368       
369        serializeForm: function( data, level ){
370       
371                var formData = [];
372       
373                for( key in data )
374                {
375                        var value = data[key];
376
377                        if( level !== undefined )
378                                key = level+'['+key+']';
379
380                        if( $.isArray(value) || $.isPlainObject(value) )
381                                formData = formData.concat( this.serializeForm( value, key ) );
382                        else
383                                formData[ formData.length ] = { name: key, value: value };
384                }
385               
386                return( formData );
387        },
388
389    blend: function( action, data ){
390
391//      if( notArray = (!$.isArray(data)) )
392//          data = [ data ];
393
394        var form = $('form[action="'+action+'"]');
395
396        form.get(0).reset();
397
398        var named = form.find( 'input[name]' );
399
400        for( var name in data )
401        {
402            named.filter( '[name="'+name+'"]' ).val( data[name] );
403        }
404    },
405
406 
407   
408    put: function( concept, filter, data, oneSide ){
409     
410      ///////////////////////////// normalize ////////////////////////////////
411        if( arguments.length == 2 )
412        {
413            data = filter;
414            filter = false;
415        }
416        if( typeof data === "undefined" ||
417            $.type(data) === "boolean" )
418        {
419            oneSide = data;
420            data = filter;
421            filter = false;
422        }
423       
424        if( !concept || !data )
425            return( false );
426
427        var decoder = "", id = false, bothSides = (typeof oneSide === "undefined"), notArray, res;
428       
429        if( $.type(filter) === "string" )
430        {
431            id = filter;
432            filter = false;
433        }
434
435        if( id )
436            data.id = id;
437
438        if( notArray = ( $.type( data ) !== "array" ) )
439            data = [ data ];
440
441        if( res = internalUrl.exec( concept ) )
442        {
443            //TODO: verificar se a decodificaçao deve ser feita em cada item do array
444            data = this.decode( concept, data );
445            concept = res[1];
446            decoder = res[2];
447        }
448
449      ////////////////////////////////////////////////////////////////////////
450
451        if( bothSides || !oneSide )
452        {
453            var result = false, links = this.links( concept ), nestedLinks = this.links( concept, true ),
454            current = this.check( concept ) || {}, ids = [];
455
456            for( var i = 0; i < data.length; i++ )
457            {
458                var key = ids[ ids.length ] = data[i].id || this.generateId( concept ), updateSet = {};
459
460                ////////////////////////////// linkage /////////////////////////////////   
461                for( var link in links )
462                {
463                    if( data[i][link] )
464                    {
465                        var notArray2 = false;
466
467                        if( notArray2 = this.hasOne( concept, link ) )
468                            data[i][link] = [ data[i][link] ];
469
470                        var _this = this, dependency = this.isDependency( concept, link );
471
472                        $.each( data[i][link], function( ii, el ){
473
474                                var isRef = false;
475
476                                if( isRef = ($.type(el) === "string") )
477                                    el = { id: el };
478
479                                //removido pois o mesmo esta gerando inconsistencia em tudo
480//                              if( DataLayer.isConcept( links[link], nestedLinks[link] ) )
481                                if( !DataLayer.hasOne( links[link], nestedLinks[link] ) )
482                                {
483                                    el[ nestedLinks[link] ] = el[ nestedLinks[link] ] || [];
484                                    el[ nestedLinks[link] ].push( key );
485                                }
486                                else
487                                    el[ nestedLinks[link] ] = key;
488
489                                if( isRef && ( !current[ key ] || !current[ key ][ link ] ||
490                                               (notArray2 ? current[ key ][ link ] !== el.id : !$.inArray( el.id, current[ key ][ link ] )) ) )
491                                {
492                                    updateSet[ links[link] ] = updateSet[ links[link] ] || [];
493                                    updateSet[ links[link] ].push( el );
494                                }
495                                else if( !isRef )
496                                    data[i][link][ii] = _this.put( links[link], el, oneSide );
497                            });
498
499                        if( notArray2 )
500                            data[i][link] = data[i][link][0];
501                    }
502                }
503                //////////////////////////////////////////////////////////////////////////
504
505                if( data[i].id )
506                    data[i] = this.merge( current[ data[i].id ], data[i] );
507
508                 current[ key ] = data[i];
509
510                if( bothSides )
511                  this.report( concept, key, data[i] );
512            }
513
514            this.store( concept, current );
515
516            for( var setKey in updateSet )
517            {
518                if( bothSides )
519                    for( var i = 0; i < updateSet[ setKey ].length; i++ )
520                      this.report( setKey, updateSet[ setKey ][i].id, updateSet[ setKey ][i] );
521                   
522                DataLayer.put( setKey, updateSet[ setKey ], false );
523            }
524        }
525
526        if( oneSide )
527            this.commit( concept, ids/*, true */);
528
529        this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', true );
530
531        return( notArray ? ids[0] : ids );
532
533    },
534   
535    remove: function( concept, id, oneSide ){
536     
537        var bothSides = (typeof oneSide === "undefined"),
538
539        links = this.links( concept ), nestedLinks = this.links( concept, true ), ids = [],
540
541        current = this.check( concept, id );
542
543        if( !current ) return;
544       
545        if( typeof id === "string" )
546            current.id = id;
547
548        if( notArray = ( $.type( current ) !== "array" ) )
549            current = [ current ];
550
551        for( var i = 0; i < current.length; i++ )
552        {
553            var currentId = ids[ ids.length ] = current[i].id;
554
555            if( bothSides )
556              this.report( concept, currentId, false );
557
558            if( bothSides || !oneSide )
559              this.del( concept, currentId );
560
561            for( var link in links )
562            {
563                if( !current[i][link] )
564                    continue;
565
566                if( this.hasOne( concept, link ) )
567                    current[i][link] = [ current[i][link] ];
568
569                $.each( current[i][link], function( ii, el ){
570
571                        el = DataLayer.storage.cache[links[link]][el];
572
573                        if( notArrayNested = ( $.type( el[ nestedLinks[link] ] ) !== "array" ) )
574                            el[ nestedLinks[link] ] = [ el[nestedLinks[link]] ];
575
576                        el[ nestedLinks[link] ] = $.grep( el[ nestedLinks[link] ], function( nested, iii ){
577                            return ( currentId !== nested );
578                        });
579
580                        if( notArrayNested )
581                            el[ nestedLinks[link] ] = el[ nestedLinks[link] ][0] || false;
582                        if(!el[ nestedLinks[link] ] || !el[ nestedLinks[link] ].length)
583                                delete el[ nestedLinks[link] ];
584                });
585            }
586        }
587
588        if( oneSide )
589            this.commit( concept, ids );
590
591        this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', false );
592    },
593   
594    report: function( concept, id, data )
595    {     
596        var current = this.check( ':current', concept ) || {};
597
598        if( !current[ id ] )
599            current[ id ] = this.check( concept, id ) || {};
600       
601        this.store( ':current', concept, current );
602
603        var diff = this.diff( current[ id ], data );
604
605        var diffs = this.check( ':diff', concept ) || {};
606
607        if( diffs[ id ] )
608            diff = this.merge( diffs[ id ], diff );
609
610        if( !diff || !$.isEmptyObject( diff ) )
611            diffs[ id ] = diff;
612
613        this.store( ':diff', concept, diffs );
614    },
615
616//     enqueue: function( queueName, concept, id, data ){
617//
618//      var queue = this.check( ':' + queueName, concept ) || {};
619//
620//
621//     },
622//     
623//     dequeue: function( queueName, concept, id ){
624//
625//     
626//
627//     },
628   
629   
630   
631    rollback: function( concept, ids ){
632     
633        var queue = this.prepareQ( 'current', concept, ids );
634
635        ids = [];
636
637        for( var id in queue )
638        {
639             this.put( concept, id, queue[id], false );
640
641             ids[ ids.length ] = id;
642        }
643
644        this.clearQ( concept, ( ids.length ? ids : false ) );
645
646        this.broadcast( concept, 'revert' );
647     
648    },
649   
650    prepareQ: function( queueName, concept, ids ){
651     
652      var notArray = false;
653     
654      if( notArray = ($.type(concept) !== "array") )
655          concept = [ concept ];
656     
657      var q = {};
658     
659      for( var i = 0; i < concept.length; i++ )
660      {
661          var queue = this.check( ':' + queueName, concept[i] || false );
662         
663          if( !queue ) continue;
664
665          if( ids )
666          {
667              if( $.type(ids) !== "array" )
668                  ids = [ ids ];
669
670              var filtered = {};
671
672              for( var ii = 0; ii < ids.length; ii++ )
673              {
674                  filtered[ ids[ii] ] = queue[ ids[ii] ];
675              }
676
677              queue = filtered;
678          }
679
680          q[ concept[i] ] = queue;
681      }
682     
683      return( notArray ? q[ concept[0] ] : q );
684    },
685   
686    clearQ: function( concept, ids ){
687     
688        var current = this.check( ':current', concept || false );
689        var diffs = this.check( ':diff', concept || false );
690
691        if( !ids )
692            current = diffs = {};
693        else
694        {
695            if( notArray = ($.type(ids) !== "array") )
696              ids = [ ids ];
697
698            for( var i = 0; i < ids.length; i++ )
699            {
700                delete current[ ids[i] ];
701                delete diffs[ ids[i] ];
702            }
703        }
704
705        this.store( ':current', concept, current );
706        this.store( ':diff', concept, diffs );
707    },
708
709    commit: function( concept, ids, callback ){
710     
711        var queue = this.prepareQ( 'diff', concept, ids );
712
713        this.sync( queue, !$.isArray(concept) && concept || false, callback );
714    },
715   
716    sync: function( queue, concept, callback ){
717
718        if( !queue || $.isEmptyObject( queue ) )
719            return;
720
721        if( concept )
722        {
723          var helper = {};
724          helper[concept] = queue;
725          queue = helper;
726        }
727
728        var data = {}, URIs = {};
729
730        for( var concept in queue )
731            for( var id in queue[concept] )
732            {
733                data[ this.URI( concept, id ) ] = queue[concept][id];
734                URIs[ this.URI( concept, id ) ] = { concept: concept, id: id };
735            }
736
737        if( $.isEmptyObject( data ) )
738            return;
739
740        this.dispatch( "Sync", data, function( data, status, jqXHR ){
741
742//          switch( status )
743//          {
744//            case "error":
745//            case "parsererror":
746//              return DataLayer.rollback( concept, URI );
747//            case "success":
748//              return DataLayer.commit();
749//            case "timeout":
750//            case "notmodified":
751//          }
752
753            var received = DataLayer.receive( data );
754
755            for( var URI in URIs )
756                if( typeof received[URI] !== "undefined" )
757                    DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
758
759            if( callback )
760                callback( received );
761
762//          for( var URI in data )
763//          {
764//              var parsed = DataLayer.parseURI( URI ),
765//   
766//              concept = parsed[1], id = parsed[3];
767//
768//              if( $.type(data[URI]) === "string" )
769//              {
770//                //TODO:threat the exception thrown
771//                DataLayer.rollback( concept, id );
772//                delete URIs[ URI ];
773//                continue;
774//              }
775//
776//              if( data[URI] === false ){
777//                DataLayer.remove( concept, id, false );
778//                continue;
779//              }
780//
781//              if( id !== data[URI].id )
782//                DataLayer.move( concept, id, data[URI].id );
783//             
784//              DataLayer.put( concept, id, data[URI], false );
785//          }
786//         
787//          for( var URI in URIs )
788//               DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
789//         
790//          if( callback )
791//              callback();
792
793        }, true );
794
795    },
796   
797    receive: function( data ){
798     
799        var received = {};
800       
801            for( var URI in data )
802            {
803                var parsed = DataLayer.parseURI( URI ),
804   
805            concept = parsed[4], id = parsed[5];
806
807            received[ URI ] = data[ URI ];
808
809                if( $.type(data[URI]) === "string" )
810                {
811                  //TODO:threat the exception thrown
812                  DataLayer.rollback( concept, id );
813                  continue;
814                }
815
816                if( data[URI] === false ){
817                  DataLayer.remove( concept, id, false );
818                  continue;
819                }
820
821                if( id !== data[URI].id )
822                  DataLayer.move( concept, id, data[URI].id );
823               
824                DataLayer.put( concept,  data[URI].id || id, data[URI], false );
825            }
826           
827        return( received );
828           
829    },
830   
831    unique: function( origArr ){
832
833        var newArr = [];
834     
835        for ( var x = 0; x < origArr.length; x++ )
836        {
837                var found = false;
838            for ( var y = 0; !found && y < newArr.length; y++ )
839                if ( origArr[x] === newArr[y] ) 
840                  found = true;
841
842            if ( !found )
843                newArr[ newArr.length ] = origArr[x];
844        }
845
846        return newArr;
847    },
848
849    merge: function( current, data ){
850     
851        return this.copy(  data, current );
852
853//      return $.extend( current, data );
854
855    },
856   
857    // clone objects, skip other types.
858    clone: function(target) {
859            if ( typeof target == 'object' ) {
860                    Clone.prototype = target;
861                    return new Clone();
862            } else {
863                    return target;
864            }
865    },
866     
867    // Shallow Copy
868    shallowCopy: function(target) {
869            if (typeof target !== 'object' ) {
870                    return target;  // non-object have value sematics, so target is already a copy.
871            } else {
872                    var value = target.valueOf();
873                    if (target != value) {
874                            // the object is a standard object wrapper for a native type, say String.
875                            // we can make a copy by instantiating a new object around the value.
876                            return new target.constructor(value);
877                    } else {
878                            // ok, we have a normal object. If possible, we'll clone the original's prototype
879                            // (not the original) to get an empty object with the same prototype chain as
880                            // the original.  If just copy the instance properties.  Otherwise, we have to
881                            // copy the whole thing, property-by-property.
882                            if ( target instanceof target.constructor && target.constructor !== Object ) {
883                                    var c = clone(target.constructor.prototype);
884     
885                                    // give the copy all the instance properties of target.  It has the same
886                                    // prototype as target, so inherited properties are already there.
887                                    for ( var property in target) {
888                                            if (target.hasOwnProperty(property)) {
889                                                    c[property] = target[property];
890                                            }
891                                    }
892                            } else {
893                                    var c = {};
894                                    for ( var property in target ) c[property] = target[property];
895                            }
896                           
897                            return c;
898                    }
899            }
900    },
901
902    // entry point for deep copy.
903    // source is the object to be deep copied.
904    // depth is an optional recursion limit. Defaults to 256.
905    // deep copy handles the simple cases itself: non-objects and object's we've seen before.
906    // For complex cases, it first identifies an appropriate DeepCopier, then delegate the details of copying the object to him.
907    copy: function(source, result, depth) {
908     
909            // null is a special case: it's the only value of type 'object' without properties.
910            if ( source === null ) return null;
911
912            // All non-objects use value semantics and don't need explict copying.       
913                if ( typeof source !== 'object' ) return source;
914
915            if( !depth || !(depth instanceof RecursionHelper) ) depth = new RecursionHelper(depth);
916
917            var cachedResult = depth.getCachedResult(source);
918
919            // we've already seen this object during this deep copy operation
920            // so can immediately return the result.  This preserves the cyclic
921            // reference structure and protects us from infinite recursion.
922            if ( cachedResult ) return cachedResult;
923
924            // objects may need special handling depending on their class.  There is
925            // a class of handlers call "DeepCopiers"  that know how to copy certain
926            // objects.  There is also a final, generic deep copier that can handle any object.
927            for ( var i=0; i<this.comparators.length; i++ ) {
928
929                    var comparator = this.comparators[i];
930
931                    if ( comparator.can(source) ) {
932       
933                            // once we've identified which DeepCopier to use, we need to call it in a very
934                            // particular order: create, cache, populate.  This is the key to detecting cycles.
935                            // We also keep track of recursion depth when calling the potentially recursive
936                            // populate(): this is a fail-fast to prevent an infinite loop from consuming all
937                            // available memory and crashing or slowing down the browser.
938     
939                            if( !result )
940                                // Start by creating a stub object that represents the copy.
941                                result = comparator.create(source);
942                            else if( !comparator.can(result) )
943                                throw new Error("can't compare diferent kind of objects.");
944
945                            // we now know the deep copy of source should always be result, so if we encounter
946                            // source again during this deep copy we can immediately use result instead of
947                            // descending into it recursively. 
948                            depth.cacheResult(source, result);
949
950                            // only DeepCopier.populate() can recursively deep copy.  So, to keep track
951                            // of recursion depth, we increment this shared counter before calling it,
952                            // and decrement it afterwards.
953                            depth.depth++;
954                            if ( depth.depth > depth.maxDepth ) {
955                                    throw new Error("Exceeded max recursion depth in deep copy.");
956                            }
957
958                            // It's now safe to let the comparator recursively deep copy its properties.
959                            var returned = comparator.populate( function(source, result) { return DataLayer.copy(source, result, depth); }, source, result );
960       
961                                if(returned)
962                                        result = returned;
963
964                            depth.depth--;
965
966                            return result;
967                    }
968            }
969            // the generic copier can handle anything, so we should never reach this line.
970            throw new Error("no DeepCopier is able to copy " + source);
971    },
972
973    // publicly expose the list of deepCopiers.
974    comparators: [],
975
976    // make deep copy() extensible by allowing others to
977    // register their own custom Comparators.
978    registerComparator: function(comparatorOptions) {
979
980          // publicly expose the Comparator class.
981          var comparator = {
982
983              // determines if this Comparator can handle the given object.
984              can: function(source) { return false; },
985   
986              // starts the deep copying process by creating the copy object.  You
987              // can initialize any properties you want, but you can't call recursively
988              // into the copy().
989              create: function(source) { },
990
991              // Completes the deep copy of the source object by populating any properties
992              // that need to be recursively deep copied.  You can do this by using the
993              // provided deepCopyAlgorithm instance's copy() method.  This will handle
994              // cyclic references for objects already deepCopied, including the source object
995              // itself.  The "result" passed in is the object returned from create().
996              populate: function(deepCopyAlgorithm, source, result) {}
997          };
998
999          for ( var key in comparatorOptions ) comparator[key] = comparatorOptions[key];
1000
1001          this.comparators.unshift( comparator );
1002    },
1003 
1004    diff: function( base, toDiff ){
1005
1006        if( typeof base === 'undefined' || $.isEmptyObject(base) )
1007            return( toDiff );
1008
1009        if( toDiff === false )
1010            return( false );
1011
1012        toDiff = $.extend( {}, toDiff );
1013
1014        for( var key in toDiff )
1015        {
1016            switch( $.type(toDiff[key]) )
1017            {
1018              case 'object':
1019                if( $.isEmptyObject(toDiff[key] = this.diff( base[key], toDiff[key] )) )
1020                  delete toDiff[key];
1021              break;
1022              case 'array':
1023                if( base[key] && !(toDiff[key] = $.grep( toDiff[key], function( el, i ){ return( $.inArray( el, base[key] ) === -1 ); } )).length )
1024                  delete toDiff[key];
1025              break;
1026              default:
1027                if( base[key] == toDiff[key] )
1028                  delete toDiff[key];
1029            }
1030        }
1031
1032        return( toDiff );
1033
1034    },
1035   
1036    links: function( concept, reverse ){
1037
1038        if( !this.links[ concept ] )
1039        {
1040            var result = this.dispatch( "links", { concept: concept } ) || false;
1041
1042            if( !result )
1043                return( false );
1044
1045            this.concepts[ concept ] = $.extend( this.concepts[ concept ] || {},
1046                                                 result['concepts'] || {} );
1047
1048            this.links[ concept ] =  result['links'] || {};
1049           
1050            this.notArray[ concept ] = result['hasOne'] || {};
1051
1052           this.nestedLinks[ concept ] = result['nestedLinks'] || {};
1053        }
1054
1055        if( reverse )
1056        {
1057            return( this.nestedLinks[ concept ] );
1058        }
1059
1060        return( this.links[ concept ] );
1061
1062    },
1063   
1064    isDependency: function( concept, attr ){
1065     
1066        if( typeof this.concepts[concept] === "undefined" )
1067        {
1068            this.links( concept );
1069        }
1070
1071        return !!this.concepts[ concept ][ attr ];
1072    },
1073   
1074    hasOne: function( concept, attr ){
1075     
1076        if( typeof this.notArray[concept] === "undefined" )
1077        {
1078            this.links( concept );
1079        }
1080
1081        return !!this.notArray[ concept ][ attr ];
1082    },
1083   
1084    URI: function( concept, URI, context ){
1085     
1086        if( res = internalUrl.exec( concept ) )
1087            concept = res[1];
1088       
1089        context = context ? "(" + context + ")" : "";
1090     
1091        if( URI )
1092            return( concept + context + "://" + URI );
1093        else
1094            return( concept );
1095     
1096    },
1097   
1098    parseURI: function( URI ){
1099
1100        return internalUri.exec( URI ) || false;
1101
1102    },
1103   
1104   
1105   
1106   
1107    generateId: function( concept ){
1108     
1109        var newId = this.counter + "(javascript)";
1110     
1111        this.store( ":counter", (this.counter++) + "" );
1112       
1113        return( newId );
1114    },
1115   
1116
1117   
1118
1119    get: function( concept, /*URI, */filter, oneSide ){
1120
1121        ///////////////////////////// normalize ////////////////////////////////
1122        if( arguments.length == 2 && $.type(filter) === "boolean" )
1123        {
1124            oneSide = filter;
1125            filter = false;
1126        }
1127       
1128        var encoder = false, id = false, bothSides = (typeof oneSide === 'undefined'), res;
1129       
1130        if( $.type(filter) === "string" )
1131        {
1132            id = filter;
1133            filter = false;
1134        }
1135
1136        filter = filter || false;
1137
1138        if( !concept )
1139            return( false );
1140
1141        if( res = internalUrl.exec( concept ) )
1142        {
1143            encoder = concept;
1144            concept = res[1];
1145
1146            if( filter )
1147                filter = this.criteria( encoder, filter );
1148        }
1149       
1150        if ( $.type(filter) === "array" )
1151        {
1152            filter = { filter: filter, criteria: false };
1153        }
1154       
1155        //////////////////////////////////////////////////////////////////////////
1156       
1157        var result = false;
1158       
1159        if( bothSides || !oneSide )
1160            result = this.check( concept, id || filter );
1161       
1162        if (bothSides && filter.filter)
1163                result = false;
1164
1165               
1166        if( !result && (bothSides || oneSide) )
1167        {
1168            result = this.request( concept, id || filter.filter, filter.criteria );
1169
1170            if( result && bothSides && (!filter ||
1171                                        !filter.criteria ||
1172                                        !filter.criteria.format) )
1173            {
1174              var newResult = [];
1175           
1176              $.each( result, function( i, res ){
1177                        newResult[ i ] = $.extend( {}, res );
1178                  });
1179
1180              this.put( concept, id, newResult, false );
1181            }
1182        }
1183
1184        if( /*result &&*/ encoder )
1185            result = this.encode( encoder, result, filter ); //TODO: retirar o filtro no método encode
1186
1187        return( result );
1188    },
1189   
1190    filter: function( base, filter, criteria ){
1191     
1192        if( !$.isArray( filter || [] ) )
1193            filter = filter.filter || false;
1194
1195        if( !filter )
1196            return( base );
1197
1198        var filtered = [];
1199     
1200        for( var key in base )
1201            if( this.storage.filter( base[key], filter ) )
1202                filtered[ filtered.length ] = key;
1203
1204        return( filtered );
1205    },
1206
1207        converterType: function( filter ){
1208
1209                        return isNaN(parseInt(filter)) ? filter : parseInt(filter);
1210               
1211        },
1212
1213   
1214    compare: function( operator, base, test ){
1215     
1216        base = this.converterType(base);
1217        test = this.converterType(test);
1218         
1219    switch( operator ){
1220       
1221        case '*':  return RegExp( ".*" + test + ".*" ).test( base );
1222        case '^':  return RegExp( "^" + test +  ".*" ).test( base );
1223        case '$':  return RegExp( ".*"  + test + "$" ).test( base );
1224
1225        case '&':  return ( base && test );
1226        case '|':  return ( base || test );
1227
1228        case '=':  return ( base == test );
1229        case '<=': return ( base <= test );
1230        case '>=': return ( base >= test );
1231        case '>':  return ( base <  test );
1232        case '<':  return ( base >  test );
1233       
1234        default: return true;
1235        }
1236     
1237    },
1238   
1239//     clone: function( object ){
1240//
1241//      new { prototype: object };
1242//
1243//     },
1244
1245    check: function( namespace, filter ){
1246
1247        if( !namespace )
1248            return( false );
1249
1250        var result = this.storage.get( namespace );
1251
1252        if( !filter || !result )
1253          return( result || false );
1254
1255        var keys = DataLayer.copy( filter );
1256
1257        if( notArray = $.type(keys) === "string" )
1258            keys = [ keys ];
1259        else if( $.type(keys) !== "array" )
1260                keys = this.filter( result, keys.filter, keys.criteria );
1261
1262        var res = [];
1263
1264        for( var i = 0; i < keys.length; i++ )
1265            res[ res.length ] = result[keys[i]];
1266
1267        return( notArray ? res[0] || false : res.length ? res : false );
1268    },
1269
1270    storage: {
1271     
1272        cache: {},
1273     
1274        set: function( key, value ){
1275
1276            this.cache[key] = value;
1277
1278        },
1279        get: function( key ){
1280
1281            return DataLayer.copy( this.cache[key] );
1282
1283        },
1284        del: function( key ){
1285
1286            delete this.cache[key];
1287
1288        },
1289       
1290        filter: function( base, filter ){
1291     
1292            var bool, op = filter.shift();
1293
1294            switch( op )
1295            {
1296                case 'IN':
1297                  for( var i = 0, f = []; i < filter[1].length || !(filter = f); i++ )
1298                      f[i] = [ '=', filter[0], filter[1][i] ];
1299                case 'OR':
1300                    op = '|';
1301                    bool = false;
1302                break;
1303                case 'AND':
1304                    op = '&';
1305                    bool = true;
1306                break;
1307                default : return DataLayer.compare( op, base[ filter[0] ], filter[1] );
1308            }
1309           
1310            for( var strict = bool;
1311
1312                filter.length && ( strict ? bool : !bool );
1313           
1314                bool = DataLayer.compare( op, bool, this.filter( base, filter.shift() ) ) );
1315
1316            return( bool );
1317        }
1318    },
1319
1320    flush: function(){
1321
1322    },
1323   
1324    restore: function(){
1325     
1326    },
1327
1328    store: function( namespace, key, data ){
1329
1330        if( !data )
1331          return this.storage.set( namespace, key );
1332
1333        var res = this.check( namespace ) || {};
1334
1335        res[key] = data;
1336
1337        return this.storage.set( namespace, res );
1338    },
1339
1340    del: function( namespace, key ){
1341     
1342        if( !key )
1343          return this.storage.del( namespace );
1344
1345        var res = this.check( namespace ) || {};
1346
1347        delete res[key];
1348
1349        return this.storage.set( namespace, res );
1350     
1351    },
1352   
1353     move: function( concept, oldId, newId ){
1354
1355        this.put( concept, newId, this.check( concept, oldId ), false );
1356
1357        this.remove( concept, oldId, false );
1358    },
1359   
1360
1361   
1362   
1363   
1364    request: function( concept, filter, criteria ){
1365
1366      var id = false, criteria = criteria || {};
1367
1368      if( $.type(filter) === "string" )
1369      {
1370          id = filter;
1371          filter = false;
1372      }
1373
1374      return this.dispatch( "request", {
1375
1376          concept: concept || '',
1377          id: id || '',
1378          filter: filter || '',
1379          criteria: criteria || '',
1380          service: criteria.service || '',
1381          properties: criteria.properties || ''
1382
1383      } );
1384    },
1385
1386   
1387    //         sync: function( data, callback ){
1388//
1389//      if( !data || $.isEmptyObject( data ) )
1390//          return;
1391//       
1392//      this.send( "Sync", data, function( data, status, jqXHR ){
1393//
1394// //       switch( status )
1395// //       {
1396// //         case "error":
1397// //         case "parsererror":
1398// //           return DataLayer.rollback( concept, URI );
1399// //         case "success":
1400// //           return DataLayer.commit();
1401// //         case "timeout":
1402// //         case "notmodified":
1403// //       }
1404//
1405//          if( callback )
1406//          {
1407//              var result = callback( data, status, jqXHR );
1408//
1409//              if( result === false )
1410//                  return;
1411//              else if( typeof result != "undefined" )
1412//                  data = result;
1413//          }
1414//
1415//          for( var URI in data )
1416//          {
1417//              var parsed = DataLayer.parseURI( URI ),
1418//   
1419//              concept = parsed[1], /*URI = parsed[3],*/
1420//
1421//              links = DataLayer.links( concept );
1422//
1423//              for( var linkName in links )
1424//              {
1425//                  var subURI = data[URI][linkName];
1426//
1427//                  if( subURI && data[subURI] )
1428//                  {
1429//                      data[URI][linkName] = DataLayer.put( linkName, subURI, data[subURI], false );
1430//
1431//                      delete( data[subURI] );
1432//                  }
1433//              }
1434//
1435//              DataLayer.put( concept, URI, data[URI], false );
1436//          }
1437//      }, true );
1438//
1439//     },
1440
1441//     report: function( concept, URI, data, sync )
1442//     {
1443//      var current = this.dequeue( 'current', concept, URI );
1444//
1445//      if( !current )
1446//          this.enqueue( 'current', concept, URI, ( current = this.check( concept, URI ) || {} ) );
1447//
1448//      var diff = this.diff( current, data );
1449//
1450//      if( !diff )
1451//          this.dequeue( 'current', concept, URI, true );
1452//      else
1453//          this.enqueue( 'diff', concept, URI, diff );
1454//     
1455//      if( sync )
1456//          this.commit( concept, URI, function(){
1457//
1458//              DataLayer.set( concept, URI, data, false );
1459//
1460//          });
1461//     },
1462   
1463//     enqueue: function( type, concept, URI, obj ){
1464//       
1465//      //var newURI = this.URI( concept, URI );
1466//     
1467//      if( !this.queue[type] )
1468//          this.queue[type] = {};
1469//
1470//      if( !this.queue['all'] )
1471//          this.queue['all'] = {};
1472//     
1473//      if( !this.queue[type][concept] )
1474//          this.queue[type][concept] = {};
1475//     
1476//      if( !this.queue['all'][type] )
1477//          this.queue['all'][type] = {};
1478//     
1479//      if( !this.queue['all'][type][/*new*/URI] )
1480//          this.queue[type][concept][URI] = this.queue['all'][type][/*new*/URI] = obj;
1481//
1482//      this.store( ':queue', this.queue );
1483//     },
1484//     
1485//     dequeue: function( type, concept, URI, remove ){
1486//       
1487//       ///////////////////////////// normalize ////////////////////////////////
1488//      if( arguments.length < 4 && $.type(URI) === 'boolean' )
1489//      {
1490//          remove = URI;
1491//          URI = false;
1492//      }
1493//      if( arguments.length < 3 && $.type(concept) === 'boolean' )
1494//      {
1495//          remove = concept;
1496//          concept = false;
1497//      }
1498//       //////////////////////////////////////////////////////////////////////////
1499//       
1500//      if( !this.queue[type] || !this.queue['all'] )
1501//          return( false );
1502//     
1503//      if( !concept )
1504//      {
1505//          var obj = this.queue['all'][type];
1506//         
1507//          if( remove )
1508//          {
1509//              delete this.queue['all'][type];
1510//              delete this.queue[type];
1511//          }
1512//
1513//          this.store( ':queue', this.queue );
1514//          return( obj );
1515//      }
1516//
1517//      if( !this.queue[type][concept] )
1518//          return( false );
1519//     
1520//      if( !URI )
1521//      {
1522//          var obj = this.queue[type][concept];
1523//
1524//          if( remove )
1525//          {
1526//              var URIs = this.queue[type][concept];
1527//
1528//              for( var subURI in URIs )
1529//                   delete this.queue['all'][type][subURI];
1530//
1531//              delete this.queue[type][concept];
1532//          }
1533//
1534//          this.store( ':queue', this.queue );
1535//          return( obj );
1536//      }
1537//
1538// //   var newURI = URI ? this.URI( concept, URI ) : concept;
1539//     
1540//      var obj = this.queue['all'][type][/*new*/URI];
1541//   
1542//      if( remove )
1543//      {
1544//          delete this.queue['all'][type][/*new*/URI];
1545//          delete this.queue[type][concept][URI];
1546//      }
1547//
1548//      this.store( ':queue', this.queue );
1549//      return( obj );
1550//     },
1551   
1552           //TODO: definir a 'usage' desta função e refatora-la
1553//     set: function( concept, filter, data, oneSide ){
1554//
1555//      ///////////////////////////// normalize ////////////////////////////////
1556//      if( arguments.length == 2 )
1557//      {
1558//          data = filter;
1559//          filter = false;
1560//      }
1561//      if( $.type(data) === "boolean" )
1562//      {
1563//          oneSide = data;
1564//          data = filter;
1565//          filter = false;
1566//      }
1567//     
1568//      if( !concept || !data )
1569//          return( false );
1570//
1571//      var decoder = "", URI = false, bothSides = (typeof oneSide === "undefined");
1572//     
1573//      if( $.type(filter) === "string" )
1574//      {
1575//          URI = filter;
1576//          filter = false;
1577//      }
1578//
1579//      if( res = internalUrl.exec( concept ) )
1580//      {
1581//          //TODO: verificar se a decodificaçao deve ser feita em cada item do array
1582//          data = this.decode( concept, data );
1583//          concept = res[1];
1584//          decoder = res[2];
1585//      }
1586//      ///////////////////////////////////////////////////////////////////////////
1587//
1588//      if( bothSides || oneSide )
1589//          this.report( concept, URI, data, !bothSides );
1590//
1591//      if( bothSides || !oneSide )
1592//      {
1593//          if( URI )
1594//          {
1595//            var helper = {};
1596//            helper[URI] = data;
1597//            data = helper;
1598//          }
1599//
1600//          for( var URI in data )
1601//          {
1602//              var current = this.check( concept, URI ) || {};
1603//
1604//              data[URI] = this.merge( current, data[URI] );
1605//
1606//              this.store( concept, URI, data[URI] );
1607//          }
1608//
1609//      }
1610//
1611//      this.broadcast( concept, oneSide ? 'client' : 'server' );
1612//
1613//      return( true );
1614//     },
1615//     put: function( concept, URI, data, oneSide ){
1616//       
1617//       ///////////////////////////// normalize ////////////////////////////////
1618//      if( $.type(URI) !== "string" && arguments.length < 4 )
1619//      {
1620//          oneSide = data;
1621//          data = URI;
1622//          URI = false;
1623//      }
1624//       ////////////////////////////////////////////////////////////////////////
1625//       
1626//       ////////////////////////////// linkage /////////////////////////////////
1627//      var result = false, links = this.links( concept );
1628//
1629//      for( var link in links )
1630//      {
1631//          if( data[link] )
1632//          {
1633//              if( $.isArray( data[link] ) )
1634//              {
1635//                  data[link] = this.put( links[link], data[link].URI, data[link], oneSide );
1636//              }
1637//              else if( $.isObject( data[link] ) )
1638//              {
1639//                  $.each( data[link], function( i, el ){
1640//
1641//                        data[link][i] = this.put( links[link], el.URI, el, oneSide );
1642//
1643//                  });
1644//              }
1645//          }
1646//      }
1647//       //////////////////////////////////////////////////////////////////////////
1648//     
1649//      if( typeof data.URI === "undefined" )
1650//      {
1651//          URI = this.add( concept, data, oneSide );
1652//      }
1653//      else if( data.URI === false )
1654//      {
1655//          status = this.remove( concept, URI, oneSide );
1656//      }
1657//      else
1658//      {
1659//          status = this.set( concept, URI, data, oneSide );
1660//      }
1661//
1662//      if( URI && data.URI && URI !== data.URI )
1663//          this.move( concept, URI, data.URI );
1664//
1665//      return( data.URI || URI );
1666//
1667//     },
1668   
1669    //     add: function( concept, data, oneSide ){
1670//       
1671//       ///////////////////////////// normalize ////////////////////////////////
1672//      if( !concept || !data )
1673//          return( false );
1674//
1675//      if( res = internalUrl.exec( concept ) )
1676//      {
1677//          //TODO: verificar se a decodificaᅵᅵo deve ser feita em cada item do array
1678//          data = this.decode( concept, data );
1679//          concept = res[1];
1680//          decoder = res[2];
1681//      }
1682//
1683//      var bothSides = (typeof oneSide === "undefined"), uris = [];
1684//
1685//      if( notArray = $.type(data) !== "array" )
1686//          data = [ data ];
1687//       //////////////////////////////////////////////////////////////////////////
1688//
1689//      for( var i = 0; i < data.length; i++ )
1690//      {
1691//          var URI = uris[i] = this.generateURI( concept );
1692//
1693//          this.set( concept, URI, data[i], oneSide );
1694//      }
1695//
1696//      return( notArray ? uris[0] : uris );
1697//     },
1698//      put: function( concept, data ){
1699//
1700//      var decoder = "";
1701//
1702//      if( res = internalUrl.exec( concept ) )
1703//      {
1704//          data = this.decode( concept, data );
1705//          concept = res[1];
1706//          decoder = res[2];
1707//      }
1708//
1709//      var New = [], Update = [], uris = [];
1710//
1711//      if( notArray = $.type(data) !== "array" )
1712//          data = [ data ];
1713//     
1714//      for( var i = 0; i < data.length; i++ )
1715//      {
1716//          if( !data[i].URI )
1717//          {
1718//              uris[ uris.length ] = data[i].URI = this.create( concept, data[i] );
1719//              New[ New.length ] = data[i];
1720//              continue;
1721//          }
1722//
1723//          for( var key in data[i] )
1724//              if( klass = this.isReference( concept, key, data[i][key] ) )
1725//                    data[i][key] = this.put( klass + decoder, data[i][key] );
1726//
1727//          Update[ Update.length ] = this.update( concept, data[i].URI, data[i] );
1728//      }
1729//
1730//      this.report( concept, { "created": New, "updated": Update });
1731//
1732//      return( notArray ? uris[0] : uris );
1733//     },
1734//     merge: function( concept, current, data ){
1735//
1736//      current = current || {};
1737//
1738//      for( var key in data )
1739//          current[key] = (klass = this.isReference( concept, key, data[key] )) ?
1740//                         this.merge( klass, current[key], data[key] ) : data[key];
1741//
1742//      return( current );
1743//     },
1744//
1745//     isReference: function( concept, key, value ){
1746//
1747//       return( ($.type(value) === "object" ||
1748//             $.type(value) === "array" )? this.links[concept][key] : false );
1749//
1750//     },
1751//     
1752//     set: function( concept, data, URI, mergeable ){
1753//     
1754//      if( URI )
1755//      {
1756//          var res = this.get( concept, true ) || {};
1757//         
1758//          if( mergeable )
1759//              data = this.merge( res[URI] || {}, data );
1760//
1761//          res[URI] = data;
1762//
1763//          data = res;
1764//      }
1765//
1766//      return this.store( concept, data );
1767//     },   
1768//
1769//     create: function( concept, data ){
1770//
1771//       if( notArray = ($.type(data) !== "array") )
1772//          data = [ data ];
1773//
1774//       var uris = [];
1775//
1776//       for( var i = 0; i < data.length; i++ )
1777//       {
1778//        uris[ uris.length ] = data[i].URI = "javascript://" + (this.counter + i);
1779//
1780//        this.set( concept, data[i], data[i].URI );
1781//       }
1782// 
1783//       this.set( ":counter", (this.counter += data.length) );
1784//
1785//       return notArray ? uris[0] : uris;
1786//     },
1787//
1788//     update: function( concept, URI, data )
1789//     {
1790//      var target = this.check( concept, URI ) || {};
1791//
1792//      target = this.merge( concept, target, data );
1793//
1794//      if( target.URI !== URI )
1795//          this.remove( concept, URI );
1796//
1797//      this.set( concept, target, target.URI );
1798//
1799//      return( target );
1800//     },
1801//
1802//     remove: function( concept, URI ){
1803//
1804//      if( !URI )
1805//          return this.storage.del( concept );
1806//
1807//      var res = this.check( concept );
1808//
1809//      delete res[URI];
1810//     
1811//      this.set( concept, res );
1812//     },
1813//
1814//     del: function( concept, URI ){
1815//
1816//      this.remove( concept, URI );
1817//
1818//      this.report( concept, { "deleted": { 'URI': URI } });
1819//     },
1820//
1821//     report: function( concept, changes ){
1822//
1823//       this.broadcast( concept, changes.created, changes.updated, changes.deleted );
1824//
1825//      if( changes.created )
1826//          this.sync( concept, changes.created, 'create' );
1827//      if( changes.updated )
1828//          this.sync( concept, changes.updated, 'update' );
1829//      if( changes.deleted )
1830//          this.sync( concept, changes.deleted, 'delete' );
1831//
1832//     },
1833//
1834//
1835//    sync: function( concept, data, type ){
1836//
1837//      if( $.type(data) !== "array" )
1838//          data = [ data ];
1839//
1840//      $.each( data, function( i, el ){
1841//
1842//         DataLayer.send( concept, el, type );
1843//
1844//      });
1845//
1846//     },
1847//     
1848//     
1849//     
1850//     
1851//
1852//     request: function( concept, URI, filter ){
1853//
1854// //       if( startsDoubleDot.test(concept) )
1855// //     return( false );
1856//
1857//       filter = filter || {};
1858//
1859//       if( URI )
1860//      filter.URI = URI;
1861//
1862//       return this.send( concept, filter, "read", true );
1863//
1864//     },
1865//
1866//     send: function( concept, data, type, wait ){
1867//
1868//       switch( type )
1869//       {
1870//      case "create": type = "POST"; break;
1871//      case "update": type = "PUT"; break;
1872//      case "delete": type = "DELETE"; break;
1873//      case "read": type = "GET"; break;
1874//       }
1875//
1876//       var url = this.basePath + concept;
1877//
1878//       var result = [], notArray = false;
1879//
1880// //      alert( data.URI );
1881//
1882//       if( data.URI && data.URI.indexOf("javascript://") !== 0 )
1883//        url += "/" + data.URI;
1884//
1885//       var callback = function( dt, textStatus, jqXHR ){
1886//
1887//          if( notArray = (!$.isArray( dt )) )
1888//              dt = [ dt ];
1889//
1890//          $.each( dt, function( i, el ){
1891//
1892//              if( !el || !el.URI )
1893//                  return;
1894//
1895//              if( data.URI )
1896//                  el = DataLayer.update( concept, data.URI, el );
1897//              else
1898//                  DataLayer.set( concept, el, el.URI );
1899//
1900//              result[ result.length ] = el;
1901//              DataLayer.broadcast( concept );
1902//        });
1903//       };
1904//
1905//       $.ajax({
1906//            'async': ( !wait ),
1907//            'url': url,
1908//            'type': type,
1909//            'success': callback,
1910//            'dataType': 'json',
1911//            'data': data/*,
1912//            'processData': false*/
1913//        });
1914//
1915//       return( notArray ? result[0] || false : result );
1916//     },
1917   
1918   
1919    generateURI: function( concept ){
1920     
1921        return this.URI( concept, this.generateId( concept ), "javascript" );
1922
1923    },
1924   
1925
1926    broadcast: function( concept, status, diff ){
1927
1928        if( this.listeners[ concept ] )
1929            for( var i = 0;
1930                i < this.listeners[ concept ].length;
1931                this.listeners[ concept ][ i++ ]( status, diff ) );
1932    },
1933
1934    listen: function( concept, listener ){
1935
1936        this.register( "listeners", concept, listener );
1937
1938    },
1939
1940    codec: function( concept, namespace, codec ){
1941
1942        if( codec.encoder )
1943            this.encoder( concept, namespace, codec.encoder );
1944        if( codec.decoder )
1945            this.decoder( concept, namespace, codec.decoder );
1946        if( codec.criteria )
1947            this.register( "criterias", concept + ":" + namespace, codec.criteria );
1948
1949    },
1950
1951    encoder: function( concept, namespace, encoder ){
1952
1953        this.register( "encoders", concept + ":" + namespace, encoder );
1954
1955    },
1956
1957    encode: function( encoder, data, filter ){
1958
1959        if( this.encoders[ encoder ] )
1960            for( var i = 0;
1961                i < this.encoders[ encoder ].length;
1962                data = this.encoders[ encoder ][ i++ ]( data, filter ) );
1963
1964        return( data );
1965    },
1966
1967    decoder: function( concept, namespace, decoder ){
1968
1969        this.register( "decoders", concept + ":" + namespace, decoder );
1970
1971    },
1972
1973    decode: function( decoder, data ){
1974
1975        if( this.decoders[ decoder ] )
1976            for( var i = 0;
1977                i < this.decoders[ decoder ].length;
1978                data = this.decoders[ decoder ][ i++ ]( data ) );
1979
1980        return( data );
1981    },
1982
1983    criteria: function( codec, filter ){
1984
1985        if( this.criterias[ codec ] )
1986            for( var i = 0;
1987                i < this.criterias[ codec ].length;
1988                filter = this.criterias[ codec ][ i++ ]( filter ) );
1989
1990        return( filter );
1991
1992    },
1993
1994    register: function( kind, concept, deployable ){
1995
1996      if( arguments.length < 3 )
1997      {
1998          deployable = concept;
1999          concept = kind;
2000          kind = 'global';
2001      }
2002
2003      if( !this[ kind ][ concept ] )
2004            this[ kind ][ concept ] = [];
2005
2006        this[ kind ][ concept ][ this[ kind ][ concept ].length ] = deployable;
2007
2008    },
2009   
2010    start: function(){
2011
2012        var timer = function(){
2013
2014              setTimeout( timer, 1 );
2015
2016              var now = parseInt( $.now() / 1000 );
2017
2018              var tasks = DataLayer.tasks[ now ];
2019
2020              if( !tasks ) return;
2021
2022              for( var i = 0; i < tasks.length; i++ )
2023              {
2024                  var result = tasks[i].task( now );
2025
2026                  if( tasks[i].factor )
2027                  DataLayer.schedule( tasks[i].task, tasks[i].factor );
2028              }
2029     
2030              delete DataLayer.tasks[ now ];
2031        };
2032
2033        setTimeout( timer, 1 );
2034    },
2035   
2036    task: function( timestamp, task, factor )
2037    {
2038        if( !this.tasks[ timestamp ] )
2039            this.tasks[ timestamp ] = [];
2040
2041        this.tasks[ timestamp ][ this.tasks[ timestamp ].length ] = { task: task, factor: factor || false };
2042    },
2043
2044    schedule: function( task, time ){
2045
2046        time = time || 1;
2047       
2048        var index = parseInt( $.now() / 1000 ) + time;
2049
2050        this.task( index, task, time );
2051    },
2052   
2053    poll: function( concept, time ){
2054     
2055      this.schedule( function( now ){
2056 
2057          DataLayer.commit( concept );
2058
2059        }, time || 5 );
2060    },
2061   
2062    init: function(){
2063     
2064        this.counter = parseInt( this.get( ":counter", false ) ) || 0;
2065
2066        if( !this.dispatchPath )
2067            this.dispatchPath = "prototype/";
2068
2069        if( !this.templatePath )
2070            this.templatePath = "";
2071
2072        if( !this.basePath )
2073            this.basePath = this.dispatchPath + "REST.php?q=";
2074
2075        this.schedule( function( now ){
2076
2077            DataLayer.flush();
2078
2079        });
2080
2081        this.start();
2082        this.me = this.dispatch( "me" );
2083    }
2084}
2085
2086// the re-usable constructor function used by clone().
2087function Clone() {}
2088
2089//Recursion Helper
2090function RecursionHelper(){ this.clear(); };
2091
2092RecursionHelper.prototype = {
2093 
2094        constructor: RecursionHelper,
2095
2096        // copiedObjects keeps track of objects already copied by this
2097        // deepCopy operation, so we can correctly handle cyclic references.
2098        copiedObjects: [],
2099
2100        depth: 0,
2101
2102        maxDepth: 256,
2103
2104        //reset the recursion helper cache
2105        clear: function(){
2106                this.copiedObjects = [];
2107                this.depth = 0;
2108        },
2109
2110        // add an object to the cache.  No attempt is made to filter duplicates;
2111        // we always check getCachedResult() before calling it.
2112        cacheResult: function(source, result) {
2113                this.copiedObjects.push([source, result]);
2114        },
2115
2116        // Returns the cached copy of a given object, or undefined if it's an
2117        // object we haven't seen before.
2118        getCachedResult: function(source) {
2119
2120                for ( var i=0; i<this.copiedObjects.length; i++ ) {
2121                        if ( this.copiedObjects[i][0] === source ) {
2122                                return this.copiedObjects[i][1];
2123                        }
2124                }
2125
2126                return undefined;
2127        }
2128};
2129
2130// Generic Object copier
2131// the ultimate fallback DeepCopier, which tries to handle the generic case.  This
2132// should work for base Objects and many user-defined classes.
2133DataLayer.registerComparator({
2134        can: function(source) { return true; },
2135
2136        create: function(source) {
2137                if ( source instanceof source.constructor ) {
2138                        return DataLayer.clone(source.constructor.prototype);
2139                } else {
2140                        return {};
2141                }
2142        },
2143
2144        populate: function(deepCopy, source, result) {
2145                for ( var key in source ) {
2146                        if ( source.hasOwnProperty(key) ) {
2147                                result[key] = deepCopy(source[key], result[key]);
2148                        }
2149                }
2150                return result;
2151        }
2152});
2153
2154// Array copier
2155DataLayer.registerComparator({
2156        can: function(source) {
2157                return ( source instanceof Array );
2158        },
2159
2160        create: function(source) {
2161                return new source.constructor();
2162        },
2163
2164        populate: function(deepCopy, source, result) {
2165                for ( var i=0; i<source.length; i++) {
2166                        result.push( deepCopy(source[i], result[i]) );
2167                }
2168                result =  DataLayer.unique( result )
2169                return result;
2170        }
2171});
2172
2173// Date copier
2174DataLayer.registerComparator({
2175        can: function(source) {
2176                return ( source instanceof Date );
2177        },
2178
2179        create: function(source) {
2180                return new Date(source);
2181        }
2182});
2183
2184// HTML DOM Node copier
2185DataLayer.registerComparator({
2186
2187        // function to detect Nodes.  In particular, we're looking
2188        // for the cloneNode method.  The global document is also defined to
2189        // be a Node, but is a special case in many ways.
2190        can: function(source) {
2191         
2192          if ( window.Node ) {
2193                  return source instanceof Node;
2194          } else {
2195                  // the document is a special Node and doesn't have many of
2196                  // the common properties so we use an identity check instead.
2197                  if ( source === document ) return true;
2198                  return (
2199                          typeof source.nodeType === 'number' &&
2200                          source.attributes &&
2201                          source.childNodes &&
2202                          source.cloneNode
2203                  );
2204          }
2205      },
2206
2207      create: function(source) {
2208              // there can only be one (document).
2209              if ( source === document ) return document;
2210
2211              // start with a shallow copy.  We'll handle the deep copy of
2212              // its children ourselves.
2213              return source.cloneNode(false);
2214      },
2215     
2216      diff: function(base, source){
2217       
2218      },
2219
2220      populate: function(deepCopy, source, result) {
2221              // we're not copying the global document, so don't have to populate it either.
2222              if ( source === document ) return document;
2223
2224              // if this Node has children, deep copy them one-by-one.
2225              if ( source.childNodes && source.childNodes.length ) {
2226                      for ( var i=0; i<source.childNodes.length; i++ ) {
2227                              var childCopy = deepCopy(source.childNodes[i], result.childNodes[i] || false );
2228                              result.appendChild(childCopy);
2229                      }
2230              }
2231                return result;
2232      }
2233});
2234
2235DataLayer.init();
2236
2237// setTimeout(function(){
2238// 
2239//     
2240//
2241// }, 1000 );
2242
2243// var DataLayer = {
2244//
2245//     get: function( concept, filter ){
2246//
2247//      var data = this.storage.get( concept );
2248//
2249//      if( !filter )
2250//          return( data );
2251//
2252//      return this.filter( data, filter );
2253//     },
2254//     
2255//     filter:function( data, filter ){
2256//       
2257//      if( filter.charAt )
2258//          filter = { URI: filter };
2259//     
2260//      var filtered = [];
2261//
2262//      $.each(data, function(i, obj){
2263//       
2264//          for( var attr in filter )
2265//              if( filter[attr] !== obj[attr] )
2266//                  return( true );
2267//
2268//          filtered[i] = obj;
2269//      });
2270//
2271//      return( filtered );
2272//     },
2273//
2274//     find: function( concept, filter, callback ){
2275//
2276//      var data = this.get( concept, filter );
2277//
2278//      if( data )
2279//          return callback( data );
2280//
2281//      //TODO: register callback like a weak listener
2282//
2283// //   $.ajax({
2284// //         type: 'GET',
2285// //         data: $.param( filter ),
2286// //         success: callback, 
2287// //         url: BASE_PATH + filter.URI || concept
2288// //   });
2289//      this.report( concept, filter );
2290//     },
2291//
2292//     put: function( concept, data, filter ){
2293//
2294//      var beforeDiff = this.store( concept, $.extend({},data) );
2295//
2296//      this.report( concept, data, filter, beforeDiff );
2297//     },
2298//     
2299//     
2300//     /*var data = {
2301//                      startTime: $.now(),
2302//                      endTime: $.now() + 1800000,
2303//                      summary: "meu querido evento",
2304//                      description: "desc do evento",
2305//                      location: "prognus software livre",
2306//                      class: 1,
2307//                      calendar: 1,
2308//                      category: 1,
2309//                      participants: [ {
2310//                                         user: { isExternal: true, mail: "user7@prognus.org", name: "user7" }
2311//                                    },{
2312//                                         user: "1003"
2313//                                    } ]
2314//
2315//                };*/
2316//     
2317//
2318//     merge:function( data, target ){
2319//       
2320//      var diff = { New: {}, Update:{}, Delete: {} };
2321//       
2322//      for( var key in data )
2323//      {
2324//          if( !target[ key ] )
2325//              diff.New[ key ] = target[ key ] = data[ key ];
2326//
2327//         
2328//       
2329//      }
2330//       
2331//     }
2332//
2333//     store: function( concept, data, filter ){
2334//
2335//      if( !data.spline )
2336//          data = [ data ];
2337//
2338//      var target = this.storage.get( concept );
2339//     
2340//      var diff = { New: {}, Update:{}, Delete: {} };
2341//
2342//      for( var i = 0; i < data.length; i++ )
2343//      {
2344//          if( data[i].URI && target[ data[i].URI ] )
2345//          {
2346//              diff.Update[ data[i].URI ] = this.merge( target[ data[i].URI ], data[i] );
2347//          }
2348//          else
2349//          {
2350//              diff.New[] = data[i];
2351//          }
2352//      }
2353//
2354//     
2355//
2356//      this.broadcast( concept, data );
2357//
2358//      if( filter )
2359//          target = this.filter( target, filter );
2360//
2361//      if( target )
2362//          data = $.extend( target, data );
2363//
2364//      this.storage.set( concept, data );
2365//
2366// //   return;
2367//     },
2368//     
2369//     set: function( concept, data, filter ){
2370//
2371//       
2372//
2373//     },
2374//
2375//     post: function( concept, data, filter, isNew ){
2376//
2377//      var callback = function(  ){ DataLayer.store( concept, data, filter ) };
2378//
2379//      //TODO: register callback like a weak listener
2380//
2381//      this.report( concept, data, filter, isNew );
2382//     },
2383//     
2384//     report: function( concept, filter, postData, isNew ){
2385//       
2386//      $.ajax({
2387//              type: postData ? isNew ? 'POST' : 'PUT' : 'GET',
2388//              data: postData || $.param( filter ),
2389//              success: function( data ){ DataLayer.broadcast( concept ) },
2390//              url: BASE_PATH + filter.URI || concept
2391//        });
2392//     },
2393//     
2394//     del:function( concept, filter ){
2395//
2396//       
2397//
2398//     }
2399//     
2400//     broadcast: function( concept, data ){
2401//
2402//     
2403//
2404//     },
2405//
2406//     pool: function(){
2407//       
2408//     },
2409//
2410//     refresh: function(){
2411//       
2412//     }
2413// };
2414
2415//
2416// DataLayer = {
2417//   
2418//     get: function( concept, filter ){
2419//
2420//      var data = this.storage.get( concept );
2421//
2422//      if( !filter )
2423//          return( data );
2424//
2425//      if( filter.charAt )
2426//          filter = { URI: filter };
2427//     
2428//      var filtered = [];
2429//
2430//      $.each(data, function(i, obj){
2431//       
2432//          for( var attr in filter )
2433//              if( filter[attr] !== obj[attr] )
2434//                  return( true );
2435//
2436//          filtered[i] = obj;
2437//      });
2438//
2439//      return( filtered );
2440//     },
2441//
2442//     find: function( concept, filter, callback ){
2443//
2444//      var data = this.get( concept, filter );
2445//
2446//      if( data )
2447//          return callback( data );
2448//
2449//       $.ajax({
2450//            type: 'GET',
2451//            data: $.param( filter ),
2452//            success: callback, 
2453//            url: filter.URI || concept
2454//      });
2455//     },
2456//
2457//     put: function( concept, data, filter ){
2458//
2459//      var target = this.get( concept, filter );
2460//
2461//      if( target )
2462//          data = $.extend( target, data );
2463//       
2464//      this.storage.set( concept, data );
2465//     
2466//      //diff
2467//     },
2468//     
2469//     post: function( concept, data, filter ){
2470//
2471//     
2472//
2473//     },
2474//     
2475//     pool: function(){
2476//       
2477//     },
2478//     
2479//     queue: function(){
2480//       
2481//     },
2482//     
2483//     dequeue: function(){
2484//       
2485//     },
2486//     
2487//     refresh: function(){
2488//       
2489//     }
2490// }
2491
2492// var DataLayer = {
2493 
2494//       cache: {},
2495 
2496//       get: function( concept, location ){
2497       
2498           /* if( location )
2499            {*/
2500//              var schema = $.data( this.cache, concept + ':schema' );
2501//              var uri = [];
2502//
2503//              $.each( schema, function( i, addrs ){
2504//                    uri[ uri.length ] = location[addrs];
2505//              });
2506
2507                /*var filter = [], result = false;
2508
2509                while( !(result = $.data( this.cache, uri.join( '.' ) )) || !(uri = uri.join('.')) )
2510                  filter[ filter.length ] = uri.pop();
2511 
2512                if( !filter.length )
2513                {
2514                    var indexes = $.data( this.cache, uri + ':indexes' );
2515
2516                    if( indexes )
2517                        Array.prototype.concat.apply( result, indexes );
2518                   
2519                    return( result );
2520                }
2521
2522                for( var i = 0; i < result.length; i++ )
2523                {
2524                   
2525                }
2526
2527                if( result.length )
2528                    return( result );
2529            }*/
2530
2531//          var data = $.data( this.cache, concept );
2532
2533//          if( !data )
2534//              $.ajax( );
2535
2536//          return( data );
2537//       },
2538     
2539//       data: function(){
2540//
2541//       
2542//
2543//       }
2544//       
2545//       search: function( concept, filter ){
2546//
2547//        var schema = $.data( this.cache, concept + ':schema' );
2548//        var uri = [];
2549//
2550//        $.each( schema, function( i, addrs ){
2551//              uri[ uri.length ] = location[addrs];
2552//        });
2553//       }
2554//       put: function( concept, data, location ){
2555
2556//          if( location )
2557//          {
2558//              var schema = $.data( this.cache, concept + ':schema');
2559//              var uri = [];
2560
2561//              $.each( schema, function( i, addrs ){
2562//                    uri[ uri.length ] = location[addrs];
2563//              });
2564
2565//              var result = false, filter = [];
2566
2567//              while( !(result = $.data( this.cache, uri.join('.')) )
2568//                  filter[ filter.length ] = uri.pop();
2569
2570//              $.data( this.cache, '
2571
2572//          }
2573
2574//              var model = this.storage.get( concept );
2575//
2576//              $.each( model, function( i, o ){
2577//                  $.each( location, function( ii, attr ){
2578//                       if( o[ii] === attr )
2579//                          return( false );
2580//                  });
2581//              });
2582
2583//          return $.data( this.cache, concept, data );
2584
2585//       },
2586//       del: function( concept, location ){
2587//
2588//          if( location )
2589//          {
2590//              var schema = $.data( this.cache, 'concepts', concept );
2591//              var uri = [];
2592//
2593//              $.each( schema, function( i, addrs ){
2594//                    uri[ uri.length ] = location[addrs];
2595//              });
2596//
2597//              concept = uri.join( '.' );
2598
2599//              var model = this.storage.get( concept );
2600//
2601//              $.each( model, function( i, o ){
2602//                  $.each( location, function( ii, attr ){
2603//                       if( o[ii] === attr )
2604//                          return( false );
2605//                  });
2606//              });
2607//          }
2608//         
2609//     
2610//          $.removeData( this.cache, concept );
2611//       }
2612// }
2613
2614// internalUrl = /^([A-z0-9-_]+)(:[A-z0-9-_]+)?$/;
2615// internalUri = /^([a-zA-Z0-9-_]+)\(([a-zA-Z0-9-_]+)\):\/\/(.*)|([a-zA-Z0-9-_]+):\/\/(.*)$/;
2616// isGeneratedId = /^\d+\(javascript\)$/;
2617// arrayName = /^([A-z0-9-_]+)\[\]$/;
2618// startsDoubleDot = /^:/;
2619// FILE = 'files';
2620// // cached_urls = {};
2621//
2622// $.ajaxPrefilter(function( options, originalOptions, jqXHR ){
2623//
2624//       if( options.url != 'undefined' && internalUrl.test( options.url ) ){
2625//
2626// //     if( !cached_urls[options.url] )
2627// //         return;
2628// //     alert( options.url + " dentro" );
2629//        jqXHR.abort();
2630//
2631//        var callback = ( options.success || options.complete || $.noop );
2632//
2633//        switch( options.type.toUpperCase() )
2634//        {
2635//          case 'GET':
2636//                return callback( DataLayer.get( options.url, /*false,*/ options.data ) );
2637//
2638//          case 'POST':
2639//                return callback( DataLayer.put( options.url, options.data ) );
2640//        }
2641//
2642//        //return( false );
2643//
2644// //     options.url = params[1];
2645// //     options.data = ( options.data || "" ) + "&" + params[2];
2646//       }
2647//
2648// });
2649//
2650// // $("a").live("click", function( event ){
2651// //
2652// //     event.preventDefault();
2653// //
2654// //     $.ajax({
2655// //
2656// //   
2657// //
2658// //     });
2659// //
2660// // });
2661//
2662// $("form").live( "submit", function( event ){
2663//
2664//     var $this = $(this), action = $this.attr('action'), res = false,
2665//     
2666//     method = $this.attr( 'method' ),
2667//     
2668//     fileInputs = $this.find('input[type="file"]');
2669//     
2670//     if( fileInputs.length && !$this.is('[enctype="multipart/form-data"]') )
2671//     {
2672//      event.preventDefault();
2673//     
2674//      var formData = $this.serializeArray(), callback = DataLayer.receive;
2675//       
2676//      if( res = internalUrl.exec( action ) )
2677//      {
2678//          var data = {}, action = res[1];
2679//
2680//          data[action] = DataLayer.form( this, fileInputs );
2681//
2682//          formData = DataLayer.serializeForm( data );
2683//             
2684//              action = DataLayer.dispatchPath + 'post.php';
2685//          callback = $.noop;
2686//      }
2687//
2688//      DataLayer.send( action,
2689//                      [ method, 'iframe json' ], {},
2690//                      //TODO: check the type for conversion
2691//                      callback,
2692//                      false, { 'formData': formData,  'fileInput': fileInputs, 'paramName': FILE + '[]' } );
2693//
2694//      return( false );
2695//     }
2696//     
2697//     if( res = internalUrl.exec( action ) )
2698//     {
2699//      event.preventDefault();
2700//
2701//      var data = DataLayer.form( this );
2702//     
2703//      switch( method.toUpperCase() )
2704//      {
2705//        case 'GET':
2706//              DataLayer.get( res[0], data );
2707//
2708//        case 'POST':
2709//              DataLayer.put( res[1], data );
2710//      }
2711//
2712//      return( false );
2713//     }
2714//
2715//     return( true );
2716// });
2717//
2718// this.storage = new $.store();
2719//
2720// DataLayer = {
2721//
2722//     links: {},
2723//     concepts: {},
2724//     listeners: {},
2725//     encoders: {},
2726//     decoders: {},
2727//     templates: {},
2728//     criterias: {},
2729//     tasks: [],
2730//
2731//     render: function( templateName, data, filter, formatter, force ){
2732//
2733//      if( $.isFunction( filter ) )
2734//      {
2735//          force = formatter;
2736//          formatter = filter;
2737//          filter = false;
2738//      }
2739//
2740//      if( typeof data === "string" )
2741//      {
2742//          data = this.get( data, filter, force ) || {};
2743//      }
2744//     
2745//      var formatting = function( template ){
2746//
2747//            if( template === false ) return( false );
2748//
2749//            if( template )
2750//                DataLayer.templates[ templateName ] = new EJS({ text: template, cache: false });
2751//
2752//            var html = DataLayer.templates[ templateName ].render( { data: data } );
2753//
2754//            if( !formatter )
2755//                return( html );
2756//
2757//            return formatter( html );
2758//      }
2759//
2760//      if( this.templates[ templateName ] )
2761//      {
2762//          return formatting();
2763//      }
2764//
2765//      return this.send( DataLayer.templatePath + templateName, 'get', false, formatting, !!!formatter );
2766//     },
2767//     
2768//     send: function( url, type, data, callback, sync, extraOptions ){
2769//       
2770//        var result = false, fired = false;
2771//       
2772//        var envelope = {
2773//
2774//            'async': ( typeof sync !== "undefined" ? !sync : !!callback ),
2775//            'url': url,
2776//            'success': function( dt, textStatus, jqXHR ){
2777//
2778//                  if( callback )
2779//                  {
2780//                      fired = true;
2781//                      result = callback( dt, textStatus, jqXHR );
2782//                  }
2783//                  else
2784//                      result = dt;
2785//
2786//              },
2787//            'complete': function( jqXHR, textStatus ){
2788//
2789//                if( !fired && callback )
2790//                    result = callback( false, textStatus, jqXHR );
2791//
2792//            },
2793//
2794//            'type': $.isArray( type ) ? type[0] : type,
2795//            'data': data
2796//
2797//          };
2798//
2799//        if( $.isArray( type ) && type[1] )
2800//            envelope['dataType'] = type[1];
2801//
2802//        if( extraOptions )
2803//            envelope = $.extend( envelope, extraOptions );
2804//
2805//        $.ajax( envelope );
2806//       
2807//        return( result );
2808//     },
2809//     
2810//     dispatch: function( dispatcher, data, callback, isPost, dataType ){
2811//       
2812//       return this.send( this.dispatchPath + dispatcher + ".php",
2813//                      [ ( isPost ? 'post' : 'get' ), dataType || 'json' ],
2814//                      data,
2815//                      callback );
2816//
2817// //       $.ajax({
2818// //         'async': !!callback,
2819// //         'url': this.dispatchPath + dispatcher + ".php",
2820// //         'type': ( isPost ? 'post' : 'get' ),
2821// //         'dataType': 'json',
2822// //         'data': data,
2823// //         'success': function( dt, textStatus, jqXHR ){
2824// //
2825// //               if( callback )
2826// //               {
2827// //                   fired = true;
2828// //                   callback( dt, textStatus, jqXHR );
2829// //               }
2830// //               else
2831// //                   result = dt;
2832// //
2833// //           },
2834// //         'complete': function( jqXHR, textStatus ){
2835// //
2836// //             if( !fired && callback )
2837// //                 callback( false, textStatus, jqXHR );
2838// //
2839// //         }/*,
2840// //         'processData': false*/
2841// //     });
2842//
2843//       //return( result );
2844//     },
2845//
2846//     form: function( target, fileInputs ){
2847//
2848//      var params = {}, $this = $(target), inputArray = $this.serializeArray();
2849//
2850//      if( !$this.is( "form" ) )
2851//          $this = $this.parents( "form" );
2852//             
2853//      if( fileInputs )
2854//              fileInputs.each( function( i, el ){
2855//
2856//            inputArray[ inputArray.length ] = { name: $(this).prop("name"), value: FILE + i };
2857//
2858//              });
2859//
2860//      $.each( inputArray, function( i, el ){
2861//
2862//          if( newName = arrayName.exec( el.name ) )
2863//              el.name = newName[1];
2864//          else if( !params[ el.name ] )
2865//              return( params[ el.name ] = el.value );
2866//
2867//          params[ el.name ] = params[ el.name ] || [];
2868//
2869//          if( $.type(params[ el.name ]) !== "array" )
2870//              params[ el.name ] = [ params[ el.name ] ];
2871//
2872//          params[ el.name ].push( el.value );
2873//      });
2874//
2875// //   alert(dump(params));
2876//
2877//      return this.decode( $this.attr( "action" ), params );
2878//     },
2879//     
2880//      serializeForm: function( data, level ){
2881//     
2882//              var formData = [];
2883//     
2884//              for( key in data )
2885//              {
2886//                      var value = data[key];
2887//
2888//                      if( level !== undefined )
2889//                              key = level+'['+key+']';
2890//
2891//                      if( $.isArray(value) || $.isPlainObject(value) )
2892//                              formData = formData.concat( this.serializeForm( value, key ) );
2893//                      else
2894//                              formData[ formData.length ] = { name: key, value: value };
2895//              }
2896//             
2897//              return( formData );
2898//      },
2899//
2900//     blend: function( action, data ){
2901//
2902// //   if( notArray = (!$.isArray(data)) )
2903// //       data = [ data ];
2904//
2905//      var form = $('form[action="'+action+'"]');
2906//
2907//      form.get(0).reset();
2908//
2909//      var named = form.find( 'input[name]' );
2910//
2911//      for( var name in data )
2912//      {
2913//          named.filter( '[name="'+name+'"]' ).val( data[name] );
2914//      }
2915//     },
2916//
2917// 
2918//     
2919//     put: function( concept, filter, data, oneSide ){
2920//       
2921//       ///////////////////////////// normalize ////////////////////////////////
2922//      if( arguments.length == 2 )
2923//      {
2924//          data = filter;
2925//          filter = false;
2926//      }
2927//      if( typeof data === "undefined" ||
2928//          $.type(data) === "boolean" )
2929//      {
2930//          oneSide = data;
2931//          data = filter;
2932//          filter = false;
2933//      }
2934//     
2935//      if( !concept || !data )
2936//          return( false );
2937//
2938//      var decoder = "", id = false, bothSides = (typeof oneSide === "undefined"), notArray, res;
2939//     
2940//      if( $.type(filter) === "string" )
2941//      {
2942//          id = filter;
2943//          filter = false;
2944//      }
2945//
2946//      if( id )
2947//          data.id = id;
2948//
2949//      if( notArray = ( $.type( data ) !== "array" ) )
2950//          data = [ data ];
2951//
2952//      if( res = internalUrl.exec( concept ) )
2953//      {
2954//          //TODO: verificar se a decodificaçao deve ser feita em cada item do array
2955//          data = this.decode( concept, data );
2956//          concept = res[1];
2957//          decoder = res[2];
2958//      }
2959//
2960//       ////////////////////////////////////////////////////////////////////////
2961//
2962//      if( bothSides || !oneSide )
2963//      {
2964//          var result = false, links = this.links( concept ),
2965//          current = this.check( concept ) || {}, ids = [];
2966//
2967//          for( var i = 0; i < data.length; i++ )
2968//          {
2969//              var key = ids[ ids.length ] = data[i].id || this.generateId( concept ), updateSet = {};
2970//
2971//              ////////////////////////////// linkage /////////////////////////////////   
2972//              for( var link in links )
2973//              {
2974//                  if( data[i][link] )
2975//                  {
2976//                      var isConcept = false;
2977//                   
2978//                      if( isConcept = this.isConcept( concept, link ) )
2979//                          data[i][link] = [ data[i][link] ];
2980//
2981//                      var _this = this;
2982//
2983//                      $.each( data[i][link], function( ii, el ){
2984//
2985//                              var isRef = false;
2986//
2987//                              if( isRef = ($.type(el) === "string") )
2988//                                  el = { id: el };
2989//
2990//                              var nestedLinks = _this.links( links[link], true );
2991//                              //removido pois o mesmo esta gerando inconsistencia em tudo
2992//                              //if( DataLayer.isConcept( links[link], nestedLinks[concept] ) )
2993//                              if( isConcept )
2994//                              {
2995//                                  el[ nestedLinks[link] ] = el[ nestedLinks[link] ] || [];
2996//                                  el[ nestedLinks[link] ].push( key );
2997//                              }
2998//                              else
2999//                                  el[ nestedLinks[link] ] = key;
3000//
3001//                              if( isRef && ( !current[ key ] || !current[ key ][ link ] ||
3002//                                             (isConcept ? current[ key ][ link ] !== el.id : !$.inArray( el.id, current[ key ][ link ] )) ) )
3003//                              {
3004//                                  updateSet[ links[link] ] = updateSet[ links[link] ] || [];
3005//                                  updateSet[ links[link] ].push( el );
3006//                              }
3007//                              else if( !isRef )
3008//                                  data[i][link][ii] = _this.put( links[link], el, oneSide );
3009//                          });
3010//
3011//                      if( isConcept )
3012//                          data[i][link] = data[i][link][0];
3013//                  }
3014//              }
3015//              //////////////////////////////////////////////////////////////////////////
3016//
3017//              if( data[i].id )
3018//                  data[i] = this.merge( current[ data[i].id ], data[i] );
3019//
3020//               current[ key ] = data[i];
3021//
3022//              if( bothSides )
3023//                this.report( concept, key, data[i] );
3024//          }
3025//
3026//          this.store( concept, current );
3027//
3028//          for( var setKey in updateSet )
3029//          {
3030//              if( bothSides )
3031//                  for( var i = 0; i < updateSet[ setKey ].length; i++ )
3032//                    this.report( setKey, updateSet[ setKey ][i].id, updateSet[ setKey ][i] );
3033//                 
3034//              DataLayer.put( setKey, updateSet[ setKey ], false );
3035//          }
3036//      }
3037//
3038//      if( oneSide )
3039//          this.commit( concept, ids/*, true */);
3040//
3041//      this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', true );
3042//
3043//      return( notArray ? ids[0] : ids );
3044//
3045//     },
3046//     
3047//     remove: function( concept, id, oneSide ){
3048//       
3049//      var bothSides = (typeof oneSide === "undefined"),
3050//
3051//      links = this.links( concept ), ids = [],
3052//
3053//      current = this.check( concept, id );
3054//
3055//      if( !current ) return;
3056//     
3057//      if( id )
3058//          current.id = id;
3059//
3060//      if( notArray = ( $.type( current ) !== "array" ) )
3061//          current = [ current ];
3062//
3063//      for( var i = 0; i < current.length; i++ )
3064//      {
3065//          var currentId = ids[ ids.length ] = current[i].id;
3066//
3067//          if( bothSides )
3068//            this.report( concept, currentId, false );
3069//
3070//          if( bothSides || !oneSide )
3071//            this.del( concept, currentId );
3072//
3073//          for( var link in links )
3074//          {
3075//              if( !current[i][link] )
3076//                  continue;
3077//
3078//              var nestedLinks = this.links( links[link], true );
3079//
3080//              if( isConcept = this.isConcept( concept, link ) )
3081//                  current[i][link] = [ current[i][link] ];
3082//
3083//              $.each( current[i][link], function( ii, el ){
3084//
3085//                      el = DataLayer.storage.cache[links[link]][el];
3086//
3087//                      if( notArrayNested = ( $.type( el[ nestedLinks[link] ] ) !== "array" ) )
3088//                          el[ nestedLinks[link] ] = [ el[nestedLinks[link]] ];
3089//
3090//                      el[ nestedLinks[link] ] = $.grep( el[ nestedLinks[link] ], function( nested, iii ){
3091//                          return ( currentId !== nested );
3092//                      });
3093//
3094//                      if( notArrayNested )
3095//                          el[ nestedLinks[link] ] = el[ nestedLinks[link] ][0] || false;
3096//                      if(!el[ nestedLinks[link] ] || !el[ nestedLinks[link] ].length)
3097//                              delete el[ nestedLinks[link] ];
3098//              });
3099//          }
3100//      }
3101//
3102//      if( oneSide )
3103//          this.commit( concept, ids );
3104//
3105//      this.broadcast( concept, oneSide ? 'server' : bothSides ? 'serverclient' : 'client', false );
3106//     },
3107//     
3108//     report: function( concept, id, data )
3109//     {     
3110//      var current = this.check( ':current', concept ) || {};
3111//
3112//      if( !current[ id ] )
3113//          current[ id ] = this.check( concept, id ) || {};
3114//     
3115//      this.store( ':current', concept, current );
3116//
3117//      var diff = this.diff( current[ id ], data );
3118//
3119//      var diffs = this.check( ':diff', concept ) || {};
3120//
3121//      if( diffs[ id ] )
3122//          diff = this.merge( diffs[ id ], diff );
3123//
3124//      if( !diff || !$.isEmptyObject( diff ) )
3125//          diffs[ id ] = diff;
3126//
3127//      this.store( ':diff', concept, diffs );
3128//     },
3129//
3130// //     enqueue: function( queueName, concept, id, data ){
3131// //
3132// //   var queue = this.check( ':' + queueName, concept ) || {};
3133// //
3134// //
3135// //     },
3136// //     
3137// //     dequeue: function( queueName, concept, id ){
3138// //
3139// //   
3140// //
3141// //     },
3142//     
3143//     
3144//     
3145//     rollback: function( concept, ids ){
3146//       
3147//      var queue = this.prepareQ( 'current', concept, ids );
3148//
3149//      ids = [];
3150//
3151//      for( var id in queue )
3152//      {
3153//           this.put( concept, id, queue[id], false );
3154//
3155//           ids[ ids.length ] = id;
3156//      }
3157//
3158//      this.clearQ( concept, ( ids.length ? ids : false ) );
3159//
3160//      this.broadcast( concept, 'revert' );
3161//       
3162//     },
3163//     
3164//     prepareQ: function( queueName, concept, ids ){
3165//       
3166//       var notArray = false;
3167//       
3168//       if( notArray = ($.type(concept) !== "array") )
3169//        concept = [ concept ];
3170//       
3171//       var q = {};
3172//       
3173//       for( var i = 0; i < concept.length; i++ )
3174//       {
3175//        var queue = this.check( ':' + queueName, concept[i] || false );
3176//       
3177//        if( !queue ) continue;
3178//
3179//        if( ids )
3180//        {
3181//            if( $.type(ids) !== "array" )
3182//                ids = [ ids ];
3183//
3184//            var filtered = {};
3185//
3186//            for( var ii = 0; ii < ids.length; ii++ )
3187//            {
3188//                filtered[ ids[ii] ] = queue[ ids[ii] ];
3189//            }
3190//
3191//            queue = filtered;
3192//        }
3193//
3194//        q[ concept[i] ] = queue;
3195//       }
3196//       
3197//       return( notArray ? q[ concept[0] ] : q );
3198//     },
3199//     
3200//     clearQ: function( concept, ids ){
3201//       
3202//              var current = this.check( ':current', concept || false );
3203//      var diffs = this.check( ':diff', concept || false );
3204//
3205//      if( !ids )
3206//          current = diffs = {};
3207//      else
3208//      {
3209//          if( notArray = ($.type(ids) !== "array") )
3210//            ids = [ ids ];
3211//
3212//          for( var i = 0; i < ids.length; i++ )
3213//          {
3214//              delete current[ ids[i] ];
3215//              delete diffs[ ids[i] ];
3216//          }
3217//      }
3218//
3219//      this.store( ':current', concept, current );
3220//      this.store( ':diff', concept, diffs );
3221//     },
3222//
3223//     commit: function( concept, ids, callback ){
3224//       
3225//      var queue = this.prepareQ( 'diff', concept, ids );
3226//
3227//      this.sync( queue, !$.isArray(concept) && concept || false, callback );
3228//     },
3229//     
3230//     sync: function( queue, concept, callback ){
3231//
3232//      if( !queue || $.isEmptyObject( queue ) )
3233//          return;
3234//
3235//      if( concept )
3236//      {
3237//        var helper = {};
3238//        helper[concept] = queue;
3239//        queue = helper;
3240//      }
3241//
3242//      var data = {}, URIs = {};
3243//
3244//      for( var concept in queue )
3245//          for( var id in queue[concept] )
3246//          {
3247//              data[ this.URI( concept, id ) ] = queue[concept][id];
3248//              URIs[ this.URI( concept, id ) ] = { concept: concept, id: id };
3249//          }
3250//
3251//      if( $.isEmptyObject( data ) )
3252//          return;
3253//
3254//      this.dispatch( "Sync", data, function( data, status, jqXHR ){
3255//
3256// //       switch( status )
3257// //       {
3258// //         case "error":
3259// //         case "parsererror":
3260// //           return DataLayer.rollback( concept, URI );
3261// //         case "success":
3262// //           return DataLayer.commit();
3263// //         case "timeout":
3264// //         case "notmodified":
3265// //       }
3266//
3267//          var received = DataLayer.receive( data );
3268//
3269//          for( var URI in URIs )
3270//              if( typeof received[URI] !== "undefined" )
3271//                  DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
3272//
3273//          if( callback )
3274//              callback( received );
3275//
3276// //       for( var URI in data )
3277// //       {
3278// //           var parsed = DataLayer.parseURI( URI ),
3279// //   
3280// //           concept = parsed[1], id = parsed[3];
3281// //
3282// //           if( $.type(data[URI]) === "string" )
3283// //           {
3284// //             //TODO:threat the exception thrown
3285// //             DataLayer.rollback( concept, id );
3286// //             delete URIs[ URI ];
3287// //             continue;
3288// //           }
3289// //
3290// //           if( data[URI] === false ){
3291// //             DataLayer.remove( concept, id, false );
3292// //             continue;
3293// //           }
3294// //
3295// //           if( id !== data[URI].id )
3296// //             DataLayer.move( concept, id, data[URI].id );
3297// //           
3298// //           DataLayer.put( concept, id, data[URI], false );
3299// //       }
3300// //       
3301// //       for( var URI in URIs )
3302// //            DataLayer.clearQ( URIs[URI].concept, URIs[URI].id );
3303// //       
3304// //       if( callback )
3305// //           callback();
3306//
3307//      }, true );
3308//
3309//     },
3310//     
3311//     receive: function( data ){
3312//       
3313//      var received = {};
3314//     
3315//          for( var URI in data )
3316//          {
3317//              var parsed = DataLayer.parseURI( URI ),
3318//   
3319//          concept = parsed[4], id = parsed[5];
3320//
3321//          received[ URI ] = data[ URI ];
3322//
3323//              if( $.type(data[URI]) === "string" )
3324//              {
3325//                //TODO:threat the exception thrown
3326//                DataLayer.rollback( concept, id );
3327//                continue;
3328//              }
3329//
3330//              if( data[URI] === false ){
3331//                DataLayer.remove( concept, id, false );
3332//                continue;
3333//              }
3334//
3335//              if( id !== data[URI].id )
3336//                DataLayer.move( concept, id, data[URI].id );
3337//             
3338//              DataLayer.put( concept, id, data[URI], false );
3339//          }
3340//         
3341//      return( received );
3342//         
3343//     },
3344//     
3345//     unique: function( origArr ){
3346//
3347//      var newArr = [];
3348//       
3349//      for ( var x = 0; x < origArr.length; x++ )
3350//      {
3351//              var found = false;
3352//          for ( var y = 0; !found && y < newArr.length; y++ )
3353//              if ( origArr[x] === newArr[y] ) 
3354//                found = true;
3355//
3356//          if ( !found )
3357//              newArr[ newArr.length ] = origArr[x];
3358//      }
3359//
3360//      return newArr;
3361//     },
3362//
3363//     merge: function( current, data ){
3364//       
3365//      return this.copy(  data, current );
3366//
3367// //   return $.extend( current, data );
3368//
3369//     },
3370//     
3371//     // clone objects, skip other types.
3372//     clone: function(target) {
3373//          if ( typeof target == 'object' ) {
3374//                  Clone.prototype = target;
3375//                  return new Clone();
3376//          } else {
3377//                  return target;
3378//          }
3379//     },
3380//       
3381//     // Shallow Copy
3382//     shallowCopy: function(target) {
3383//          if (typeof target !== 'object' ) {
3384//                  return target;  // non-object have value sematics, so target is already a copy.
3385//          } else {
3386//                  var value = target.valueOf();
3387//                  if (target != value) {
3388//                          // the object is a standard object wrapper for a native type, say String.
3389//                          // we can make a copy by instantiating a new object around the value.
3390//                          return new target.constructor(value);
3391//                  } else {
3392//                          // ok, we have a normal object. If possible, we'll clone the original's prototype
3393//                          // (not the original) to get an empty object with the same prototype chain as
3394//                          // the original.  If just copy the instance properties.  Otherwise, we have to
3395//                          // copy the whole thing, property-by-property.
3396//                          if ( target instanceof target.constructor && target.constructor !== Object ) {
3397//                                  var c = clone(target.constructor.prototype);
3398//       
3399//                                  // give the copy all the instance properties of target.  It has the same
3400//                                  // prototype as target, so inherited properties are already there.
3401//                                  for ( var property in target) {
3402//                                          if (target.hasOwnProperty(property)) {
3403//                                                  c[property] = target[property];
3404//                                          }
3405//                                  }
3406//                          } else {
3407//                                  var c = {};
3408//                                  for ( var property in target ) c[property] = target[property];
3409//                          }
3410//                         
3411//                          return c;
3412//                  }
3413//          }
3414//     },
3415//
3416//     // entry point for deep copy.
3417//     // source is the object to be deep copied.
3418//     // depth is an optional recursion limit. Defaults to 256.
3419//     // deep copy handles the simple cases itself: non-objects and object's we've seen before.
3420//     // For complex cases, it first identifies an appropriate DeepCopier, then delegate the details of copying the object to him.
3421//     copy: function(source, result, depth) {
3422//       
3423//          // null is a special case: it's the only value of type 'object' without properties.
3424//          if ( source === null ) return null;
3425//
3426//          // All non-objects use value semantics and don't need explict copying.
3427//          if ( typeof source !== 'object' ) return source;
3428//
3429//          if( !depth || !(depth instanceof RecursionHelper) ) depth = new RecursionHelper(depth);
3430//
3431//          var cachedResult = depth.getCachedResult(source);
3432//
3433//          // we've already seen this object during this deep copy operation
3434//          // so can immediately return the result.  This preserves the cyclic
3435//          // reference structure and protects us from infinite recursion.
3436//          if ( cachedResult ) return cachedResult;
3437//
3438//          // objects may need special handling depending on their class.  There is
3439//          // a class of handlers call "DeepCopiers"  that know how to copy certain
3440//          // objects.  There is also a final, generic deep copier that can handle any object.
3441//          for ( var i=0; i<this.comparators.length; i++ ) {
3442//
3443//                  var comparator = this.comparators[i];
3444//
3445//                  if ( comparator.can(source) ) {
3446//     
3447//                          // once we've identified which DeepCopier to use, we need to call it in a very
3448//                          // particular order: create, cache, populate.  This is the key to detecting cycles.
3449//                          // We also keep track of recursion depth when calling the potentially recursive
3450//                          // populate(): this is a fail-fast to prevent an infinite loop from consuming all
3451//                          // available memory and crashing or slowing down the browser.
3452//       
3453//                          if( !result )
3454//                              // Start by creating a stub object that represents the copy.
3455//                              result = comparator.create(source);
3456//                          else if( !comparator.can(result) )
3457//                              throw new Error("can't compare diferent kind of objects.");
3458//
3459//                          // we now know the deep copy of source should always be result, so if we encounter
3460//                          // source again during this deep copy we can immediately use result instead of
3461//                          // descending into it recursively. 
3462//                          depth.cacheResult(source, result);
3463//
3464//                          // only DeepCopier.populate() can recursively deep copy.  So, to keep track
3465//                          // of recursion depth, we increment this shared counter before calling it,
3466//                          // and decrement it afterwards.
3467//                          depth.depth++;
3468//                          if ( depth.depth > depth.maxDepth ) {
3469//                                  throw new Error("Exceeded max recursion depth in deep copy.");
3470//                          }
3471//
3472//                          // It's now safe to let the comparator recursively deep copy its properties.
3473//                          var returned = comparator.populate( function(source, result) { return DataLayer.copy(source, result, depth); }, source, result );
3474//     
3475//                              if(returned)
3476//                                      result = returned;
3477//
3478//                          depth.depth--;
3479//
3480//                          return result;
3481//                  }
3482//          }
3483//          // the generic copier can handle anything, so we should never reach this line.
3484//          throw new Error("no DeepCopier is able to copy " + source);
3485//     },
3486//
3487//     // publicly expose the list of deepCopiers.
3488//     comparators: [],
3489//
3490//     // make deep copy() extensible by allowing others to
3491//     // register their own custom Comparators.
3492//     registerComparator: function(comparatorOptions) {
3493//
3494//        // publicly expose the Comparator class.
3495//        var comparator = {
3496//
3497//            // determines if this Comparator can handle the given object.
3498//            can: function(source) { return false; },
3499//     
3500//            // starts the deep copying process by creating the copy object.  You
3501//            // can initialize any properties you want, but you can't call recursively
3502//            // into the copy().
3503//            create: function(source) { },
3504//
3505//            // Completes the deep copy of the source object by populating any properties
3506//            // that need to be recursively deep copied.  You can do this by using the
3507//            // provided deepCopyAlgorithm instance's copy() method.  This will handle
3508//            // cyclic references for objects already deepCopied, including the source object
3509//            // itself.  The "result" passed in is the object returned from create().
3510//            populate: function(deepCopyAlgorithm, source, result) {}
3511//        };
3512//
3513//        for ( var key in comparatorOptions ) comparator[key] = comparatorOptions[key];
3514//
3515//        this.comparators.unshift( comparator );
3516//     },
3517// 
3518//     diff: function( base, toDiff ){
3519//
3520//      if( typeof base === 'undefined' || $.isEmptyObject(base) )
3521//          return( toDiff );
3522//
3523//      if( toDiff === false )
3524//          return( false );
3525//
3526//      toDiff = $.extend( {}, toDiff );
3527//
3528//      for( var key in toDiff )
3529//      {
3530//          switch( $.type(toDiff[key]) )
3531//          {
3532//            case 'object':
3533//              if( $.isEmptyObject(toDiff[key] = this.diff( base[key], toDiff[key] )) )
3534//                delete toDiff[key];
3535//            break;
3536//            case 'array':
3537//              if( base[key] && !(toDiff[key] = $.grep( toDiff[key], function( el, i ){ return( $.inArray( el, base[key] ) === -1 ); } )).length )
3538//                delete toDiff[key];
3539//            break;
3540//            default:
3541//              if( base[key] == toDiff[key] )
3542//                delete toDiff[key];
3543//          }
3544//      }
3545//
3546//      return( toDiff );
3547//
3548//     },
3549//     
3550//     links: function( concept, reverse ){
3551//
3552//      if( !this.links[ concept ] )
3553//      {
3554//          var result = this.dispatch( "links", { concept: concept } ) || false;
3555//
3556//          if( !result )
3557//              return( false );
3558//
3559//          this.concepts[ concept ] = $.extend( this.concepts[ concept ] || {},
3560//                                               result['concepts'] || {} );
3561//
3562//          this.links[ concept ] =  result['links'] || {};
3563//          this.nestedLinks[ concept ] = result['nestedLinks'] || {};
3564//      }
3565//
3566//      if( reverse )
3567//      {
3568//          return( this.nestedLinks[ concept ] );
3569// //       var reverted = {}, llinks = this.links[ concept ];
3570// //     
3571// //       for( var key in llinks )
3572// //           reverted[ llinks[key] ] = key;
3573// //
3574// //       return( reverted );
3575//      }
3576//
3577//      return( this.links[ concept ] );
3578//
3579//     },
3580//     
3581//     isConcept: function( concept, attr ){
3582//       
3583//      if( typeof this.concepts[concept] === "undefined" )
3584//      {
3585//          this.links( concept );
3586//      }
3587//
3588//      return !!this.concepts[ concept ][ attr ];
3589//     },
3590//     
3591//     URI: function( concept, URI, context ){
3592//       
3593//      if( res = internalUrl.exec( concept ) )
3594//          concept = res[1];
3595//     
3596//      context = context ? "(" + context + ")" : "";
3597//       
3598//      if( URI )
3599//          return( concept + context + "://" + URI );
3600//      else
3601//          return( concept );
3602//       
3603//     },
3604//     
3605//     parseURI: function( URI ){
3606//
3607//      return internalUri.exec( URI ) || false;
3608//
3609//     },
3610//     
3611//     
3612//   
3613//     
3614//     generateId: function( concept ){
3615//       
3616//      var newId = this.counter + "(javascript)";
3617//       
3618//      this.store( ":counter", (this.counter++) + "" );
3619//     
3620//      return( newId );
3621//     },
3622//   
3623//
3624//   
3625//
3626//     get: function( concept, /*URI, */filter, oneSide ){
3627//
3628//      ///////////////////////////// normalize ////////////////////////////////
3629//      if( arguments.length == 2 && $.type(filter) === "boolean" )
3630//      {
3631//          oneSide = filter;
3632//          filter = false;
3633//      }
3634//     
3635//      var encoder = false, id = false, bothSides = (typeof oneSide === 'undefined'), res;
3636//     
3637//      if( $.type(filter) === "string" )
3638//      {
3639//          id = filter;
3640//          filter = false;
3641//      }
3642//
3643//      filter = filter || false;
3644//
3645//      if( !concept )
3646//          return( false );
3647//
3648//      if( res = internalUrl.exec( concept ) )
3649//      {
3650//          encoder = concept;
3651//          concept = res[1];
3652//
3653//          if( filter )
3654//              filter = this.criteria( encoder, filter );
3655//      }
3656//     
3657//      if ( $.type(filter) === "array" )
3658//      {
3659//          filter = { filter: filter, criteria: false };
3660//      }
3661//     
3662//      //////////////////////////////////////////////////////////////////////////
3663//     
3664//      var result = false;
3665//
3666//      if( bothSides || !oneSide )
3667//          result = this.check( concept, id || filter );
3668//
3669//      if( !result && (bothSides || oneSide) )
3670//      {
3671//          result = this.request( concept, id || filter.filter, filter.criteria );
3672//
3673//          if( result && bothSides && (!filter ||
3674//                                      !filter.criteria ||
3675//                                      !filter.criteria.format) )
3676//          {
3677//            var newResult = [];
3678//         
3679//            for( var i = 0; i < result.length; i++ )
3680//                newResult[i] = $.extend( {}, result[i] );
3681//
3682//            this.put( concept, id, newResult, false );
3683//          }
3684//      }
3685//
3686//      if( /*result &&*/ encoder )
3687//          result = this.encode( encoder, result, filter ); //TODO: retirar o filtro no método encode
3688//
3689//      return( result );
3690//     },
3691//     
3692//     filter: function( base, filter, criteria ){
3693//       
3694//      var filtered = [];
3695//       
3696//      for( var key in base )
3697//      {
3698// //       if( !noGroup )
3699// //           for( var i = 0, current = original; i < filter.length && ( current === original ); i++ )
3700// //               current = this.compare( operator, current, this.compare( base[key], filter[i] ) );
3701//
3702//          if( this.storage.filter( base[key], filter ) )
3703//              filtered[ filtered.length ] = key;
3704//      }
3705//
3706//      return( filtered );
3707//     },
3708//     
3709//     compare: function( operator, base, test ){
3710//       
3711//       switch( operator )
3712//       {
3713//        case '*':  return RegExp( ".*" + test + ".*" ).test( base );
3714//        case '^':  return RegExp( "^" + test +  ".*" ).test( base );
3715//        case '$':  return RegExp( ".*"  + test + "$" ).test( base );
3716//
3717//        case '&':  return ( base && test );
3718//        case '|':  return ( base || test );
3719//
3720//        case '=':  return ( base == test );
3721//        case '<=': return ( base <= test );
3722//        case '>=': return ( base >= test );
3723//        case '>':  return ( base <  test );
3724//        case '<':  return ( base >  test );
3725//       }
3726//       
3727//     },
3728//     
3729// //     clone: function( object ){
3730// //
3731// //   new { prototype: object };
3732// //
3733// //     },
3734//
3735//     check: function( namespace, keys ){
3736//
3737//      if( !namespace )
3738//          return( false );
3739//
3740//      var result = this.storage.get( namespace );
3741//
3742//      if( !keys || !result )
3743//        return( result || false );
3744//
3745//      if( notArray = $.type(keys) === "string" )
3746//          keys = [ keys ];
3747//      else if( $.type(keys) !== "array" )
3748//          keys = this.filter( result, keys.filter, keys.criteria );
3749//
3750//      var res = [];
3751//
3752//      for( var i = 0; i < keys.length; i++ )
3753//          res[ res.length ] = result[keys[i]];
3754//
3755//      return( notArray ? res[0] || false : res.length ? res : false );
3756//     },
3757//
3758//     storage: {
3759//       
3760//      cache: {},
3761//       
3762//      set: function( key, value ){
3763//
3764//          this.cache[key] = value;
3765//
3766//      },
3767//      get: function( key ){
3768//
3769//          return DataLayer.copy( this.cache[key] );
3770//
3771//      },
3772//      del: function( key ){
3773//
3774//          delete this.cache[key];
3775//
3776//      },
3777//     
3778//      filter: function( base, filter ){
3779//       
3780//          var bool, op = filter.shift();
3781//
3782//          switch( op )
3783//          {
3784//              case 'IN':
3785//                for( var i = 0, f = []; i < filter[1].length || !(filter = f); i++ )
3786//                    f[i] = [ '=', filter[0], filter[1][i] ];
3787//              case 'OR':
3788//                  op = '|';
3789//                  bool = false;
3790//              break;
3791//              case 'AND':
3792//                  op = '&';
3793//                  bool = true;
3794//              break;
3795//              default : return DataLayer.compare( op, base[ filter[0] ], filter[1] );
3796//          }
3797//         
3798//          for( var strict = bool;
3799//
3800//              filter.length && ( strict ? bool : !bool );
3801//         
3802//              bool = DataLayer.compare( op, bool, this.filter( base, filter.shift() ) ) );
3803//
3804//          return( bool );
3805//      }
3806//     },
3807//
3808//     flush: function(){
3809//
3810//     },
3811//     
3812//     restore: function(){
3813//       
3814//     },
3815//
3816//     store: function( namespace, key, data ){
3817//
3818//      if( !data )
3819//        return this.storage.set( namespace, key );
3820//
3821//      var res = this.check( namespace ) || {};
3822//
3823//      res[key] = data;
3824//
3825//      return this.storage.set( namespace, res );
3826//     },
3827//
3828//     del: function( namespace, key ){
3829//       
3830//      if( !key )
3831//        return this.storage.del( namespace );
3832//
3833//      var res = this.check( namespace ) || {};
3834//
3835//      delete res[key];
3836//
3837//      return this.storage.set( namespace, res );
3838//       
3839//     },
3840//     
3841//      move: function( concept, oldId, newId ){
3842//
3843//      this.put( concept, newId, this.check( concept, oldId ), false );
3844//
3845//      this.remove( concept, oldId, false );
3846//     },
3847//     
3848//
3849//     
3850//     
3851//     
3852//     request: function( concept, filter, criteria ){
3853//
3854//       var id = false, criteria = criteria || {};
3855//
3856//       if( $.type(filter) === "string" )
3857//       {
3858//        id = filter;
3859//        filter = false;
3860//       }
3861//
3862//       return this.dispatch( "request", {
3863//
3864//        concept: concept || '',
3865//        id: id || '',
3866//        filter: filter || '',
3867//        criteria: criteria || '',
3868//        service: criteria.service || '',
3869//        properties: criteria.properties || ''
3870//
3871//       } );
3872//     },
3873//
3874//     
3875//     //         sync: function( data, callback ){
3876// //
3877// //   if( !data || $.isEmptyObject( data ) )
3878// //       return;
3879// //       
3880// //   this.send( "Sync", data, function( data, status, jqXHR ){
3881// //
3882// // //            switch( status )
3883// // //            {
3884// // //              case "error":
3885// // //              case "parsererror":
3886// // //                return DataLayer.rollback( concept, URI );
3887// // //              case "success":
3888// // //                return DataLayer.commit();
3889// // //              case "timeout":
3890// // //              case "notmodified":
3891// // //            }
3892// //
3893// //       if( callback )
3894// //       {
3895// //           var result = callback( data, status, jqXHR );
3896// //
3897// //           if( result === false )
3898// //               return;
3899// //           else if( typeof result != "undefined" )
3900// //               data = result;
3901// //       }
3902// //
3903// //       for( var URI in data )
3904// //       {
3905// //           var parsed = DataLayer.parseURI( URI ),
3906// //   
3907// //           concept = parsed[1], /*URI = parsed[3],*/
3908// //
3909// //           links = DataLayer.links( concept );
3910// //
3911// //           for( var linkName in links )
3912// //           {
3913// //               var subURI = data[URI][linkName];
3914// //
3915// //               if( subURI && data[subURI] )
3916// //               {
3917// //                   data[URI][linkName] = DataLayer.put( linkName, subURI, data[subURI], false );
3918// //
3919// //                   delete( data[subURI] );
3920// //               }
3921// //           }
3922// //
3923// //           DataLayer.put( concept, URI, data[URI], false );
3924// //       }
3925// //   }, true );
3926// //
3927// //     },
3928//
3929// //     report: function( concept, URI, data, sync )
3930// //     {
3931// //   var current = this.dequeue( 'current', concept, URI );
3932// //
3933// //   if( !current )
3934// //       this.enqueue( 'current', concept, URI, ( current = this.check( concept, URI ) || {} ) );
3935// //
3936// //   var diff = this.diff( current, data );
3937// //
3938// //   if( !diff )
3939// //       this.dequeue( 'current', concept, URI, true );
3940// //   else
3941// //       this.enqueue( 'diff', concept, URI, diff );
3942// //   
3943// //   if( sync )
3944// //       this.commit( concept, URI, function(){
3945// //
3946// //           DataLayer.set( concept, URI, data, false );
3947// //
3948// //       });
3949// //     },
3950//     
3951// //     enqueue: function( type, concept, URI, obj ){
3952// //       
3953// //   //var newURI = this.URI( concept, URI );
3954// //   
3955// //   if( !this.queue[type] )
3956// //       this.queue[type] = {};
3957// //
3958// //   if( !this.queue['all'] )
3959// //       this.queue['all'] = {};
3960// //   
3961// //   if( !this.queue[type][concept] )
3962// //       this.queue[type][concept] = {};
3963// //   
3964// //   if( !this.queue['all'][type] )
3965// //       this.queue['all'][type] = {};
3966// //   
3967// //   if( !this.queue['all'][type][/*new*/URI] )
3968// //       this.queue[type][concept][URI] = this.queue['all'][type][/*new*/URI] = obj;
3969// //
3970// //   this.store( ':queue', this.queue );
3971// //     },
3972// //     
3973// //     dequeue: function( type, concept, URI, remove ){
3974// //       
3975// //       ///////////////////////////// normalize ////////////////////////////////
3976// //   if( arguments.length < 4 && $.type(URI) === 'boolean' )
3977// //   {
3978// //       remove = URI;
3979// //       URI = false;
3980// //   }
3981// //   if( arguments.length < 3 && $.type(concept) === 'boolean' )
3982// //   {
3983// //       remove = concept;
3984// //       concept = false;
3985// //   }
3986// //       //////////////////////////////////////////////////////////////////////////
3987// //       
3988// //   if( !this.queue[type] || !this.queue['all'] )
3989// //       return( false );
3990// //   
3991// //   if( !concept )
3992// //   {
3993// //       var obj = this.queue['all'][type];
3994// //       
3995// //       if( remove )
3996// //       {
3997// //           delete this.queue['all'][type];
3998// //           delete this.queue[type];
3999// //       }
4000// //
4001// //       this.store( ':queue', this.queue );
4002// //       return( obj );
4003// //   }
4004// //
4005// //   if( !this.queue[type][concept] )
4006// //       return( false );
4007// //   
4008// //   if( !URI )
4009// //   {
4010// //       var obj = this.queue[type][concept];
4011// //
4012// //       if( remove )
4013// //       {
4014// //           var URIs = this.queue[type][concept];
4015// //
4016// //           for( var subURI in URIs )
4017// //                delete this.queue['all'][type][subURI];
4018// //
4019// //           delete this.queue[type][concept];
4020// //       }
4021// //
4022// //       this.store( ':queue', this.queue );
4023// //       return( obj );
4024// //   }
4025// //
4026// // //        var newURI = URI ? this.URI( concept, URI ) : concept;
4027// //   
4028// //   var obj = this.queue['all'][type][/*new*/URI];
4029// //   
4030// //   if( remove )
4031// //   {
4032// //       delete this.queue['all'][type][/*new*/URI];
4033// //       delete this.queue[type][concept][URI];
4034// //   }
4035// //
4036// //   this.store( ':queue', this.queue );
4037// //   return( obj );
4038// //     },
4039//     
4040//            //TODO: definir a 'usage' desta função e refatora-la
4041// //     set: function( concept, filter, data, oneSide ){
4042// //
4043// //   ///////////////////////////// normalize ////////////////////////////////
4044// //   if( arguments.length == 2 )
4045// //   {
4046// //       data = filter;
4047// //       filter = false;
4048// //   }
4049// //   if( $.type(data) === "boolean" )
4050// //   {
4051// //       oneSide = data;
4052// //       data = filter;
4053// //       filter = false;
4054// //   }
4055// //   
4056// //   if( !concept || !data )
4057// //       return( false );
4058// //
4059// //   var decoder = "", URI = false, bothSides = (typeof oneSide === "undefined");
4060// //   
4061// //   if( $.type(filter) === "string" )
4062// //   {
4063// //       URI = filter;
4064// //       filter = false;
4065// //   }
4066// //
4067// //   if( res = internalUrl.exec( concept ) )
4068// //   {
4069// //       //TODO: verificar se a decodificaçao deve ser feita em cada item do array
4070// //       data = this.decode( concept, data );
4071// //       concept = res[1];
4072// //       decoder = res[2];
4073// //   }
4074// //   ///////////////////////////////////////////////////////////////////////////
4075// //
4076// //   if( bothSides || oneSide )
4077// //       this.report( concept, URI, data, !bothSides );
4078// //
4079// //   if( bothSides || !oneSide )
4080// //   {
4081// //       if( URI )
4082// //       {
4083// //         var helper = {};
4084// //         helper[URI] = data;
4085// //         data = helper;
4086// //       }
4087// //
4088// //       for( var URI in data )
4089// //       {
4090// //           var current = this.check( concept, URI ) || {};
4091// //
4092// //           data[URI] = this.merge( current, data[URI] );
4093// //
4094// //           this.store( concept, URI, data[URI] );
4095// //       }
4096// //
4097// //   }
4098// //
4099// //   this.broadcast( concept, oneSide ? 'client' : 'server' );
4100// //
4101// //   return( true );
4102// //     },
4103// //     put: function( concept, URI, data, oneSide ){
4104// //       
4105// //       ///////////////////////////// normalize ////////////////////////////////
4106// //   if( $.type(URI) !== "string" && arguments.length < 4 )
4107// //   {
4108// //       oneSide = data;
4109// //       data = URI;
4110// //       URI = false;
4111// //   }
4112// //       ////////////////////////////////////////////////////////////////////////
4113// //       
4114// //       ////////////////////////////// linkage /////////////////////////////////
4115// //   var result = false, links = this.links( concept );
4116// //
4117// //   for( var link in links )
4118// //   {
4119// //       if( data[link] )
4120// //       {
4121// //           if( $.isArray( data[link] ) )
4122// //           {
4123// //               data[link] = this.put( links[link], data[link].URI, data[link], oneSide );
4124// //           }
4125// //           else if( $.isObject( data[link] ) )
4126// //           {
4127// //               $.each( data[link], function( i, el ){
4128// //
4129// //                     data[link][i] = this.put( links[link], el.URI, el, oneSide );
4130// //
4131// //               });
4132// //           }
4133// //       }
4134// //   }
4135// //       //////////////////////////////////////////////////////////////////////////
4136// //     
4137// //   if( typeof data.URI === "undefined" )
4138// //   {
4139// //       URI = this.add( concept, data, oneSide );
4140// //   }
4141// //   else if( data.URI === false )
4142// //   {
4143// //       status = this.remove( concept, URI, oneSide );
4144// //   }
4145// //   else
4146// //   {
4147// //       status = this.set( concept, URI, data, oneSide );
4148// //   }
4149// //
4150// //   if( URI && data.URI && URI !== data.URI )
4151// //       this.move( concept, URI, data.URI );
4152// //
4153// //   return( data.URI || URI );
4154// //
4155// //     },
4156//     
4157//     //     add: function( concept, data, oneSide ){
4158// //       
4159// //       ///////////////////////////// normalize ////////////////////////////////
4160// //   if( !concept || !data )
4161// //       return( false );
4162// //
4163// //   if( res = internalUrl.exec( concept ) )
4164// //   {
4165// //       //TODO: verificar se a decodificaᅵᅵo deve ser feita em cada item do array
4166// //       data = this.decode( concept, data );
4167// //       concept = res[1];
4168// //       decoder = res[2];
4169// //   }
4170// //
4171// //   var bothSides = (typeof oneSide === "undefined"), uris = [];
4172// //
4173// //   if( notArray = $.type(data) !== "array" )
4174// //       data = [ data ];
4175// //       //////////////////////////////////////////////////////////////////////////
4176// //
4177// //   for( var i = 0; i < data.length; i++ )
4178// //   {
4179// //       var URI = uris[i] = this.generateURI( concept );
4180// //
4181// //       this.set( concept, URI, data[i], oneSide );
4182// //   }
4183// //
4184// //   return( notArray ? uris[0] : uris );
4185// //     },
4186// //      put: function( concept, data ){
4187// //
4188// //   var decoder = "";
4189// //
4190// //   if( res = internalUrl.exec( concept ) )
4191// //   {
4192// //       data = this.decode( concept, data );
4193// //       concept = res[1];
4194// //       decoder = res[2];
4195// //   }
4196// //
4197// //   var New = [], Update = [], uris = [];
4198// //
4199// //   if( notArray = $.type(data) !== "array" )
4200// //       data = [ data ];
4201// //   
4202// //   for( var i = 0; i < data.length; i++ )
4203// //   {
4204// //       if( !data[i].URI )
4205// //       {
4206// //           uris[ uris.length ] = data[i].URI = this.create( concept, data[i] );
4207// //           New[ New.length ] = data[i];
4208// //           continue;
4209// //       }
4210// //
4211// //       for( var key in data[i] )
4212// //           if( klass = this.isReference( concept, key, data[i][key] ) )
4213// //                 data[i][key] = this.put( klass + decoder, data[i][key] );
4214// //
4215// //       Update[ Update.length ] = this.update( concept, data[i].URI, data[i] );
4216// //   }
4217// //
4218// //   this.report( concept, { "created": New, "updated": Update });
4219// //
4220// //   return( notArray ? uris[0] : uris );
4221// //     },
4222// //     merge: function( concept, current, data ){
4223// //
4224// //   current = current || {};
4225// //
4226// //   for( var key in data )
4227// //       current[key] = (klass = this.isReference( concept, key, data[key] )) ?
4228// //                      this.merge( klass, current[key], data[key] ) : data[key];
4229// //
4230// //   return( current );
4231// //     },
4232// //
4233// //     isReference: function( concept, key, value ){
4234// //
4235// //       return( ($.type(value) === "object" ||
4236// //          $.type(value) === "array" )? this.links[concept][key] : false );
4237// //
4238// //     },
4239// //     
4240// //     set: function( concept, data, URI, mergeable ){
4241// //   
4242// //   if( URI )
4243// //   {
4244// //       var res = this.get( concept, true ) || {};
4245// //       
4246// //       if( mergeable )
4247// //           data = this.merge( res[URI] || {}, data );
4248// //
4249// //       res[URI] = data;
4250// //
4251// //       data = res;
4252// //   }
4253// //
4254// //   return this.store( concept, data );
4255// //     },   
4256// //
4257// //     create: function( concept, data ){
4258// //
4259// //       if( notArray = ($.type(data) !== "array") )
4260// //       data = [ data ];
4261// //
4262// //       var uris = [];
4263// //
4264// //       for( var i = 0; i < data.length; i++ )
4265// //       {
4266// //     uris[ uris.length ] = data[i].URI = "javascript://" + (this.counter + i);
4267// //
4268// //     this.set( concept, data[i], data[i].URI );
4269// //       }
4270// // 
4271// //       this.set( ":counter", (this.counter += data.length) );
4272// //
4273// //       return notArray ? uris[0] : uris;
4274// //     },
4275// //
4276// //     update: function( concept, URI, data )
4277// //     {
4278// //   var target = this.check( concept, URI ) || {};
4279// //
4280// //   target = this.merge( concept, target, data );
4281// //
4282// //   if( target.URI !== URI )
4283// //       this.remove( concept, URI );
4284// //
4285// //   this.set( concept, target, target.URI );
4286// //
4287// //   return( target );
4288// //     },
4289// //
4290// //     remove: function( concept, URI ){
4291// //
4292// //   if( !URI )
4293// //       return this.storage.del( concept );
4294// //
4295// //   var res = this.check( concept );
4296// //
4297// //   delete res[URI];
4298// //   
4299// //   this.set( concept, res );
4300// //     },
4301// //
4302// //     del: function( concept, URI ){
4303// //
4304// //   this.remove( concept, URI );
4305// //
4306// //   this.report( concept, { "deleted": { 'URI': URI } });
4307// //     },
4308// //
4309// //     report: function( concept, changes ){
4310// //
4311// //       this.broadcast( concept, changes.created, changes.updated, changes.deleted );
4312// //
4313// //   if( changes.created )
4314// //       this.sync( concept, changes.created, 'create' );
4315// //   if( changes.updated )
4316// //       this.sync( concept, changes.updated, 'update' );
4317// //   if( changes.deleted )
4318// //       this.sync( concept, changes.deleted, 'delete' );
4319// //
4320// //     },
4321// //
4322// //
4323// //    sync: function( concept, data, type ){
4324// //
4325// //   if( $.type(data) !== "array" )
4326// //       data = [ data ];
4327// //
4328// //   $.each( data, function( i, el ){
4329// //
4330// //      DataLayer.send( concept, el, type );
4331// //
4332// //   });
4333// //
4334// //     },
4335// //     
4336// //     
4337// //     
4338// //     
4339// //
4340// //     request: function( concept, URI, filter ){
4341// //
4342// // //       if( startsDoubleDot.test(concept) )
4343// // //          return( false );
4344// //
4345// //       filter = filter || {};
4346// //
4347// //       if( URI )
4348// //   filter.URI = URI;
4349// //
4350// //       return this.send( concept, filter, "read", true );
4351// //
4352// //     },
4353// //
4354// //     send: function( concept, data, type, wait ){
4355// //
4356// //       switch( type )
4357// //       {
4358// //   case "create": type = "POST"; break;
4359// //   case "update": type = "PUT"; break;
4360// //   case "delete": type = "DELETE"; break;
4361// //   case "read": type = "GET"; break;
4362// //       }
4363// //
4364// //       var url = this.basePath + concept;
4365// //
4366// //       var result = [], notArray = false;
4367// //
4368// // //      alert( data.URI );
4369// //
4370// //       if( data.URI && data.URI.indexOf("javascript://") !== 0 )
4371// //     url += "/" + data.URI;
4372// //
4373// //       var callback = function( dt, textStatus, jqXHR ){
4374// //
4375// //       if( notArray = (!$.isArray( dt )) )
4376// //           dt = [ dt ];
4377// //
4378// //       $.each( dt, function( i, el ){
4379// //
4380// //           if( !el || !el.URI )
4381// //               return;
4382// //
4383// //           if( data.URI )
4384// //               el = DataLayer.update( concept, data.URI, el );
4385// //           else
4386// //               DataLayer.set( concept, el, el.URI );
4387// //
4388// //           result[ result.length ] = el;
4389// //           DataLayer.broadcast( concept );
4390// //     });
4391// //       };
4392// //
4393// //       $.ajax({
4394// //         'async': ( !wait ),
4395// //         'url': url,
4396// //         'type': type,
4397// //         'success': callback,
4398// //         'dataType': 'json',
4399// //         'data': data/*,
4400// //         'processData': false*/
4401// //     });
4402// //
4403// //       return( notArray ? result[0] || false : result );
4404// //     },
4405//     
4406//     
4407//     generateURI: function( concept ){
4408//       
4409//      return this.URI( concept, this.generateId( concept ), "javascript" );
4410//
4411//     },
4412//     
4413//
4414//     broadcast: function( concept, status, diff ){
4415//
4416//      if( this.listeners[ concept ] )
4417//          for( var i = 0;
4418//              i < this.listeners[ concept ].length;
4419//              this.listeners[ concept ][ i++ ]( status, diff ) );
4420//     },
4421//
4422//     listen: function( concept, listener ){
4423//
4424//      this.register( "listeners", concept, listener );
4425//
4426//     },
4427//
4428//     codec: function( concept, namespace, codec ){
4429//
4430//      if( codec.encoder )
4431//          this.encoder( concept, namespace, codec.encoder );
4432//      if( codec.decoder )
4433//          this.decoder( concept, namespace, codec.decoder );
4434//      if( codec.criteria )
4435//          this.register( "criterias", concept + ":" + namespace, codec.criteria );
4436//
4437//     },
4438//
4439//     encoder: function( concept, namespace, encoder ){
4440//
4441//      this.register( "encoders", concept + ":" + namespace, encoder );
4442//
4443//     },
4444//
4445//     encode: function( encoder, data, filter ){
4446//
4447//      if( this.encoders[ encoder ] )
4448//          for( var i = 0;
4449//              i < this.encoders[ encoder ].length;
4450//              data = this.encoders[ encoder ][ i++ ]( data, filter ) );
4451//
4452//      return( data );
4453//     },
4454//
4455//     decoder: function( concept, namespace, decoder ){
4456//
4457//      this.register( "decoders", concept + ":" + namespace, decoder );
4458//
4459//     },
4460//
4461//     decode: function( decoder, data ){
4462//
4463//      if( this.decoders[ decoder ] )
4464//          for( var i = 0;
4465//              i < this.decoders[ decoder ].length;
4466//              data = this.decoders[ decoder ][ i++ ]( data ) );
4467//
4468//      return( data );
4469//     },
4470//
4471//     criteria: function( codec, filter ){
4472//
4473//      if( this.criterias[ codec ] )
4474//          for( var i = 0;
4475//              i < this.criterias[ codec ].length;
4476//              filter = this.criterias[ codec ][ i++ ]( filter ) );
4477//
4478//      return( filter );
4479//
4480//     },
4481//
4482//     register: function( kind, concept, deployable ){
4483//
4484//       if( arguments.length < 3 )
4485//       {
4486//        deployable = concept;
4487//        concept = kind;
4488//        kind = 'global';
4489//       }
4490//
4491//       if( !this[ kind ][ concept ] )
4492//          this[ kind ][ concept ] = [];
4493//
4494//      this[ kind ][ concept ][ this[ kind ][ concept ].length ] = deployable;
4495//
4496//     },
4497//     
4498//     start: function(){
4499//
4500//      var timer = function(){
4501//
4502//            setTimeout( timer, 1 );
4503//
4504//            var now = parseInt( $.now() / 1000 );
4505//
4506//            var tasks = DataLayer.tasks[ now ];
4507//
4508//            if( !tasks ) return;
4509//
4510//            for( var i = 0; i < tasks.length; i++ )
4511//            {
4512//                var result = tasks[i].task( now );
4513//
4514//                if( tasks[i].factor )
4515//                DataLayer.schedule( tasks[i].task, tasks[i].factor );
4516//            }
4517//       
4518//            delete DataLayer.tasks[ now ];
4519//      };
4520//
4521//      setTimeout( timer, 1 );
4522//     },
4523//     
4524//     task: function( timestamp, task, factor )
4525//     {
4526//      if( !this.tasks[ timestamp ] )
4527//          this.tasks[ timestamp ] = [];
4528//
4529//      this.tasks[ timestamp ][ this.tasks[ timestamp ].length ] = { task: task, factor: factor || false };
4530//     },
4531//
4532//     schedule: function( task, time ){
4533//
4534//      time = time || 1;
4535//     
4536//      var index = parseInt( $.now() / 1000 ) + time;
4537//
4538//      this.task( index, task, time );
4539//     },
4540//     
4541//     poll: function( concept, time ){
4542//       
4543//       this.schedule( function( now ){
4544//   
4545//        DataLayer.commit( concept );
4546//
4547//      }, time || 5 );
4548//     },
4549//     
4550//     init: function(){
4551//       
4552//      this.counter = parseInt( this.get( ":counter", false ) ) || 0;
4553//
4554//      if( !this.dispatchPath )
4555//          this.dispatchPath = "../../";
4556//
4557//      if( !this.templatePath )
4558//          this.templatePath = "";
4559//
4560//      if( !this.basePath )
4561//          this.basePath = this.dispatchPath + "REST.php?q=";
4562//
4563//      this.schedule( function( now ){
4564//
4565//          DataLayer.flush();
4566//
4567//      });
4568//
4569//      this.start();
4570//     }
4571// }
4572//
4573// // the re-usable constructor function used by clone().
4574// function Clone() {}
4575//
4576// //Recursion Helper
4577// function RecursionHelper(){ this.clear(); };
4578//
4579// RecursionHelper.prototype = {
4580//   
4581//      constructor: RecursionHelper,
4582//
4583//      // copiedObjects keeps track of objects already copied by this
4584//      // deepCopy operation, so we can correctly handle cyclic references.
4585//      copiedObjects: [],
4586//
4587//      depth: 0,
4588//
4589//      maxDepth: 256,
4590//
4591//      //reset the recursion helper cache
4592//      clear: function(){
4593//              this.copiedObjects = [];
4594//              this.depth = 0;
4595//      },
4596//
4597//      // add an object to the cache.  No attempt is made to filter duplicates;
4598//      // we always check getCachedResult() before calling it.
4599//      cacheResult: function(source, result) {
4600//              this.copiedObjects.push([source, result]);
4601//      },
4602//
4603//      // Returns the cached copy of a given object, or undefined if it's an
4604//      // object we haven't seen before.
4605//      getCachedResult: function(source) {
4606//
4607//              for ( var i=0; i<this.copiedObjects.length; i++ ) {
4608//                      if ( this.copiedObjects[i][0] === source ) {
4609//                              return this.copiedObjects[i][1];
4610//                      }
4611//              }
4612//
4613//              return undefined;
4614//      }
4615// };
4616//
4617// // Generic Object copier
4618// // the ultimate fallback DeepCopier, which tries to handle the generic case.  This
4619// // should work for base Objects and many user-defined classes.
4620// DataLayer.registerComparator({
4621//      can: function(source) { return true; },
4622//
4623//      create: function(source) {
4624//              if ( source instanceof source.constructor ) {
4625//                      return DataLayer.clone(source.constructor.prototype);
4626//              } else {
4627//                      return {};
4628//              }
4629//      },
4630//
4631//      populate: function(deepCopy, source, result) {
4632//              for ( var key in source ) {
4633//                      if ( source.hasOwnProperty(key) ) {
4634//                              result[key] = deepCopy(source[key], result[key]);
4635//                      }
4636//              }
4637//              return result;
4638//      }
4639// });
4640//
4641// // Array copier
4642// DataLayer.registerComparator({
4643//      can: function(source) {
4644//              return ( source instanceof Array );
4645//      },
4646//
4647//      create: function(source) {
4648//              return new source.constructor();
4649//      },
4650//
4651//      populate: function(deepCopy, source, result) {
4652//              for ( var i=0; i<source.length; i++) {
4653//                      result.push( deepCopy(source[i], result[i]) );
4654//              }
4655//              result =  DataLayer.unique( result )
4656//              return result;
4657//      }
4658// });
4659//
4660// // Date copier
4661// DataLayer.registerComparator({
4662//      can: function(source) {
4663//              return ( source instanceof Date );
4664//      },
4665//
4666//      create: function(source) {
4667//              return new Date(source);
4668//      }
4669// });
4670//
4671// // HTML DOM Node copier
4672// DataLayer.registerComparator({
4673//
4674//      // function to detect Nodes.  In particular, we're looking
4675//      // for the cloneNode method.  The global document is also defined to
4676//      // be a Node, but is a special case in many ways.
4677//      can: function(source) {
4678//       
4679//        if ( window.Node ) {
4680//                return source instanceof Node;
4681//        } else {
4682//                // the document is a special Node and doesn't have many of
4683//                // the common properties so we use an identity check instead.
4684//                if ( source === document ) return true;
4685//                return (
4686//                        typeof source.nodeType === 'number' &&
4687//                        source.attributes &&
4688//                        source.childNodes &&
4689//                        source.cloneNode
4690//                );
4691//        }
4692//       },
4693//
4694//       create: function(source) {
4695//            // there can only be one (document).
4696//            if ( source === document ) return document;
4697//
4698//            // start with a shallow copy.  We'll handle the deep copy of
4699//            // its children ourselves.
4700//            return source.cloneNode(false);
4701//       },
4702//       
4703//       diff: function(base, source){
4704//     
4705//       },
4706//
4707//       populate: function(deepCopy, source, result) {
4708//            // we're not copying the global document, so don't have to populate it either.
4709//            if ( source === document ) return document;
4710//
4711//            // if this Node has children, deep copy them one-by-one.
4712//            if ( source.childNodes && source.childNodes.length ) {
4713//                    for ( var i=0; i<source.childNodes.length; i++ ) {
4714//                            var childCopy = deepCopy(source.childNodes[i], result.childNodes[i] || false );
4715//                            result.appendChild(childCopy);
4716//                    }
4717//            }
4718//              return result;
4719//       }
4720// });
4721//
4722// DataLayer.init();
4723//
4724// // setTimeout(function(){
4725// // 
4726// //     
4727// //
4728// // }, 1000 );
4729//
4730// // var DataLayer = {
4731// //
4732// //     get: function( concept, filter ){
4733// //
4734// //   var data = this.storage.get( concept );
4735// //
4736// //   if( !filter )
4737// //       return( data );
4738// //
4739// //   return this.filter( data, filter );
4740// //     },
4741// //     
4742// //     filter:function( data, filter ){
4743// //       
4744// //   if( filter.charAt )
4745// //       filter = { URI: filter };
4746// //   
4747// //   var filtered = [];
4748// //
4749// //   $.each(data, function(i, obj){
4750// //     
4751// //       for( var attr in filter )
4752// //           if( filter[attr] !== obj[attr] )
4753// //               return( true );
4754// //
4755// //       filtered[i] = obj;
4756// //   });
4757// //
4758// //   return( filtered );
4759// //     },
4760// //
4761// //     find: function( concept, filter, callback ){
4762// //
4763// //   var data = this.get( concept, filter );
4764// //
4765// //   if( data )
4766// //       return callback( data );
4767// //
4768// //   //TODO: register callback like a weak listener
4769// //
4770// // //        $.ajax({
4771// // //              type: 'GET',
4772// // //              data: $.param( filter ),
4773// // //              success: callback, 
4774// // //              url: BASE_PATH + filter.URI || concept
4775// // //        });
4776// //   this.report( concept, filter );
4777// //     },
4778// //
4779// //     put: function( concept, data, filter ){
4780// //
4781// //   var beforeDiff = this.store( concept, $.extend({},data) );
4782// //
4783// //   this.report( concept, data, filter, beforeDiff );
4784// //     },
4785// //     
4786// //     
4787// //     /*var data = {
4788// //                   startTime: $.now(),
4789// //                   endTime: $.now() + 1800000,
4790// //                   summary: "meu querido evento",
4791// //                   description: "desc do evento",
4792// //                   location: "prognus software livre",
4793// //                   class: 1,
4794// //                   calendar: 1,
4795// //                   category: 1,
4796// //                   participants: [ {
4797// //                                      user: { isExternal: true, mail: "user7@prognus.org", name: "user7" }
4798// //                                 },{
4799// //                                      user: "1003"
4800// //                                 } ]
4801// //
4802// //             };*/
4803// //     
4804// //
4805// //     merge:function( data, target ){
4806// //       
4807// //   var diff = { New: {}, Update:{}, Delete: {} };
4808// //       
4809// //   for( var key in data )
4810// //   {
4811// //       if( !target[ key ] )
4812// //           diff.New[ key ] = target[ key ] = data[ key ];
4813// //
4814// //       
4815// //     
4816// //   }
4817// //       
4818// //     }
4819// //
4820// //     store: function( concept, data, filter ){
4821// //
4822// //   if( !data.spline )
4823// //       data = [ data ];
4824// //
4825// //   var target = this.storage.get( concept );
4826// //   
4827// //   var diff = { New: {}, Update:{}, Delete: {} };
4828// //
4829// //   for( var i = 0; i < data.length; i++ )
4830// //   {
4831// //       if( data[i].URI && target[ data[i].URI ] )
4832// //       {
4833// //           diff.Update[ data[i].URI ] = this.merge( target[ data[i].URI ], data[i] );
4834// //       }
4835// //       else
4836// //       {
4837// //           diff.New[] = data[i];
4838// //       }
4839// //   }
4840// //
4841// //   
4842// //
4843// //   this.broadcast( concept, data );
4844// //
4845// //   if( filter )
4846// //       target = this.filter( target, filter );
4847// //
4848// //   if( target )
4849// //       data = $.extend( target, data );
4850// //
4851// //   this.storage.set( concept, data );
4852// //
4853// // //        return;
4854// //     },
4855// //     
4856// //     set: function( concept, data, filter ){
4857// //
4858// //       
4859// //
4860// //     },
4861// //
4862// //     post: function( concept, data, filter, isNew ){
4863// //
4864// //   var callback = function(  ){ DataLayer.store( concept, data, filter ) };
4865// //
4866// //   //TODO: register callback like a weak listener
4867// //
4868// //   this.report( concept, data, filter, isNew );
4869// //     },
4870// //     
4871// //     report: function( concept, filter, postData, isNew ){
4872// //       
4873// //   $.ajax({
4874// //           type: postData ? isNew ? 'POST' : 'PUT' : 'GET',
4875// //           data: postData || $.param( filter ),
4876// //           success: function( data ){ DataLayer.broadcast( concept ) },
4877// //           url: BASE_PATH + filter.URI || concept
4878// //     });
4879// //     },
4880// //     
4881// //     del:function( concept, filter ){
4882// //
4883// //       
4884// //
4885// //     }
4886// //     
4887// //     broadcast: function( concept, data ){
4888// //
4889// //   
4890// //
4891// //     },
4892// //
4893// //     pool: function(){
4894// //       
4895// //     },
4896// //
4897// //     refresh: function(){
4898// //       
4899// //     }
4900// // };
4901//
4902// //
4903// // DataLayer = {
4904// //   
4905// //     get: function( concept, filter ){
4906// //
4907// //   var data = this.storage.get( concept );
4908// //
4909// //   if( !filter )
4910// //       return( data );
4911// //
4912// //   if( filter.charAt )
4913// //       filter = { URI: filter };
4914// //   
4915// //   var filtered = [];
4916// //
4917// //   $.each(data, function(i, obj){
4918// //     
4919// //       for( var attr in filter )
4920// //           if( filter[attr] !== obj[attr] )
4921// //               return( true );
4922// //
4923// //       filtered[i] = obj;
4924// //   });
4925// //
4926// //   return( filtered );
4927// //     },
4928// //
4929// //     find: function( concept, filter, callback ){
4930// //
4931// //   var data = this.get( concept, filter );
4932// //
4933// //   if( data )
4934// //       return callback( data );
4935// //
4936// //    $.ajax({
4937// //         type: 'GET',
4938// //         data: $.param( filter ),
4939// //         success: callback, 
4940// //         url: filter.URI || concept
4941// //   });
4942// //     },
4943// //
4944// //     put: function( concept, data, filter ){
4945// //
4946// //   var target = this.get( concept, filter );
4947// //
4948// //   if( target )
4949// //       data = $.extend( target, data );
4950// //       
4951// //   this.storage.set( concept, data );
4952// //   
4953// //   //diff
4954// //     },
4955// //     
4956// //     post: function( concept, data, filter ){
4957// //
4958// //   
4959// //
4960// //     },
4961// //     
4962// //     pool: function(){
4963// //       
4964// //     },
4965// //     
4966// //     queue: function(){
4967// //       
4968// //     },
4969// //     
4970// //     dequeue: function(){
4971// //       
4972// //     },
4973// //     
4974// //     refresh: function(){
4975// //       
4976// //     }
4977// // }
4978//
4979// // var DataLayer = {
4980//   
4981// //       cache: {},
4982//   
4983// //       get: function( concept, location ){
4984//     
4985//         /* if( location )
4986//          {*/
4987// //           var schema = $.data( this.cache, concept + ':schema' );
4988// //           var uri = [];
4989// //
4990// //           $.each( schema, function( i, addrs ){
4991// //                 uri[ uri.length ] = location[addrs];
4992// //           });
4993//
4994//              /*var filter = [], result = false;
4995//
4996//              while( !(result = $.data( this.cache, uri.join( '.' ) )) || !(uri = uri.join('.')) )
4997//                filter[ filter.length ] = uri.pop();
4998//   
4999//              if( !filter.length )
5000//              {
5001//                  var indexes = $.data( this.cache, uri + ':indexes' );
5002//
5003//                  if( indexes )
5004//                      Array.prototype.concat.apply( result, indexes );
5005//                 
5006//                  return( result );
5007//              }
5008//
5009//              for( var i = 0; i < result.length; i++ )
5010//              {
5011//                 
5012//              }
5013//
5014//              if( result.length )
5015//                  return( result );
5016//          }*/
5017//
5018// //       var data = $.data( this.cache, concept );
5019//
5020// //       if( !data )
5021// //           $.ajax( );
5022//
5023// //       return( data );
5024// //       },
5025//       
5026// //       data: function(){
5027// //
5028// //     
5029// //
5030// //       }
5031// //       
5032// //       search: function( concept, filter ){
5033// //
5034// //     var schema = $.data( this.cache, concept + ':schema' );
5035// //     var uri = [];
5036// //
5037// //     $.each( schema, function( i, addrs ){
5038// //           uri[ uri.length ] = location[addrs];
5039// //     });
5040// //       }
5041// //       put: function( concept, data, location ){
5042//
5043// //       if( location )
5044// //       {
5045// //           var schema = $.data( this.cache, concept + ':schema');
5046// //           var uri = [];
5047//
5048// //           $.each( schema, function( i, addrs ){
5049// //                 uri[ uri.length ] = location[addrs];
5050// //           });
5051//
5052// //           var result = false, filter = [];
5053//
5054// //           while( !(result = $.data( this.cache, uri.join('.')) )
5055// //               filter[ filter.length ] = uri.pop();
5056//
5057// //           $.data( this.cache, '
5058//
5059// //       }
5060//
5061// //           var model = this.storage.get( concept );
5062// //
5063// //           $.each( model, function( i, o ){
5064// //               $.each( location, function( ii, attr ){
5065// //                    if( o[ii] === attr )
5066// //                       return( false );
5067// //               });
5068// //           });
5069//
5070// //       return $.data( this.cache, concept, data );
5071//
5072// //       },
5073// //       del: function( concept, location ){
5074// //
5075// //       if( location )
5076// //       {
5077// //           var schema = $.data( this.cache, 'concepts', concept );
5078// //           var uri = [];
5079// //
5080// //           $.each( schema, function( i, addrs ){
5081// //                 uri[ uri.length ] = location[addrs];
5082// //           });
5083// //
5084// //           concept = uri.join( '.' );
5085//
5086// //           var model = this.storage.get( concept );
5087// //
5088// //           $.each( model, function( i, o ){
5089// //               $.each( location, function( ii, attr ){
5090// //                    if( o[ii] === attr )
5091// //                       return( false );
5092// //               });
5093// //           });
5094// //       }
5095// //       
5096// //   
5097// //       $.removeData( this.cache, concept );
5098// //       }
5099// // }
Note: See TracBrowser for help on using the repository browser.