PDA

View Full Version : Ext.ux.touch.ListPullRefresh



VinylFox
10 Nov 2010, 8:46 AM
*UPDATE* I have implemented the event based system suggested here, doing away with static methods, and I'm very happy with the results - please take a look at the updated code on Github and read my Blog post about the List Pull Refresh (Pull to Refresh) plugin (http://www.vinylfox.com/list-pull-refresh-plugin-for-sencha-touch/).

This is a WIP, but I thought I would share it anyway. It's the typical 'Pull to Refresh' feature that you see in twitter and facebook apps for iPhone. Works with Lists right now, but should also work with a straight DataView.

http://content.screencast.com/users/VinylFox/folders/Jing/media/7060ea80-1e2c-4cd6-a102-a53120d7e665/2010-11-29_1457.png

Like I said, WIP, so there is still work to be done - please feel free to fork it on Github and send me a pull request with your changes.

https://github.com/VinylFox/Ext.ux.touch.ListPullRefresh

Usage:

{
xtype: 'list',
...,
plugins: [new Ext.ux.touch.ListPullRefresh({
listeners: {
'released': function(plugin,list){
// call the plugins processComplete method to hide the 'loading' indicator
your_store.on('load',plugin.processComplete,plugin,{single:true});
// do whatever needs to happen for reload
your_store.load();
}
}
})],
...
}

Enjoy

xnakxx
10 Nov 2010, 10:41 AM
Beautiful!!!

Steffen Hiller
10 Nov 2010, 5:21 PM
Didn't test it yet, but I suppose it's nice. :)

herkulano
25 Nov 2010, 1:53 PM
Made some small adjustments:

----------------------------------------------
Support for SenchaTouch 1.0.
Using default SenchaTouch list loading.
Hide when not being used.
Follow Scroller.
----------------------------------------------

https://github.com/herkulano/Ext.ux.touch.ListPullRefresh

26 Nov 2010, 8:00 AM
Pretty suick dude!

VinylFox
26 Nov 2010, 10:00 AM
Made some small adjustments...

Thanks a ton. I pulled your changes into the main branch. Nice work.

Also fixed up the styling a bit.

herkulano
26 Nov 2010, 11:42 AM
Thanks, i merged your changes and it looks great. :)

gkatz
28 Nov 2010, 1:31 AM
did not download it yet but I think its a great addition. is it going to stay an extension? or does sencha have plans for incorporating such a functionality with the formal distribution?
also, is this forum the place to look for sencha touch extensions? or is there another place?
thanks.

VinylFox
29 Nov 2010, 7:01 AM
I doubt this will be added as part of the library. It's easy enough to implement as a plugin, and it's not native iOS behavior.

It appears that this will be the forum to look for plugins and extensions. If that changes, then the relevant posts will be moved.

29 Nov 2010, 7:03 AM
Yes. the Sencha team has to be very wary of the library's size.

SimonFlack
17 Dec 2010, 4:44 AM
This plugin was just what I needed. Although I needed it for paging.

Here is my modified version.

http://lh6.ggpht.com/_yBxLihSnjhA/TQtawagFSsI/AAAAAAAABAo/kP-BWkbb9bM/s640/paging.jpg

Usage:


plugins: [new mobile.plugins.ListPullPager({
previousFn: function(cb,scope){
mobile.commissions.store.previousPage();
cb.call(this);
},
nextFn: function(cb,scope){
mobile.commissions.store.nextPage();
cb.call(this);
}
})]

Plugin:


Ext.apply(Ext.anims, {
rotate: new Ext.Anim({
autoClear: false,
out: false,
before: function(el) {
var d = '';
if (this.dir == 'ccw'){
d = '-';
}

this.from = {
'-webkit-transform': 'rotate('+d+''+this.fromAngle+'deg)'
};

this.to = {
'-webkit-transform': 'rotate('+d+''+this.toAngle+'deg)'
};

}
})
});

Ext.ns('mobile.plugins');

