parent
e25d478f6a
commit
fff0d9352b
@ -0,0 +1,74 @@ |
|||||||
|
""" Test previewing images. """ |
||||||
|
|
||||||
|
import os |
||||||
|
|
||||||
|
from selenium.common.exceptions import ElementClickInterceptedException |
||||||
|
|
||||||
|
from asl_articles.search import SEARCH_ALL_PUBLISHERS, SEARCH_ALL_PUBLICATIONS, SEARCH_ALL_ARTICLES |
||||||
|
from asl_articles.tests.test_publishers import create_publisher, edit_publisher |
||||||
|
from asl_articles.tests.test_publications import create_publication, edit_publication |
||||||
|
from asl_articles.tests.test_articles import create_article, edit_article |
||||||
|
from asl_articles.tests.utils import init_tests, find_child, find_children, wait_for, \ |
||||||
|
do_search, get_search_results, call_with_retry |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def test_image_preview( webdriver, flask_app, dbconn ): |
||||||
|
"""Test previewing images.""" |
||||||
|
|
||||||
|
# initialize |
||||||
|
init_tests( webdriver, flask_app, dbconn ) |
||||||
|
|
||||||
|
def do_test( create, edit, refresh ): |
||||||
|
|
||||||
|
# create a new object |
||||||
|
webdriver.refresh() |
||||||
|
create() |
||||||
|
results = get_search_results() |
||||||
|
assert len(results) == 1 |
||||||
|
sr = results[0] |
||||||
|
|
||||||
|
# add images to the object |
||||||
|
# NOTE: We're testing that images in an object already on-screen is updated correctly. |
||||||
|
fname = os.path.join( os.path.split(__file__)[0], "fixtures/images/1.gif" ) |
||||||
|
description = 'foo <img src="/images/app.png" style="height:2em;" class="preview"> bar' |
||||||
|
edit( sr, fname, description ) |
||||||
|
_check_previewable_images( sr ) |
||||||
|
|
||||||
|
# refresh the object |
||||||
|
# NOTE: We're testing that images in an object loaded afresh is set up correctly. |
||||||
|
webdriver.refresh() |
||||||
|
wait_for( 2, lambda: find_child( "#search-form" ) ) |
||||||
|
results = refresh() |
||||||
|
assert len(results) == 1 |
||||||
|
_check_previewable_images( results[0] ) |
||||||
|
|
||||||
|
# do the tests |
||||||
|
do_test( |
||||||
|
lambda: create_publisher( { "name": "Test publisher" } ), |
||||||
|
lambda sr, fname, description: edit_publisher( sr, { "image": fname, "description": description } ), |
||||||
|
lambda: do_search( SEARCH_ALL_PUBLISHERS ) |
||||||
|
) |
||||||
|
do_test( |
||||||
|
lambda: create_publication( { "name": "Test publication" } ), |
||||||
|
lambda sr, fname, description: edit_publication( sr, { "image": fname, "description": description } ), |
||||||
|
lambda: do_search( SEARCH_ALL_PUBLICATIONS ) |
||||||
|
) |
||||||
|
do_test( |
||||||
|
lambda: create_article( { "title": "Test article" } ), |
||||||
|
lambda sr, fname, description: edit_article( sr, { "image": fname, "snippet": description } ), |
||||||
|
lambda: do_search( SEARCH_ALL_ARTICLES ) |
||||||
|
) |
||||||
|
|
||||||
|
# --------------------------------------------------------------------- |
||||||
|
|
||||||
|
def _check_previewable_images( sr ): |
||||||
|
"""Check that previewable images are working correctly.""" |
||||||
|
images = list( find_children( "a.preview img", sr ) ) |
||||||
|
assert len(images) == 2 |
||||||
|
for img in images: |
||||||
|
assert find_child( ".jquery-image-zoom" ) is None |
||||||
|
img.click() |
||||||
|
preview = wait_for( 2, lambda: find_child( ".jquery-image-zoom" ) ) |
||||||
|
call_with_retry( preview.click, [ElementClickInterceptedException] ) |
||||||
|
wait_for( 2, lambda: find_child( ".jquery-image-zoom" ) is None ) |
@ -0,0 +1,48 @@ |
|||||||
|
div.jquery-image-zoom { |
||||||
|
line-height: 0; |
||||||
|
font-size: 0; |
||||||
|
|
||||||
|
z-index: 10; |
||||||
|
|
||||||
|
border: 5px solid #fff; |
||||||
|
background: #eee; /* TM 25jan15: Added this to make it easier to see images with transparent backgrounds. */ |
||||||
|
margin: -5px; |
||||||
|
|
||||||
|
-webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); |
||||||
|
-moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); |
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); |
||||||
|
} |
||||||
|
|
||||||
|
div.jquery-image-zoom a { |
||||||
|
background: url(/jQuery/imageZoom/jquery.imageZoom.png) no-repeat; |
||||||
|
|
||||||
|
display: block; |
||||||
|
width: 25px; |
||||||
|
height: 25px; |
||||||
|
|
||||||
|
position: absolute; |
||||||
|
left: -17px; |
||||||
|
top: -17px; |
||||||
|
/* IE-users are prolly used to close-link in right-hand corner */ |
||||||
|
*left: auto; |
||||||
|
*right: -17px; |
||||||
|
|
||||||
|
text-decoration: none; |
||||||
|
text-indent: -100000px; |
||||||
|
outline: 0; |
||||||
|
|
||||||
|
z-index: 11; |
||||||
|
} |
||||||
|
|
||||||
|
div.jquery-image-zoom a:hover { |
||||||
|
background-position: left -25px; |
||||||
|
} |
||||||
|
|
||||||
|
div.jquery-image-zoom img, |
||||||
|
div.jquery-image-zoom embed, |
||||||
|
div.jquery-image-zoom object, |
||||||
|
div.jquery-image-zoom div { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
margin: 0; |
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
/*** |
||||||
|
@title: |
||||||
|
Image Zoom |
||||||
|
|
||||||
|
@version: |
||||||
|
2.0 |
||||||
|
|
||||||
|
@author: |
||||||
|
Andreas Lagerkvist |
||||||
|
|
||||||
|
@date: |
||||||
|
2008-08-31 |
||||||
|
|
||||||
|
@url: |
||||||
|
http://andreaslagerkvist.com/jquery/image-zoom/
|
||||||
|
|
||||||
|
@license: |
||||||
|
http://creativecommons.org/licenses/by/3.0/
|
||||||
|
|
||||||
|
@copyright: |
||||||
|
2008 Andreas Lagerkvist (andreaslagerkvist.com) |
||||||
|
|
||||||
|
@requires: |
||||||
|
jquery, jquery.imageZoom.css, jquery.imageZoom.png |
||||||
|
|
||||||
|
@does: |
||||||
|
This plug-in makes links pointing to images open in the "Image Zoom". Clicking a link will zoom out the clicked image to its target-image. Click anywhere on the image or the close-button to zoom the image back in. Only ~3k minified. |
||||||
|
|
||||||
|
@howto: |
||||||
|
jQuery(document.body).imageZoom(); Would make every link pointing to an image in the document open in the zoom. |
||||||
|
|
||||||
|
@exampleHTML: |
||||||
|
<ul> |
||||||
|
<li><a href="http://exscale.se/__files/3d/bloodcells.jpg">Bloodcells</a></li> |
||||||
|
<li><a href="http://exscale.se/__files/3d/x-wing.jpg">X-Wing</a></li> |
||||||
|
<li><a href="http://exscale.se/__files/3d/weve-moved.jpg">We've moved</a></li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
<ul> |
||||||
|
<li><a href="http://exscale.se/__files/3d/lamp-and-mates/lamp-and-mates-01.jpg"><img src="http://exscale.se/__files/3d/lamp-and-mates/lamp-and-mates-01_small.jpg" alt="Lamp and Mates" /></a></li> |
||||||
|
<li><a href="http://exscale.se/__files/3d/stugan-winter.jpg"><img src="http://exscale.se/__files/3d/stugan-winter_small.jpg" alt="The Cottage - Winter time" /></a></li> |
||||||
|
<li><a href="http://exscale.se/__files/3d/ps2.jpg"><img src="http://exscale.se/__files/3d/ps2_small.jpg" alt="PS2" /></a></li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
@exampleJS: |
||||||
|
// I don't run it because my site already uses imgZoom
|
||||||
|
// jQuery(document.body).imageZoom();
|
||||||
|
***/ |
||||||
|
jQuery.fn.imageZoom = function (conf) { |
||||||
|
// Some config. If you set dontFadeIn: 0 and hideClicked: 0 imgzoom will act exactly like fancyzoom
|
||||||
|
var config = jQuery.extend({ |
||||||
|
speed: 200, // Animation-speed of zoom
|
||||||
|
dontFadeIn: 1, // 1 = Do not fade in, 0 = Do fade in
|
||||||
|
hideClicked: 1, // Whether to hide the image that was clicked to bring up the imgzoom
|
||||||
|
imageMargin: 30, // Margin from image-edge to window-edge if image is larger than screen
|
||||||
|
className: 'jquery-image-zoom', |
||||||
|
loading: 'Loading...' |
||||||
|
}, conf); |
||||||
|
config.doubleSpeed = config.speed / 4; // Used for fading in the close-button
|
||||||
|
|
||||||
|
return this.click(function(e) { |
||||||
|
// Make sure the target-element is a link (or an element inside a link)
|
||||||
|
var clickedElement = jQuery(e.target); // The element that was actually clicked
|
||||||
|
var clickedLink = clickedElement.is('a') ? clickedElement : clickedElement.parents('a'); // If it's not an a, check if any of its parents is
|
||||||
|
// TM MAR/20: Removed the check on the filename extension (it was looking for an image-type extension).
|
||||||
|
clickedLink = (clickedLink && clickedLink.is('a')) ? clickedLink : false; // If it was an a or child of an a, make sure it points to an image
|
||||||
|
var clickedImg = (clickedLink && clickedLink.find('img').length) ? clickedLink.find('img') : false; // See if the clicked link contains and image
|
||||||
|
|
||||||
|
// Only continue if a link pointing to an image was clicked
|
||||||
|
if (clickedLink) { |
||||||
|
// These functions are used when the imaeg starts and stops loading (displays either 'loading..' or fades out the clicked img slightly)
|
||||||
|
clickedLink.oldText = clickedLink.text(); |
||||||
|
|
||||||
|
clickedLink.setLoadingImg = function () { |
||||||
|
if (clickedImg) { |
||||||
|
clickedImg.css({opacity: '0.5'}); |
||||||
|
} |
||||||
|
else { |
||||||
|
clickedLink.text(config.loading); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
clickedLink.setNotLoadingImg = function () { |
||||||
|
if (clickedImg) { |
||||||
|
clickedImg.css({opacity: '1'}); |
||||||
|
} |
||||||
|
else { |
||||||
|
clickedLink.text(clickedLink.oldText); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// The URI to the image we are going to display
|
||||||
|
var displayImgSrc = clickedLink.attr('href'); |
||||||
|
|
||||||
|
// If an imgzoom wiv this image is already open dont do nathin
|
||||||
|
if (jQuery('div.' + config.className + ' img[src="' + displayImgSrc + '"]').length) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// This function is run once the displayImgSrc-img has loaded (below)
|
||||||
|
var preloadOnload = function (pload) { |
||||||
|
// The clicked-link is faded out during loading, fade it back in
|
||||||
|
clickedLink.setNotLoadingImg(); |
||||||
|
|
||||||
|
// Now set some vars we need
|
||||||
|
var dimElement = clickedImg ? clickedImg : clickedLink; // The element used to retrieve dimensions of imgzoom before zoom (either clicked link or img inside)
|
||||||
|
var hideClicked = clickedImg ? config.hideClicked : 0; // Whether to hide clicked link (set in config but always true for non-image-links)
|
||||||
|
var offset = dimElement.offset(); // Offset of clicked link (or image inside)
|
||||||
|
var imgzoomBefore = { // The dimensions of the imgzoom _before_ it is zoomed out
|
||||||
|
width: dimElement.outerWidth(), |
||||||
|
height: dimElement.outerHeight(), |
||||||
|
left: offset.left, |
||||||
|
top: offset.top/*, |
||||||
|
opacity: config.dontFadeIn*/ |
||||||
|
}; |
||||||
|
var imgzoom = jQuery('<div><img src="' + displayImgSrc + '" alt=""/></div>').css('position', 'absolute').appendTo(document.body); // We don't want any class-name or any other contents part from the image when we calculate the new dimensions of the imgzoom
|
||||||
|
var imgzoomAfter = { // The dimensions of the imgzoom _after_ it is zoomed out
|
||||||
|
width: pload.width, |
||||||
|
height: pload.height/*, |
||||||
|
opacity: 1*/ |
||||||
|
}; |
||||||
|
var windowDim = { |
||||||
|
width: jQuery(window).width(), |
||||||
|
height: jQuery(window).height() |
||||||
|
}; |
||||||
|
// Make sure imgzoom isn't wider than screen
|
||||||
|
if (imgzoomAfter.width > (windowDim.width - config.imageMargin * 2)) { |
||||||
|
var nWidth = windowDim.width - config.imageMargin * 2; |
||||||
|
imgzoomAfter.height = (nWidth / imgzoomAfter.width) * imgzoomAfter.height; |
||||||
|
imgzoomAfter.width = nWidth; |
||||||
|
} |
||||||
|
// Now make sure it isn't taller
|
||||||
|
if (imgzoomAfter.height > (windowDim.height - config.imageMargin * 2)) { |
||||||
|
var nHeight = windowDim.height - config.imageMargin * 2; |
||||||
|
imgzoomAfter.width = (nHeight / imgzoomAfter.height) * imgzoomAfter.width; |
||||||
|
imgzoomAfter.height = nHeight; |
||||||
|
} |
||||||
|
// Center imgzoom
|
||||||
|
imgzoomAfter.left = (windowDim.width - imgzoomAfter.width) / 2 + jQuery(window).scrollLeft(); |
||||||
|
imgzoomAfter.top = (windowDim.height - imgzoomAfter.height) / 2 + jQuery(window).scrollTop(); |
||||||
|
var closeButton = jQuery('<a href="#">Close</a>').appendTo(imgzoom).hide(); // The button that closes the imgzoom (we're adding this after the calculation of the dimensions)
|
||||||
|
|
||||||
|
// Hide the clicked link if set so in config
|
||||||
|
if (hideClicked) { |
||||||
|
clickedLink.css('visibility', 'hidden'); |
||||||
|
} |
||||||
|
|
||||||
|
// Now animate the imgzoom from its small size to its large size, and then fade in the close-button
|
||||||
|
imgzoom.addClass(config.className).css(imgzoomBefore).animate(imgzoomAfter, config.speed, function () { |
||||||
|
closeButton.fadeIn(config.doubleSpeed); |
||||||
|
}); |
||||||
|
|
||||||
|
// This function closes the imgzoom
|
||||||
|
var hideImgzoom = function () { |
||||||
|
closeButton.fadeOut(config.doubleSpeed, function () { |
||||||
|
imgzoom.animate(imgzoomBefore, config.speed, function () { |
||||||
|
clickedLink.css('visibility', 'visible'); |
||||||
|
imgzoom.remove(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
// Close imgzoom when you click the closeButton or the imgzoom
|
||||||
|
imgzoom.click(hideImgzoom); |
||||||
|
closeButton.click(hideImgzoom); |
||||||
|
}; |
||||||
|
|
||||||
|
// Preload image
|
||||||
|
var preload = new Image(); |
||||||
|
|
||||||
|
preload.src = displayImgSrc; |
||||||
|
|
||||||
|
if (preload.complete) { |
||||||
|
preloadOnload(preload); |
||||||
|
} |
||||||
|
else { |
||||||
|
clickedLink.setLoadingImg(); |
||||||
|
|
||||||
|
preload.onload = function () { |
||||||
|
preloadOnload(preload); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// Finally return false from the click so the browser doesn't actually follow the link...
|
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// NOTE: We used to close up on ESC, but we want to do this on *any* keypress (e.g. if the user
|
||||||
|
// starts typing in the search query box) or click (e.g. if the user clicks on a menu).
|
||||||
|
$(document).keydown( () => { $("div.jquery-image-zoom a").click() ; } ) ; |
||||||
|
$(document).click( () => { $("div.jquery-image-zoom a").click() ; } ) ; |
After Width: | Height: | Size: 1.7 KiB |
@ -0,0 +1,72 @@ |
|||||||
|
import React from "react" ; |
||||||
|
import ReactDOM from "react-dom" ; |
||||||
|
import $ from "jquery" ; |
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
export class PreviewableImage extends React.Component |
||||||
|
{ |
||||||
|
// NOTE: While the "react-modal-image" component seems to work nicely, how can we use it
|
||||||
|
// on arbitrary images in user-defined content?
|
||||||
|
// This class is a wrapper around the jQuery-based imageZoom plugin.
|
||||||
|
|
||||||
|
render() { |
||||||
|
return ( <a href={this.props.url} className="preview" target="_blank" rel="noopener noreferrer"> |
||||||
|
<img src={this.props.url} className={this.props.className} alt={this.props.altText} /> |
||||||
|
</a> ) ; |
||||||
|
} |
||||||
|
|
||||||
|
static initPreviewableImages() { |
||||||
|
// load the imageZoom script
|
||||||
|
$.getScript( "/jQuery/imageZoom/jquery.imageZoom.js" ) ; |
||||||
|
// load the imageZoom CSS
|
||||||
|
let cssNode = document.createElement( "link" ) ; |
||||||
|
cssNode.type = "text/css" ; |
||||||
|
cssNode.rel = "stylesheet" ; |
||||||
|
cssNode.href = "/jQuery/imageZoom/jquery.imageZoom.css" ; |
||||||
|
let headNode = document.getElementsByTagName( "head" )[0] ; |
||||||
|
headNode.appendChild( cssNode ) ; |
||||||
|
} |
||||||
|
|
||||||
|
static adjustHtmlForPreviewableImages( html ) { |
||||||
|
// FUDGE! The imageZoom plugin requires images to be wrapped with a <a class="preview"> tag.
|
||||||
|
// I was hoping to be able to let the user enable the preview functionality for images
|
||||||
|
// by simply adding a "preview" attribute to their <img> tags, then locating them after render
|
||||||
|
// and dynamically wrapping them with the necessary <a class="preview"> tag, but React doesn't
|
||||||
|
// seem to like that :-/
|
||||||
|
// We instead look for such images in the HTML returned to us by the backend server, and fix it up
|
||||||
|
// before rendering it.
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
if ( ! html ) |
||||||
|
return "" ; |
||||||
|
|
||||||
|
// locate <img> tags with a class of "preview", and wrap them in a <a class="preview">.
|
||||||
|
let buf=[], pos=0 ; |
||||||
|
const img_regex = /<img [^>]*class\s*=\s*["']preview["'][^>]*>/g ; |
||||||
|
const url_regex = /src\s*=\s*["'](.*?)['"]/ |
||||||
|
for ( const match of html.matchAll( img_regex ) ) { |
||||||
|
buf.push( html.substr( pos, match.index-pos ) ) ; |
||||||
|
const match2 = url_regex.exec( match[0] ) ; |
||||||
|
if ( match2 ) { |
||||||
|
buf.push( |
||||||
|
"<a href='" + match2[1] + "' class='preview'>", |
||||||
|
match[0], |
||||||
|
"</a>" |
||||||
|
) ; |
||||||
|
} else |
||||||
|
buf.push( match[0] ) ; |
||||||
|
pos = match.index + match[0].length ; |
||||||
|
} |
||||||
|
buf.push( html.substr( pos ) ) ; |
||||||
|
|
||||||
|
return buf.join( "" ) ; |
||||||
|
} |
||||||
|
|
||||||
|
static activatePreviewableImages( rootNode ) { |
||||||
|
// locate images marked as previewable and activate them
|
||||||
|
let $elems = $( ReactDOM.findDOMNode( rootNode ) ).find( "a.preview" ) ; |
||||||
|
$elems.imageZoom() ; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue