mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-11-03 21:00:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			523 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			523 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			HTML
		
	
	
		
			Executable File
		
	
	
	
	
 | 
						|
<!--
 | 
						|
TODO:
 | 
						|
- basic structure
 | 
						|
	ribbons					DONE
 | 
						|
	images					DONE
 | 
						|
	indicators
 | 
						|
- basic control elements
 | 
						|
	touch zones / buttons
 | 
						|
		next				DONE
 | 
						|
		prev				DONE
 | 
						|
		shift up			DONE
 | 
						|
		shift down			DONE
 | 
						|
		promote				DONE
 | 
						|
		demote				DONE
 | 
						|
		zoom in				~		need real zooming...
 | 
						|
		zoom out			~		need real zooming...
 | 
						|
		toggle single image	DONE
 | 
						|
- image sorting
 | 
						|
	- will affect:
 | 
						|
		- promote
 | 
						|
		- demote
 | 
						|
		- shift up
 | 
						|
		- shift down
 | 
						|
		- ribbon merging
 | 
						|
- add promote/demote events (to attach structure editors)...
 | 
						|
- add real images...
 | 
						|
- make all the code relative to the current selection (multiple instances on a page support)
 | 
						|
- make this into a jquery plugin...
 | 
						|
- add dynamic loading and unloading for very large sets...
 | 
						|
 | 
						|
- first stage refactoring:
 | 
						|
	- merge almost identical functions...
 | 
						|
 | 
						|
ISSUES:
 | 
						|
	- jumping...
 | 
						|
-->
 | 
						|
 | 
						|
<script src="jquery.js"></script>
 | 
						|
<script>
 | 
						|
 | 
						|
$(document).ready(function() {
 | 
						|
	// current state...
 | 
						|
	if($('.current-ribbon').length == 0){
 | 
						|
		$('.ribbon').first().addClass('current-ribbon')
 | 
						|
	}
 | 
						|
	if($('.current-image').length == 0){
 | 
						|
		$('.current-ribbon').children('.image').first().addClass('current-image')
 | 
						|
	}
 | 
						|
 | 
						|
	// setup event handlers...
 | 
						|
	$(document).keydown(handleKeys)
 | 
						|
	$(".image").click(handleClick)
 | 
						|
 | 
						|
 | 
						|
	// set the default position...
 | 
						|
	$('.current-image').click()
 | 
						|
});
 | 
						|
 | 
						|
 | 
						|
function handleClick(e) {
 | 
						|
 | 
						|
	var cur = $(this)
 | 
						|
 | 
						|
	// switch classes...
 | 
						|
	cur.parents().siblings().children(".image").removeClass("current-image")
 | 
						|
	cur.siblings(".image").removeClass("current-image")
 | 
						|
 | 
						|
	cur.siblings().children(".image").removeClass("current-image")
 | 
						|
	cur.parents().siblings(".ribbon").removeClass("current-ribbon")
 | 
						|
 | 
						|
	cur.addClass("current-image")
 | 
						|
	cur.parents(".ribbon").addClass("current-ribbon")
 | 
						|
 | 
						|
 | 
						|
	var container = cur.parents('.container')
 | 
						|
	var field = cur.parents(".field")
 | 
						|
 | 
						|
	var image_offset = cur.offset()
 | 
						|
	var field_offset = field.offset()
 | 
						|
 | 
						|
	// center the current image...
 | 
						|
	field.css({
 | 
						|
			left: field_offset.left - image_offset.left + (container.innerWidth() - cur.innerWidth())/2, 
 | 
						|
			top: field_offset.top - image_offset.top + (container.innerHeight() - cur.innerHeight())/2 
 | 
						|
		})
 | 
						|
 | 
						|
 | 
						|
	// XXX do I need this???
 | 
						|
	e.preventDefault();
 | 
						|
}
 | 
						|
 | 
						|