mobile.plugins.ListPullPager = Ext.extend(Ext.util.Observable, {
langPullPrevious: 'Pull down to load previous...',
langReleasePrevious: 'Release to load previous...',
langPullNext: 'Pull up to load next...',
langReleaseNext: 'Release to load next...',
langLoading: 'Loading...',

loading: false,
//define the functions to call for loading.
previousFn: undefined,
nextFn: undefined,
// private
init: function(cmp){
this.cmp = cmp;
this.lastUpdate = new Date();
cmp.loadingText = undefined;
cmp.on('render', this.initPullHandler, this);
if (!this.previousFn || !this.nextFn){
cmp.getStore().on('load', this.reloadComplete, this);
}
},
// private
initPullHandler: function(){
//Previous
this.previousTpl = new Ext.XTemplate(
'<div class="pullprevious" style="height: {h}; text-align: bottom;">'+
'<div class="msgwrap" style="height: 75px; bottom: 0px; position: relative;">'+
'<span class="arrow {s}"></span>'+
'<span class="msg">{m}</span>'+
'</div>'+
'</div>');
this.previousEl = this.previousTpl.insertBefore(this.cmp.scroller.el, {h:0,m:this.langPullPrevious,l:this.lastUpdate}, true);
this.previousEl.hide();
Ext.Element.cssTranslate(this.previousEl, {x:0, y:+75});
//this.cmp.scroller.on('offsetchange', this.handlePrevious, this);

//Next
this.nextTpl = new Ext.XTemplate(
'<div class="pullnext" style="height: {h}; text-align: bottom;">'+
'<div class="msgwrap" style="height: 75px; bottom: 0px; position: relative;">'+
'<span class="arrow {s}"></span>'+
'<span class="msg">{m}</span>'+
'</div>'+
'</div>');
this.nextEl = this.nextTpl.insertAfter(this.cmp.scroller.el, {h:0,m:this.langPullNext,l:this.lastUpdate}, true);
this.nextEl.hide();
Ext.Element.cssTranslate(this.nextEl, {x:0, y:-75});


this.cmp.scroller.on('offsetchange', this.handleOffsettChange, this);
},

handleOffsettChange: function(scroller, offset) {
if (scroller.direction === 'vertical' && !this.loading){

var heightOfList = scroller.size.height - scroller.containerBox.height;

if (offset.y > 0){
//console.log('up');
this.handlePrevious(scroller, offset, heightOfList);
}

if (offset.y < (heightOfList * -1)) {
//console.log('down');
this.handleNext(scroller, offset, heightOfList);
}
}
},

//private
handlePrevious: function(scroller, offset, heightOfList){
if (scroller.direction === 'vertical' && !this.loading){
if (offset){
Ext.Element.cssTranslate(this.previousEl, {x:0, y:offset.y-75});
if (offset.y > 75){
// state 1
if (this.state !== 1){
this.prevState = this.state;
this.state = 1;
this.previousTpl.overwrite(this.previousEl, {h:offset.y,m:this.langReleasePrevious,l:this.lastUpdate});
Ext.Anim.run(this.previousEl.select('.arrow').first(),'rotate',{dir:'ccw',fromAngle:0,toAngle:180});
}
}else if (!scroller.isDragging()){
// state 3
if (this.state !== 3){
this.prevState = this.state;
this.state = 3;
if (this.prevState == 1){
this.loading = true;
this.lastUpdate = new Date();
this.previousEl.hide();
if (this.previousFn){
this.previousFn.call(this,this.previousComplete,this);
}else{
this.cmp.getStore().load();
}
}
}
}else{
// state 2
if (this.state !== 2){
this.prevState = this.state;
this.state = 2;
this.previousTpl.overwrite(this.previousEl, {h:offset.y,m:this.langPullPrevious,l:this.lastUpdate});
this.previousEl.show();
if (this.prevState == 1){
Ext.Anim.run(this.previousEl.select('.arrow').first(),'rotate',{dir:'cw',fromAngle:180,toAngle:0});
}
}
}
}
}
},
handleNext: function(scroller, offset, heightOfList){
if (scroller.direction === 'vertical' && !this.loading){
if (offset){
Ext.Element.cssTranslate(this.nextEl, {x:0, y:offset.y});
if (offset.y < ((heightOfList + 75)*-1)){
// state 1
if (this.state !== 1){
this.prevState = this.state;
this.state = 1;
this.nextTpl.overwrite(this.nextEl, {h:offset.y,m:this.langReleaseNext,l:this.lastUpdate});
Ext.Anim.run(this.nextEl.select('.arrow').first(),'rotate',{dir:'ccw',fromAngle:180,toAngle:0});
}
}else if (!scroller.isDragging()){
// state 3
if (this.state !== 3){
this.prevState = this.state;
this.state = 3;
if (this.prevState == 1){
this.loading = true;
this.lastUpdate = new Date();
this.nextEl.hide();
if (this.nextFn){
this.nextFn.call(this,this.nextComplete,this);
}else{
this.cmp.getStore().load();
}
}
}
}else{
// state 2
if (this.state !== 2){
this.prevState = this.state;
this.state = 2;
this.nextTpl.overwrite(this.nextEl, {h:offset.y,m:this.langPullNext,l:this.lastUpdate});
this.nextEl.show();
if (this.prevState == 1){
Ext.Anim.run(this.nextEl.select('.arrow').first(),'rotate',{dir:'cw',fromAngle:180,toAngle:0});
}
}
}
}
}
},
//private
previousComplete: function(){
this.loading = false;
this.lastUpdate = new Date();
this.previousTpl.overwrite(this.previousEl, {h:0,m:this.langPullPrevious,l:this.lastUpdate});
},
nextComplete: function(){
this.loading = false;
this.lastUpdate = new Date();
this.nextTpl.overwrite(this.nextEl, {h:0,m:this.langPullNext,l:this.lastUpdate});
}
});

