Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / docs / source / DataViewTransition.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 <div id="cls-Ext.ux.DataViewTransition"></div>/**
16  * @class Ext.ux.DataViewTransition
17  * @extends Object
18  * @author Ed Spencer (http://extjs.com)
19  * Transition plugin for DataViews
20  */
21 Ext.ux.DataViewTransition = Ext.extend(Object, {
22
23     <div id="prop-Ext.ux.DataViewTransition-defaults"></div>/**
24      * @property defaults
25      * @type Object
26      * Default configuration options for all DataViewTransition instances
27      */
28     defaults: {
29         duration  : 750,
30         idProperty: 'id'
31     },
32     
33     <div id="method-Ext.ux.DataViewTransition-constructor"></div>/**
34      * Creates the plugin instance, applies defaults
35      * @constructor
36      * @param {Object} config Optional config object
37      */
38     constructor: function(config) {
39         Ext.apply(this, config || {}, this.defaults);
40     },
41
42     <div id="method-Ext.ux.DataViewTransition-init"></div>/**
43      * Initializes the transition plugin. Overrides the dataview's default refresh function
44      * @param {Ext.DataView} dataview The dataview
45      */
46     init: function(dataview) {
47         <div id="prop-Ext.ux.DataViewTransition-dataview"></div>/**
48          * @property dataview
49          * @type Ext.DataView
50          * Reference to the DataView this instance is bound to
51          */
52         this.dataview = dataview;
53         
54         var idProperty = this.idProperty;
55         dataview.blockRefresh = true;
56         dataview.updateIndexes = dataview.updateIndexes.createSequence(function() {
57             this.getTemplateTarget().select(this.itemSelector).each(function(element, composite, index) {
58                 element.id = element.dom.id = String.format("{0}-{1}", dataview.id, store.getAt(index).get(idProperty));
59             }, this);
60         }, dataview);
61         
62         <div id="prop-Ext.ux.DataViewTransition-dataviewID"></div>/**
63          * @property dataviewID
64          * @type String
65          * The string ID of the DataView component. This is used internally when animating child objects
66          */
67         this.dataviewID = dataview.id;
68         
69         <div id="prop-Ext.ux.DataViewTransition-cachedStoreData"></div>/**
70          * @property cachedStoreData
71          * @type Object
72          * A cache of existing store data, keyed by id. This is used to determine
73          * whether any items were added or removed from the store on data change
74          */
75         this.cachedStoreData = {};
76         
77         var store = dataview.store;
78         
79         //cache the data immediately, and again on any load operation
80         this.cacheStoreData(store);
81         store.on('load', this.cacheStoreData, this);
82         
83         store.on('datachanged', function(store) {
84             var parentEl = dataview.getTemplateTarget(),
85                 calcItem = store.getAt(0),
86                 added    = this.getAdded(store),
87                 removed  = this.getRemoved(store),
88                 previous = this.getRemaining(store),
89                 existing = Ext.apply({}, previous, added);
90             
91             //hide old items
92             Ext.each(removed, function(item) {
93                 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).fadeOut({
94                     remove  : false,
95                     duration: duration / 1000,
96                     useDisplay: true
97                 });
98             }, this);
99             
100             //store is empty
101             if (calcItem == undefined) {
102                 this.cacheStoreData(store);
103                 return;
104             }
105             
106             var el = parentEl.child("#" + this.dataviewID + "-" + calcItem.get(this.idProperty));
107             
108             //calculate the number of rows and columns we have
109             var itemCount   = store.getCount(),
110                 itemWidth   = el.getMargins('lr') + el.getWidth(),
111                 itemHeight  = el.getMargins('bt') + el.getHeight(),
112                 dvWidth     = parentEl.getWidth(),
113                 columns     = Math.floor(dvWidth / itemWidth),
114                 rows        = Math.ceil(itemCount / columns),
115                 currentRows = Math.ceil(this.getExistingCount() / columns);
116             
117             //make sure the correct styles are applied to the parent element
118             parentEl.applyStyles({
119                 display : 'block',
120                 position: 'relative'
121                 // ,
122                 // height  : Ext.max([rows, currentRows]) * itemHeight,
123                 // width   : columns * itemWidth
124             });
125             
126             //stores the current top and left values for each element (discovered below)
127             var oldPositions = {},
128                 newPositions = {},
129                 elCache      = {};
130             
131             //find current positions of each element and save a reference in the elCache
132             Ext.iterate(previous, function(id, item) {
133                 var id = item.get(this.idProperty),
134                     el = elCache[id] = parentEl.child('#' + this.dataviewID + '-' + id);
135                 
136                 oldPositions[id] = {
137                     top : el.getTop()  - parentEl.getTop()  - el.getMargins('t') - parentEl.getPadding('t'),
138                     left: el.getLeft() - parentEl.getLeft() - el.getMargins('l') - parentEl.getPadding('l')
139                 };
140             }, this);
141             
142             //set absolute positioning on all DataView items. We need to set position, left and 
143             //top at the same time to avoid any flickering
144             Ext.iterate(previous, function(id, item) {
145                 var oldPos = oldPositions[id],
146                     el     = elCache[id];
147                     
148                 if (el.getStyle('position') != 'absolute') {
149                     elCache[id].applyStyles({
150                         position: 'absolute',
151                         left    : oldPos.left + "px",
152                         top     : oldPos.top + "px",
153
154                         //we set the width here to make ListViews work correctly. This is not needed for DataViews
155                         width   : el.getWidth(!Ext.isIE || Ext.isStrict),
156                         height  : el.getHeight(!Ext.isIE || Ext.isStrict)
157                     });
158                 }
159             });
160             
161             //get new positions
162             var index = 0;
163             Ext.iterate(store.data.items, function(item) {
164                 var id = item.get(idProperty),
165                     el = elCache[id];
166                 
167                 var column = index % columns,
168                     row    = Math.floor(index / columns),
169                     top    = row    * itemHeight,
170                     left   = column * itemWidth;
171                 
172                 newPositions[id] = {
173                     top : top,
174                     left: left
175                 };
176                 
177                 index ++;
178             }, this);
179             
180             //do the movements
181             var startTime  = new Date(),
182                 duration   = this.duration,
183                 dataviewID = this.dataviewID;
184             
185             var doAnimate = function() {
186                 var elapsed  = new Date() - startTime,
187                     fraction = elapsed / duration;
188                 
189                 if (fraction >= 1) {
190                     for (var id in newPositions) {
191                         Ext.fly(dataviewID + '-' + id).applyStyles({
192                             top : newPositions[id].top + "px",
193                             left: newPositions[id].left + "px"
194                         });
195                     }
196                     
197                     Ext.TaskMgr.stop(task);
198                 } else {
199                     //move each item
200                     for (var id in newPositions) {
201                         if (!previous[id]) continue;
202                         
203                         var oldPos  = oldPositions[id],
204                             newPos  = newPositions[id],
205                             oldTop  = oldPos.top,
206                             newTop  = newPos.top,
207                             oldLeft = oldPos.left,
208                             newLeft = newPos.left,
209                             diffTop = fraction * Math.abs(oldTop  - newTop),
210                             diffLeft= fraction * Math.abs(oldLeft - newLeft),
211                             midTop  = oldTop  > newTop  ? oldTop  - diffTop  : oldTop  + diffTop,
212                             midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
213                         
214                         Ext.fly(dataviewID + '-' + id).applyStyles({
215                             top : midTop + "px",
216                             left: midLeft + "px"
217                         });
218                     }
219                 }
220             };
221             
222             var task = {
223                 run     : doAnimate,
224                 interval: 20,
225                 scope   : this
226             };
227             
228             Ext.TaskMgr.start(task);
229             
230             //show new items
231             Ext.iterate(added, function(id, item) {
232                 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).applyStyles({
233                     top : newPositions[id].top + "px",
234                     left: newPositions[id].left + "px"
235                 }).fadeIn({
236                     remove  : false,
237                     duration: duration / 1000
238                 });
239             }, this);
240             
241             this.cacheStoreData(store);
242         }, this);
243     },
244     
245     <div id="method-Ext.ux.DataViewTransition-cacheStoreData"></div>/**
246      * Caches the records from a store locally for comparison later
247      * @param {Ext.data.Store} store The store to cache data from
248      */
249     cacheStoreData: function(store) {
250         this.cachedStoreData = {};
251         
252         store.each(function(record) {
253              this.cachedStoreData[record.get(this.idProperty)] = record;
254         }, this);
255     },
256     
257     <div id="method-Ext.ux.DataViewTransition-getExisting"></div>/**
258      * Returns all records that were already in the DataView
259      * @return {Object} All existing records
260      */
261     getExisting: function() {
262         return this.cachedStoreData;
263     },
264     
265     <div id="method-Ext.ux.DataViewTransition-getExistingCount"></div>/**
266      * Returns the total number of items that are currently visible in the DataView
267      * @return {Number} The number of existing items
268      */
269     getExistingCount: function() {
270         var count = 0,
271             items = this.getExisting();
272         
273         for (var k in items) count++;
274         
275         return count;
276     },
277     
278     <div id="method-Ext.ux.DataViewTransition-getAdded"></div>/**
279      * Returns all records in the given store that were not already present
280      * @param {Ext.data.Store} store The updated store instance
281      * @return {Object} Object of records not already present in the dataview in format {id: record}
282      */
283     getAdded: function(store) {
284         var added = {};
285         
286         store.each(function(record) {
287             if (this.cachedStoreData[record.get(this.idProperty)] == undefined) {
288                 added[record.get(this.idProperty)] = record;
289             }
290         }, this);
291         
292         return added;
293     },
294     
295     <div id="method-Ext.ux.DataViewTransition-getRemoved"></div>/**
296      * Returns all records that are present in the DataView but not the new store
297      * @param {Ext.data.Store} store The updated store instance
298      * @return {Array} Array of records that used to be present
299      */
300     getRemoved: function(store) {
301         var removed = [];
302         
303         for (var id in this.cachedStoreData) {
304             if (store.findExact(this.idProperty, Number(id)) == -1) removed.push(this.cachedStoreData[id]);
305         }
306         
307         return removed;
308     },
309     
310     <div id="method-Ext.ux.DataViewTransition-getRemaining"></div>/**
311      * Returns all records that are already present and are still present in the new store
312      * @param {Ext.data.Store} store The updated store instance
313      * @return {Object} Object of records that are still present from last time in format {id: record}
314      */
315     getRemaining: function(store) {
316       var remaining = {};
317       
318       store.each(function(record) {
319           if (this.cachedStoreData[record.get(this.idProperty)] != undefined) {
320               remaining[record.get(this.idProperty)] = record;
321           }
322       }, this);
323       
324       return remaining;
325     }
326 });</pre>    
327 </body>
328 </html>