mirror of
https://github.com/pinry/pinry.git
synced 2025-11-14 17:05:50 +01:00
Rewrite the display of recent pins without the use of third-party js libs
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
from tastypie.resources import ModelResource
|
||||
from tastypie import fields
|
||||
from tastypie.authentication import BasicAuthentication
|
||||
from tastypie.authorization import DjangoAuthorization
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from pinry.pins.models import Pin
|
||||
|
||||
|
||||
class UserResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = User.objects.all()
|
||||
resource_name = 'user'
|
||||
excludes = ['email', 'password', 'is_superuser', 'first_name',
|
||||
'last_name', 'is_active', 'is_staff', 'last_login', 'date_joined']
|
||||
include_resource_uri = False
|
||||
|
||||
|
||||
class PinResource(ModelResource): # pylint: disable-msg=R0904
|
||||
tags = fields.ListField()
|
||||
submitter = fields.ForeignKey(UserResource, 'submitter', full=True)
|
||||
|
||||
class Meta:
|
||||
queryset = Pin.objects.all()
|
||||
@@ -17,6 +25,7 @@ class PinResource(ModelResource): # pylint: disable-msg=R0904
|
||||
include_resource_uri = False
|
||||
filtering = {
|
||||
'published': ['gt'],
|
||||
'submitter': ['exact']
|
||||
}
|
||||
|
||||
def build_filters(self, filters=None):
|
||||
@@ -37,13 +46,3 @@ class PinResource(ModelResource): # pylint: disable-msg=R0904
|
||||
tags = bundle.data.get('tags', [])
|
||||
bundle.obj.tags.set(*tags)
|
||||
return super(PinResource, self).save_m2m(bundle)
|
||||
|
||||
|
||||
class UserResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = User.objects.all()
|
||||
resource_name = 'auth/user'
|
||||
excludes = ['email', 'password', 'is_superuser']
|
||||
# Add it here.
|
||||
authentication = BasicAuthentication()
|
||||
authorization = DjangoAuthorization()
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
from tastypie.api import Api
|
||||
|
||||
from .api import PinResource
|
||||
from .api import UserResource
|
||||
|
||||
|
||||
pin_resource = PinResource()
|
||||
user_resource = UserResource()
|
||||
v1_api = Api(api_name='v1')
|
||||
v1_api.register(PinResource())
|
||||
v1_api.register(UserResource())
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'', include(pin_resource.urls)),
|
||||
url(r'', include(user_resource.urls)),
|
||||
url(r'^api/', include(v1_api.urls)),
|
||||
)
|
||||
|
||||
@@ -70,18 +70,14 @@ body {
|
||||
|
||||
#pins {
|
||||
top: 70px;
|
||||
position: absolute;
|
||||
background: #eee;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.pin {
|
||||
background: #fff;
|
||||
width: 200px;
|
||||
float: left;
|
||||
border: 1px solid #ccc;
|
||||
padding: 20px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.pin img {
|
||||
|
||||
@@ -1,121 +1,85 @@
|
||||
/**
|
||||
* Based on Wookmark's endless scroll.
|
||||
*/
|
||||
var apiURL = '/api/pin/?format=json&offset='
|
||||
var page = 0;
|
||||
var handler = null;
|
||||
var globalTag = null;
|
||||
var isLoading = false;
|
||||
$(window).load(function() {
|
||||
function tileLayout() {
|
||||
// Config
|
||||
var blockMargin = 20;
|
||||
var blockWidth = 240;
|
||||
// End Config
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
var blockContainer = $('#pins');
|
||||
var blocks = blockContainer.children('.pin');
|
||||
var rowSize = Math.floor(blockContainer.width()/blockWidth);
|
||||
var blockWidth = (blockContainer.width()-blockMargin*(rowSize))/rowSize;
|
||||
var colHeights = []
|
||||
|
||||
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(tag) {
|
||||
isLoading = true;
|
||||
$('#loader').show();
|
||||
|
||||
if (tag !== undefined) {
|
||||
globalTag = tag;
|
||||
window.history.pushState(tag, 'Pinry - Tag - '+tag, '/pins/tag/'+tag+'/');
|
||||
} else if (url(2) == 'tag') {
|
||||
tag = url(3);
|
||||
globalTag = tag;
|
||||
window.history.pushState(tag, 'Pinry - Tag - '+tag, '/pins/tag/'+tag+'/');
|
||||
}
|
||||
|
||||
if (tag !== undefined) {
|
||||
$('#pins').html('');
|
||||
page = 0;
|
||||
if (tag != null)
|
||||
$('.tags').html('<span class="label tag" onclick="loadData(null)">' + tag + ' x</span>');
|
||||
else {
|
||||
$('.tags').html('');
|
||||
window.history.pushState(tag, 'Pinry - Recent Pins', '/pins/');
|
||||
for (var i=0; i < rowSize; i++) {
|
||||
colHeights[i] = 0;
|
||||
}
|
||||
|
||||
for (var b=0; b < blocks.length; b++) {
|
||||
block = blocks.eq(b);
|
||||
|
||||
var col = -1;
|
||||
var colHeight = 0;
|
||||
for (var i=0; i < rowSize; i++) {
|
||||
if (col < 0) {
|
||||
col = 0;
|
||||
colHeight = colHeights[col];
|
||||
} else {
|
||||
if (colHeight > colHeights[i]) {
|
||||
col = i;
|
||||
colHeight = colHeights[col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block.css({
|
||||
'margin-left': blockWidth*col+col*blockMargin
|
||||
});
|
||||
|
||||
blockMarginTop = blockMargin;
|
||||
block.css({
|
||||
'margin-top': colHeight+blockMarginTop
|
||||
});
|
||||
colHeights[col] += block.height()+blockMarginTop;
|
||||
|
||||
block.css('display', 'block');
|
||||
}
|
||||
|
||||
$('.spinner').css('display', 'none');
|
||||
blockContainer.css('height', colHeights.sort().slice(-1)[0]);
|
||||
}
|
||||
|
||||
var loadURL = apiURL+(page*30);
|
||||
if (globalTag !== null) loadURL += "&tag=" + tag;
|
||||
|
||||
$.ajax({
|
||||
url: loadURL,
|
||||
success: onLoadData
|
||||
});
|
||||
};
|
||||
var offset = 0;
|
||||
|
||||
/**
|
||||
* Receives data from the API, creates HTML for images and updates the layout
|
||||
*/
|
||||
function onLoadData(data) {
|
||||
data = data.objects;
|
||||
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 += '<div class="pin-options">';
|
||||
html += '<a href="/pins/delete-pin/'+image.id+'">';
|
||||
html += '<i class="icon-trash"></i>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
html += '<a class="fancybox" rel="pins" href="'+image.image+'">';
|
||||
html += '<img src="'+image.thumbnail+'" width="200" >';
|
||||
html += '</a>';
|
||||
if (image.description) html += '<p>'+image.description+'</p>';
|
||||
if (image.tags) {
|
||||
html += '<p>';
|
||||
for (tag in image.tags) {
|
||||
html += '<span class="label tag" onclick="loadData(\'' + image.tags[tag] + '\')">' + image.tags[tag] + '</span> ';
|
||||
}
|
||||
html += '</p>';
|
||||
}
|
||||
html += '</div>';
|
||||
function loadPins() {
|
||||
$('.spinner').css('display', 'block');
|
||||
$.get('/api/v1/pin/?format=json&offset='+String(offset), function(pins) {
|
||||
console.log(pins.objects[0])
|
||||
var source = $('#pins-template').html();
|
||||
var template = Handlebars.compile(source);
|
||||
var context = {
|
||||
pins: pins.objects
|
||||
}
|
||||
var html = template(context);
|
||||
$('#pins').append(html);
|
||||
|
||||
$('#pins').ajaxStop(function() {
|
||||
tileLayout();
|
||||
});
|
||||
|
||||
offset += 30;
|
||||
});
|
||||
}
|
||||
|
||||
$('#pins').append(html);
|
||||
|
||||
applyLayout();
|
||||
};
|
||||
|
||||
$(document).ready(new function() {
|
||||
$(document).bind('scroll', onScroll);
|
||||
loadData();
|
||||
});
|
||||
loadPins();
|
||||
|
||||
/**
|
||||
* On clicking an image show fancybox original.
|
||||
*/
|
||||
$('.fancybox').fancybox({
|
||||
openEffect: 'none',
|
||||
closeEffect: 'none'
|
||||
$(window).resize(function() {
|
||||
tileLayout();
|
||||
})
|
||||
|
||||
$(window).scroll(function() {
|
||||
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
|
||||
loadPins();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% load new_pin %}
|
||||
{% load compress %}
|
||||
{% load verbatim %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -46,13 +47,35 @@
|
||||
|
||||
{% new_pin request %}
|
||||
|
||||
{% verbatim %}
|
||||
<script id="pins-template" type="text/x-handlebars-template">
|
||||
{{#each pins}}
|
||||
<div class="pin">
|
||||
<img src="{{thumbnail}}">
|
||||
{{#if description}}
|
||||
<p>{{description}}</p>
|
||||
{{/if}}
|
||||
{{#if tags}}
|
||||
<p>
|
||||
{{#each tags}}
|
||||
<span class="label">{{this}}</span>
|
||||
{{/each}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</script>
|
||||
{% endverbatim %}
|
||||
|
||||
{% compress js %}
|
||||
<script src="/static/vendor/jquery/1.7.2/jquery.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/fancybox/2.0.6/jquery.fancybox.js"></script>
|
||||
<script src="/static/vendor/imagesloaded/2.0.1/jquery.imagesloaded.js"></script>
|
||||
<script src="/static/vendor/js-url/1.7.2/js-url.js"></script>
|
||||
<script src="/static/vendor/handlebars/handlebars.js"></script>
|
||||
|
||||
<script>
|
||||
var username = "{{ user.username }}";
|
||||
var isSuperuser = {{ user.is_superuser|lower }};
|
||||
</script>
|
||||
|
||||
<script src="/static/core/js/pinry.js"></script>
|
||||
<script src="/static/core/js/messages.js"></script>
|
||||
|
||||
44
pinry/core/templatetags/verbatim.py
Normal file
44
pinry/core/templatetags/verbatim.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
jQuery templates use constructs like:
|
||||
|
||||
{{if condition}} print something{{/if}}
|
||||
|
||||
This, of course, completely screws up Django templates,
|
||||
because Django thinks {{ and }} mean something.
|
||||
|
||||
Wrap {% verbatim %} and {% endverbatim %} around those
|
||||
blocks of jQuery templates and this will try its best
|
||||
to output the contents with no changes.
|
||||
"""
|
||||
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
class VerbatimNode(template.Node):
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
def render(self, context):
|
||||
return self.text
|
||||
|
||||
|
||||
@register.tag
|
||||
def verbatim(parser, token):
|
||||
text = []
|
||||
while 1:
|
||||
token = parser.tokens.pop(0)
|
||||
if token.contents == 'endverbatim':
|
||||
break
|
||||
if token.token_type == template.TOKEN_VAR:
|
||||
text.append('{{')
|
||||
elif token.token_type == template.TOKEN_BLOCK:
|
||||
text.append('{%')
|
||||
text.append(token.contents)
|
||||
if token.token_type == template.TOKEN_VAR:
|
||||
text.append('}}')
|
||||
elif token.token_type == template.TOKEN_BLOCK:
|
||||
text.append('%}')
|
||||
return VerbatimNode(''.join(text))
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{% block yield %}
|
||||
<div id="pins"></div>
|
||||
<div id="loader" class="container">
|
||||
<div class="container spinner">
|
||||
<div class="row">
|
||||
<div class="span2 offset5">
|
||||
<img src="/static/core/img/loader.gif" alt="Loader">
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.conf import settings
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^api/', include('pinry.api.urls', namespace='api')),
|
||||
url(r'^pins/', include('pinry.pins.urls', namespace='pins')),
|
||||
url(r'', include('pinry.api.urls', namespace='api')),
|
||||
url(r'', include('pinry.core.urls', namespace='core')),
|
||||
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
2201
pinry/vendor/static/vendor/handlebars/handlebars.js
vendored
Normal file
2201
pinry/vendor/static/vendor/handlebars/handlebars.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
Django==1.4.4
|
||||
South==0.7.4
|
||||
Pillow==1.7.7
|
||||
django-tastypie==0.9.11
|
||||
django-tastypie==0.9.12
|
||||
django_compressor==1.2
|
||||
cssmin==0.1.4
|
||||
jsmin==2.0.2
|
||||
|
||||
Reference in New Issue
Block a user