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

Revision 5919, 124.0 KB checked in by natan, 12 years ago (diff)

Ticket #2603 - Atualização no remove do DataLayer? e compatibilização com o IE 8

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