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

Revision 6230, 125.4 KB checked in by acoutinho, 12 years ago (diff)

Ticket #2758 - Implementacao dos recursos de contatos dinamicos no modelo de rest

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