Ext.preg('listpullpager', mobile.plugins.ListPullPager);

CSS


div.pullprevious, div.pullprevious div.msgwrap {
background-color: #e1e1e1 !important;
}

div.pullprevious span.arrow {
width: 32px;
height: 60px;
position: absolute;
top: 0;
left: 10px;
background-image: url('../images/blackArrow.png');
background-repeat: no-repeat;
margin: 10px 0 0 25px;
}

div.loadingspacer span.loading {
width: 32px;
height: 60px;
position: absolute;
top: 0;
left: 10px;
background-image: url('../images/loader.gif');
background-repeat: no-repeat;
background-position: 0 7px;
}

div.pullprevious span.msg, div.loadingspacer span.msg {
width: 80%;
text-align: center;
font-size: 0.9em;
position: absolute;
top: 12px;
left: 40px;
margin: 5px 0 5px 40px;
font-weight: bold;
}

div.pullprevious span.lastupdate {
width: 80%;
text-align: center;
font-size: 0.7em;
position: absolute;
top: 25px;
left: 40px;
margin: 15px 0 5px 40px;
}

div.pullnext, div.pullnext div.msgwrap {
background-color: #e1e1e1 !important;
}

div.pullnext span.arrow {
width: 32px;
height: 60px;
position: absolute;
top: 0;
left: 10px;
background-image: url('../images/blackArrow.png');
background-repeat: no-repeat;
margin: 10px 0 0 25px;
-webkit-transform: rotate(180deg);
}

div.pullnext span.msg, div.loadingspacer span.msg {
width: 80%;
text-align: center;
font-size: 0.9em;
position: absolute;
top: 12px;
left: 40px;
margin: 5px 0 5px 40px;
font-weight: bold;
}

div.pullnext span.lastupdate {
width: 80%;
text-align: center;
font-size: 0.7em;
position: absolute;
top: 25px;
left: 40px;
margin: 15px 0 5px 40px;
}


Figured I'd post it here just incase its of value to somebody.

elmasse
19 Dec 2010, 6:18 PM
Excellent work guys! Just one suggestion: Instead of having different functions to be attached for previousFn and nextFn why not using events? So we can attach as many fn as we need and also you can create a before/after events for each one of those.

VinylFox
20 Dec 2010, 11:07 AM
...Instead of having different functions to be attached for previousFn and nextFn why not using events? So we can attach as many fn as we need and also you can create a before/after events for each one of those.

