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