source: trunk/prototype/api/datalayer.js @ 6286

Revision 6286, 125.1 KB checked in by acoutinho, 12 years ago (diff)

Ticket #2479 - Iconsistencia ao remover evento de duas agendas simultaneas

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