Excellent idea. Will do.

buz
28 Dec 2010, 1:24 AM
In that case maybe you can make it a plugin for every panel. I think it would be more useful, because other panels without a list can be updated on request to.

Maybe you could extend this component and create a "PullToSearch" =) would be great. (if you have time)

VinylFox
10 Jan 2011, 10:41 AM
I have updated this to fire a 'released' event instead of calling a static method. Check out Github for the most recent version and the readme file on how to use it now.

https://github.com/VinylFox/Ext.ux.touch.ListPullRefresh

Also check out my blog for a write up - http://www.vinylfox.com/list-pull-refresh-plugin-for-sencha-touch/

mvoss
20 Feb 2011, 5:27 AM
Hi,
nice work. It works nice! One little problem with the loading mask though. It seems this plugin hides the lists loading mask. I am guessing this is because you do not want the loading mask when the list is updated with this plugin (by pulling). However I would like to show the loading mask the first time the list is loaded. Is this possible?

regards,
Markus

VinylFox
28 Feb 2011, 7:43 AM
That's a good point Markus, Ill look into how to fix that.

mvoss
7 Mar 2011, 11:27 AM
Hi,

did you have any time to have a look at this? I think this would be a nice feature to have!

Regards,
Markus

rockinthesixstring
27 Jun 2011, 9:54 AM
I've got this working, however when I pull down, the grey area (#e1e1e1) is only as tall as the text, and above the grey is the Sencha off white again. How can I make it so that the grey goes all the way to the top, no matter how far down I pull?

Thanks.

dawesi
27 Jun 2011, 11:43 PM
Hi,
nice work. It works nice! One little problem with the loading mask though. It seems this plugin hides the lists loading mask. I am guessing this is because you do not want the loading mask when the list is updated with this plugin (by pulling). However I would like to show the loading mask the first time the list is loaded. Is this possible?

regards,
Markus


I guess you could always add the plugin using the load event until it's tweaked?!

mvoss
30 Jun 2011, 10:09 AM
Hi,

thank you for continuing to work on this but this is implemented in Sencha Touch now. Have a look at the documentation:

http://dev.sencha.com/deploy/touch/docs/

search for ListPagingPlugin.

Regards,
Markus

VinylFox
1 Jul 2011, 10:49 AM
Yeah, look at the dates.

justinKohactive
18 Nov 2011, 4:04 PM
https://github.com/j-mcnally/Sencha2-PullToRefresh

Based on your great plugin

marceloverdijk
19 Jan 2012, 4:40 AM
Will this plugin be available in Sencha Touch 2.0? I can't see it preview3.

Steffen Hiller
19 Jan 2012, 6:45 AM
Check src/plugin/PullRefresh.js :) Looking at it right now.

justinKohactive
19 Jan 2012, 12:18 PM
I have ported this:

https://github.com/j-mcnally/Sencha2-PullToRefresh

openxtiger
30 Jan 2012, 5:30 PM
This plugin was just what I needed. Although I needed it for paging.

Here is my modified version.

Plugin:




mobile.plugins.ListPullPager = ...




That great! but had some bugs,i modify and extend it!

Usage:


plugins: ["listpager"]

plugins: ["listpullpager"]

plugins: [{ptype:"listpullpager",isAutoButtons:false}]
Plugin:


