3 * Copyright(c) 2006-2010 Ext JS, Inc.
5 * http://www.extjs.com/license
8 * @class Ext.ux.DataViewTransition
10 * @author Ed Spencer (http://extjs.com)
11 * Transition plugin for DataViews
13 Ext.ux.DataViewTransition = Ext.extend(Object, {
18 * Default configuration options for all DataViewTransition instances
27 * Creates the plugin instance, applies defaults
29 * @param {Object} config Optional config object
31 constructor: function(config) {
32 Ext.apply(this, config || {}, this.defaults);
36 * Initializes the transition plugin. Overrides the dataview's default refresh function
37 * @param {Ext.DataView} dataview The dataview
39 init: function(dataview) {
43 * Reference to the DataView this instance is bound to
45 this.dataview = dataview;
47 var idProperty = this.idProperty;
48 dataview.blockRefresh = true;
49 dataview.updateIndexes = dataview.updateIndexes.createSequence(function() {
50 this.getTemplateTarget().select(this.itemSelector).each(function(element, composite, index) {
51 element.id = element.dom.id = String.format("{0}-{1}", dataview.id, store.getAt(index).get(idProperty));
56 * @property dataviewID
58 * The string ID of the DataView component. This is used internally when animating child objects
60 this.dataviewID = dataview.id;
63 * @property cachedStoreData
65 * A cache of existing store data, keyed by id. This is used to determine
66 * whether any items were added or removed from the store on data change
68 this.cachedStoreData = {};
70 var store = dataview.store;
72 //cache the data immediately, and again on any load operation
73 this.cacheStoreData(store);
74 store.on('load', this.cacheStoreData, this);
76 store.on('datachanged', function(store) {
77 var parentEl = dataview.getTemplateTarget(),
78 calcItem = store.getAt(0),
79 added = this.getAdded(store),
80 removed = this.getRemoved(store),
81 previous = this.getRemaining(store),
82 existing = Ext.apply({}, previous, added);
85 if (calcItem == undefined) return;
87 var el = parentEl.child("#" + this.dataviewID + "-" + calcItem.get('id'));
89 //calculate the number of rows and columns we have
90 var itemCount = store.getCount(),
91 itemWidth = el.getMargins('lr') + el.getWidth(),
92 itemHeight = el.getMargins('bt') + el.getHeight(),
93 dvWidth = parentEl.getWidth(),
94 columns = Math.floor(dvWidth / itemWidth),
95 rows = Math.ceil(itemCount / columns),
96 currentRows = Math.ceil(this.getExistingCount() / columns);
98 //make sure the correct styles are applied to the parent element
99 parentEl.applyStyles({
103 // height : Ext.max([rows, currentRows]) * itemHeight,
104 // width : columns * itemWidth
107 //stores the current top and left values for each element (discovered below)
108 var oldPositions = {},
112 //find current positions of each element and save a reference in the elCache
113 Ext.iterate(previous, function(id, item) {
114 var id = item.get('id'),
115 el = elCache[id] = parentEl.child('#' + this.dataviewID + '-' + id);
117 oldPositions[item.get('id')] = {
118 top : el.getTop() - parentEl.getTop() - el.getMargins('t') - parentEl.getPadding('t'),
119 left: el.getLeft() - parentEl.getLeft() - el.getMargins('l') - parentEl.getPadding('l')
123 //set absolute positioning on all DataView items. We need to set position, left and
124 //top at the same time to avoid any flickering
125 Ext.iterate(previous, function(id, item) {
126 var oldPos = oldPositions[id],
129 if (el.getStyle('position') != 'absolute') {
130 elCache[id].applyStyles({
131 position: 'absolute',
135 //we set the width here to make ListViews work correctly. This is not needed for DataViews
136 width : el.getWidth(!Ext.isIE),
137 height : el.getHeight(!Ext.isIE)
144 Ext.iterate(store.data.items, function(item) {
145 var id = item.get(idProperty),
148 var column = index % columns,
149 row = Math.floor(index / columns),
150 top = row * itemHeight,
151 left = column * itemWidth;
162 var startTime = new Date(),
163 duration = this.duration,
164 dataviewID = this.dataviewID;
166 var doAnimate = function() {
167 var elapsed = new Date() - startTime,
168 fraction = elapsed / duration;
171 for (var id in newPositions) {
172 Ext.fly(dataviewID + '-' + id).applyStyles({
173 top : newPositions[id].top,
174 left: newPositions[id].left
178 Ext.TaskMgr.stop(task);
181 for (var id in newPositions) {
182 if (!previous[id]) continue;
184 var oldPos = oldPositions[id],
185 newPos = newPositions[id],
188 oldLeft = oldPos.left,
189 newLeft = newPos.left,
190 diffTop = fraction * Math.abs(oldTop - newTop),
191 diffLeft= fraction * Math.abs(oldLeft - newLeft),
192 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
193 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
195 Ext.fly(dataviewID + '-' + id).applyStyles({
209 Ext.TaskMgr.start(task);
212 Ext.each(removed, function(item) {
213 Ext.fly(this.dataviewID + '-' + item.get('id')).fadeOut({
215 duration: duration / 1000,
221 Ext.iterate(added, function(id, item) {
222 Ext.fly(this.dataviewID + '-' + item.get('id')).applyStyles({
223 top : newPositions[id].top,
224 left: newPositions[id].left
227 duration: duration / 1000
231 this.cacheStoreData(store);
236 * Caches the records from a store locally for comparison later
237 * @param {Ext.data.Store} store The store to cache data from
239 cacheStoreData: function(store) {
240 this.cachedStoreData = {};
242 store.each(function(record) {
243 this.cachedStoreData[record.get('id')] = record;
248 * Returns all records that were already in the DataView
249 * @return {Object} All existing records
251 getExisting: function() {
252 return this.cachedStoreData;
256 * Returns the total number of items that are currently visible in the DataView
257 * @return {Number} The number of existing items
259 getExistingCount: function() {
261 items = this.getExisting();
263 for (var k in items) count++;
269 * Returns all records in the given store that were not already present
270 * @param {Ext.data.Store} store The updated store instance
271 * @return {Object} Object of records not already present in the dataview in format {id: record}
273 getAdded: function(store) {
276 store.each(function(record) {
277 if (this.cachedStoreData[record.get('id')] == undefined) {
278 added[record.get('id')] = record;
286 * Returns all records that are present in the DataView but not the new store
287 * @param {Ext.data.Store} store The updated store instance
288 * @return {Array} Array of records that used to be present
290 getRemoved: function(store) {
293 for (var id in this.cachedStoreData) {
294 if (store.findExact('id', Number(id)) == -1) removed.push(this.cachedStoreData[id]);
301 * Returns all records that are already present and are still present in the new store
302 * @param {Ext.data.Store} store The updated store instance
303 * @return {Object} Object of records that are still present from last time in format {id: record}
305 getRemaining: function(store) {
308 store.each(function(record) {
309 if (this.cachedStoreData[record.get('id')] != undefined) {
310 remaining[record.get('id')] = record;