diff --git a/pinry/api/__init__.py b/pinry/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pinry/api/urls.py b/pinry/api/urls.py new file mode 100644 index 0000000..65c6980 --- /dev/null +++ b/pinry/api/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import patterns, include, url + + +urlpatterns = patterns('', + url(r'^pins/recent/(?P\d*)/$', 'pinry.api.views.pins_recent', name='pins-recent'), +) diff --git a/pinry/api/views.py b/pinry/api/views.py new file mode 100644 index 0000000..3220d10 --- /dev/null +++ b/pinry/api/views.py @@ -0,0 +1,21 @@ +from django.utils import simplejson +from django.http import HttpResponse + +from pinry.pins.models import Pin + + +def pins_recent(request, page=1): + start_pin = abs(int(page) - 1) * 25 + end_pin = int(page) * 25 + + pins = Pin.objects.order_by('-id')[start_pin:end_pin] + recent_pins = [] + for pin in pins: + recent_pins.append({ + 'id': pin.id, + 'thumbnail': pin.image.url_200x1000, + 'original': pin.image.url, + 'description': pin.description, + }) + + return HttpResponse(simplejson.dumps(recent_pins), mimetype="application/json") diff --git a/pinry/core/static/core/css/pinry.css b/pinry/core/static/core/css/pinry.css index 6d9b494..81d3f9b 100644 --- a/pinry/core/static/core/css/pinry.css +++ b/pinry/core/static/core/css/pinry.css @@ -29,6 +29,11 @@ body { text-decoration: underline; } +#loader { + margin-top: 70px; + text-align: center; +} + #pins { top: 70px; position: absolute; @@ -41,6 +46,7 @@ body { float: left; border: 1px solid #ccc; padding: 20px; + display: none; } .pin img { diff --git a/pinry/core/static/core/img/loader.gif b/pinry/core/static/core/img/loader.gif new file mode 100644 index 0000000..c69e937 Binary files /dev/null and b/pinry/core/static/core/img/loader.gif differ diff --git a/pinry/core/static/core/js/pinry.js b/pinry/core/static/core/js/pinry.js index f671bb8..b7037bd 100644 --- a/pinry/core/static/core/js/pinry.js +++ b/pinry/core/static/core/js/pinry.js @@ -1,10 +1,84 @@ -$(window).bind('load', function() { - $('.pin').wookmark({ - offset: 3, - itemWidth: 242, - autoResize: true +/** + * Based on Wookmark's endless scroll. + */ +$(window).ready(function () { + var apiURL = '/api/pins/recent/' + var page = 1; + var handler = null; + var isLoading = false; + + /** + * When scrolled all the way to the bottom, add more tiles. + */ + function onScroll(event) { + if(!isLoading) { + var closeToBottom = ($(window).scrollTop() + $(window).height() > $(document).height() - 100); + if(closeToBottom) loadData(); + } + }; + + function applyLayout() { + $('#pins').imagesLoaded(function() { + // Clear our previous layout handler. + if(handler) handler.wookmarkClear(); + + // Create a new layout handler. + handler = $('#pins .pin'); + handler.wookmark({ + autoResize: true, + offset: 3, + itemWidth: 242 + }); + }); + }; + + /** + * Loads data from the API. + */ + function loadData() { + isLoading = true; + $('#loader').show(); + + $.ajax({ + url: apiURL+page, + success: onLoadData + }); + }; + + /** + * Receives data from the API, creates HTML for images and updates the layout + */ + function onLoadData(data) { + isLoading = false; + $('#loader').hide(); + + page++; + + var html = ''; + var i=0, length=data.length, image; + for(; i'; + html += ''; + html += ''; + html += '

'+image.description+'

'; + html += ''; + } + + $('#pins').append(html); + + applyLayout(); + }; + + $(document).ready(new function() { + $(document).bind('scroll', onScroll); + loadData(); }); + /** + * On clicking an image show fancybox original. + */ $('.fancybox').fancybox({ openEffect: 'none', closeEffect: 'none' diff --git a/pinry/core/templates/core/base.html b/pinry/core/templates/core/base.html index 7e8a442..841be8a 100644 --- a/pinry/core/templates/core/base.html +++ b/pinry/core/templates/core/base.html @@ -39,11 +39,13 @@ + {% else %} + {% endif %} diff --git a/pinry/pins/templates/pins/recent_pins.html b/pinry/pins/templates/pins/recent_pins.html index e93ea31..f79ca90 100644 --- a/pinry/pins/templates/pins/recent_pins.html +++ b/pinry/pins/templates/pins/recent_pins.html @@ -3,18 +3,12 @@ {% block title %}Recent Pins{% endblock %} {% block yield %} -
- {% for pin in pins %} -
- {% if pin.image %} - - {{ pin.description }} - - {% endif %} - {% if pin.description %} -

Description: {{ pin.description }}

