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
26 * Creates the plugin instance, applies defaults
28 * @param {Object} config Optional config object
30 constructor: function(config) {
31 Ext.apply(this, config || {}, this.defaults);
35 * Initializes the transition plugin. Overrides the dataview's default refresh function
36 * @param {Ext.DataView} dataview The dataview
38 init: function(dataview) {
42 * Reference to the DataView this instance is bound to
44 this.dataview = dataview;
46 var idProperty = this.idProperty;
47 dataview.blockRefresh = true;
48 dataview.updateIndexes = dataview.updateIndexes.createSequence(function() {
49 this.getTemplateTarget().select(this.itemSelector).each(function(element, composite, index) {
50 element.id = element.dom.id = String.format("{0}-{1}", dataview.id, store.getAt(index).get(idProperty));
55 * @property dataviewID
57 * The string ID of the DataView component. This is used internally when animating child objects
59 this.dataviewID = dataview.id;
62 * @property cachedStoreData
64 * A cache of existing store data, keyed by id. This is used to determine
65 * whether any items were added or removed from the store on data change
67 this.cachedStoreData = {};
69 var store = dataview.store;
71 //cache the data immediately, and again on any load operation
72 this.cacheStoreData(store);
73 store.on('load', this.cacheStoreData, this);
75 store.on('datachanged', function(store) {
76 var parentEl = dataview.getTemplateTarget(),
77 calcItem = store.getAt(0),
78 added = this.getAdded(store),
79 removed = this.getRemoved(store),
80 previous = this.getRemaining(store),
81 existing = Ext.apply({}, previous, added);
84 Ext.each(removed, function(item) {
85 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).fadeOut({
87 duration: duration / 1000,
93 if (calcItem == undefined) {
94 this.cacheStoreData(store);
98 var el = parentEl.child("#" + this.dataviewID + "-" + calcItem.get(this.idProperty));
100 //calculate the number of rows and columns we have
101 var itemCount = store.getCount(),
102 itemWidth = el.getMargins('lr') + el.getWidth(),
103 itemHeight = el.getMargins('bt') + el.getHeight(),
104 dvWidth = parentEl.getWidth(),
105 columns = Math.floor(dvWidth / itemWidth),
106 rows = Math.ceil(itemCount / columns),
107 currentRows = Math.ceil(this.getExistingCount() / columns);
109 //make sure the correct styles are applied to the parent element
110 parentEl.applyStyles({
114 // height : Ext.max([rows, currentRows]) * itemHeight,
115 // width : columns * itemWidth
118 //stores the current top and left values for each element (discovered below)
119 var oldPositions = {},
123 //find current positions of each element and save a reference in the elCache
124 Ext.iterate(previous, function(id, item) {
125 var id = item.get(this.idProperty),
126 el = elCache[id] = parentEl.child('#' + this.dataviewID + '-' + id);
129 top : el.getTop() - parentEl.getTop() - el.getMargins('t') - parentEl.getPadding('t'),
130 left: el.getLeft() - parentEl.getLeft() - el.getMargins('l') - parentEl.getPadding('l')
134 //set absolute positioning on all DataView items. We need to set position, left and
135 //top at the same time to avoid any flickering
136 Ext.iterate(previous, function(id, item) {
137 var oldPos = oldPositions[id],
140 if (el.getStyle('position') != 'absolute') {
141 elCache[id].applyStyles({
142 position: 'absolute',
146 //we set the width here to make ListViews work correctly. This is not needed for DataViews
147 width : el.getWidth(!Ext.isIE),
148 height : el.getHeight(!Ext.isIE)
155 Ext.iterate(store.data.items, function(item) {
156 var id = item.get(idProperty),
159 var column = index % columns,
160 row = Math.floor(index / columns),
161 top = row * itemHeight,
162 left = column * itemWidth;
173 var startTime = new Date(),
174 duration = this.duration,
175 dataviewID = this.dataviewID;
177 var doAnimate = function() {
178 var elapsed = new Date() - startTime,
179 fraction = elapsed / duration;
182 for (var id in newPositions) {
183 Ext.fly(dataviewID + '-' + id).applyStyles({
184 top : newPositions[id].top,
185 left: newPositions[id].left
189 Ext.TaskMgr.stop(task);
192 for (var id in newPositions) {
193 if (!previous[id]) continue;
195 var oldPos = oldPositions[id],
196 newPos = newPositions[id],
199 oldLeft = oldPos.left,
200 newLeft = newPos.left,
201 diffTop = fraction * Math.abs(oldTop - newTop),
202 diffLeft= fraction * Math.abs(oldLeft - newLeft),
203 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
204 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
206 Ext.fly(dataviewID + '-' + id).applyStyles({
220 Ext.TaskMgr.start(task);
223 Ext.iterate(added, function(id, item) {
224 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).applyStyles({
225 top : newPositions[id].top,
226 left: newPositions[id].left
229 duration: duration / 1000
233 this.cacheStoreData(store);
238 * Caches the records from a store locally for comparison later
239 * @param {Ext.data.Store} store The store to cache data from
241 cacheStoreData: function(store) {
242 this.cachedStoreData = {};
244 store.each(function(record) {
245 this.cachedStoreData[record.get(this.idProperty)] = record;
250 * Returns all records that were already in the DataView
251 * @return {Object} All existing records
253 getExisting: function() {
254 return this.cachedStoreData;
258 * Returns the total number of items that are currently visible in the DataView
259 * @return {Number} The number of existing items
261 getExistingCount: function() {
263 items = this.getExisting();
265 for (var k in items) count++;
271 * Returns all records in the given store that were not already present
272 * @param {Ext.data.Store} store The updated store instance
273 * @return {Object} Object of records not already present in the dataview in format {id: record}
275 getAdded: function(store) {
278 store.each(function(record) {
279 if (this.cachedStoreData[record.get(this.idProperty)] == undefined) {
280 added[record.get(this.idProperty)] = record;
288 * Returns all records that are present in the DataView but not the new store
289 * @param {Ext.data.Store} store The updated store instance
290 * @return {Array} Array of records that used to be present
292 getRemoved: function(store) {
295 for (var id in this.cachedStoreData) {
296 if (store.findExact(this.idProperty, Number(id)) == -1) removed.push(this.cachedStoreData[id]);
303 * Returns all records that are already present and are still present in the new store
304 * @param {Ext.data.Store} store The updated store instance
305 * @return {Object} Object of records that are still present from last time in format {id: record}
307 getRemaining: function(store) {
310 store.each(function(record) {
311 if (this.cachedStoreData[record.get(this.idProperty)] != undefined) {
312 remaining[record.get(this.idProperty)] = record;