mirror of
https://github.com/pinry/pinry.git
synced 2025-11-14 09:05:41 +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.resources import ModelResource
|
||||||
from tastypie import fields
|
from tastypie import fields
|
||||||
from tastypie.authentication import BasicAuthentication
|
|
||||||
from tastypie.authorization import DjangoAuthorization
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from pinry.pins.models import Pin
|
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
|
class PinResource(ModelResource): # pylint: disable-msg=R0904
|
||||||
tags = fields.ListField()
|
tags = fields.ListField()
|
||||||
|
submitter = fields.ForeignKey(UserResource, 'submitter', full=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
queryset = Pin.objects.all()
|
queryset = Pin.objects.all()
|
||||||
@@ -17,6 +25,7 @@ class PinResource(ModelResource): # pylint: disable-msg=R0904
|
|||||||
include_resource_uri = False
|
include_resource_uri = False
|
||||||
filtering = {
|
filtering = {
|
||||||
'published': ['gt'],
|
'published': ['gt'],
|
||||||
|
'submitter': ['exact']
|
||||||
}
|
}
|
||||||
|
|
||||||
def build_filters(self, filters=None):
|
def build_filters(self, filters=None):
|
||||||
@@ -37,13 +46,3 @@ class PinResource(ModelResource): # pylint: disable-msg=R0904
|
|||||||
tags = bundle.data.get('tags', [])
|
tags = bundle.data.get('tags', [])
|
||||||
bundle.obj.tags.set(*tags)
|
bundle.obj.tags.set(*tags)
|
||||||
return super(PinResource, self).save_m2m(bundle)
|
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 django.conf.urls import patterns, include, url
|
||||||
|
|
||||||
|
from tastypie.api import Api
|
||||||
|
|
||||||
from .api import PinResource
|
from .api import PinResource
|
||||||
from .api import UserResource
|
from .api import UserResource
|
||||||
|
|
||||||
|
|
||||||
pin_resource = PinResource()
|
v1_api = Api(api_name='v1')
|
||||||
user_resource = UserResource()
|
v1_api.register(PinResource())
|
||||||
|
v1_api.register(UserResource())
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'', include(pin_resource.urls)),
|
url(r'^api/', include(v1_api.urls)),
|
||||||
url(r'', include(user_resource.urls)),
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,18 +70,14 @@ body {
|
|||||||
|
|
||||||
#pins {
|
#pins {
|
||||||
top: 70px;
|
top: 70px;
|
||||||
position: absolute;
|
|
||||||
background: #eee;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin {
|
.pin {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
float: left;
|
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: none;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pin img {
|
.pin img {
|
||||||
|
|||||||
@@ -1,121 +1,85 @@
|
|||||||
/**
|
$(window).load(function() {
|
||||||
* Based on Wookmark's endless scroll.
|
function tileLayout() {
|
||||||
*/
|
// Config
|
||||||
var apiURL = '/api/pin/?format=json&offset='
|
var blockMargin = 20;
|
||||||
var page = 0;
|
var blockWidth = 240;
|
||||||
var handler = null;
|
// End Config
|
||||||
var globalTag = null;
|
|
||||||
var isLoading = false;
|
|
||||||
|
|
||||||
/**
|
var blockContainer = $('#pins');
|
||||||
* When scrolled all the way to the bottom, add more tiles.
|
var blocks = blockContainer.children('.pin');
|
||||||
*/
|
var rowSize = Math.floor(blockContainer.width()/blockWidth);
|
||||||
function onScroll(event) {
|
var blockWidth = (blockContainer.width()-blockMargin*(rowSize))/rowSize;
|
||||||
if(!isLoading) {
|
var colHeights = []
|
||||||
var closeToBottom = ($(window).scrollTop() + $(window).height() > $(document).height() - 100);
|
|
||||||
if(closeToBottom) loadData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function applyLayout() {
|
for (var i=0; i < rowSize; i++) {
|
||||||
$('#pins').imagesLoaded(function() {
|
colHeights[i] = 0;
|
||||||
// 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 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);
|
var offset = 0;
|
||||||
if (globalTag !== null) loadURL += "&tag=" + tag;
|
|
||||||
|
|
||||||
$.ajax({
|
function loadPins() {
|
||||||
url: loadURL,
|
$('.spinner').css('display', 'block');
|
||||||
success: onLoadData
|
$.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() {
|
||||||
* Receives data from the API, creates HTML for images and updates the layout
|
tileLayout();
|
||||||
*/
|
});
|
||||||
function onLoadData(data) {
|
|
||||||
data = data.objects;
|
|
||||||
isLoading = false;
|
|
||||||
$('#loader').hide();
|
|
||||||
|
|
||||||
page++;
|
offset += 30;
|
||||||
|
});
|
||||||
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>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#pins').append(html);
|
loadPins();
|
||||||
|
|
||||||
applyLayout();
|
$(window).resize(function() {
|
||||||
};
|
tileLayout();
|
||||||
|
})
|
||||||
|
|
||||||
$(document).ready(new function() {
|
$(window).scroll(function() {
|
||||||
$(document).bind('scroll', onScroll);
|
if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
|
||||||
loadData();
|
loadPins();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
/**
|
|
||||||
* On clicking an image show fancybox original.
|
|
||||||
*/
|
|
||||||
$('.fancybox').fancybox({
|
|
||||||
openEffect: 'none',
|
|
||||||
closeEffect: 'none'
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{% load new_pin %}
|
{% load new_pin %}
|
||||||
{% load compress %}
|
{% load compress %}
|
||||||
|
{% load verbatim %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -46,13 +47,35 @@
|
|||||||
|
|
||||||
{% new_pin request %}
|
{% 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 %}
|
{% compress js %}
|
||||||
<script src="/static/vendor/jquery/1.7.2/jquery.js"></script>
|
<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/bootstrap/2.0.3/js/bootstrap.js"></script>
|
||||||
<script src="/static/vendor/wookmark/0.5/jquery.wookmark.js"></script>
|
<script src="/static/vendor/handlebars/handlebars.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>
|
||||||
<script src="/static/vendor/js-url/1.7.2/js-url.js"></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/pinry.js"></script>
|
||||||
<script src="/static/core/js/messages.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 %}
|
{% block yield %}
|
||||||
<div id="pins"></div>
|
<div id="pins"></div>
|
||||||
<div id="loader" class="container">
|
<div class="container spinner">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span2 offset5">
|
<div class="span2 offset5">
|
||||||
<img src="/static/core/img/loader.gif" alt="Loader">
|
<img src="/static/core/img/loader.gif" alt="Loader">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from django.conf import settings
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^api/', include('pinry.api.urls', namespace='api')),
|
|
||||||
url(r'^pins/', include('pinry.pins.urls', namespace='pins')),
|
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')),
|
url(r'', include('pinry.core.urls', namespace='core')),
|
||||||
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
) + 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
|
Django==1.4.4
|
||||||
South==0.7.4
|
South==0.7.4
|
||||||
Pillow==1.7.7
|
Pillow==1.7.7
|
||||||
django-tastypie==0.9.11
|
django-tastypie==0.9.12
|
||||||
django_compressor==1.2
|
django_compressor==1.2
|
||||||
cssmin==0.1.4
|
cssmin==0.1.4
|
||||||
jsmin==2.0.2
|
jsmin==2.0.2
|
||||||
|
|||||||
Reference in New Issue
Block a user