Ext.ux.DataViewTransition = Ext.extend(Object, {
defaults: {
duration : 750,
idProperty:
'id'
},
constructor:
function
(config) {
Ext.apply(
this
, config || {},
this
.defaults);
},
init:
function
(dataview) {
this
.dataview = dataview;
var
idProperty =
this
.idProperty;
dataview.blockRefresh =
true
;
dataview.updateIndexes = dataview.updateIndexes.createSequence(
function
() {
this
.getTemplateTarget().select(
this
.itemSelector).each(
function
(element, composite, index) {
element.id = element.dom.id = String.format(
"{0}-{1}"
, dataview.id, store.getAt(index).get(idProperty));
},
this
);
}, dataview);
this
.dataviewID = dataview.id;
this
.cachedStoreData = {};
var
store = dataview.store;
this
.cacheStoreData(store);
store.on('load
', this.cacheStoreData, this);
store.on('
datachanged
', function(store) {
var parentEl = dataview.getTemplateTarget(),
calcItem = store.getAt(0),
added = this.getAdded(store),
removed = this.getRemoved(store),
previous = this.getRemaining(store),
existing = Ext.apply({}, previous, added);
//hide old items
Ext.each(removed, function(item) {
Ext.fly(this.dataviewID + '
-
' + item.get(this.idProperty)).fadeOut({
remove : false,
duration: duration / 1000,
useDisplay: true
});
}, this);
//store is empty
if (calcItem == undefined) {
this.cacheStoreData(store);
return;
}
var el = parentEl.child("#" + this.dataviewID + "-" + calcItem.get(this.idProperty));
//calculate the number of rows and columns we have
var itemCount = store.getCount(),
itemWidth = el.getMargins('
lr
') + el.getWidth(),
itemHeight = el.getMargins('
bt
') + el.getHeight(),
dvWidth = parentEl.getWidth(),
columns = Math.floor(dvWidth / itemWidth),
rows = Math.ceil(itemCount / columns),
currentRows = Math.ceil(this.getExistingCount() / columns);
//make sure the correct styles are applied to the parent element
parentEl.applyStyles({
display : '
block
',
position: '
relative
'
// ,
// height : Ext.max([rows, currentRows]) * itemHeight,
// width : columns * itemWidth
});
//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.get(this.idProperty),
el = elCache[id] = parentEl.child('
#' + this.dataviewID + '-' + id);
oldPositions[id] = {
top : el.getTop() - parentEl.getTop() - el.getMargins('t
') - parentEl.getPadding('
t
'),
left: el.getLeft() - parentEl.getLeft() - el.getMargins('
l
') - parentEl.getPadding('
l
')
};
}, 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
') {
elCache[id].applyStyles({
position: '
absolute
',
left : oldPos.left + "px",
top : oldPos.top + "px",
//we set the width here to make ListViews work correctly. This is not needed for DataViews
width : el.getWidth(!Ext.isIE || Ext.isStrict),
height : el.getHeight(!Ext.isIE || Ext.isStrict)
});
}
});
//get new positions
var index = 0;
Ext.iterate(store.data.items, function(item) {
var id = item.get(idProperty),
el = elCache[id];
var column = index % columns,
row = Math.floor(index / columns),
top = row * itemHeight,
left = column * itemWidth;
newPositions[id] = {
top : top,
left: 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;
if (fraction >= 1) {
for (var id in newPositions) {
Ext.fly(dataviewID + '
-
' + id).applyStyles({
top : newPositions[id].top + "px",
left: newPositions[id].left + "px"
});
}
Ext.TaskMgr.stop(task);
} else {
//move each item
for (var id in newPositions) {
if (!previous[id]) continue;
var oldPos = oldPositions[id],
newPos = newPositions[id],
oldTop = oldPos.top,
newTop = newPos.top,
oldLeft = oldPos.left,
newLeft = newPos.left,
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;
Ext.fly(dataviewID + '
-
' + id).applyStyles({
top : midTop + "px",
left: midLeft + "px"
});
}
}
};
var task = {
run : doAnimate,
interval: 20,
scope : this
};
Ext.TaskMgr.start(task);
//show new items
Ext.iterate(added, function(id, item) {
Ext.fly(this.dataviewID + '
-' + item.get(
this
.idProperty)).applyStyles({
top : newPositions[id].top +
"px"
,
left: newPositions[id].left +
"px"
}).fadeIn({
remove :
false
,
duration: duration / 1000
});
},
this
);
this
.cacheStoreData(store);
},
this
);
},
cacheStoreData:
function
(store) {
this
.cachedStoreData = {};
store.each(
function
(record) {
this
.cachedStoreData[record.get(
this
.idProperty)] = record;
},
this
);
},
getExisting:
function
() {
return
this
.cachedStoreData;
},
getExistingCount:
function
() {
var
count = 0,
items =
this
.getExisting();
for
(
var
k
in
items) count++;
return
count;
},
getAdded:
function
(store) {
var
added = {};
store.each(
function
(record) {
if
(
this
.cachedStoreData[record.get(
this
.idProperty)] == undefined) {
added[record.get(
this
.idProperty)] = record;
}
},
this
);
return
added;
},
getRemoved:
function
(store) {
var
removed = [];
for
(
var
id
in
this
.cachedStoreData) {
if
(store.findExact(
this
.idProperty, Number(id)) == -1) removed.push(
this
.cachedStoreData[id]);
}
return
removed;
},
getRemaining:
function
(store) {
var
remaining = {};
store.each(
function
(record) {
if
(
this
.cachedStoreData[record.get(
this
.idProperty)] != undefined) {
remaining[record.get(
this
.idProperty)] = record;
}
},
this
);
return
remaining;
}
});