Ext.plugins.ListPaging = Ext.extend(Ext.util.Observable, {
isAutoButtons:true,
init:function (list) {
this.store = list.store;


list.load = this.load;
if (this.isAutoButtons) {
list.prevBtn = this.prevBtn = new Ext.Button({text:"1", handler:this.onPrevTap, ui:"back", scope:this});
list.nextBtn = this.nextBtn = new Ext.Button({text:"1", handler:this.onNextTap, ui:"forward", scope:this});
list.reloadBtn = this.reloadBtn = new Ext.Button({iconMask:true,ui:'plain',iconCls:'refresh',handler:this.onRefreshAction, scope:this});
}
this.store.on("load", this.handleStoreLoad, this);
},
onPrevTap:function () {
this.loadPage(-1);
},
onNextTap:function () {
this.loadPage(1);
},
onRefreshAction:function() {
this.loadPage(0);
},
handlePrevPage:function () {
this.loadPage(-1);
},
handleNextPage:function () {
this.loadPage(1);
},
reload:function () {
this.loadPage(0);
},
load:function() {
var store = this.store;
store.currentPage = 1;
store.baseParams["start"] = 0;
store.baseParams["limit"] = this.store.pageSize;
store.load({params:this.store.baseParams});
},
loadPage:function (cpage) {
var store = this.store;
var totalNum = this.getTotalRecs();
var numPages = Math.ceil(totalNum / store.pageSize);
var page = this.store.currentPage + cpage;
if (page < 1) {
page = 1;
}
if (page > numPages) {
page = numPages;
}
store.currentPage = page;
store.baseParams["start"] = (page - 1) * this.store.pageSize;
store.baseParams["limit"] = this.store.pageSize;
store.load({params:this.store.baseParams});
},
getTotalPage:function () {
var store = this.store;
var totalNum = this.getTotalRecs();
return Math.ceil(totalNum / store.pageSize);
},
getTotalRecs:function () {
var store = this.store;
var proxy = store.getProxy();
var reader = proxy.getReader();
return reader.getTotal(reader.rawData);
},
handleStoreLoadBtn:function (store, pageMatch) {
var currentPage = store.currentPage;
if (currentPage > 1) {
this.prevBtn.setText((currentPage - 1) + "/" + pageMatch);
this.prevBtn.enable();
} else {
this.prevBtn.setText(pageMatch == 0 ? 0 : 1);
this.prevBtn.disable();
}
if (currentPage < pageMatch - 1) {
this.nextBtn.setText((currentPage + 1) + "/" + pageMatch);
this.nextBtn.enable();
} else {
this.nextBtn.setText(pageMatch);
this.nextBtn.disable();
}
},
handleStoreLoad:function (store, recs, success) {
if (success) {
var totalNum = this.getTotalRecs();
var numPages = Math.ceil(totalNum / store.pageSize);
if (store.currentPage > numPages) store.currentPage = numPages;
if (store.currentPage < 1) store.currentPage = 1;
if (this.isAutoButtons) {
this.handleStoreLoadBtn(store, numPages);
}
}
}
});


Ext.preg('listpager', Ext.plugins.ListPaging);



Ext.apply(Ext.anims, {
rotate: new Ext.Anim({
autoClear: false,
out: false,
before: function(el) {
var d = '';
if (this.dir == 'ccw') {
d = '-';
}


this.from = {
'-webkit-transform': 'rotate(' + d + '' + this.fromAngle + 'deg)'
};


this.to = {
'-webkit-transform': 'rotate(' + d + '' + this.toAngle + 'deg)'
};


}
})
});



