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

Revision 6412, 125.3 KB checked in by acoutinho, 12 years ago (diff)

Ticket #2834 - Inconsistência com lixo no javaScript

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