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.ux.DataViewTransition = Ext.extend(Object, {
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 dataview.blockRefresh = true;
56 dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
57 this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
58 element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, dataview.store.getAt(index).get(idProperty));
63 * @property dataviewID
65 * The string ID of the DataView component. This is used internally when animating child objects
67 this.dataviewID = dataview.id;
70 * @property cachedStoreData
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
75 this.cachedStoreData = {};
77 //var store = dataview.store;
79 //catch the store data with the snapshot immediately
80 this.cacheStoreData(dataview.store.snapshot);
82 dataview.store.on('datachanged', function(store) {
83 var parentEl = dataview.getTargetEl(),
84 calcItem = store.getAt(0),
85 added = this.getAdded(store),
86 removed = this.getRemoved(store),
87 previous = this.getRemaining(store),
88 existing = Ext.apply({}, previous, added);
91 Ext.each(removed, function(item) {
92 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({
101 if (calcItem == undefined) {
102 this.cacheStoreData(store);
106 var el = Ext.get(this.dataviewID + "-" + calcItem.get(this.idProperty));
108 //calculate the number of rows and columns we have
109 var itemCount = store.getCount(),
110 itemWidth = el.getMargin('lr') + el.getWidth(),
111 itemHeight = el.getMargin('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);
117 //make sure the correct styles are applied to the parent element
118 parentEl.applyStyles({
123 //stores the current top and left values for each element (discovered below)
124 var oldPositions = {},
128 //find current positions of each element and save a reference in the elCache
129 Ext.iterate(previous, function(id, item) {
130 var id = item.get(this.idProperty),
131 el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
134 top : el.getTop() - parentEl.getTop() - el.getMargin('t') - parentEl.getPadding('t'),
135 left: el.getLeft() - parentEl.getLeft() - el.getMargin('l') - parentEl.getPadding('l')
139 //set absolute positioning on all DataView items. We need to set position, left and
140 //top at the same time to avoid any flickering
141 Ext.iterate(previous, function(id, item) {
142 var oldPos = oldPositions[id],
145 if (el.getStyle('position') != 'absolute') {
146 elCache[id].applyStyles({
147 position: 'absolute',
148 left : oldPos.left + "px",
149 top : oldPos.top + "px",
151 //we set the width here to make ListViews work correctly. This is not needed for DataViews
152 width : el.getWidth(!Ext.isIE || Ext.isStrict),
153 height : el.getHeight(!Ext.isIE || Ext.isStrict)
160 Ext.iterate(store.data.items, function(item) {
161 var id = item.get(idProperty),
164 var column = index % columns,
165 row = Math.floor(index / columns),
166 top = row * itemHeight,
167 left = column * itemWidth;
178 var startTime = new Date(),
179 duration = this.duration,
180 dataviewID = this.dataviewID;
182 var doAnimate = function() {
183 var elapsed = new Date() - startTime,
184 fraction = elapsed / duration;
187 for (var id in newPositions) {
188 Ext.fly(dataviewID + '-' + id).applyStyles({
189 top : newPositions[id].top + "px",
190 left: newPositions[id].left + "px"
194 Ext.TaskManager.stop(task);
197 for (var id in newPositions) {
198 if (!previous[id]) continue;
200 var oldPos = oldPositions[id],
201 newPos = newPositions[id],
204 oldLeft = oldPos.left,
205 newLeft = newPos.left,
206 diffTop = fraction * Math.abs(oldTop - newTop),
207 diffLeft= fraction * Math.abs(oldLeft - newLeft),
208 midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
209 midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
211 Ext.fly(dataviewID + '-' + id).applyStyles({
225 Ext.TaskManager.start(task);
229 for (var k in added) {
232 if (Ext.global.console && Ext.global.console.log) {
233 Ext.global.console.log('added:', count);
238 Ext.iterate(added, function(id, item) {
239 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).applyStyles({
240 top : newPositions[item.get(this.idProperty)].top + "px",
241 left : newPositions[item.get(this.idProperty)].left + "px"
244 Ext.fly(this.dataviewID + '-' + item.get(this.idProperty)).animate({
251 this.cacheStoreData(store);
256 * Caches the records from a store locally for comparison later
257 * @param {Ext.data.Store} store The store to cache data from
259 cacheStoreData: function(store) {
260 this.cachedStoreData = {};
262 store.each(function(record) {
263 this.cachedStoreData[record.get(this.idProperty)] = record;
268 * Returns all records that were already in the DataView
269 * @return {Object} All existing records
271 getExisting: function() {
272 return this.cachedStoreData;
276 * Returns the total number of items that are currently visible in the DataView
277 * @return {Number} The number of existing items
279 getExistingCount: function() {
281 items = this.getExisting();
283 for (var k in items) count++;
289 * Returns all records in the given store that were not already present
290 * @param {Ext.data.Store} store The updated store instance
291 * @return {Object} Object of records not already present in the dataview in format {id: record}
293 getAdded: function(store) {
296 store.each(function(record) {
297 if (this.cachedStoreData[record.get(this.idProperty)] == undefined) {
298 added[record.get(this.idProperty)] = record;
306 * Returns all records that are present in the DataView but not the new store
307 * @param {Ext.data.Store} store The updated store instance
308 * @return {Array} Array of records that used to be present
310 getRemoved: function(store) {
313 for (var id in this.cachedStoreData) {
314 if (store.findExact(this.idProperty, Number(id)) == -1) {
315 removed.push(this.cachedStoreData[id]);
323 * Returns all records that are already present and are still present in the new store
324 * @param {Ext.data.Store} store The updated store instance
325 * @return {Object} Object of records that are still present from last time in format {id: record}
327 getRemaining: function(store) {
330 store.each(function(record) {
331 if (this.cachedStoreData[record.get(this.idProperty)] != undefined) {
332 remaining[record.get(this.idProperty)] = record;