Ext.plugins.ListPullPager = Ext.extend(Ext.plugins.ListPaging, {
langPullPrevious: 'Pull down to load {0} of {1},total {2}',
pullPreviousText: 'Pull down to load previous...',
langReleasePrevious: 'Release to load previous...',
langPullNext: 'Pull up to load page {0} of {1} ,total {2}',
pullNextText: 'Pull up to load next...',
langReleaseNext: 'Release to load next...',
langLoading: 'Loading...',


loading: false,
// private
init: function(list) {
this.list = list;
this.lastUpdated = new Date();
list.loadingText = undefined;
list.on('render', this.initPullHandler, this);
Ext.plugins.ListPullPager.superclass.init.apply(this, arguments);
},
// private
initPullHandler: function() {
//Previous
this.previousTpl = new Ext.XTemplate(
'<div class="x-list-pullprevpage">',
'<div class="x-list-pullprevpage-arrow"></div>',
'<div class="x-list-pullrefresh-wrap">',
'<h3 class="x-list-pullrefresh-message">{m}</h3>',
'<div class="x-list-pullrefresh-updated">Last Updated: <span>{l:date("m/d/Y h:iA")}</span></div>',
'</div>',
'</div>'
);
this.previousEl = this.previousTpl.insertBefore(this.list.scroller.el,
{m:this.pullPreviousText,l:this.lastUpdated}, true);
this.previousEl.hide();


Ext.Element.cssTranslate(this.previousEl, {x:0, y:-75});


//Next
this.nextTpl = new Ext.XTemplate(
'<div class="x-list-pullnextpage">',
'<div class="x-list-pullnextpage-arrow"></div>',
'<div class="x-list-pullrefresh-wrap">',
'<h3 class="x-list-pullrefresh-message">{m}</h3>',
'<div class="x-list-pullrefresh-updated">Last Updated: <span>{l:date("m/d/Y h:iA")}</span></div>',
'</div>',
'</div>'
);
this.nextEl = this.nextTpl.insertAfter(this.list.scroller.el,
{m:this.pullNextText,l:this.lastUpdated}, true);
this.nextEl.hide();
Ext.Element.cssTranslate(this.nextEl, {x:0, y:-90});




this.list.scroller.on('offsetchange', this.handleOffsettChange, this);
},


handleOffsettChange: function(scroller, offset) {
if (scroller.direction === 'vertical' && !this.loading) {


var heightOfList = scroller.size.height - scroller.containerBox.height;


if (offset.y > 0) {
//console.log('up:'+offset);
this.handlePrevious(scroller, offset, heightOfList);
}


if (offset.y < (heightOfList * -1)) {
//console.log('down:'+offset);
this.handleNext(scroller, offset, heightOfList);
}
}
},


//private
handlePrevious: function(scroller, offset, heightOfList) {
if (scroller.direction === 'vertical' && !this.loading) {
if (offset) {
Ext.Element.cssTranslate(this.previousEl, {x:0, y:offset.y - 76});


if (offset.y > 90) {
// state 1
if (this.state !== 1) {
this.prevState = this.state;
this.state = 1;
this.previousTpl.overwrite(this.previousEl, {m:this.langReleasePrevious,l:this.lastUpdated});
Ext.Anim.run(this.previousEl.select('.x-list-pullprevpage-arrow').first(), 'rotate', {dir:'ccw',fromAngle:0,toAngle:180});
}
} else if (!scroller.isDragging()) {
// state 3
if (this.state !== 3) {
this.prevState = this.state;
this.state = 3;
this.previousEl.hide();
if (this.prevState == 1) {
this.loading = true;
this.lastUpdated = new Date();
this.loadPage(-1);
this.callback = this.previousComplete();
}
}
} else {
// state 2
if (this.state !== 2) {
this.prevState = this.state;
this.state = 2;
this.previousTpl.overwrite(this.previousEl, {m:this.pullPreviousText,l:this.lastUpdated});
this.previousEl.show();
if (this.prevState == 1) {
Ext.Anim.run(this.previousEl.select('.x-list-pullprevpage-arrow').first(), 'rotate', {dir:'cw',fromAngle:180,toAngle:0});
}
}
}
}
}
},
handleNext: function(scroller, offset, heightOfList) {
if (scroller.direction === 'vertical' && !this.loading) {
if (offset) {
Ext.Element.cssTranslate(this.nextEl, {x:0, y:offset.y});


if (offset.y < ((heightOfList + 90) * -1)) {
// state 1
if (this.state !== 1) {
this.nextState = this.state;
this.state = 1;
this.nextTpl.overwrite(this.nextEl, {m:this.langReleaseNext,l:this.lastUpdated});
Ext.Anim.run(this.nextEl.select('.x-list-pullnextpage-arrow').first(), 'rotate', {dir:'ccw',fromAngle:180,toAngle:0});
}
} else if (!scroller.isDragging()) {
// state 3
if (this.state !== 3) {
this.nextState = this.state;
this.state = 3;
this.nextEl.hide();
if (this.nextState == 1) {
this.loading = true;
this.lastUpdated = new Date();
this.loadPage(1);
this.callback = this.nextComplete();
}
}
} else {
// state 2
if (this.state !== 2) {
this.nextState = this.state;
this.state = 2;
this.nextTpl.overwrite(this.nextEl, {m:this.pullNextText,l:this.lastUpdated});
this.nextEl.show();
if (this.nextState == 1) {
Ext.Anim.run(this.nextEl.select('.x-list-pullnextpage-arrow').first(), 'rotate', {dir:'cw',fromAngle:0,toAngle:180});
}
}
}
}
}
},
//private
previousComplete: function() {
this.loading = false;
this.lastUpdated = new Date();
this.previousTpl.overwrite(this.previousEl, {m:this.pullPreviousText,l:this.lastUpdated});
},
nextComplete: function() {
this.loading = false;
this.lastUpdated = new Date();
this.nextTpl.overwrite(this.nextEl, {m:this.pullNextText,l:this.lastUpdated});
},
handleStoreLoad:function (store, recs, success) {
Ext.plugins.ListPullPager.superclass.handleStoreLoad.apply(this, arguments);
var totalNum = this.getTotalRecs();
var numPages = Math.ceil(totalNum / store.pageSize);
if (store.currentPage > numPages) store.currentPage = numPages;
if (store.currentPage < 1) store.currentPage = 1;
var c = store.currentPage;
this.pullPreviousText = Ext.util.Format.format(this.langPullPrevious, c > 1 ? c - 1 : 1, numPages, totalNum);
this.pullNextText = Ext.util.Format.format(this.langPullNext, c < numPages ? c + 1 : numPages, numPages, totalNum);
if (this.callback) {
this.callback();
}


}
});


