##// END OF EJS Templates
Load full images only on click is simple image viewer is enabled
neko259 -
r1128:83db86e2 default
parent child Browse files
Show More
@@ -1,107 +1,105 b''
1 1 import hashlib
2 2 import os
3 3 from random import random
4 4 import time
5 5 from django.db import models
6 6 from boards import thumbs
7 7 from boards.models.base import Viewable
8 8
9 9 __author__ = 'neko259'
10 10
11 11
12 12 IMAGE_THUMB_SIZE = (200, 150)
13 13 IMAGES_DIRECTORY = 'images/'
14 14 FILE_EXTENSION_DELIMITER = '.'
15 15 HASH_LENGTH = 36
16 16
17 17 CSS_CLASS_IMAGE = 'image'
18 18 CSS_CLASS_THUMB = 'thumb'
19 19
20 20
21 21 class PostImageManager(models.Manager):
22 22 def create_with_hash(self, image):
23 23 image_hash = self.get_hash(image)
24 24 existing = self.filter(hash=image_hash)
25 25 if len(existing) > 0:
26 26 post_image = existing[0]
27 27 else:
28 28 post_image = PostImage.objects.create(image=image)
29 29
30 30 return post_image
31 31
32 32 def get_hash(self, image):
33 33 """
34 34 Gets hash of an image.
35 35 """
36 36 md5 = hashlib.md5()
37 37 for chunk in image.chunks():
38 38 md5.update(chunk)
39 39 return md5.hexdigest()
40 40
41 41
42 42 class PostImage(models.Model, Viewable):
43 43 objects = PostImageManager()
44 44
45 45 class Meta:
46 46 app_label = 'boards'
47 47 ordering = ('id',)
48 48
49 49 def _update_image_filename(self, filename):
50 50 """
51 51 Gets unique image filename
52 52 """
53 53
54 54 path = IMAGES_DIRECTORY
55 55
56 56 # TODO Use something other than random number in file name
57 57 new_name = '{}{}.{}'.format(
58 58 str(int(time.mktime(time.gmtime()))),
59 59 str(int(random() * 1000)),
60 60 filename.split(FILE_EXTENSION_DELIMITER)[-1:][0])
61 61
62 62 return os.path.join(path, new_name)
63 63
64 64 width = models.IntegerField(default=0)
65 65 height = models.IntegerField(default=0)
66 66
67 67 pre_width = models.IntegerField(default=0)
68 68 pre_height = models.IntegerField(default=0)
69 69
70 70 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
71 71 blank=True, sizes=(IMAGE_THUMB_SIZE,),
72 72 width_field='width',
73 73 height_field='height',
74 74 preview_width_field='pre_width',
75 75 preview_height_field='pre_height')
76 76 hash = models.CharField(max_length=HASH_LENGTH)
77 77
78 78 def save(self, *args, **kwargs):
79 79 """
80 80 Saves the model and computes the image hash for deduplication purposes.
81 81 """
82 82
83 83 if not self.pk and self.image:
84 84 self.hash = PostImage.objects.get_hash(self.image)
85 85 super(PostImage, self).save(*args, **kwargs)
86 86
87 87 def __str__(self):
88 88 return self.image.url
89 89
90 90 def get_view(self):
91 91 return '<div class="{}">' \
92 92 '<a class="{}" href="{full}">' \
93 93 '<img class="post-image-preview"' \
94 94 ' src="{}"' \
95 95 ' alt="{}"' \
96 96 ' width="{}"' \
97 97 ' height="{}"' \
98 98 ' data-width="{}"' \
99 99 ' data-height="{}" />' \
100 '<img class="post-image-full"' \
101 ' src="{full}" />' \
102 100 '</a>' \
103 101 '</div>'\
104 102 .format(CSS_CLASS_IMAGE, CSS_CLASS_THUMB,
105 103 self.image.url_200x150,
106 104 str(self.hash), str(self.pre_width),
107 105 str(self.pre_height), str(self.width), str(self.height), full=self.image.url)
@@ -1,102 +1,101 b''
1 1 .ui-button {
2 2 display: none;
3 3 }
4 4
5 5 .ui-dialog-content {
6 6 padding: 0;
7 7 min-height: 0;
8 8 }
9 9
10 10 .mark_btn {
11 11 cursor: pointer;
12 12 }
13 13
14 14 .img-full {
15 15 position: fixed;
16 16 background-color: #CCC;
17 17 border: 1px solid #000;
18 18 cursor: pointer;
19 19 }
20 20
21 21 .strikethrough {
22 22 text-decoration: line-through;
23 23 }
24 24
25 25 .post_preview {
26 26 z-index: 300;
27 27 position:absolute;
28 28 }
29 29
30 30 .gallery_image {
31 31 display: inline-block;
32 32 }
33 33
34 34 @media print {
35 35 .post-form-w {
36 36 display: none;
37 37 }
38 38 }
39 39
40 40 input[name="image"] {
41 41 display: block;
42 42 width: 100px;
43 43 height: 100px;
44 44 cursor: pointer;
45 45 position: absolute;
46 46 opacity: 0;
47 47 z-index: 1;
48 48 }
49 49
50 50 .file_wrap {
51 51 width: 100px;
52 52 height: 100px;
53 53 border: solid 1px white;
54 54 display: inline-block;
55 55 }
56 56
57 57 form > .file_wrap {
58 58 float: left;
59 59 }
60 60
61 61 .file-thumb {
62 62 width: 100px;
63 63 height: 100px;
64 64 background-size: cover;
65 65 background-position: center;
66 66 }
67 67
68 68 .compact-form-text {
69 69 margin-left:110px;
70 70 }
71 71
72 72 textarea, input {
73 73 -moz-box-sizing: border-box;
74 74 -webkit-box-sizing: border-box;
75 75 box-sizing: border-box;
76 76 }
77 77
78 78 .compact-form-text > textarea {
79 79 height: 100px;
80 80 width: 100%;
81 81 }
82 82
83 83 .post-button-form {
84 84 display: inline;
85 85 }
86 86
87 87 .post-button-form > button {
88 88 border: none;
89 89 margin: inherit;
90 90 padding: inherit;
91 91 background: none;
92 92 font-size: inherit;
93 93 }
94 94
95 95 #form-close-button {
96 96 display: none;
97 97 }
98 98
99 99 .post-image-full {
100 display: none;
101 100 width: 100%;
102 101 }
@@ -1,133 +1,148 b''
1 1 /*
2 2 @licstart The following is the entire license notice for the
3 3 JavaScript code in this page.
4 4
5 5
6 6 Copyright (C) 2013 neko259
7 7
8 8 The JavaScript code in this page is free software: you can
9 9 redistribute it and/or modify it under the terms of the GNU
10 10 General Public License (GNU GPL) as published by the Free Software
11 11 Foundation, either version 3 of the License, or (at your option)
12 12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15 15
16 16 As additional permission under GNU GPL version 3 section 7, you
17 17 may distribute non-source (e.g., minimized or compacted) forms of
18 18 that code without the copy of the GNU GPL normally required by
19 19 section 4, provided you include this license notice and a URL
20 20 through which recipients can access the Corresponding Source.
21 21
22 22 @licend The above is the entire license notice
23 23 for the JavaScript code in this page.
24 24 */
25 25
26 26
27 27 var IMAGE_VIEWERS = [
28 28 ['simple', new SimpleImageViewer()],
29 29 ['popup', new PopupImageViewer()]
30 30 ];
31 31
32 var FULL_IMG_CLASS = 'post-image-full';
33
32 34
33 35 function ImageViewer() {}
34 36 ImageViewer.prototype.view = function (post) {};
35 37
36 38 function SimpleImageViewer() {}
37 39 SimpleImageViewer.prototype.view = function (post) {
38 post.find('img').toggle();
40 var images = post.find('img');
41 images.toggle();
42
43 // When we first enlarge an image, a full image needs to be created
44 if (images.length == 1) {
45 var parent = images.first().parent();
46 var link = parent.attr('href');
47
48 var fullImg = $('<img />')
49 .addClass(FULL_IMG_CLASS)
50 .attr('src', link);
51
52 parent.append(fullImg);
53 }
39 54 };
40 55
41 56 function PopupImageViewer() {}
42 57 PopupImageViewer.prototype.view = function (post) {
43 58 var margin = 20; //..change
44 59
45 60 var el = post;
46 61 var thumb_id = 'full' + el.find('img').attr('alt');
47 62
48 63 var existingPopups = $('#' + thumb_id);
49 64 if(!existingPopups.length) {
50 65 var imgElement= el.find('img');
51 66
52 67 var img_w = imgElement.attr('data-width');
53 68 var img_h = imgElement.attr('data-height');
54 69
55 70 var win = $(window);
56 71
57 72 var win_w = win.width();
58 73 var win_h = win.height();
59 74 //new image size
60 75 if (img_w > win_w) {
61 76 img_h = img_h * (win_w/img_w) - margin;
62 77 img_w = win_w - margin;
63 78 }
64 79 if (img_h > win_h) {
65 80 img_w = img_w * (win_h/img_h) - margin;
66 81 img_h = win_h - margin;
67 82 }
68 83
69 84 var img_pv = new Image();
70 85 var newImage = $(img_pv);
71 86 newImage.addClass('img-full')
72 87 .attr('id', thumb_id)
73 88 .attr('src', $(el).attr('href'))
74 89 .appendTo($(el))
75 90 .css({
76 91 'width': img_w,
77 92 'height': img_h,
78 93 'left': (win_w - img_w) / 2,
79 94 'top': ((win_h - img_h) / 2)
80 95 })
81 96 //scaling preview
82 97 .mousewheel(function(event, delta) {
83 98 var cx = event.originalEvent.clientX,
84 99 cy = event.originalEvent.clientY,
85 100 i_w = parseFloat(newImage.width()),
86 101 i_h = parseFloat(newImage.height()),
87 102 newIW = i_w * (delta > 0 ? 1.25 : 0.8),
88 103 newIH = i_h * (delta > 0 ? 1.25 : 0.8);
89 104
90 105 newImage.width(newIW);
91 106 newImage.height(newIH);
92 107 //set position
93 108 newImage.css({
94 109 left: parseInt(cx - (newIW/i_w) * (cx - parseInt($(img_pv).position().left, 10)), 10),
95 110 top: parseInt(cy - (newIH/i_h) * (cy - parseInt($(img_pv).position().top, 10)), 10)
96 111 });
97 112
98 113 return false;
99 114 }
100 115 )
101 116 .draggable({
102 117 addClasses: false,
103 118 stack: '.img-full'
104 119 });
105 120 } else {
106 121 existingPopups.remove();
107 122 }
108 123 };
109 124
110 125 function addImgPreview() {
111 126 var viewerName = $('body').attr('data-image-viewer');
112 127 var viewer = ImageViewer();
113 128 for (var i = 0; i < IMAGE_VIEWERS.length; i++) {
114 129 var item = IMAGE_VIEWERS[i];
115 130 if (item[0] === viewerName) {
116 131 viewer = item[1];
117 132 break;
118 133 }
119 134 }
120 135
121 136 //keybind
122 137 $(document).on('keyup.removepic', function(e) {
123 138 if(e.which === 27) {
124 139 $('.img-full').remove();
125 140 }
126 141 });
127 142
128 143 $('body').on('click', '.thumb', function() {
129 144 viewer.view($(this));
130 145
131 146 return false;
132 147 });
133 148 }
General Comments 0
You need to be logged in to leave comments. Login now