<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-ux-DataView-Animated'>/**
|
</span> * @author Ed Spencer (http://sencha.com)
|
* Transition plugin for DataViews
|
*/
|
Ext.define('Ext.ux.DataView.Animated', {
|
|
<span id='Ext-ux-DataView-Animated-property-defaults'> /**
|
</span> * @property defaults
|
* @type Object
|
* Default configuration options for all DataViewTransition instances
|
*/
|
defaults: {
|
duration : 750,
|
idProperty: 'id'
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-constructor'> /**
|
</span> * Creates the plugin instance, applies defaults
|
* @constructor
|
* @param {Object} config Optional config object
|
*/
|
constructor: function(config) {
|
Ext.apply(this, config || {}, this.defaults);
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-init'> /**
|
</span> * Initializes the transition plugin. Overrides the dataview's default refresh function
|
* @param {Ext.view.View} dataview The dataview
|
*/
|
init: function(dataview) {
|
<span id='Ext-ux-DataView-Animated-property-dataview'> /**
|
</span> * @property dataview
|
* @type Ext.view.View
|
* Reference to the DataView this instance is bound to
|
*/
|
this.dataview = dataview;
|
|
var store = dataview.store;
|
|
dataview.blockRefresh = true;
|
dataview.updateIndexes = Ext.Function.createSequence(dataview.updateIndexes, function() {
|
this.getTargetEl().select(this.itemSelector).each(function(element, composite, index) {
|
element.id = element.dom.id = Ext.util.Format.format("{0}-{1}", dataview.id, store.getAt(index).internalId);
|
}, this);
|
}, dataview);
|
|
<span id='Ext-ux-DataView-Animated-property-dataviewID'> /**
|
</span> * @property dataviewID
|
* @type String
|
* The string ID of the DataView component. This is used internally when animating child objects
|
*/
|
this.dataviewID = dataview.id;
|
|
<span id='Ext-ux-DataView-Animated-property-cachedStoreData'> /**
|
</span> * @property cachedStoreData
|
* @type Object
|
* A cache of existing store data, keyed by id. This is used to determine
|
* whether any items were added or removed from the store on data change
|
*/
|
this.cachedStoreData = {};
|
|
//catch the store data with the snapshot immediately
|
this.cacheStoreData(store.data || store.snapshot);
|
|
dataview.on('resize', function() {
|
var store = dataview.store;
|
if (store.getCount() > 0) {
|
// reDraw.call(this, store);
|
}
|
}, this);
|
|
dataview.store.on('datachanged', reDraw, this);
|
|
function reDraw(store) {
|
var parentEl = dataview.getTargetEl(),
|
calcItem = store.getAt(0),
|
added = this.getAdded(store),
|
removed = this.getRemoved(store),
|
previous = this.getRemaining(store);
|
|
// Not yet rendered
|
if (!parentEl) {
|
return;
|
}
|
|
//make sure the correct styles are applied to the parent element
|
if (Ext.isIEQuirks) {
|
parentEl.applyStyles({
|
zoom: 1,
|
display : 'block',
|
position: 'relative'
|
});
|
}
|
|
//hide old items
|
Ext.each(removed, function(item) {
|
var id = this.dataviewID + '-' + item.internalId;
|
Ext.fly(id).animate({
|
remove : false,
|
duration: duration,
|
opacity : 0,
|
useDisplay: true,
|
callback: function() {
|
Ext.fly(id).setDisplayed(false);
|
}
|
});
|
}, this);
|
|
//store is empty
|
if (calcItem == undefined) {
|
this.cacheStoreData(store);
|
return;
|
}
|
|
this.cacheStoreData(store);
|
|
var el = Ext.get(this.dataviewID + "-" + calcItem.internalId);
|
|
//if there is nothing rendered, force a refresh and return. This happens when loading asynchronously (was not
|
//covered correctly in previous versions, which only accepted local data)
|
if (!el) {
|
dataview.refresh();
|
return true;
|
}
|
|
//calculate the number of rows and columns we have
|
var itemWidth = el.getMargin('lr') + el.getWidth(),
|
itemHeight = el.getMargin('bt') + el.getHeight(),
|
dvWidth = parentEl.dom.clientWidth,
|
columns = Math.floor(dvWidth / itemWidth),
|
rtl = this.dataview.getHierarchyState().rtl,
|
styleSide = rtl ? 'right' : 'left',
|
newStyle;
|
|
//stores the current top and left values for each element (discovered below)
|
var oldPositions = {},
|
newPositions = {},
|
elCache = {};
|
|
//find current positions of each element and save a reference in the elCache
|
Ext.iterate(previous, function(id, item) {
|
var id = item.internalId,
|
el = elCache[id] = Ext.get(this.dataviewID + '-' + id);
|
|
oldPositions[id] = {
|
top : el.getY() - parentEl.getY() - el.getMargin('t') - parentEl.getPadding('t')
|
};
|
oldPositions[id][styleSide] = this.getItemX(el);
|
}, this);
|
|
//set absolute positioning on all DataView items. We need to set position, left and
|
//top at the same time to avoid any flickering
|
Ext.iterate(previous, function(id, item) {
|
var oldPos = oldPositions[id],
|
el = elCache[id];
|
|
if (el.getStyle('position') != 'absolute') {
|
newStyle = {
|
position: 'absolute',
|
top : oldPos.top + "px"
|
};
|
newStyle[styleSide] = oldPos[styleSide] + "px";
|
elCache[id].applyStyles(newStyle);
|
}
|
});
|
|
//get new positions
|
var index = 0;
|
Ext.iterate(store.data.items, function(item) {
|
var id = item.internalId,
|
column = index % columns,
|
row = Math.floor(index / columns),
|
top = row * itemHeight,
|
left = column * itemWidth;
|
|
newPositions[id] = {
|
top : top
|
};
|
newPositions[id][styleSide] = left;
|
|
index ++;
|
}, this);
|
|
//do the movements
|
var startTime = new Date(),
|
duration = this.duration,
|
dataviewID = this.dataviewID;
|
|
var doAnimate = function() {
|
var elapsed = new Date() - startTime,
|
fraction = elapsed / duration,
|
id;
|
|
if (fraction >= 1) {
|
for (id in newPositions) {
|
newStyle = {
|
top : newPositions[id].top + "px"
|
};
|
newStyle[styleSide] = newPositions[id][styleSide] + "px";
|
|
Ext.fly(dataviewID + '-' + id).applyStyles(newStyle);
|
}
|
|
Ext.TaskManager.stop(task);
|
} else {
|
//move each item
|
for (id in newPositions) {
|
if (!previous[id]) {
|
continue;
|
}
|
|
var oldPos = oldPositions[id],
|
newPos = newPositions[id],
|
oldTop = oldPos.top,
|
newTop = newPos.top,
|
oldLeft = oldPos[styleSide],
|
newLeft = newPos[styleSide],
|
diffTop = fraction * Math.abs(oldTop - newTop),
|
diffLeft= fraction * Math.abs(oldLeft - newLeft),
|
midTop = oldTop > newTop ? oldTop - diffTop : oldTop + diffTop,
|
midLeft = oldLeft > newLeft ? oldLeft - diffLeft : oldLeft + diffLeft;
|
|
newStyle = {
|
top : midTop + "px"
|
};
|
newStyle[styleSide] = midLeft + "px";
|
Ext.fly(dataviewID + '-' + id).applyStyles(newStyle).setDisplayed(true);
|
}
|
}
|
};
|
|
var task = {
|
run : doAnimate,
|
interval: 20,
|
scope : this
|
};
|
|
Ext.TaskManager.start(task);
|
|
//show new items
|
Ext.iterate(added, function(id, item) {
|
newStyle = {
|
top : newPositions[item.internalId].top + "px"
|
};
|
newStyle[styleSide] = newPositions[item.internalId][styleSide] + "px";
|
Ext.fly(this.dataviewID + '-' + item.internalId).applyStyles(newStyle).setDisplayed(true);
|
|
Ext.fly(this.dataviewID + '-' + item.internalId).animate({
|
remove : false,
|
duration: duration,
|
opacity : 1
|
});
|
}, this);
|
|
this.cacheStoreData(store);
|
}
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getItemX'> getItemX: function(el) {
|
</span> var rtl = this.dataview.getHierarchyState().rtl,
|
parentEl = el.up('');
|
|
if (rtl) {
|
return parentEl.getViewRegion().right - el.getRegion().right + el.getMargin('r');
|
} else {
|
return el.getX() - parentEl.getX() - el.getMargin('l') - parentEl.getPadding('l');
|
}
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-cacheStoreData'> /**
|
</span> * Caches the records from a store locally for comparison later
|
* @param {Ext.data.Store} store The store to cache data from
|
*/
|
cacheStoreData: function(store) {
|
this.cachedStoreData = {};
|
|
store.each(function(record) {
|
this.cachedStoreData[record.internalId] = record;
|
}, this);
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getExisting'> /**
|
</span> * Returns all records that were already in the DataView
|
* @return {Object} All existing records
|
*/
|
getExisting: function() {
|
return this.cachedStoreData;
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getExistingCount'> /**
|
</span> * Returns the total number of items that are currently visible in the DataView
|
* @return {Number} The number of existing items
|
*/
|
getExistingCount: function() {
|
var count = 0,
|
items = this.getExisting();
|
|
for (var k in items) {
|
count++;
|
}
|
|
return count;
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getAdded'> /**
|
</span> * Returns all records in the given store that were not already present
|
* @param {Ext.data.Store} store The updated store instance
|
* @return {Object} Object of records not already present in the dataview in format {id: record}
|
*/
|
getAdded: function(store) {
|
var added = {};
|
|
store.each(function(record) {
|
if (this.cachedStoreData[record.internalId] == undefined) {
|
added[record.internalId] = record;
|
}
|
}, this);
|
|
return added;
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getRemoved'> /**
|
</span> * Returns all records that are present in the DataView but not the new store
|
* @param {Ext.data.Store} store The updated store instance
|
* @return {Array} Array of records that used to be present
|
*/
|
getRemoved: function(store) {
|
var removed = [],
|
id;
|
|
for (id in this.cachedStoreData) {
|
if (store.findBy(function(record) {return record.internalId == id;}) == -1) {
|
removed.push(this.cachedStoreData[id]);
|
}
|
}
|
|
return removed;
|
},
|
|
<span id='Ext-ux-DataView-Animated-method-getRemaining'> /**
|
</span> * Returns all records that are already present and are still present in the new store
|
* @param {Ext.data.Store} store The updated store instance
|
* @return {Object} Object of records that are still present from last time in format {id: record}
|
*/
|
getRemaining: function(store) {
|
var remaining = {};
|
|
store.each(function(record) {
|
if (this.cachedStoreData[record.internalId] != undefined) {
|
remaining[record.internalId] = record;
|
}
|
}, this);
|
|
return remaining;
|
}
|
});
|
</pre>
|
</body>
|
</html>
|