Ext.preg('listpullpager', Ext.plugins.ListPullPager);



CSS



.x-list-pullprevpage {
display: -webkit-box;
display: box;
-webkit-box-orient: horizontal;
box-orient: horizontal;
-webkit-box-align: center;
box-align: center;
-webkit-box-pack: center;
box-pack: center;
left: 0;
width: 100%;
height: 0
}
.x-list-pullnextpage {
display: -webkit-box;
display: box;
-webkit-box-orient: horizontal;
box-orient: horizontal;
-webkit-box-align: center;
box-align: center;
-webkit-box-pack: center;
box-pack: center;
position: absolute;
left: 0;
width: 100%;
height: 0
}


.x-list-pullrefresh-message {
font-weight: bold;
font-size: 1.0em;
margin-bottom: 0.1em;
text-align: center
}


.x-list-pullprevpage-arrow {
width: 2.5em;
height: 3.0em;
-webkit-mask: center center url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wBHwEEFZRBU1UAAAI7SURBVGje7dq/a5NRFMbxz6ttHSym4CK4+A8UtZPugpOrqKNiETqIq2tB3EQoIgjiIhZdCoIguLu1SkGFLi6Cmz+KQ432OvQGXjRN75vcN4ltDpwpL9xvnvecc885SRFCMMy2z5DbGBRFkfr8/kzn/k55KISwBZhoRUbATYRkBasqnsGadRyYU8GiLgXHB5IkFb71WEYF7UkFx0cKtiuGw2ohhP/kqku0CRzPdO5b/KwjSSaGOUmGHlBGwNrq4IF+KzgqM/18xQdxPtO5T/FjV8TgrgOcGGXxbs7iKdzMdO4tfN2TzcLAsvgEGgmFOhfgqYRC/Q1vWoAbuKJ/y6RLCauR22UF32MJF4ckeZ/g3d8xuIRpzAwYbjmybAVrCKG8fmvgHg4PCO4LrsX4E0L4B1BU8Y7+Lzc3cQOr5ZukXZlZxaP4Tfpp98tw273ist3F6T7Bvcb1dndxJ8AGFnGkZrjPuNCKuyqAYkY/rHEn08TlmLlddTPLWIiAdfjCdnApMVi2BziTWb1XuLpTP5gKOIWXOJoJ7hPO7tRyVQGEk3ieIR6bOIeV3B31Cubj9diLz6fAVY3Bsj2OCnRjL2JJSZ5JugE8FAvrsYpwH2Ph/15poOtyqpvBeuwjU3y9my6pF0CYw69En+t27Ox1Ll609ZNWJ3/Wy1zcK2ADax3g1hJmnVoBWwPXRhu4jfiZQQPCbBvA2Ryrj5y7mXI8LubazeQEnMSH6JPDCNiaZ6ZzbreK0d9SRoAjwM72B0xIAaqN33EkAAAAAElFTkSuQmCC') no-repeat;
-webkit-mask-size: 2em 3em;
-webkit-transform: rotate(0deg);
background-color: #111
}


