mirror of
https://github.com/pinry/pinry.git
synced 2025-11-13 08:35:41 +01:00
Started the creation of an API and implemented infinite scroll. Closes issue #7.
This commit is contained in:
0
pinry/api/__init__.py
Normal file
0
pinry/api/__init__.py
Normal file
6
pinry/api/urls.py
Normal file
6
pinry/api/urls.py
Normal file
@@ -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'),
|
||||||
|
)
|
||||||
21
pinry/api/views.py
Normal file
21
pinry/api/views.py
Normal file
@@ -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;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loader {
|
||||||
|
margin-top: 70px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
#pins {
|
#pins {
|
||||||
top: 70px;
|
top: 70px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -41,6 +46,7 @@ body {
|
|||||||
float: left;
|
float: left;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin img {
|
.pin img {
|
||||||
|
|||||||
BIN
pinry/core/static/core/img/loader.gif
Normal file
BIN
pinry/core/static/core/img/loader.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1,10 +1,84 @@
|
|||||||
$(window).bind('load', function() {
|
/**
|
||||||
$('.pin').wookmark({
|
* 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,
|
offset: 3,
|
||||||
itemWidth: 242,
|
itemWidth: 242
|
||||||
autoResize: true
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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({
|
$('.fancybox').fancybox({
|
||||||
openEffect: 'none',
|
openEffect: 'none',
|
||||||
closeEffect: 'none'
|
closeEffect: 'none'
|
||||||
|
|||||||
@@ -39,11 +39,13 @@
|
|||||||
<script src="/static/vendor/bootstrap/2.0.3/js/bootstrap.js"></script>
|
<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/wookmark/0.5/jquery.wookmark.js"></script>
|
||||||
<script src="/static/vendor/fancybox/2.0.6/jquery.fancybox.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 %}
|
{% else %}
|
||||||
<script src="/static/vendor/jquery/1.7.2/jquery.min.js"></script>
|
<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/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/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/fancybox/2.0.6/jquery.fancybox.pack.js"></script>
|
||||||
|
<script src="/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script src="/static/core/js/pinry.js"></script>
|
<script src="/static/core/js/pinry.js"></script>
|
||||||
|
|||||||
@@ -3,18 +3,12 @@
|
|||||||
{% block title %}Recent Pins{% endblock %}
|
{% block title %}Recent Pins{% endblock %}
|
||||||
|
|
||||||
{% block yield %}
|
{% block yield %}
|
||||||
<div id="pins">
|
<div id="pins"></div>
|
||||||
{% for pin in pins %}
|
<div id="loader" class="container">
|
||||||
<div class="pin">
|
<div class="row">
|
||||||
{% if pin.image %}
|
<div class="span2 offset5">
|
||||||
<a class="fancybox" rel="pins" href="{{ pin.image.url }}">
|
<img src="/static/core/img/loader.gif" alt="Loader">
|
||||||
<img src="{{ pin.image.url_200x1000 }}" alt="{{ pin.description }}">
|
</div>
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if pin.description %}
|
|
||||||
<p><strong>Description:</strong> {{ pin.description }}</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ from .forms import PinForm
|
|||||||
|
|
||||||
|
|
||||||
def recent_pins(request):
|
def recent_pins(request):
|
||||||
context = {
|
return TemplateResponse(request, 'pins/recent_pins.html', None)
|
||||||
'pins': Pin.objects.order_by('-id')[:50],
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'pins/recent_pins.html', context)
|
|
||||||
|
|
||||||
|
|
||||||
def new_pin(request):
|
def new_pin(request):
|
||||||
|
|||||||
@@ -53,4 +53,5 @@ INSTALLED_APPS = (
|
|||||||
'pinry.vendor',
|
'pinry.vendor',
|
||||||
'pinry.core',
|
'pinry.core',
|
||||||
'pinry.pins',
|
'pinry.pins',
|
||||||
|
'pinry.api',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ from django.conf import settings
|
|||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'', include('pinry.core.urls', namespace='core')),
|
url(r'', include('pinry.core.urls', namespace='core')),
|
||||||
url(r'^pins/', include('pinry.pins.urls', namespace='pins')),
|
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)
|
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
112
pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js
vendored
Normal file
112
pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js
vendored
Normal file
@@ -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);
|
||||||
2
pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js
vendored
Normal file
2
pinry/vendor/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.min.js
vendored
Normal file
@@ -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);
|
||||||
Reference in New Issue
Block a user