var keys = {
 | 
						|
	toggleHelpKeys: [72],
 | 
						|
	toggleRibbonView: [32],
 | 
						|
	closeKeys: [27, 88, 67],
 | 
						|
 | 
						|
	firstKeys: [36],
 | 
						|
	lastKeys: [35],
 | 
						|
	previousKeys: [37, 80],
 | 
						|
	nextKeys: [39, 78],
 | 
						|
	promoteKeys: [40],
 | 
						|
	demoteKeys: [38],
 | 
						|
 | 
						|
	ignoreKeys: [16, 17, 18],
 | 
						|
 | 
						|
	helpShowOnUnknownKey: true
 | 
						|
}
 | 
						|
 | 
						|
function handleKeys(event){
 | 
						|
	var code = event.keyCode, fn = $.inArray;
 | 
						|
	var _ = (fn(code, keys.closeKeys) >= 0) ? function(){}()
 | 
						|
		: (fn(code, keys.firstKeys) >= 0) ? firstImage()
 | 
						|
		: (fn(code, keys.nextKeys) >= 0) ? nextImage()
 | 
						|
		: (fn(code, keys.previousKeys) >= 0) ? prevImage()
 | 
						|
		: (fn(code, keys.lastKeys) >= 0) ? lastImage()
 | 
						|
		: (fn(code, keys.promoteKeys) >= 0) ? function(){
 | 
						|
			if(event.shiftKey){
 | 
						|
				if(event.ctrlKey){
 | 
						|
					createRibbonBelow()
 | 
						|
				}
 | 
						|
				promoteImage()
 | 
						|
			} else {
 | 
						|
				focusBelowRibbon()
 | 
						|
			}
 | 
						|
		}()
 | 
						|
		: (fn(code, keys.demoteKeys) >= 0) ? function(){
 | 
						|
			if(event.shiftKey){
 | 
						|
				if(event.ctrlKey){
 | 
						|
					createRibbonAbove()
 | 
						|
				}
 | 
						|
				demoteImage()
 | 
						|
			} else {
 | 
						|
				focusAboveRibbon()
 | 
						|
			}
 | 
						|
		}()
 | 
						|
		: (fn(code, keys.toggleRibbonView) >= 0) ? toggleRibbonView()
 | 
						|
		: (fn(code, keys.ignoreKeys) >= 0) ? false
 | 
						|
		// XXX
 | 
						|
		: (keys.helpShowOnUnknownKey) ? function(){alert(code)}()
 | 
						|
		: false;
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// modes...
 | 
						|
function showRibbon(){
 | 
						|
	$('.single-image-mode').removeClass('single-image-mode')
 | 
						|
}
 | 
						|
function showSingle(){
 | 
						|
	$('.field').not('.single-image-mode').addClass('single-image-mode')
 | 
						|
}
 | 
						|
function toggleRibbonView(){
 | 
						|
	if($('.single-image-mode').length > 0){
 | 
						|
		showRibbon()
 | 
						|
	} else {
 | 
						|
		showSingle()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// XXX need to reposition the whole thing correctly...
 | 
						|
function toggleWideView(){
 | 
						|
	if($('.wide-view-mode').length > 0){
 | 
						|
		$('.wide-view-mode')
 | 
						|
			.removeClass('wide-view-mode')
 | 
						|
			.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
 | 
						|
				$('.current-image').click()
 | 
						|
				return true
 | 
						|
			});
 | 
						|
		
 | 
						|
	} else {
 | 
						|
		showRibbon()
 | 
						|
		//$('.container')
 | 
						|
		$('.field')
 | 
						|
			.not('.wide-view-mode')
 | 
						|
				.addClass('wide-view-mode')
 | 
						|
				.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
 | 
						|
					$('.current-image').click()
 | 
						|
					return true
 | 
						|
				});
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// basic navigation...
 | 
						|
function firstImage(){
 | 
						|
	$('.current-ribbon').children('.image').first().click()
 | 
						|
}
 | 
						|
 | 
						|
function prevImage(){
 | 
						|
	$('.current-image')
 | 
						|
		.prev('.image')
 | 
						|
			.click()
 | 
						|
}
 | 
						|
 | 
						|
function nextImage(){
 | 
						|
	$('.current-image')
 | 
						|
		.next('.image')
 | 
						|
			.click()
 | 
						|
}
 | 
						|
 | 
						|
function lastImage(){
 | 
						|
	$('.current-ribbon').children('.image').last().click()
 | 
						|
}
 | 
						|
 | 
						|
// XXX select appropriate image...
 | 
						|
function focusAboveRibbon(){
 | 
						|
	$('.current-ribbon').prev('.ribbon').children('.image').first().click()
 | 
						|
}
 | 
						|
 | 
						|
// XXX select appropriate image...
 | 
						|
function focusBelowRibbon(){
 | 
						|
	$('.current-ribbon').next('.ribbon').children('.image').first().click()
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// create ribbon above/below helpers...
 | 
						|
function createRibbonAbove(){
 | 
						|
	return $('<div class="new-ribbon"></div>')
 | 
						|
		.insertBefore('.current-ribbon')
 | 
						|
		// HACK: without this, the class change below will not animate...
 | 
						|
		.show()
 | 
						|
		.addClass('ribbon')
 | 
						|
		.removeClass('new-ribbon')
 | 
						|
}
 | 
						|
 | 
						|
function createRibbonBelow(){
 | 
						|
	return $('<div class="new-ribbon"></div>')
 | 
						|
		.insertAfter('.current-ribbon')
 | 
						|
		// HACK: without this, the class change below will not animate...
 | 
						|
		.show()
 | 
						|
		.addClass('ribbon')
 | 
						|
		.removeClass('new-ribbon')
 | 
						|
}
 | 
						|
 | 
						|
// Modifiers...
 | 
						|
 | 
						|
// XXX sort elements correctly...
 | 
						|
function mergeRibbonsUp(){
 | 
						|
	$('.current-ribbon')
 | 
						|
		.prev('.ribbon')
 | 
						|
			.children()
 | 
						|
				.detach()
 | 
						|
				.insertAfter('.current-image')
 | 
						|
	$('.current-ribbon')
 | 
						|
		.prev('.ribbon')
 | 
						|
			.slideUp(function(){
 | 
						|
				$(this).remove()
 | 
						|
				$('.current-image').click()
 | 
						|
			})
 | 
						|
}
 | 
						|
 | 
						|
// XXX sort elements correctly...
 | 
						|
function mergeRibbonsDown(){
 | 
						|
	$('.current-ribbon')
 | 
						|
		.next('.ribbon')
 | 
						|
			.children()
 | 
						|
				.detach()
 | 
						|
				.insertAfter('.current-image')
 | 
						|
	$('.current-ribbon')
 | 
						|
		.next('.ribbon')
 | 
						|
			.slideUp(function(){
 | 
						|
				$(this).remove()
 | 
						|
				$('.current-image').click()
 | 
						|
			})
 | 
						|
}
 | 
						|
 | 
						|
// XXX sort elements correctly...
 | 
						|
// XXX do animations...
 | 
						|
function promoteImage(){
 | 
						|
	if($('.current-ribbon').next('.ribbon').length == 0){
 | 
						|
		createRibbonBelow()
 | 
						|
	}
 | 
						|
	// XXX sort elements correctly...
 | 
						|
	if($('.current-ribbon').children('.image').length == 1){
 | 
						|
		// XXX this adds image to the head while the below portion adds it to the tail...
 | 
						|
		mergeRibbonsDown()
 | 
						|
	} else {
 | 
						|
		img = $('.current-image')
 | 
						|
		if(img.next('.image').length == 0){
 | 
						|
			prevImage()
 | 
						|
		} else {
 | 
						|
			nextImage()
 | 
						|
		}
 | 
						|
		img
 | 
						|
			.detach()
 | 
						|
			.appendTo($('.current-ribbon').next('.ribbon'))
 | 
						|
	}
 | 
						|
	$('.current-image').click()
 | 
						|
}
 | 
						|
 | 
						|
// XXX sort elements correctly...
 | 
						|
// XXX do animations...
 | 
						|
function demoteImage(){
 | 
						|
	if($('.current-ribbon').prev('.ribbon').length == 0){
 | 
						|
		createRibbonAbove()
 | 
						|
	}
 | 
						|
	// XXX sort elements correctly...
 | 
						|
	if($('.current-ribbon').children('.image').length == 1){
 | 
						|
		// XXX this adds image to the head while thebelow portion adds it to the tail...
 | 
						|
		mergeRibbonsUp()
 | 
						|
	} else {
 | 
						|
		img = $('.current-image')
 | 
						|
		if(img.next('.image').length == 0){
 | 
						|
			prevImage()
 | 
						|
		} else {
 | 
						|
			nextImage()
 | 
						|
		}
 | 
						|
		img
 | 
						|
			.detach()
 | 
						|
			.appendTo($('.current-ribbon').prev('.ribbon'))
 | 
						|
	}
 | 
						|
	$('.current-image').click()
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
</script>
 | 
						|
 | 
						|
<style>
 | 
						|
	.image {
 | 
						|
		position: relative;
 | 
						|
		display: inline-block;
 | 
						|
 | 
						|
		opacity: 0.3;
 | 
						|
 | 
						|
		-webkit-transition: all 0.5s ease;
 | 
						|
		-moz-transition: all 0.5s ease;
 | 
						|
		-o-transition: all 0.5s ease;
 | 
						|
		-ms-transition: all 0.5s ease;	
 | 
						|
		transition: all 0.5s ease;
 | 
						|
 | 
						|
		cursor: hand;
 | 
						|
	}
 | 
						|
 | 
						|
	.mock-image {
 | 
						|
		width: 350px;
 | 
						|
		height: 350px;
 | 
						|
 | 
						|
		background: blue;
 | 
						|
	}
 | 
						|
 | 
						|
	.container {
 | 
						|
		overflow: hidden;
 | 
						|
		width: 800px;
 | 
						|
		height: 500px;
 | 
						|
	}
 | 
						|
 | 
						|
	.field {
 | 
						|
		position: relative;
 | 
						|
		overflow: visible;
 | 
						|
		top: 0px;
 | 
						|
		left: -100px;
 | 
						|
 | 
						|
		-webkit-transition: all 0.5s ease;
 | 
						|
		-moz-transition: all 0.5s ease;
 | 
						|
		-o-transition: all 0.5s ease;
 | 
						|
		-ms-transition: all 0.5s ease;	
 | 
						|
		transition: all 0.5s ease;
 | 
						|
	}
 | 
						|
 | 
						|
	.ribbon {
 | 
						|
		height: 360px;
 | 
						|
		/* XXX make this expand dynamically */
 | 
						|
		width: 10000px;
 | 
						|
		overflow: visible;
 | 
						|
		padding-top: 2px;
 | 
						|
		padding-bottom: 2px;
 | 
						|
		text-align: center;
 | 
						|
		opacity: 0.2;
 | 
						|
 | 
						|
		-webkit-transition: all 0.5s ease;
 | 
						|
		-moz-transition: all 0.5s ease;
 | 
						|
		-o-transition: all 0.5s ease;
 | 
						|
		-ms-transition: all 0.5s ease;	
 | 
						|
		transition: all 0.5s ease;
 | 
						|
 | 
						|
	}
 | 
						|
	.new-ribbon {
 | 
						|
		height: 0px;
 | 
						|
 | 
						|
		-webkit-transition: all 0.5s ease;
 | 
						|
		-moz-transition: all 0.5s ease;
 | 
						|
		-o-transition: all 0.5s ease;
 | 
						|
		-ms-transition: all 0.5s ease;	
 | 
						|
		transition: all 0.5s ease;
 | 
						|
	}
 | 
						|
 | 
						|
	.current-image {
 | 
						|
		opacity: 1.0;
 | 
						|
	}
 | 
						|
 | 
						|
	.current-ribbon {
 | 
						|
		padding-top: 20px;
 | 
						|
		padding-bottom: 20px;
 | 
						|
 | 
						|
		opacity: 1.0;
 | 
						|
 | 
						|
		-webkit-transition: all 0.5s ease;
 | 
						|
		-moz-transition: all 0.5s ease;
 | 
						|
		-o-transition: all 0.5s ease;
 | 
						|
		-ms-transition: all 0.5s ease;	
 | 
						|
		transition: all 0.5s ease;
 | 
						|
	}
 | 
						|
 | 
						|
	.current-ribbon .image {
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/* single image theme (start everything with .single-image-mode) 
 | 
						|
	 *
 | 
						|
	 * XXX need to make this touch friendly...
 | 
						|
	 */
 | 
						|
	.single-image-mode .image {
 | 
						|
		opacity: 0.0;
 | 
						|
	}
 | 
						|
 | 
						|
	.single-image-mode .image:hover {
 | 
						|
		opacity: 0.5;
 | 
						|
	}
 | 
						|
 | 
						|
	.single-image-mode .current-image:hover, .single-image-mode .current-image {
 | 
						|
		opacity: 1.0;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/* wide view mode */
 | 
						|
 | 
						|
	/* XXX not yet working correctly...
 | 
						|
	.wide-view-mode {
 | 
						|
		transform: scale(0.2,0.2);
 | 
						|
		-ms-transform: scale(0.2,0.2);
 | 
						|
		-webkit-transform: scale(0.2,0.2);
 | 
						|
		-o-transform: scale(0.2,0.2);
 | 
						|
		-moz-transform: scale(0.2,0.2);
 | 
						|
	}
 | 
						|
	*/
 | 
						|
	.wide-view-mode .mock-image {
 | 
						|
		width: 50px;
 | 
						|
		height: 50px;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	.wide-view-mode .ribbon {
 | 
						|
		height: 60px;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
</style>
 | 
						|
 | 
						|
 | 
						|
<div class="container">
 | 
						|
	<div class="field">
 | 
						|
		<div class="ribbon">
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
			<div class="image mock-image"></div>
 | 
						|
		</div>
 | 
						|
	</div>
 | 
						|
</div>
 | 
						|
 | 
						|
<button onclick="firstImage()">first (home)</button>
 | 
						|
<button onclick="prevImage()">prev (left)</button>
 | 
						|
<button onclick="nextImage()">next (right)</button>
 | 
						|
<button onclick="lastImage()">last (end)</button>
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="showSingle()">single</button>
 | 
						|
<button onclick="showRibbon()">ribbon</button>
 | 
						|
<button onclick="toggleRibbonView()">toggle ribbon view (space)</button>
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="toggleWideView()">toggle wide view</button>
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="createRibbonAbove()">create ribbon above</button><br>
 | 
						|
<button onclick="createRibbonBelow()">create ribbon below</button>
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="mergeRibbonsUp()">merge ribbons up</button><br>
 | 
						|
<button onclick="mergeRibbonsDown()">merge ribbons down</button>
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="demoteImage()">demote image (shift-up)</button><br>
 | 
						|
<button onclick="promoteImage()">promote image (shift-down)</button><br>
 | 
						|
NOTE: ctrl-shift-up / ctrl-shift-down will demote / promote an image to a new empty ribbon (the default if no ribbon exists)
 | 
						|
 | 
						|
<br><br>
 | 
						|
 | 
						|
<button onclick="focusAboveRibbon()">focus above ribbon (up)</button><br>
 | 
						|
<button onclick="focusBelowRibbon()">focus below ribbon (down)</button>
 | 
						|
 | 
						|
 | 
						|
<!-- vim:set ts=4 sw=4 nowrap : -->
 |