@@ -0,0 +1,6 @@ | |||
from django.conf.urls import patterns, include, url | |||
urlpatterns = patterns('', | |||
url(r'^pins/recent/(?P<page>\d*)/$', 'pinry.api.views.pins_recent', name='pins-recent'), | |||
) |
@@ -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") |
@@ -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 { | |||
@@ -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<length; i++) { | |||
image = data[i]; | |||
html += '<div class="pin">'; | |||
html += '<a class="fancybox" rel="pins" href="'+image.original+'">'; | |||
html += '<img src="'+image.thumbnail+'" width="200" height="'+Math.round(image.height/image.width*200)+'">'; | |||
html += '</a>'; | |||
html += '<p>'+image.description+'</p>'; | |||
html += '</div>'; | |||
} | |||
$('#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' | |||
@@ -39,11 +39,13 @@ | |||
<script src="/static/vendor/bootstrap/2.0.3/js/bootstrap.js"></script> | |||
<script src="/static/vendor/wookmark/0.5/jquery.wookmark.js"></script> | |||
<script src="/static/vendor/fancybox/2.0.6/jquery.fancybox.js"></script> | |||
<script src="/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js"></script> | |||
{% else %} | |||
<script src="/static/vendor/jquery/1.7.2/jquery.min.js"></script> | |||
<script src="/static/vendor/bootstrap/2.0.3/js/bootstrap.min.js"></script> | |||
<script src="/static/vendor/wookmark/0.5/jquery.wookmark.min.js"></script> | |||
<script src="/static/vendor/fancybox/2.0.6/jquery.fancybox.pack.js"></script> | |||
<script src="/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js"></script> | |||
{% endif %} | |||
<script src="/static/core/js/pinry.js"></script> | |||
@@ -3,18 +3,12 @@ | |||
{% block title %}Recent Pins{% endblock %} | |||
{% block yield %} | |||
<div id="pins"> | |||
{% for pin in pins %} | |||
<div class="pin"> | |||
{% if pin.image %} | |||
<a class="fancybox" rel="pins" href="{{ pin.image.url }}"> | |||
<img src="{{ pin.image.url_200x1000 }}" alt="{{ pin.description }}"> | |||
</a> | |||
{% endif %} | |||
{% if pin.description %} | |||
<p><strong>Description:</strong> {{ pin.description }}</p> | |||
{% endif %} | |||
<div id="pins"></div> | |||
<div id="loader" class="container"> | |||
<div class="row"> | |||
<div class="span2 offset5"> | |||
<img src="/static/core/img/loader.gif" alt="Loader"> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</div> | |||
{% endblock %} |
@@ -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): | |||
@@ -53,4 +53,5 @@ INSTALLED_APPS = ( | |||
'pinry.vendor', | |||
'pinry.core', | |||
'pinry.pins', | |||
'pinry.api', | |||
) |
@@ -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) |
@@ -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); |
@@ -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); |