3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.ux.DataViewTransition
18 * @author Ed Spencer (http://sencha.com)
19 * Transition plugin for DataViews
21 Ext.define('Ext.ux.DataView.Animated', {
26 * Default configuration options for all DataViewTransition instances
34 * Creates the plugin instance, applies defaults
36 * @param {Object} config Optional config object
38 constructor: function(config) {
39 Ext.apply(this, config || {}, this.defaults);
43 * Initializes the transition plugin. Overrides the dataview's default refresh function
44 * @param {Ext.view.View} dataview The dataview
46 init: function(dataview) {
50 * Reference to the DataView this instance is bound to
52 this.dataview = dataview;
54 var idProperty = this.idProperty,
55 store = dataview.store;
57 dataview.blockRefresh = true;
58 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
59 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
60 element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId);
65 * @property dataviewID
67 * The string ID of the DataView component. This is used internally when animating child objects
69 this.dataviewID = dataview.id;
72 * @property cachedStoreData
74 * A cache of existing store data, keyed by id. This is used to determine
75 * whether any items were added or removed from the store on data change
77 this.cachedStoreData = {};
79 //catch the store data with the snapshot immediately
80 this.cacheStoreData(store.data || store.snapshot);
82 dataview.on('resize', function() {
83 var store = dataview.store;
84 if (store.getCount() > 0) {
85 // reDraw.call(this, store);
89 dataview.store.on('datachanged', reDraw, this);
91 function reDraw(store) {
92 var parentEl = dataview.getTargetEl(),
93 calcItem = store.getAt(0),
94 added = this.getAdded(store),
95 removed = this.getRemoved(store),
96 previous = this.getRemaining(store),
97 existing = Ext.apply({}, previous, added);
100 Ext.each(removed, function(item) {
101 var id = this.dataviewID + '-' + item.internalId;
102 Ext.fly(id).animate({
107 callback: function() {
108 Ext.fly(id).setDisplayed(false);
114 if (calcItem == undefined) {
115 this.cacheStoreData(store);
119 this.cacheStoreData(store);
121 var el = Ext.get(this.dataviewID + "-" + calcItem.internalId);
123 //if there is nothing rendered, force a refresh and return. This happens when loading asynchronously (was not
124 //covered correctly in previous versions, which only accepted local data)
130 //calculate the number of rows and columns we have
131 var itemCount = store.getCount(),
132 itemWidth = el.getMargin('lr') + el.getWidth(),
133 itemHeight = el.getMargin('bt') + el.getHeight(),
134 dvWidth = parentEl.getWidth(),
135 columns = Math.floor(dvWidth / itemWidth),
136 rows = Math.ceil(itemCount / columns),
137 currentRows = Math.ceil(this.getExistingCount() / columns);
139 //stores the current top and left values for each element (discovered below)
140 var oldPositions = {},
144 //find current positions of each element and save a reference in the elCache
145 Ext.iterate(previous, function(id, item) {
146 var id = item.internalId,
147 el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
150 top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'),
151 left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l')
155 //make sure the correct styles are applied to the parent element
156 parentEl.applyStyles({
161 //set absolute positioning on all DataView items. We need to set position, left and
162 //top at the same time to avoid any flickering
163 Ext.iterate(previous, function(id, item) {
164 var oldPos = oldPositions[id],
167 if (el.getStyle('position') != 'absolute') {
168 elCache[id].applyStyles({
169 position: 'absolute',
170 left : oldPos.left + "px",
171 top : oldPos.top + "px"
178 Ext.iterate(store.data.items, function(item) {
179 var id = item.internalId,
182 var column = index % columns,
183 row = Math.floor(index / columns),
184 top = row * itemHeight,
185 left = column * itemWidth;
196 var startTime = new Date(),
197 duration = this.duration,
198 dataviewID = this.dataviewID;
200 var doAnimate = function() {
201 var elapsed = new Date() - startTime,
202 fraction = elapsed / duration,
206 for (id in newPositions) {
207 Ext.fly(dataviewID + '-' + id).applyStyles({
208 top : newPositions[id].top + "px",
209 left: newPositions[id].left + "px"
213 Ext.TaskManager.stop(task);
216 for (id in newPositions) {
221 var oldPos = oldPositions[id],
222 newPos = newPositions[id],
225 oldLeft = oldPos.left,
226 newLeft = newPos.left,
227 diffTop = fraction * Math.abs(oldTop - newTop),
228 diffLeft= fraction * Math.abs(oldLeft - newLeft),
229 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
230 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
232 Ext.fly(dataviewID + '-' + id).applyStyles({
235 }).setDisplayed(true);
246 Ext.TaskManager.start(task);
249 Ext.iterate(added, function(id, item) {
250 Ext.fly(this.dataviewID + '-' + item.internalId).applyStyles({
251 top : newPositions[item.internalId].top + "px",
252 left : newPositions[item.internalId].left + "px"
253 }).setDisplayed(true);
255 Ext.fly(this.dataviewID + '-' + item.internalId).animate({
262 this.cacheStoreData(store);
267 * Caches the records from a store locally for comparison later
268 * @param {Ext.data.Store} store The store to cache data from
270 cacheStoreData: function(store) {
271 this.cachedStoreData = {};
273 store.each(function(record) {
274 this.cachedStoreData[record.internalId] = record;
279 * Returns all records that were already in the DataView
280 * @return {Object} All existing records
282 getExisting: function() {
283 return this.cachedStoreData;
287 * Returns the total number of items that are currently visible in the DataView
288 * @return {Number} The number of existing items
290 getExistingCount: function() {
292 items = this.getExisting();
294 for (var k in items) {
302 * Returns all records in the given store that were not already present
303 * @param {Ext.data.Store} store The updated store instance
304 * @return {Object} Object of records not already present in the dataview in format {id: record}
306 getAdded: function(store) {
309 store.each(function(record) {
310 if (this.cachedStoreData[record.internalId] == undefined) {
311 added[record.internalId] = record;
319 * Returns all records that are present in the DataView but not the new store
320 * @param {Ext.data.Store} store The updated store instance
321 * @return {Array} Array of records that used to be present
323 getRemoved: function(store) {
327 for (id in this.cachedStoreData) {
328 if (store.findBy(function(record) {return record.internalId == id;}) == -1) {
329 removed.push(this.cachedStoreData[id]);
337 * Returns all records that are already present and are still present in the new store
338 * @param {Ext.data.Store} store The updated store instance
339 * @return {Object} Object of records that are still present from last time in format {id: record}
341 getRemaining: function(store) {
344 store.each(function(record) {
345 if (this.cachedStoreData[record.internalId] != undefined) {
346 remaining[record.internalId] = record;