witten
/
luminotes
Archived
1
0
Fork 0

You can now resize embedded images (small, medium, or large).

Fixed a bug that potentially caused link pulldowns to open in the wrong
location when the page was scrolled past the top.
This commit is contained in:
Dan Helfman 2008-06-18 17:14:07 -07:00
parent 9c5d734fad
commit 41a85bb41f
5 changed files with 198 additions and 21 deletions

5
NEWS
View File

@ -1,3 +1,8 @@
1.4.5: June 18, 2008:
* You can now resize embedded images (small, medium, or large).
* Fixed a bug that potentially caused link pulldowns to open in the wrong
location when the page was scrolled past the top.
1.4.4: June 17, 2008:
* Links to embedded images now show up within the note tree's list of links.
* Links to files that have not yet been uploaded (or have been deleted) are

View File

@ -333,15 +333,18 @@ class Files( object ):
@grab_user_id
@validate(
file_id = Valid_id(),
user_id = Valid_id( none_okay = True ),
max_size = Valid_int( min = 10, max = 1000, none_okay = True ),
user_id = Valid_id( none_okay = True )
)
def thumbnail( self, file_id, user_id = None ):
def thumbnail( self, file_id, max_size = None, user_id = None ):
"""
Return a thumbnail for a file that a user has previously uploaded. If a thumbnail cannot be
generated for the given file, return a default thumbnail image.
@type file_id: unicode
@param file_id: id of the file to return a thumbnail for
@type max_size: int or NoneType
@param max_size: maximum thumbnail width or height in pixels (optional, defaults to a small size)
@type user_id: unicode or NoneType
@param user_id: id of current logged-in user (if any)
@rtype: generator
@ -360,14 +363,17 @@ class Files( object ):
cherrypy.response.headerMap[ u"Content-Type" ] = u"image/png"
DEFAULT_MAX_THUMBNAIL_SIZE = 125
if not max_size:
max_size = DEFAULT_MAX_THUMBNAIL_SIZE
# attempt to open the file as an image
image_buffer = None
try:
image = Upload_file.open_image( file_id )
# scale the image down into a thumbnail
THUMBNAIL_MAX_SIZE = ( 125, 125 ) # in pixels
image.thumbnail( THUMBNAIL_MAX_SIZE, Image.ANTIALIAS )
image.thumbnail( ( max_size, max_size ), Image.ANTIALIAS )
# save the image into a memory buffer
image_buffer = StringIO()

View File

@ -566,6 +566,82 @@ class Test_files( Test_controller ):
assert image
assert image.size == ( 125, 50 )
def test_thumbnail_with_max_size( self ):
self.login()
# make the test image big enough to require scaling down
image = Image.open( StringIO( self.IMAGE_DATA ) )
image = image.transform( ( 250, 250 ), Image.QUAD, range( 8 ) )
image_data = StringIO()
image.save( image_data, "PNG" )
self.http_upload(
"/files/upload?file_id=%s" % self.file_id,
dict(
notebook_id = self.notebook.object_id,
note_id = self.note.object_id,
),
filename = self.filename,
file_data = image_data.getvalue(),
content_type = self.content_type,
session_id = self.session_id,
)
max_size = 225
result = self.http_get(
"/files/thumbnail?file_id=%s&max_size=%s" % ( self.file_id, max_size ),
session_id = self.session_id,
)
headers = result[ u"headers" ]
assert headers
assert headers[ u"Content-Type" ] == self.content_type
assert u"Content-Disposition" not in headers
file_data = "".join( result[ u"body" ] )
image = Image.open( StringIO( file_data ) )
assert image
assert image.size == ( max_size, max_size )
def test_thumbnail_with_max_size_without_scaling( self ):
self.login()
# make the test image big enough to require scaling down
image = Image.open( StringIO( self.IMAGE_DATA ) )
image = image.transform( ( 250, 250 ), Image.QUAD, range( 8 ) )
image_data = StringIO()
image.save( image_data, "PNG" )
self.http_upload(
"/files/upload?file_id=%s" % self.file_id,
dict(
notebook_id = self.notebook.object_id,
note_id = self.note.object_id,
),
filename = self.filename,
file_data = image_data.getvalue(),
content_type = self.content_type,
session_id = self.session_id,
)
max_size = 300
result = self.http_get(
"/files/thumbnail?file_id=%s&max_size=%s" % ( self.file_id, max_size ),
session_id = self.session_id,
)
headers = result[ u"headers" ]
assert headers
assert headers[ u"Content-Type" ] == self.content_type
assert u"Content-Disposition" not in headers
file_data = "".join( result[ u"body" ] )
image = Image.open( StringIO( file_data ) )
assert image
assert image.size == ( 250, 250 )
def test_thumbnail_with_non_image( self ):
self.login()
@ -620,6 +696,28 @@ class Test_files( Test_controller ):
assert headers
assert headers.get( "Location" ) == u"http:///login?after_login=%s" % urllib.quote( path )
def test_thumbnail_with_invalid_max_size( self ):
self.login()
self.http_upload(
"/files/upload?file_id=%s" % self.file_id,
dict(
notebook_id = self.notebook.object_id,
note_id = self.note.object_id,
),
filename = self.filename,
file_data = self.file_data, # not a valid image
content_type = self.content_type,
session_id = self.session_id,
)
result = self.http_get(
"/files/thumbnail?file_id=%s&max_size=0" % self.file_id,
session_id = self.session_id,
)
assert u"max size" in result[ u"body" ][ 0 ]
def test_thumbnail_without_access( self ):
self.login()

View File

@ -2524,10 +2524,8 @@ function calculate_position( node, anchor, relative_to ) {
position.x += relative_pos.x;
position.y += relative_pos.y;
// Work around an IE "feature" in which an element within an iframe changes its absolute
// position based on how far the page is scrolled.
if ( /MSIE/.test( navigator.userAgent ) )
position.y -= getElement( "html" ).scrollTop;
// adjust the vertical position based on how far the page has scrolled
position.y -= getElement( "html" ).scrollTop;
}
}
@ -2983,6 +2981,12 @@ Upload_pulldown.prototype.shutdown = function () {
this.link.pulldown = null;
}
SMALL_MAX_IMAGE_SIZE = 125;
MEDIUM_MAX_IMAGE_SIZE = 300;
LARGE_MAX_IMAGE_SIZE = 500;
function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral ) {
link.pulldown = this;
this.link = link;
@ -3028,6 +3032,9 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
// if the link is an image thumbnail link, update the contents of the file link pulldown accordingly
var image = getFirstElementByTagAndClassName( "img", null, this.link );
var embed_attributes = { "type": "checkbox", "class": "pulldown_checkbox", "id": "embed_checkbox" };
var small_size_attributes = { "type": "radio", "id": "small_size_radio", "name": "size", "value": "small" };
var medium_size_attributes = { "type": "radio", "id": "medium_size_radio", "name": "size", "value": "medium" };
var large_size_attributes = { "type": "radio", "id": "large_size_radio", "name": "size", "value": "large" };
var left_justify_attributes = { "type": "radio", "id": "left_justify_radio", "name": "justify", "value": "left" };
var center_justify_attributes = { "type": "radio", "id": "center_justify_radio", "name": "justify", "value": "center" };
var right_justify_attributes = { "type": "radio", "id": "right_justify_radio", "name": "justify", "value": "right" };
@ -3036,17 +3043,30 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
addElementClass( this.thumbnail_span, "undisplayed" );
embed_attributes[ "checked" ] = "true";
if ( hasElementClass( image, "left_justified" ) )
left_justify_attributes[ "checked" ] = "true";
else if ( hasElementClass( image, "center_justified" ) )
var src = parseQueryString( image.src.split( "?" ).pop() );
var max_size = src[ "max_size" ];
if ( max_size == LARGE_MAX_IMAGE_SIZE )
large_size_attributes[ "checked" ] = "true";
else if ( max_size == MEDIUM_MAX_IMAGE_SIZE )
medium_size_attributes[ "checked" ] = "true";
else
small_size_attributes[ "checked" ] = "true";
if ( hasElementClass( image, "center_justified" ) )
center_justify_attributes[ "checked" ] = "true";
else if ( hasElementClass( image, "right_justified" ) )
right_justify_attributes[ "checked" ] = "true";
else
left_justify_attributes[ "checked" ] = "true";
} else {
small_size_attributes[ "checked" ] = "true";
left_justify_attributes[ "checked" ] = "true";
}
this.embed_checkbox = createDOM( "input", embed_attributes );
this.small_size_radio = createDOM( "input", small_size_attributes );
this.medium_size_radio = createDOM( "input", medium_size_attributes );
this.large_size_radio = createDOM( "input", large_size_attributes );
this.left_justify_radio = createDOM( "input", left_justify_attributes );
this.center_justify_radio = createDOM( "input", center_justify_attributes );
this.right_justify_radio = createDOM( "input", right_justify_attributes );
@ -3055,9 +3075,22 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
"show image within note"
);
var small_size_label = createDOM( "label",
{ "for": "small_size_radio", "class": "radio_label", "title": "Display a small thumbnail of this image." },
"small"
);
var medium_size_label = createDOM( "label",
{ "for": "medium_size_radio", "class": "radio_label", "title": "Display a medium thumbnail of this image." },
"medium"
);
var large_size_label = createDOM( "label",
{ "for": "large_size_radio", "class": "radio_label", "title": "Display a large thumbnail of this image." },
"large"
);
var left_justify_label = createDOM( "label",
{ "for": "left_justify_radio", "class": "radio_label", "title": "Left justify this image within the note." },
"left justify"
"left"
);
var center_justify_label = createDOM( "label",
{ "for": "center_justify_radio", "class": "radio_label", "title": "Center this image horizontally within the note." },
@ -3065,13 +3098,20 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
);
var right_justify_label = createDOM( "label",
{ "for": "right_justify_radio", "class": "radio_label", "title": "Right justify this image within the note." },
"right justify"
"right"
);
this.image_justify_area = createDOM( "div", { "class": "undisplayed" },
createDOM( "table" , { "id": "justify_table" },
this.image_settings_area = createDOM( "div", { "class": "undisplayed" },
createDOM( "table" , { "id": "image_settings_table" },
createDOM( "tbody", {},
createDOM( "tr", {},
createDOM( "td", { "class": "field_label" }, "size: " ),
createDOM( "td", {}, this.small_size_radio, small_size_label ),
createDOM( "td", {}, this.medium_size_radio, medium_size_label ),
createDOM( "td", {}, this.large_size_radio, large_size_label )
),
createDOM( "tr", {},
createDOM( "td", { "class": "field_label" }, "position: " ),
createDOM( "td", {}, this.left_justify_radio, left_justify_label ),
createDOM( "td", {}, this.center_justify_radio, center_justify_label ),
createDOM( "td", {}, this.right_justify_radio, right_justify_label )
@ -3081,7 +3121,7 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
);
if ( image )
removeElementClass( this.image_justify_area, "undisplayed" );
removeElementClass( this.image_settings_area, "undisplayed" );
appendChildNodes( this.div, createDOM( "span", { "class": "field_label" }, "filename: " ) );
appendChildNodes( this.div, this.filename_field );
@ -3089,7 +3129,7 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
appendChildNodes( this.div, " " );
appendChildNodes( this.div, this.delete_button );
appendChildNodes( this.div, createDOM( "div", {}, this.embed_checkbox, embed_label ) );
appendChildNodes( this.div, this.image_justify_area );
appendChildNodes( this.div, this.image_settings_area );
// get the file's name and size from the server
this.invoker.invoke(
@ -3109,6 +3149,9 @@ function File_link_pulldown( wiki, notebook_id, invoker, editor, link, ephemeral
connect( this.delete_button, "onclick", function ( event ) { self.delete_button_clicked( event ); } );
connect( this.embed_checkbox, "onclick", function ( event ) { self.embed_clicked( event ); } );
connect( this.small_size_radio, "onclick", function ( event ) { self.resize_image( event, "small" ); } );
connect( this.medium_size_radio, "onclick", function ( event ) { self.resize_image( event, "medium" ); } );
connect( this.large_size_radio, "onclick", function ( event ) { self.resize_image( event, "large" ); } );
connect( this.left_justify_radio, "onclick", function ( event ) { self.justify_image( event, "left" ); } );
connect( this.center_justify_radio, "onclick", function ( event ) { self.justify_image( event, "center" ); } );
connect( this.right_justify_radio, "onclick", function ( event ) { self.justify_image( event, "right" ); } );
@ -3180,17 +3223,18 @@ File_link_pulldown.prototype.delete_button_clicked = function ( event ) {
File_link_pulldown.prototype.embed_clicked = function ( event ) {
if ( this.embed_checkbox.checked ) {
var image = createDOM( "img", { "src": "/files/thumbnail?file_id=" + this.file_id, "class": "left_justified" } );
var image = createDOM( "img", { "src": "/files/thumbnail?file_id=" + this.file_id + "&max_size=" + SMALL_MAX_IMAGE_SIZE, "class": "left_justified" } );
var image_span = createDOM( "span", {}, image );
this.link_title = link_title( this.link );
this.link.innerHTML = image_span.innerHTML;
addElementClass( this.thumbnail_span, "undisplayed" );
removeElementClass( this.image_justify_area, "undisplayed" );
removeElementClass( this.image_settings_area, "undisplayed" );
} else {
this.justify_image( "left" );
this.left_justify_radio.checked = true;
this.small_size_radio.checked = true;
removeElementClass( this.thumbnail_span, "undisplayed" );
addElementClass( this.image_justify_area, "undisplayed" );
addElementClass( this.image_settings_area, "undisplayed" );
this.link.innerHTML = this.link_title || this.filename_field.value || this.previous_filename;
}
@ -3198,6 +3242,30 @@ File_link_pulldown.prototype.embed_clicked = function ( event ) {
this.editor.resize();
}
File_link_pulldown.prototype.resize_image = function ( event, position ) {
var image = getFirstElementByTagAndClassName( "img", null, this.link );
if ( !image )
return;
if ( position == "large" ) {
var max_size = LARGE_MAX_IMAGE_SIZE;
} else if ( position == "medium" ) {
var max_size = MEDIUM_MAX_IMAGE_SIZE;
} else {
var max_size = SMALL_MAX_IMAGE_SIZE;
}
// when the newly resized image finishes loading, update the pulldown position and resize the
// editor
var self = this;
connect( image, "onload", function () {
self.update_position( self.link, self.editor.iframe );
self.editor.resize();
} );
image.setAttribute( "src", "/files/thumbnail?file_id=" + this.file_id + "&max_size=" + max_size );
}
File_link_pulldown.prototype.justify_image = function ( event, position ) {
var image = getFirstElementByTagAndClassName( "img", null, this.link );
if ( !image )

View File

@ -23,7 +23,7 @@ class Toolbar( Div ):
) ),
Div( Input(
type = u"image",
id = u"attachFile", title = u"attach file",
id = u"attachFile", title = u"attach file or image",
src = u"/static/images/toolbar/attach_button.png",
width = u"40", height = u"40",
class_ = "image_button",