- {% endif %} +
+
+
+
+ Loader
- {% endfor %} +
{% endblock %} diff --git a/pinry/pins/views.py b/pinry/pins/views.py index ec1a17a..41a7689 100644 --- a/pinry/pins/views.py +++ b/pinry/pins/views.py @@ -7,10 +7,7 @@ from .forms import PinForm def recent_pins(request): - context = { - 'pins': Pin.objects.order_by('-id')[:50], - } - return TemplateResponse(request, 'pins/recent_pins.html', context) + return TemplateResponse(request, 'pins/recent_pins.html', None) def new_pin(request): diff --git a/pinry/settings/__init__.py b/pinry/settings/__init__.py index 534956b..4bf8925 100644 --- a/pinry/settings/__init__.py +++ b/pinry/settings/__init__.py @@ -53,4 +53,5 @@ INSTALLED_APPS = ( 'pinry.vendor', 'pinry.core', 'pinry.pins', + 'pinry.api', ) diff --git a/pinry/urls.py b/pinry/urls.py index eee2e64..02d2c8e 100644 --- a/pinry/urls.py +++ b/pinry/urls.py @@ -6,4 +6,5 @@ from django.conf import settings urlpatterns = patterns('', url(r'', include('pinry.core.urls', namespace='core')), url(r'^pins/', include('pinry.pins.urls', namespace='pins')), + url(r'^api/', include('pinry.api.urls', namespace='api')), ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js b/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js new file mode 100644 index 0000000..a488974 --- /dev/null +++ b/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js @@ -0,0 +1,112 @@ +/*! + * jQuery imagesLoaded plugin v2.0.1 + * http://github.com/desandro/imagesloaded + * + * MIT License. by Paul Irish et al. + */ + +/*jshint curly: true, eqeqeq: true, noempty: true, strict: true, undef: true, browser: true */ +/*global jQuery: false */ + +;(function($, undefined) { +'use strict'; + +// blank image data-uri bypasses webkit log warning (thx doug jones) +var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='; + +$.fn.imagesLoaded = function( callback ) { + var $this = this, + deferred = $.isFunction($.Deferred) ? $.Deferred() : 0, + hasNotify = $.isFunction(deferred.notify), + $images = $this.find('img').add( $this.filter('img') ), + loaded = [], + proper = [], + broken = []; + + function doneLoading() { + var $proper = $(proper), + $broken = $(broken); + + if ( deferred ) { + if ( broken.length ) { + deferred.reject( $images, $proper, $broken ); + } else { + deferred.resolve( $images ); + } + } + + if ( $.isFunction( callback ) ) { + callback.call( $this, $images, $proper, $broken ); + } + } + + function imgLoaded( img, isBroken ) { + // don't proceed if BLANK image, or image is already loaded + if ( img.src === BLANK || $.inArray( img, loaded ) !== -1 ) { + return; + } + + // store element in loaded images array + loaded.push( img ); + + // keep track of broken and properly loaded images + if ( isBroken ) { + broken.push( img ); + } else { + proper.push( img ); + } + + // cache image and its state for future calls + $.data( img, 'imagesLoaded', { isBroken: isBroken, src: img.src } ); + + // trigger deferred progress method if present + if ( hasNotify ) { + deferred.notifyWith( $(img), [ isBroken, $images, $(proper), $(broken) ] ); + } + + // call doneLoading and clean listeners if all images are loaded + if ( $images.length === loaded.length ){ + setTimeout( doneLoading ); + $images.unbind( '.imagesLoaded' ); + } + } + + // if no images, trigger immediately + if ( !$images.length ) { + doneLoading(); + } else { + $images.bind( 'load.imagesLoaded error.imagesLoaded', function( event ){ + // trigger imgLoaded + imgLoaded( event.target, event.type === 'error' ); + }).each( function( i, el ) { + var src = el.src; + + // find out if this image has been already checked for status + // if it was, and src has not changed, call imgLoaded on it + var cached = $.data( el, 'imagesLoaded' ); + if ( cached && cached.src === src ) { + imgLoaded( el, cached.isBroken ); + return; + } + + // if complete is true and browser supports natural sizes, try + // to check for image status manually + if ( el.complete && el.naturalWidth !== undefined ) { + imgLoaded( el, el.naturalWidth === 0 || el.naturalHeight === 0 ); + return; + } + + // cached images don't fire load sometimes, so we reset src, but only when + // dealing with IE, or image is complete (loaded) and failed manual check + // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f + if ( el.readyState || el.complete ) { + el.src = BLANK; + el.src = src; + } + }); + } + + return deferred ? deferred.promise( $this ) : $this; +}; + +})(jQuery); \ No newline at end of file diff --git a/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js b/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js new file mode 100644 index 0000000..a5fd97b --- /dev/null +++ b/pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js @@ -0,0 +1,2 @@ +(function(c,n){var k="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";c.fn.imagesLoaded=function(l){function m(){var b=c(h),a=c(g);d&&(g.length?d.reject(e,b,a):d.resolve(e));c.isFunction(l)&&l.call(f,e,b,a)}function i(b,a){b.src===k||-1!==c.inArray(b,j)||(j.push(b),a?g.push(b):h.push(b),c.data(b,"imagesLoaded",{isBroken:a,src:b.src}),o&&d.notifyWith(c(b),[a,e,c(h),c(g)]),e.length===j.length&&(setTimeout(m),e.unbind(".imagesLoaded")))}var f=this,d=c.isFunction(c.Deferred)?c.Deferred(): +0,o=c.isFunction(d.notify),e=f.find("img").add(f.filter("img")),j=[],h=[],g=[];e.length?e.bind("load.imagesLoaded error.imagesLoaded",function(b){i(b.target,"error"===b.type)}).each(function(b,a){var e=a.src,d=c.data(a,"imagesLoaded");if(d&&d.src===e)i(a,d.isBroken);else if(a.complete&&a.naturalWidth!==n)i(a,0===a.naturalWidth||0===a.naturalHeight);else if(a.readyState||a.complete)a.src=k,a.src=e}):m();return d?d.promise(f):f}})(jQuery);