Changeset 5341 for trunk/prototype/app/datalayer.js
- Timestamp:
- 01/10/12 11:25:51 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/prototype/app/datalayer.js
r5283 r5341 48 48 $("form").live( "submit", function( event ){ 49 49 50 var action = $(this).attr("action"), res = false; 50 var $this = $(this), action = $this.attr('action'), res = false, 51 52 method = $this.attr( 'method' ), 53 54 fileInputs = $this.find('input[type="file"]'); 55 56 if( fileInputs.length && !$this.is('[enctype="multipart/form-data"]') ) 57 { 58 event.preventDefault(); 59 60 DataLayer.send( action, 61 [ method, 'iframe json' ], {}, 62 //TODO: check the type for conversion 63 DataLayer.receive, 64 false, { 'formData': $this.serializeArray(), 'fileInput': fileInputs } ); 65 66 return( false ); 67 } 51 68 52 69 if( res = internalUrl.exec( action ) ) … … 55 72 56 73 var data = DataLayer.form( this ); 74 75 switch( method.toUpperCase() ) 76 { 77 case 'GET': 78 DataLayer.get( res[0], data ); 79 80 case 'POST': 57 81 DataLayer.put( res[1], data ); 82 } 58 83 59 84 return( false ); … … 63 88 }); 64 89 65 $.storage = new $.store();90 this.storage = new $.store(); 66 91 67 92 DataLayer = { … … 113 138 }, 114 139 115 send: function( url, type, data, callback, sync ){140 send: function( url, type, data, callback, sync, extraOptions ){ 116 141 117 142 var result = false, fired = false; … … 147 172 envelope['dataType'] = type[1]; 148 173 174 if( extraOptions ) 175 envelope = $.extend( envelope, extraOptions ); 176 149 177 $.ajax( envelope ); 150 178 … … 152 180 }, 153 181 154 dispatch: function( dispatcher, data, callback, isPost ){182 dispatch: function( dispatcher, data, callback, isPost, dataType ){ 155 183 156 184 return this.send( this.dispatchPath + dispatcher + ".php", 157 [ ( isPost ? 'post' : 'get' ), 'json' ],185 [ ( isPost ? 'post' : 'get' ), dataType || 'json' ], 158 186 data, 159 187 callback ); … … 300 328 $.each( data[i][link], function( ii, el ){ 301 329 302 var isRef = false 330 var isRef = false; 303 331 304 332 if( isRef = ($.type(el) === "string") ) … … 332 360 333 361 if( data[i].id ) 334 data[i] = this.merge( concept,current[ data[i].id ], data[i] );362 data[i] = this.merge( current[ data[i].id ], data[i] ); 335 363 336 364 current[ key ] = data[i]; … … 344 372 for( var setKey in updateSet ) 345 373 { 346 DataLayer.put( setKey, updateSet[ setKey ], false );347 348 374 if( bothSides ) 349 375 for( var i = 0; i < updateSet[ setKey ].length; i++ ) 350 376 this.report( setKey, updateSet[ setKey ][i].id, updateSet[ setKey ][i] ); 377 378 DataLayer.put( setKey, updateSet[ setKey ], false ); 351 379 } 352 380 } … … 383 411 current[ id ] = this.check( concept, id ) || {}; 384 412 385 this.store( ':current', concept, current );413 this.store( ':current', concept, current[ id ] ); 386 414 387 415 var diff = this.diff( current[ id ], data ); … … 389 417 var diffs = this.check( ':diff', concept ) || {}; 390 418 419 if( diffs[ id ] ) 420 diff = this.merge( diffs[ id ], diff ); 421 422 if( !$.isEmptyObject( diff ) ) 391 423 diffs[ id ] = diff; 392 424 … … 430 462 prepareQ: function( queueName, concept, ids ){ 431 463 464 var notArray = false; 465 432 466 if( notArray = ($.type(concept) !== "array") ) 433 467 concept = [ concept ]; 468 469 var q = {}; 434 470 435 471 for( var i = 0; i < concept.length; i++ ) … … 437 473 var queue = this.check( ':' + queueName, concept[i] || false ); 438 474 475 if( !queue ) continue; 476 439 477 if( ids ) 440 478 { … … 451 489 queue = filtered; 452 490 } 491 492 q[ concept[i] ] = queue; 453 493 } 454 494 455 return( queue);495 return( notArray ? q[ concept[0] ] : q ); 456 496 }, 457 497 458 498 clearQ: function( concept, ids ){ 459 499 460 var current = this.check( ':current', concept || false );500 // var current = this.check( ':current', concept || false ); 461 501 var diffs = this.check( ':diff', concept || false ); 462 502 463 503 if( !ids ) 464 current =diffs = {};504 /* current =*/ diffs = {}; 465 505 else 466 506 { … … 470 510 for( var i = 0; i < ids.length; i++ ) 471 511 { 472 delete current[ ids[i] ];512 // delete current[ ids[i] ]; 473 513 delete diffs[ ids[i] ]; 474 514 } 475 515 } 476 516 477 this.store( ':current', concept, current );517 // this.store( ':current', concept, current ); 478 518 this.store( ':diff', concept, diffs ); 479 519 }, … … 483 523 var queue = this.prepareQ( 'diff', concept, ids ); 484 524 485 this.sync( queue, concept || false, callback );525 this.sync( queue, !$.isArray(concept) && concept || false, callback ); 486 526 }, 487 527 … … 523 563 // } 524 564 565 var received = DataLayer.receive( data ); 566 567 for( var URI in URIs ) 568 if( typeof received[URI] !== "undefined" ) 569 DataLayer.clearQ( URIs[URI].concept, URIs[URI].id ); 570 571 if( callback ) 572 callback( received ); 573 574 // for( var URI in data ) 575 // { 576 // var parsed = DataLayer.parseURI( URI ), 577 // 578 // concept = parsed[1], id = parsed[3]; 579 // 580 // if( $.type(data[URI]) === "string" ) 581 // { 582 // //TODO:threat the exception thrown 583 // DataLayer.rollback( concept, id ); 584 // delete URIs[ URI ]; 585 // continue; 586 // } 587 // 588 // if( data[URI] === false ){ 589 // DataLayer.remove( concept, id, false ); 590 // continue; 591 // } 592 // 593 // if( id !== data[URI].id ) 594 // DataLayer.move( concept, id, data[URI].id ); 595 // 596 // DataLayer.put( concept, id, data[URI], false ); 597 // } 598 // 599 // for( var URI in URIs ) 600 // DataLayer.clearQ( URIs[URI].concept, URIs[URI].id ); 601 // 602 // if( callback ) 603 // callback(); 604 605 }, true ); 606 607 }, 608 609 receive: function( data ){ 610 611 var received = {}; 612 525 613 for( var URI in data ) 526 614 { 527 615 var parsed = DataLayer.parseURI( URI ), 528 616 529 concept = parsed[1], id = parsed[3]; 617 concept = parsed[4], id = parsed[5]; 618 619 received[ URI ] = data[ URI ]; 530 620 531 621 if( $.type(data[URI]) === "string" ) … … 533 623 //TODO:threat the exception thrown 534 624 DataLayer.rollback( concept, id ); 535 delete URIs[ URI ];536 625 continue; 537 626 } … … 548 637 } 549 638 550 for( var URI in URIs ) 551 DataLayer.clearQ( URIs[URI].concept, URIs[URI].id ); 639 return( received ); 552 640 553 if( callback ) 554 callback(); 555 556 }, true ); 557 558 }, 559 560 receive: function( concept, data ){ 561 562 this.put( concept, data, false ); 563 564 // if( typeof data.URI === "undefined" ) 565 // { 566 // URI = this.add( concept, data, oneSide ); 567 // } 568 // else if( data.URI === false ) 569 // { 570 // status = this.remove( concept, URI, oneSide ); 571 // } 572 // else 573 // { 574 // status = this.set( concept, URI, data, oneSide ); 575 // } 576 // 577 // if( URI && data.URI && URI !== data.URI ) 578 // this.move( concept, URI, data.URI ); 641 }, 642 643 644 645 merge: function( current, data ){ 646 647 return this.copy( data, current ); 648 649 // return $.extend( current, data ); 650 651 }, 652 653 // clone objects, skip other types. 654 clone: function(target) { 655 if ( typeof target == 'object' ) { 656 Clone.prototype = target; 657 return new Clone(); 658 } else { 659 return target; 660 } 661 }, 662 663 // Shallow Copy 664 shallowCopy: function(target) { 665 if (typeof target !== 'object' ) { 666 return target; // non-object have value sematics, so target is already a copy. 667 } else { 668 var value = target.valueOf(); 669 if (target != value) { 670 // the object is a standard object wrapper for a native type, say String. 671 // we can make a copy by instantiating a new object around the value. 672 return new target.constructor(value); 673 } else { 674 // ok, we have a normal object. If possible, we'll clone the original's prototype 675 // (not the original) to get an empty object with the same prototype chain as 676 // the original. If just copy the instance properties. Otherwise, we have to 677 // copy the whole thing, property-by-property. 678 if ( target instanceof target.constructor && target.constructor !== Object ) { 679 var c = clone(target.constructor.prototype); 680 681 // give the copy all the instance properties of target. It has the same 682 // prototype as target, so inherited properties are already there. 683 for ( var property in target) { 684 if (target.hasOwnProperty(property)) { 685 c[property] = target[property]; 686 } 687 } 688 } else { 689 var c = {}; 690 for ( var property in target ) c[property] = target[property]; 691 } 692 693 return c; 694 } 695 } 696 }, 697 698 // entry point for deep copy. 699 // source is the object to be deep copied. 700 // depth is an optional recursion limit. Defaults to 256. 701 // deep copy handles the simple cases itself: non-objects and object's we've seen before. 702 // For complex cases, it first identifies an appropriate DeepCopier, then delegate the details of copying the object to him. 703 copy: function(source, result, depth) { 704 705 // null is a special case: it's the only value of type 'object' without properties. 706 if ( source === null ) return null; 707 708 // All non-objects use value semantics and don't need explict copying. 709 if ( typeof source !== 'object' ) return source; 710 711 if( !depth || !(depth instanceof RecursionHelper) ) depth = new RecursionHelper(depth); 712 713 var cachedResult = depth.getCachedResult(source); 714 715 // we've already seen this object during this deep copy operation 716 // so can immediately return the result. This preserves the cyclic 717 // reference structure and protects us from infinite recursion. 718 if ( cachedResult ) return cachedResult; 719 720 // objects may need special handling depending on their class. There is 721 // a class of handlers call "DeepCopiers" that know how to copy certain 722 // objects. There is also a final, generic deep copier that can handle any object. 723 for ( var i=0; i<this.comparators.length; i++ ) { 724 725 var comparator = this.comparators[i]; 726 727 if ( comparator.can(source) ) { 579 728 580 581 }, 582 583 584 585 merge: function( concept, current, data ){ 586 587 return $.extend( current, data ); 588 729 // once we've identified which DeepCopier to use, we need to call it in a very 730 // particular order: create, cache, populate. This is the key to detecting cycles. 731 // We also keep track of recursion depth when calling the potentially recursive 732 // populate(): this is a fail-fast to prevent an infinite loop from consuming all 733 // available memory and crashing or slowing down the browser. 734 735 if( !result ) 736 // Start by creating a stub object that represents the copy. 737 result = comparator.create(source); 738 else if( !comparator.can(result) ) 739 throw new Error("can't compare diferent kind of objects."); 740 741 // we now know the deep copy of source should always be result, so if we encounter 742 // source again during this deep copy we can immediately use result instead of 743 // descending into it recursively. 744 depth.cacheResult(source, result); 745 746 // only DeepCopier.populate() can recursively deep copy. So, to keep track 747 // of recursion depth, we increment this shared counter before calling it, 748 // and decrement it afterwards. 749 depth.depth++; 750 if ( depth.depth > depth.maxDepth ) { 751 throw new Error("Exceeded max recursion depth in deep copy."); 752 } 753 754 var thisPass = this; 755 756 // It's now safe to let the comparator recursively deep copy its properties. 757 comparator.populate( function(source, result) { return thisPass.copy(source, result, depth); }, source, result ); 758 759 depth.depth--; 760 761 return result; 762 } 763 } 764 // the generic copier can handle anything, so we should never reach this line. 765 throw new Error("no DeepCopier is able to copy " + source); 766 }, 767 768 // publicly expose the list of deepCopiers. 769 comparators: [], 770 771 // make deep copy() extensible by allowing others to 772 // register their own custom Comparators. 773 registerComparator: function(comparatorOptions) { 774 775 // publicly expose the Comparator class. 776 var comparator = { 777 778 // determines if this Comparator can handle the given object. 779 can: function(source) { return false; }, 780 781 // starts the deep copying process by creating the copy object. You 782 // can initialize any properties you want, but you can't call recursively 783 // into the copy(). 784 create: function(source) { }, 785 786 // Completes the deep copy of the source object by populating any properties 787 // that need to be recursively deep copied. You can do this by using the 788 // provided deepCopyAlgorithm instance's copy() method. This will handle 789 // cyclic references for objects already deepCopied, including the source object 790 // itself. The "result" passed in is the object returned from create(). 791 populate: function(deepCopyAlgorithm, source, result) {} 792 }; 793 794 for ( var key in comparatorOptions ) comparator[key] = comparatorOptions[key]; 795 796 this.comparators.unshift( comparator ); 589 797 }, 590 798 591 799 diff: function( base, toDiff ){ 592 800 593 // var df = toDiff; 594 // 595 // for( var key in base ){ 596 // 597 // if( base[key] === df[key] ) 598 // delete df[key]; 599 // 600 // 601 // } 801 if( typeof base === 'undefined' || $.isEmptyObject(base) ) 802 return( toDiff ); 803 602 804 if( toDiff === false ) 603 805 return( false ); 604 806 605 var toDiff = $.extend( {}, toDiff ); 606 607 if( isGeneratedId.test( toDiff.id ) ) 608 delete toDiff['id']; 807 toDiff = $.extend( {}, toDiff ); 808 809 for( var key in toDiff ) 810 { 811 switch( $.type(toDiff[key]) ) 812 { 813 case 'object': 814 if( $.isEmptyObject(toDiff[key] = this.diff( base[key], toDiff[key] )) ) 815 delete toDiff[key]; 816 break; 817 case 'array': 818 if( base[key] && !(toDiff[key] = $.grep( toDiff[key], function( el, i ){ return( $.inArray( el, base[key] ) === -1 ); } )).length ) 819 delete toDiff[key]; 820 break; 821 default: 822 if( base[key] == toDiff[key] ) 823 delete toDiff[key]; 824 } 825 } 609 826 610 827 return( toDiff ); … … 733 950 result = this.request( concept, id || filter.filter, filter.criteria ); 734 951 735 if( result && bothSides )952 if( result && bothSides && !filter.criteria.format ) 736 953 { 737 954 var newResult = []; … … 807 1024 }, 808 1025 1026 // clone: function( object ){ 1027 // 1028 // new { prototype: object }; 1029 // 1030 // }, 809 1031 810 1032 check: function( namespace, keys ){ … … 813 1035 return( false ); 814 1036 815 var result = $.storage.get( namespace );1037 var result = this.storage.get( namespace ); 816 1038 817 1039 if( !keys || !result ) … … 831 1053 }, 832 1054 1055 storage: { 1056 1057 cache: {}, 1058 1059 set: function( key, value ){ 1060 1061 this.cache[key] = value; 1062 1063 }, 1064 get: function( key ){ 1065 1066 return DataLayer.copy( this.cache[key] ); 1067 1068 }, 1069 del: function( key ){ 1070 1071 delete this.cache[key]; 1072 1073 } 1074 }, 1075 1076 flush: function(){ 1077 1078 }, 1079 1080 restore: function(){ 1081 1082 }, 1083 833 1084 store: function( namespace, key, data ){ 834 1085 835 1086 if( !data ) 836 return $.storage.set( namespace, key );1087 return this.storage.set( namespace, key ); 837 1088 838 1089 var res = this.check( namespace ) || {}; … … 840 1091 res[key] = data; 841 1092 842 return $.storage.set( namespace, res );1093 return this.storage.set( namespace, res ); 843 1094 }, 844 1095 … … 846 1097 847 1098 if( !key ) 848 return $.storage.del( namespace );1099 return this.storage.del( namespace ); 849 1100 850 1101 var res = this.check( namespace ) || {}; … … 852 1103 delete res[key]; 853 1104 854 return $.storage.set( namespace, res );1105 return this.storage.set( namespace, res ); 855 1106 856 1107 }, … … 858 1109 move: function( concept, oldId, newId ){ 859 1110 860 this. store( concept, newId, this.check( concept, oldId ));1111 this.put( concept, newId, this.check( concept, oldId ), false ); 861 1112 862 1113 this.del( concept, oldId ); … … 1308 1559 // 1309 1560 // if( !URI ) 1310 // return $.storage.del( concept );1561 // return this.storage.del( concept ); 1311 1562 // 1312 1563 // var res = this.check( concept ); … … 1499 1750 register: function( kind, concept, deployable ){ 1500 1751 1752 if( arguments.length < 3 ) 1753 { 1754 deployable = concept; 1755 concept = kind; 1756 kind = 'global'; 1757 } 1758 1501 1759 if( !this[ kind ][ concept ] ) 1502 1760 this[ kind ][ concept ] = []; … … 1522 1780 var result = tasks[i].task( now ); 1523 1781 1782 if( tasks[i].factor ) 1524 1783 DataLayer.schedule( tasks[i].task, tasks[i].factor ); 1525 1784 } … … 1531 1790 }, 1532 1791 1792 task: function( timestamp, task, factor ) 1793 { 1794 if( !this.tasks[ timestamp ] ) 1795 this.tasks[ timestamp ] = []; 1796 1797 this.tasks[ timestamp ][ this.tasks[ timestamp ].length ] = { task: task, factor: factor || false }; 1798 }, 1799 1533 1800 schedule: function( task, time ){ 1534 1801 … … 1537 1804 var index = parseInt( $.now() / 1000 ) + time; 1538 1805 1539 if( !this.tasks[ index ] ) 1540 this.tasks[ index ] = []; 1541 1542 this.tasks[ index ][ this.tasks[ index ].length ] = { task: task, factor: time }; 1806 this.task( index, task, time ); 1543 1807 }, 1544 1808 … … 1565 1829 this.basePath = this.dispatchPath + "REST.php?q="; 1566 1830 1831 this.schedule( function( now ){ 1832 1833 DataLayer.flush(); 1834 1835 }); 1836 1567 1837 this.start(); 1568 1838 } 1569 1839 } 1570 1840 1841 // the re-usable constructor function used by clone(). 1842 function Clone() {} 1843 1844 //Recursion Helper 1845 function RecursionHelper(){ this.clear(); }; 1846 1847 RecursionHelper.prototype = { 1848 1849 constructor: RecursionHelper, 1850 1851 // copiedObjects keeps track of objects already copied by this 1852 // deepCopy operation, so we can correctly handle cyclic references. 1853 copiedObjects: [], 1854 1855 depth: 0, 1856 1857 maxDepth: 256, 1858 1859 //reset the recursion helper cache 1860 clear: function(){ 1861 this.copiedObjects = []; 1862 this.depth = 0; 1863 }, 1864 1865 // add an object to the cache. No attempt is made to filter duplicates; 1866 // we always check getCachedResult() before calling it. 1867 cacheResult: function(source, result) { 1868 this.copiedObjects.push([source, result]); 1869 }, 1870 1871 // Returns the cached copy of a given object, or undefined if it's an 1872 // object we haven't seen before. 1873 getCachedResult: function(source) { 1874 1875 for ( var i=0; i<this.copiedObjects.length; i++ ) { 1876 if ( this.copiedObjects[i][0] === source ) { 1877 return this.copiedObjects[i][1]; 1878 } 1879 } 1880 1881 return undefined; 1882 } 1883 }; 1884 1885 // Generic Object copier 1886 // the ultimate fallback DeepCopier, which tries to handle the generic case. This 1887 // should work for base Objects and many user-defined classes. 1888 DataLayer.registerComparator({ 1889 can: function(source) { return true; }, 1890 1891 create: function(source) { 1892 if ( source instanceof source.constructor ) { 1893 return DataLayer.clone(source.constructor.prototype); 1894 } else { 1895 return {}; 1896 } 1897 }, 1898 1899 populate: function(deepCopy, source, result) { 1900 for ( var key in source ) { 1901 if ( source.hasOwnProperty(key) ) { 1902 result[key] = deepCopy(source[key], result[key]); 1903 } 1904 } 1905 return result; 1906 } 1907 }); 1908 1909 // Array copier 1910 DataLayer.registerComparator({ 1911 can: function(source) { 1912 return ( source instanceof Array ); 1913 }, 1914 1915 create: function(source) { 1916 return new source.constructor(); 1917 }, 1918 1919 populate: function(deepCopy, source, result) { 1920 for ( var i=0; i<source.length; i++) { 1921 result.push( deepCopy(source[i], result[i]) ); 1922 } 1923 return result; 1924 } 1925 }); 1926 1927 // Date copier 1928 DataLayer.registerComparator({ 1929 can: function(source) { 1930 return ( source instanceof Date ); 1931 }, 1932 1933 create: function(source) { 1934 return new Date(source); 1935 } 1936 }); 1937 1938 // HTML DOM Node copier 1939 DataLayer.registerComparator({ 1940 1941 // function to detect Nodes. In particular, we're looking 1942 // for the cloneNode method. The global document is also defined to 1943 // be a Node, but is a special case in many ways. 1944 can: function(source) { 1945 1946 if ( window.Node ) { 1947 return source instanceof Node; 1948 } else { 1949 // the document is a special Node and doesn't have many of 1950 // the common properties so we use an identity check instead. 1951 if ( source === document ) return true; 1952 return ( 1953 typeof source.nodeType === 'number' && 1954 source.attributes && 1955 source.childNodes && 1956 source.cloneNode 1957 ); 1958 } 1959 }, 1960 1961 create: function(source) { 1962 // there can only be one (document). 1963 if ( source === document ) return document; 1964 1965 // start with a shallow copy. We'll handle the deep copy of 1966 // its children ourselves. 1967 return source.cloneNode(false); 1968 }, 1969 1970 diff: function(base, source){ 1971 1972 }, 1973 1974 populate: function(deepCopy, source, result) { 1975 // we're not copying the global document, so don't have to populate it either. 1976 if ( source === document ) return document; 1977 1978 // if this Node has children, deep copy them one-by-one. 1979 if ( source.childNodes && source.childNodes.length ) { 1980 for ( var i=0; i<source.childNodes.length; i++ ) { 1981 var childCopy = deepCopy(source.childNodes[i], result.childNodes[i] || false ); 1982 result.appendChild(childCopy); 1983 } 1984 } 1985 } 1986 }); 1987 1571 1988 DataLayer.init(); 1572 1989 … … 1581 1998 // get: function( concept, filter ){ 1582 1999 // 1583 // var data = $.storage.get( concept );2000 // var data = this.storage.get( concept ); 1584 2001 // 1585 2002 // if( !filter ) … … 1672 2089 // data = [ data ]; 1673 2090 // 1674 // var target = $.storage.get( concept );2091 // var target = this.storage.get( concept ); 1675 2092 // 1676 2093 // var diff = { New: {}, Update:{}, Delete: {} }; … … 1698 2115 // data = $.extend( target, data ); 1699 2116 // 1700 // $.storage.set( concept, data );2117 // this.storage.set( concept, data ); 1701 2118 // 1702 2119 // // return; … … 1754 2171 // get: function( concept, filter ){ 1755 2172 // 1756 // var data = $.storage.get( concept );2173 // var data = this.storage.get( concept ); 1757 2174 // 1758 2175 // if( !filter ) … … 1798 2215 // data = $.extend( target, data ); 1799 2216 // 1800 // $.storage.set( concept, data );2217 // this.storage.set( concept, data ); 1801 2218 // 1802 2219 // //diff … … 1908 2325 // } 1909 2326 1910 // var model = $.storage.get( concept );2327 // var model = this.storage.get( concept ); 1911 2328 // 1912 2329 // $.each( model, function( i, o ){ … … 1933 2350 // concept = uri.join( '.' ); 1934 2351 1935 // var model = $.storage.get( concept );2352 // var model = this.storage.get( concept ); 1936 2353 // 1937 2354 // $.each( model, function( i, o ){
Note: See TracChangeset
for help on using the changeset viewer.