.x-list-pullnextpage-arrow {
width: 2.5em;
height: 3.0em;
-webkit-mask: center center url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAA8CAYAAAAUufjgAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wBHwEEFZRBU1UAAAI7SURBVGje7dq/a5NRFMbxz6ttHSym4CK4+A8UtZPugpOrqKNiETqIq2tB3EQoIgjiIhZdCoIguLu1SkGFLi6Cmz+KQ432OvQGXjRN75vcN4ltDpwpL9xvnvecc885SRFCMMy2z5DbGBRFkfr8/kzn/k55KISwBZhoRUbATYRkBasqnsGadRyYU8GiLgXHB5IkFb71WEYF7UkFx0cKtiuGw2ohhP/kqku0CRzPdO5b/KwjSSaGOUmGHlBGwNrq4IF+KzgqM/18xQdxPtO5T/FjV8TgrgOcGGXxbs7iKdzMdO4tfN2TzcLAsvgEGgmFOhfgqYRC/Q1vWoAbuKJ/y6RLCauR22UF32MJF4ckeZ/g3d8xuIRpzAwYbjmybAVrCKG8fmvgHg4PCO4LrsX4E0L4B1BU8Y7+Lzc3cQOr5ZukXZlZxaP4Tfpp98tw273ist3F6T7Bvcb1dndxJ8AGFnGkZrjPuNCKuyqAYkY/rHEn08TlmLlddTPLWIiAdfjCdnApMVi2BziTWb1XuLpTP5gKOIWXOJoJ7hPO7tRyVQGEk3ieIR6bOIeV3B31Cubj9diLz6fAVY3Bsj2OCnRjL2JJSZ5JugE8FAvrsYpwH2Ph/15poOtyqpvBeuwjU3y9my6pF0CYw69En+t27Ox1Ll609ZNWJ3/Wy1zcK2ADax3g1hJmnVoBWwPXRhu4jfiZQQPCbBvA2Ryrj5y7mXI8LubazeQEnMSH6JPDCNiaZ6ZzbreK0d9SRoAjwM72B0xIAaqN33EkAAAAAElFTkSuQmCC') no-repeat;
-webkit-mask-size: 2em 3em;
-webkit-transform: rotate(180deg);
background-color: #111
}

anthonyterrell
20 Jun 2012, 12:16 PM
Hello Everyone,

I'm currently attempting to apply this plugin to a list in which accesses the Twitter API. My question is, if there is a PullRefresh.js, why do we need a plugin to do this? I understand that the Sencha developers are trying to watch the build size, so does this mean they are simply adding a place holder for an optional plugin?

I followed the Readme for the Sencha Touch 2 port (https://github.com/j-mcnally/Sencha2-PullToRefresh) of the pull to refresh plugin and am receiving this error. Wasn't sure if anyone could shed some light on what may be happening.


TypeError: 'undefined' is not a constructor (evaluating 'new Ext.Anim')
(file:///Users/anthonyterrell/Dropbox/MarketArt/Collaboration/Mobile2.0/sdk/src/ux/kohactive/RefreshableList.js?_dc=1340222244605)Failed to load resource: The requested URL was not found on this server.
(file:///Users/anthonyterrell/Dropbox/MarketArt/Collaboration/Mobile2.0/sdk/sencha-touch.js)Error: [Ext.Loader] Failed loading 'sdk/src/ux/kohactive/RefreshableList.js', please verify that the file exists


Thanks!

akarsh478
20 Jul 2012, 2:43 AM
very nice, very helpful. But can someone help me how can I use this plugin to implement the same pull to refresh for bottom of the list? Thanks in advance guys.