2012-06-17 16:46:46 +04:00
|
|
|
// XXX need a uniform way to address images (filename?)
|
2012-08-04 20:26:38 +04:00
|
|
|
/******************************************* Setup Data and Globals **/
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
var DEBUG = true
|
|
|
|
|
|
2012-08-05 21:03:37 +04:00
|
|
|
// the list of style modes...
|
|
|
|
|
// these are swithched through in order by toggleBackgroundModes()
|
|
|
|
|
var BACKGROUND_MODES = [
|
|
|
|
|
'dark',
|
|
|
|
|
'black'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
// remember the default backgrounds for modes...
|
|
|
|
|
// ...this effectively makes the modes independant...
|
2012-08-10 20:44:48 +04:00
|
|
|
// NOTE: null represent the default value (no class set)...
|
2012-08-10 19:40:26 +04:00
|
|
|
// NOTE: these will change if changed in runtime...
|
|
|
|
|
var NORMAL_MODE_BG = 'dark'
|
2012-08-10 20:44:48 +04:00
|
|
|
var SINGLE_IMAGE_MODE_BG = 'black'
|
2012-08-10 19:40:26 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// ribbon/single view modes...
|
|
|
|
|
// store the scale before we went into single image mode...
|
|
|
|
|
// NOTE: these will change if changed in runtime...
|
|
|
|
|
var ORIGINAL_FIELD_SCALE = 1.0
|
|
|
|
|
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
// this sets the zooming factor used in manual zooming...
|
|
|
|
|
var ZOOM_FACTOR = 2
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
// sets the amount of move when a key is pressed...
|
2012-08-04 20:26:38 +04:00
|
|
|
var MOVE_DELTA = 50
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
/********************************************************** Helpers **/
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 20:25:49 +04:00
|
|
|
function getImageOrder(img){
|
|
|
|
|
// XXX HACK need to parseInt this because '13' is less than '2'...
|
|
|
|
|
// ...figure a way out of this!!!
|
|
|
|
|
return parseInt($(img).attr('id'))
|
|
|
|
|
}
|
|
|
|
|
function setImageOrder(img, order){
|
|
|
|
|
return $(img).attr({'id': order})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
// this will create a function that will add/remove a css_class to elem
|
2012-08-10 19:40:26 +04:00
|
|
|
// calling the optional callbacks before and/or after.
|
|
|
|
|
//
|
|
|
|
|
// elem is a jquery compatible object; default use-case: a css selector.
|
|
|
|
|
//
|
2012-08-09 06:25:09 +04:00
|
|
|
// the resulting function understands the folowing arguments:
|
|
|
|
|
// - 'on' : switch mode on
|
|
|
|
|
// - 'off' : switch mode off
|
|
|
|
|
// - '?' : return current state ('on'|'off')
|
|
|
|
|
// - no arguments : toggle the state
|
2012-08-10 19:40:26 +04:00
|
|
|
//
|
|
|
|
|
// NOTE: of only one callback is given then it will be called after the
|
|
|
|
|
// class change...
|
|
|
|
|
// a way around this is to pass an empty function as callback_b
|
|
|
|
|
//
|
2012-08-08 01:45:22 +04:00
|
|
|
function createCSSClassToggler(elem, css_class, callback_a, callback_b){
|
|
|
|
|
// prepare the pre/post callbacks...
|
|
|
|
|
if(callback_b == null){
|
|
|
|
|
var callback_pre = null
|
|
|
|
|
var callback_post = callback_a
|
|
|
|
|
} else {
|
|
|
|
|
var callback_pre = callback_a
|
|
|
|
|
var callback_post = callback_b
|
|
|
|
|
}
|
2012-08-08 06:21:00 +04:00
|
|
|
// build the acual toggler function...
|
2012-08-08 01:45:22 +04:00
|
|
|
return function(action){
|
2012-08-09 06:25:09 +04:00
|
|
|
if(action == null || action == '?'){
|
|
|
|
|
var getter = action == '?' ? true : false
|
2012-08-08 01:45:22 +04:00
|
|
|
action = 'on'
|
|
|
|
|
// get current state...
|
|
|
|
|
if( $(elem).hasClass(css_class) ){
|
|
|
|
|
action = 'off'
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
if(getter){
|
|
|
|
|
// as the above actions indicate intent and not state,
|
|
|
|
|
// we'll need to swap the values...
|
|
|
|
|
return action == 'on' ? 'off' : 'on'
|
|
|
|
|
}
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
if(callback_pre != null){
|
|
|
|
|
callback_pre(action)
|
|
|
|
|
}
|
|
|
|
|
// play with the class...
|
2012-08-08 01:57:04 +04:00
|
|
|
if(action == 'on'){
|
2012-08-08 01:45:22 +04:00
|
|
|
$(elem).addClass(css_class)
|
|
|
|
|
} else {
|
|
|
|
|
$(elem).removeClass(css_class)
|
|
|
|
|
}
|
|
|
|
|
if(callback_post != null){
|
|
|
|
|
callback_post(action)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// disable transitions on obj, call func then enable transitions back...
|
|
|
|
|
function doWithoutTransitions(obj, func){
|
|
|
|
|
obj
|
|
|
|
|
.addClass('unanimated')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
func()
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
obj.removeClass('unanimated')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// find an image object after which to position image ID...
|
|
|
|
|
// used for two main tasks:
|
|
|
|
|
// - positioning promoted/demoted images
|
|
|
|
|
// - centering ribbons
|
|
|
|
|
// returns:
|
|
|
|
|
// - null - empty ribbon or no element greater id should be first
|
|
|
|
|
// - element
|
|
|
|
|
// XXX do we need to make ids numbers for this to work?
|
2012-08-10 20:25:49 +04:00
|
|
|
function getImageBefore_lin(id, ribbon, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-08-08 01:45:22 +04:00
|
|
|
// walk the ribbon till we find two images one with an ID less and
|
|
|
|
|
// another greater that id...
|
|
|
|
|
var images = ribbon.children('.image')
|
|
|
|
|
var prev = null
|
|
|
|
|
for(var i=0; i < images.length; i++){
|
2012-08-10 20:25:49 +04:00
|
|
|
// XXX replace the id attr with a universal getter
|
|
|
|
|
if(get_order(images[i]) > id){
|
2012-08-08 01:45:22 +04:00
|
|
|
return prev
|
|
|
|
|
}
|
|
|
|
|
prev = $(images[i])
|
|
|
|
|
}
|
|
|
|
|
return prev
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// generic binery search for element just before the id...
|
|
|
|
|
// NOTE: if id is in lst, this will return the element just before it.
|
|
|
|
|
// NOTE: lst must be sorted.
|
2012-08-10 20:25:49 +04:00
|
|
|
function binarySearch(id, lst, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = function(o){return o}
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// empty list...
|
|
|
|
|
if(lst.length == 0){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// current section length
|
|
|
|
|
var l = Math.round((lst.length-1)/2)
|
|
|
|
|
// current position...
|
|
|
|
|
var i = l
|
|
|
|
|
|
|
|
|
|
while(true){
|
2012-08-10 20:25:49 +04:00
|
|
|
var i_id = get_order(lst[i])
|
2012-08-08 01:45:22 +04:00
|
|
|
// beginning of the array...
|
|
|
|
|
if(i == 0){
|
|
|
|
|
if(id > i_id){
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
// we got a hit...
|
|
|
|
|
if(i_id == id){
|
|
|
|
|
return i-1
|
|
|
|
|
}
|
|
|
|
|
// we are at the end...
|
|
|
|
|
if(i == lst.length-1 && id > i_id){
|
|
|
|
|
return i
|
|
|
|
|
}
|
2012-08-10 20:25:49 +04:00
|
|
|
var ii_id = get_order(lst[i+1])
|
2012-08-08 01:45:22 +04:00
|
|
|
// test if id is between i and i+1...
|
|
|
|
|
if( i_id < id && id < ii_id ){
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
// prepare for next iteration...
|
|
|
|
|
// NOTE: we saturate the values so we will never get out of bounds.
|
|
|
|
|
l = Math.round(l/2)
|
|
|
|
|
if(id < i_id){
|
|
|
|
|
// lower half...
|
|
|
|
|
i = Math.max(0, i-l)
|
|
|
|
|
} else {
|
|
|
|
|
// upper half...
|
|
|
|
|
i = Math.min(i+l, lst.length-1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// wrapper around binarySearch.
|
|
|
|
|
// this is here to make binarySearch simpler to test and debug...
|
2012-08-10 20:25:49 +04:00
|
|
|
function getImageBefore_bin(id, ribbon, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-08-08 01:45:22 +04:00
|
|
|
var images = ribbon.children('.image')
|
2012-08-10 20:25:49 +04:00
|
|
|
var i = binarySearch(id, images, get_order)
|
2012-08-08 01:45:22 +04:00
|
|
|
if(i == null){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
return $(images[i])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set the default search...
|
|
|
|
|
var getImageBefore = getImageBefore_bin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
/************************************************** Setup Functions **/
|
|
|
|
|
// XXX is this a correct place for these?
|
2012-06-17 16:46:46 +04:00
|
|
|
|
|
|
|
|
function setDefaultInitialState(){
|
2012-07-24 14:55:10 +04:00
|
|
|
if($('.current.ribbon').length == 0){
|
|
|
|
|
$('.ribbon').first().addClass('current')
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-07-24 14:55:10 +04:00
|
|
|
if($('.current.image').length == 0){
|
|
|
|
|
$('.current.ribbon').children('.image').first().addClass('current')
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-06 16:24:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setupEvents(){
|
|
|
|
|
// resize...
|
2012-08-06 16:19:58 +04:00
|
|
|
$(window).resize(function() {
|
|
|
|
|
// XXX HACK
|
|
|
|
|
$('.current.image').click()
|
|
|
|
|
})
|
2012-08-06 16:24:20 +04:00
|
|
|
// keyboard...
|
2012-08-10 19:40:26 +04:00
|
|
|
if(DEBUG){
|
|
|
|
|
$(document)
|
|
|
|
|
.keydown(makeKeyboardHandler(keybindings, function(k){alert(k)}))
|
|
|
|
|
} else {
|
|
|
|
|
$(document)
|
|
|
|
|
.keydown(makeKeyboardHandler(keybindings))
|
|
|
|
|
}
|
2012-08-06 16:24:20 +04:00
|
|
|
// swipe...
|
2012-06-08 18:30:54 +04:00
|
|
|
$('.viewer')
|
2012-06-13 15:16:43 +04:00
|
|
|
.swipe({
|
|
|
|
|
swipeLeft: nextImage,
|
|
|
|
|
swipeRight: prevImage,
|
2012-06-16 21:52:23 +04:00
|
|
|
swipeUp: shiftImageUp,
|
2012-08-04 20:26:38 +04:00
|
|
|
swipeDown: shiftImageDown
|
2012-06-13 15:16:43 +04:00
|
|
|
})
|
2012-08-10 19:40:26 +04:00
|
|
|
// dragging...
|
|
|
|
|
// XXX make this work seamlessly with touchSwipe...
|
|
|
|
|
// XXX cancel clicks while dragging...
|
|
|
|
|
// XXX this does not work on android...
|
|
|
|
|
$('.field').draggable()
|
2012-06-17 16:46:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setupControlElements(){
|
|
|
|
|
// images...
|
2012-08-10 19:40:26 +04:00
|
|
|
$(".image").click(handleImageClick)
|
2012-06-08 18:30:54 +04:00
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
// buttons...
|
2012-08-04 16:07:08 +04:00
|
|
|
$('.screen-button.next-image').click(nextImage)
|
|
|
|
|
$('.screen-button.prev-image').click(prevImage)
|
2012-08-08 01:45:22 +04:00
|
|
|
// XXX rename classes to "shift-image-up" and "shift-image-down"...
|
2012-08-04 16:07:08 +04:00
|
|
|
$('.screen-button.demote').click(shiftImageUp)
|
|
|
|
|
$('.screen-button.promote').click(shiftImageDown)
|
|
|
|
|
$('.screen-button.zoom-in').click(function(){scaleContainerBy(ZOOM_FACTOR)})
|
|
|
|
|
$('.screen-button.zoom-out').click(function(){scaleContainerBy(1/ZOOM_FACTOR)})
|
|
|
|
|
$('.screen-button.toggle-wide').click(toggleWideView)
|
|
|
|
|
$('.screen-button.toggle-single').click(toggleSingleImageMode)
|
|
|
|
|
$('.screen-button.fit-three').click(fitThreeImages)
|
2012-08-05 22:06:52 +04:00
|
|
|
$('.screen-button.show-controls').click(showControls)
|
2012-08-04 16:07:08 +04:00
|
|
|
$('.screen-button.settings').click(function(){alert('not implemented yet...')})
|
2012-06-17 16:46:46 +04:00
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
|
2012-08-09 06:25:09 +04:00
|
|
|
/**************************************************** Serialization **/
|
|
|
|
|
|
|
|
|
|
|
2012-06-08 18:30:54 +04:00
|
|
|
function loadImages(json){
|
|
|
|
|
var images = json.images
|
|
|
|
|
var ribbon = $('.ribbon').last()
|
|
|
|
|
|
|
|
|
|
$('.image').remove()
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < images.length; i++){
|
2012-08-10 20:25:49 +04:00
|
|
|
setImageOrder($('<div class="image"></div>')
|
|
|
|
|
.css({ 'background-image': 'url('+images[i]+')' }), i)
|
|
|
|
|
.click(handleImageClick)
|
|
|
|
|
.appendTo(ribbon)
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
ribbon.children().first().click()
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
|
2012-08-09 17:53:07 +04:00
|
|
|
/* bulid a JSON object from current state...
|
|
|
|
|
*
|
2012-08-09 06:25:09 +04:00
|
|
|
* format:
|
|
|
|
|
* {
|
|
|
|
|
* ribbons: [
|
2012-08-09 17:53:07 +04:00
|
|
|
* <image-id>: {
|
2012-08-09 06:25:09 +04:00
|
|
|
* url: <image-URL>,
|
|
|
|
|
* },
|
|
|
|
|
* ...
|
|
|
|
|
* ]
|
|
|
|
|
* }
|
|
|
|
|
*/
|
2012-08-11 03:38:42 +04:00
|
|
|
// XXX add incremental or partial updates...
|
2012-08-10 20:25:49 +04:00
|
|
|
function buildJSON(get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
var ribbons = $('.ribbon')
|
|
|
|
|
res = {
|
|
|
|
|
ribbons: []
|
|
|
|
|
}
|
|
|
|
|
for(var i=0; i < ribbons.length; i++){
|
|
|
|
|
var images = $(ribbons[i]).children('.image')
|
2012-08-09 17:53:07 +04:00
|
|
|
var ribbon = {}
|
2012-08-09 06:25:09 +04:00
|
|
|
res.ribbons[res.ribbons.length] = ribbon
|
|
|
|
|
for(var j=0; j < images.length; j++){
|
|
|
|
|
var image = $(images[j])
|
2012-08-10 20:25:49 +04:00
|
|
|
var id = get_order(image)
|
2012-08-09 06:29:29 +04:00
|
|
|
ribbon[id] = {
|
2012-08-09 06:25:09 +04:00
|
|
|
// unwrap the url...
|
2012-08-11 03:38:42 +04:00
|
|
|
// XXX would be nice to make this a relative path... (???)
|
2012-08-09 06:25:09 +04:00
|
|
|
url: /url\((.*)\)/.exec(image.css('background-image'))[1],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 20:25:49 +04:00
|
|
|
// XXX use this instead of loadImages(...)
|
2012-08-09 17:53:07 +04:00
|
|
|
// XXX might be good to add images in packs here, not one by one...
|
2012-08-11 02:59:11 +04:00
|
|
|
function loadJSON(data, set_order){
|
|
|
|
|
if(set_order == null){
|
|
|
|
|
set_order = setImageOrder
|
|
|
|
|
}
|
2012-08-09 06:25:09 +04:00
|
|
|
var ribbons = data.ribbons
|
|
|
|
|
var field = $('.field')
|
|
|
|
|
|
|
|
|
|
// drop all old content...
|
|
|
|
|
field.children().remove()
|
|
|
|
|
|
|
|
|
|
for(var i=0; i < ribbons.length; i++){
|
|
|
|
|
var images = ribbons[i]
|
|
|
|
|
// create ribbon...
|
|
|
|
|
var ribbon = $('<div class="ribbon"></div>')
|
|
|
|
|
.appendTo(field)
|
2012-08-09 17:53:07 +04:00
|
|
|
for(var j in images){
|
2012-08-09 06:25:09 +04:00
|
|
|
var image = $(images[j])
|
|
|
|
|
// create image...
|
2012-08-11 02:59:11 +04:00
|
|
|
set_order($('<div class="image"></div>')
|
2012-08-10 20:25:49 +04:00
|
|
|
.css({ 'background-image': 'url('+image.attr('url')+')' }), j)
|
|
|
|
|
.click(handleImageClick)
|
|
|
|
|
.appendTo(ribbon)
|
2012-08-09 06:25:09 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$('.image').first().click()
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
|
2012-08-10 18:31:32 +04:00
|
|
|
/*
|
|
|
|
|
* The folowing two functions will get the vertical and horizontal
|
2012-08-10 19:40:26 +04:00
|
|
|
* distance components between the points a and A, centers of the small
|
2012-08-10 18:31:32 +04:00
|
|
|
* and large squares respectively.
|
|
|
|
|
* One of the squares is .field and the other is .container,
|
|
|
|
|
* which is small or big is not important.
|
|
|
|
|
*
|
|
|
|
|
* +---------------+-------+
|
|
|
|
|
* | | |
|
|
|
|
|
* | | |
|
|
|
|
|
* | + a . . | . . . | . +
|
|
|
|
|
* | . | | +- getCurrentVerticalOffset(...)
|
|
|
|
|
* | . + A | . . . | . +
|
|
|
|
|
* +---------------+ |
|
|
|
|
|
* | . . |
|
|
|
|
|
* | . . |
|
|
|
|
|
* | . . |
|
|
|
|
|
* +-----------------------+
|
|
|
|
|
* . .
|
|
|
|
|
* +-+-+
|
|
|
|
|
* +------------------- getCurrentHorizontalOffset(...)
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Adding this distance to margins of one of the sqares will effectively
|
|
|
|
|
* allign the two points.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: neither function accunts for field margins.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// get the vertical offset of the center of square from center of container
|
|
|
|
|
// NOTE: this does not account for field margins
|
|
|
|
|
function getCurrentVerticalOffset(image){
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var scale = getElementScale($('.field'))
|
|
|
|
|
|
|
|
|
|
var ribbons = $('.ribbon')
|
|
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
var images = ribbon.children('.image')
|
|
|
|
|
|
|
|
|
|
// vertical...
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
var h = ribbons.outerHeight(true)
|
|
|
|
|
// margin...
|
|
|
|
|
var mh = h - ribbons.outerHeight()
|
|
|
|
|
// current ribbon position (1-based)
|
|
|
|
|
var rn = ribbons.index(ribbon) + 1
|
|
|
|
|
// relative position to field...
|
|
|
|
|
// XXX is there a better way to get this?
|
|
|
|
|
var t = rn * (h - mh/2)
|
|
|
|
|
|
|
|
|
|
return -t + H/2 + h/2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get the horizontal offset of the center of square from center of container
|
|
|
|
|
// NOTE: this does not account for field margins
|
|
|
|
|
function getCurrentHorizontalOffset(image){
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
var images = ribbon.children('.image')
|
|
|
|
|
|
|
|
|
|
var W = $('.container').width()
|
|
|
|
|
var w = images.outerWidth(true)
|
|
|
|
|
// margin...
|
|
|
|
|
var mw = w - images.outerWidth()
|
|
|
|
|
// current square position (1-based)
|
|
|
|
|
var sn = images.index(image) + 1
|
|
|
|
|
var l = sn * (w - mw/2)
|
|
|
|
|
|
|
|
|
|
return -l + W/2 + w/2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function centerSquare(){
|
|
|
|
|
$('.field').css({
|
|
|
|
|
'margin-top': getCurrentVerticalOffset()
|
|
|
|
|
})
|
|
|
|
|
// horizontal...
|
|
|
|
|
alignRibbon()
|
|
|
|
|
centerCurrentImage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function alignRibbon(image, position){
|
|
|
|
|
// default values...
|
|
|
|
|
if(image == null){
|
|
|
|
|
image = $('.image.current')
|
|
|
|
|
}
|
|
|
|
|
if(position == null){
|
|
|
|
|
position = 'center'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ribbon = image.parents('.ribbon')
|
|
|
|
|
|
|
|
|
|
// account for margined field...
|
|
|
|
|
// NOTE: this enables us to cheat and shift all the ribbons just
|
|
|
|
|
// by changing field margin-left...
|
|
|
|
|
var cml = parseFloat($('.field').css('margin-left'))
|
|
|
|
|
if(!cml){
|
|
|
|
|
cml = 0
|
|
|
|
|
}
|
|
|
|
|
var h_offset = getCurrentHorizontalOffset(image) - cml
|
|
|
|
|
var w = $('.image').outerWidth(true)
|
|
|
|
|
|
|
|
|
|
switch(position){
|
|
|
|
|
case 'before':
|
|
|
|
|
ribbon.css({'margin-left': h_offset - w/2})
|
|
|
|
|
return true
|
|
|
|
|
case 'center':
|
|
|
|
|
ribbon.css({'margin-left': h_offset})
|
|
|
|
|
return true
|
|
|
|
|
case 'after':
|
|
|
|
|
ribbon.css({'margin-left': h_offset + w/2})
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
2012-08-09 17:53:07 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/*************************************************** Event Handlers **/
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
// handle click for images...
|
|
|
|
|
function handleImageClick(){
|
2012-07-24 15:38:54 +04:00
|
|
|
// set classes...
|
|
|
|
|
$('.current').removeClass('current')
|
|
|
|
|
$(this)
|
|
|
|
|
.addClass('current')
|
|
|
|
|
.parents('.ribbon')
|
|
|
|
|
.addClass('current')
|
|
|
|
|
// position the field and ribbons...
|
|
|
|
|
centerSquare()
|
2012-07-31 17:41:54 +04:00
|
|
|
alignRibbons()
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
|
|
|
|
|
2012-08-04 19:15:35 +04:00
|
|
|
function clickAfterTransitionsDone(img){
|
|
|
|
|
if(img == null){
|
|
|
|
|
img = $('.current.image')
|
|
|
|
|
}
|
|
|
|
|
$('.viewer')
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){
|
|
|
|
|
img.click()
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-07-31 17:41:54 +04:00
|
|
|
// center other ribbons relative to current image...
|
2012-08-04 20:26:38 +04:00
|
|
|
// NOTE: only two ribbons are positioned at this point...
|
2012-08-10 20:25:49 +04:00
|
|
|
function alignRibbons(get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-08-04 18:57:54 +04:00
|
|
|
// XXX might be good to move this to a more generic location...
|
2012-08-10 20:25:49 +04:00
|
|
|
var id = get_order($('.current.image'))
|
2012-07-31 17:38:50 +04:00
|
|
|
var directions = ['prev', 'next']
|
|
|
|
|
for(var i in directions){
|
|
|
|
|
var ribbon = $('.current.ribbon')[directions[i]]('.ribbon')
|
|
|
|
|
if(ribbon.length == 1){
|
|
|
|
|
var img = getImageBefore(id, ribbon)
|
|
|
|
|
if(img != null){
|
|
|
|
|
alignRibbon(img, 'before')
|
|
|
|
|
} else {
|
|
|
|
|
// there are no images before...
|
|
|
|
|
alignRibbon(ribbon.children('.image').first(), 'after')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-24 15:38:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-08 23:19:40 +04:00
|
|
|
/*
|
|
|
|
|
* Basic key format:
|
|
|
|
|
* <key-code> : <callback>,
|
|
|
|
|
* <key-code> : {
|
2012-08-10 19:40:26 +04:00
|
|
|
* 'default': <callback>,
|
2012-08-08 23:19:40 +04:00
|
|
|
* // a modifier can be any single modifier, like shift or a
|
|
|
|
|
* // combination of modifers like 'ctrl+shift', given in order
|
|
|
|
|
* // of priority.
|
|
|
|
|
* // supported modifiers are (in order of priority):
|
|
|
|
|
* // - ctrl
|
|
|
|
|
* // - alt
|
|
|
|
|
* // - shift
|
|
|
|
|
* <modifer>: [...]
|
|
|
|
|
* },
|
|
|
|
|
* // alias...
|
|
|
|
|
* <key-code-a> : <key-code-b>,
|
2012-08-10 20:25:49 +04:00
|
|
|
*
|
|
|
|
|
* XXX might need to add meta information to generate sensible help...
|
2012-08-08 23:19:40 +04:00
|
|
|
*/
|
|
|
|
|
function makeKeyboardHandler(keybindings, unhandled){
|
|
|
|
|
if(unhandled == null){
|
|
|
|
|
unhandled = function(){return false}
|
|
|
|
|
}
|
|
|
|
|
return function(evt){
|
|
|
|
|
var key = evt.keyCode
|
|
|
|
|
// XXX ugly...
|
|
|
|
|
var modifers = evt.ctrlKey ? 'ctrl' : ''
|
|
|
|
|
modifers += evt.altKey ? (modifers != '' ? '+alt' : 'alt') : ''
|
|
|
|
|
modifers += evt.shiftKey ? (modifers != '' ? '+shift' : 'shift') : ''
|
|
|
|
|
|
|
|
|
|
var handler = keybindings[key]
|
|
|
|
|
|
|
|
|
|
// alias...
|
|
|
|
|
while (typeof(handler) == typeof(123)) {
|
|
|
|
|
handler = keybindings[handler]
|
|
|
|
|
}
|
|
|
|
|
// no handler...
|
|
|
|
|
if(handler == null){
|
|
|
|
|
return unhandled(key)
|
|
|
|
|
}
|
|
|
|
|
// complex handler...
|
|
|
|
|
if(typeof(handler) == typeof({})){
|
|
|
|
|
var callback = handler[modifers]
|
|
|
|
|
if(callback == null){
|
|
|
|
|
callback = handler['default']
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-08 23:19:40 +04:00
|
|
|
if(callback != null){
|
|
|
|
|
callback()
|
|
|
|
|
return false
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-08 23:19:40 +04:00
|
|
|
} else {
|
2012-08-10 19:40:26 +04:00
|
|
|
// simple callback...
|
2012-08-08 23:19:40 +04:00
|
|
|
handler()
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return unhandled(key)
|
|
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-16 21:52:23 +04:00
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
|
|
|
|
/************************************************************ Modes **/
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
var toggleSingleImageMode = createCSSClassToggler('.viewer', 'single-image-mode',
|
|
|
|
|
// pre...
|
|
|
|
|
function(action){
|
|
|
|
|
if(action == 'on'){
|
2012-08-10 19:40:26 +04:00
|
|
|
NORMAL_MODE_BG = getBackgroundMode()
|
|
|
|
|
ORIGINAL_FIELD_SCALE = getElementScale($('.field'))
|
2012-08-10 21:07:21 +04:00
|
|
|
// do this only when coming out of single image mode...
|
2012-08-10 20:44:48 +04:00
|
|
|
} else if(toggleSingleImageMode('?') == 'on'){
|
2012-08-10 19:40:26 +04:00
|
|
|
SINGLE_IMAGE_MODE_BG = getBackgroundMode()
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// post...
|
|
|
|
|
function(action){
|
|
|
|
|
if(action == 'on'){
|
|
|
|
|
fitImage()
|
2012-08-10 19:40:26 +04:00
|
|
|
setBackgroundMode(SINGLE_IMAGE_MODE_BG)
|
2012-08-08 01:45:22 +04:00
|
|
|
} else {
|
2012-08-10 19:40:26 +04:00
|
|
|
setContainerScale(ORIGINAL_FIELD_SCALE)
|
|
|
|
|
setBackgroundMode(NORMAL_MODE_BG)
|
2012-08-08 01:45:22 +04:00
|
|
|
}
|
|
|
|
|
clickAfterTransitionsDone()
|
2012-08-10 19:40:26 +04:00
|
|
|
})
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
|
2012-06-16 22:12:50 +04:00
|
|
|
// wide view mode toggle...
|
2012-08-10 19:40:26 +04:00
|
|
|
var toggleWideView = createCSSClassToggler('.viewer', 'wide-view-mode',
|
2012-08-10 20:25:49 +04:00
|
|
|
// pre...
|
2012-08-10 19:40:26 +04:00
|
|
|
function(action){
|
|
|
|
|
if(action == 'on'){
|
|
|
|
|
ORIGINAL_FIELD_SCALE = getElementScale($('.field'))
|
|
|
|
|
setContainerScale(0.1)
|
|
|
|
|
} else {
|
|
|
|
|
setContainerScale(ORIGINAL_FIELD_SCALE)
|
|
|
|
|
}
|
2012-08-10 20:25:49 +04:00
|
|
|
},
|
|
|
|
|
// post...
|
|
|
|
|
function(){})
|
2012-08-05 21:03:37 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getBackgroundMode(){
|
|
|
|
|
var mode = null
|
|
|
|
|
// find a mode to set...
|
|
|
|
|
for(var i = 0; i < BACKGROUND_MODES.length; i++){
|
|
|
|
|
// we found our mode...
|
|
|
|
|
if( $('.' + BACKGROUND_MODES[i]).length > 0 ){
|
|
|
|
|
return BACKGROUND_MODES[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set the background mode
|
|
|
|
|
// NOTE: passing null will set the default.
|
|
|
|
|
function setBackgroundMode(mode){
|
|
|
|
|
var cur = BACKGROUND_MODES.indexOf(mode)
|
|
|
|
|
|
|
|
|
|
// invalid mode...
|
|
|
|
|
if( cur == -1 && mode != null ){
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
// set the mode...
|
|
|
|
|
if(mode != null){
|
|
|
|
|
$('.viewer').addClass(mode)
|
|
|
|
|
}
|
|
|
|
|
// remove all others...
|
|
|
|
|
for(var i = 0; i < BACKGROUND_MODES.length; i++){
|
|
|
|
|
if( i == cur ){
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
mode = BACKGROUND_MODES[i]
|
|
|
|
|
$('.' + mode).removeClass(mode)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this will toggle through background theems: none -> dark -> black
|
|
|
|
|
function toggleBackgroundModes(){
|
2012-08-05 22:06:52 +04:00
|
|
|
var mode = getBackgroundMode()
|
|
|
|
|
// default -> first
|
|
|
|
|
if(mode == null){
|
|
|
|
|
setBackgroundMode(BACKGROUND_MODES[0])
|
|
|
|
|
// last -> default...
|
|
|
|
|
} else if(mode == BACKGROUND_MODES[BACKGROUND_MODES.length-1]){
|
|
|
|
|
setBackgroundMode()
|
|
|
|
|
// next...
|
|
|
|
|
} else {
|
|
|
|
|
setBackgroundMode(BACKGROUND_MODES[BACKGROUND_MODES.indexOf(mode)+1])
|
2012-08-05 21:03:37 +04:00
|
|
|
}
|
2012-08-05 22:06:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
var toggleSingleImageModeTransitions = createCSSClassToggler('.viewer', 'no-single-image-transitions')
|
2012-08-05 22:06:52 +04:00
|
|
|
|
|
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
var toggleControls = createCSSClassToggler('.viewer', 'hidden-controls')
|
|
|
|
|
var showControls = function(){toggleControls('on')}
|
|
|
|
|
var hideControls = function(){toggleControls('off')}
|
2012-08-05 22:06:52 +04:00
|
|
|
|
2012-08-05 21:03:37 +04:00
|
|
|
|
2012-08-08 01:45:22 +04:00
|
|
|
var toggleTransitions = createCSSClassToggler('.viewer', 'transitions-enabled')
|
|
|
|
|
var enableTransitions = function(){toggleTransitions('on')}
|
|
|
|
|
var disableTransitions = function(){toggleTransitions('off')}
|
2012-08-05 21:03:37 +04:00
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
2012-08-02 17:54:40 +04:00
|
|
|
/********************************************************* Movement **/
|
|
|
|
|
|
2012-08-10 18:31:32 +04:00
|
|
|
/* Set the transform-origin to the center of the current view...
|
|
|
|
|
*/
|
|
|
|
|
function centerOrigin(){
|
|
|
|
|
var mt = parseFloat($('.field').css('margin-top'))
|
|
|
|
|
var ml = parseFloat($('.field').css('margin-left'))
|
|
|
|
|
var cml = parseFloat($('.current.ribbon').css('margin-left'))
|
|
|
|
|
|
|
|
|
|
var t = parseFloat($('.field').css('top'))
|
|
|
|
|
var l = parseFloat($('.field').css('left'))
|
|
|
|
|
var w = $('.field').width()
|
|
|
|
|
var h = $('.field').height()
|
|
|
|
|
var W = $('.container').width()
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
|
|
|
|
|
var ot = -getCurrentVerticalOffset() + H/2 - t
|
|
|
|
|
var ol = -ml + W/2 - l
|
|
|
|
|
|
|
|
|
|
$('.field').css({
|
|
|
|
|
'transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-o-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-moz-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-webkit-transform-origin': ol + 'px ' + ot + 'px',
|
|
|
|
|
'-ms-transform-origin': ol + 'px ' + ot + 'px'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// XXX for debugging...
|
|
|
|
|
$('.origin-marker').css({
|
|
|
|
|
'top': ot,
|
|
|
|
|
'left': ol
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
// XXX these work oddly when page is scaled in maxthon...
|
2012-08-04 20:26:38 +04:00
|
|
|
// XXX virtually identical, see of can be merged...
|
2012-08-02 17:54:40 +04:00
|
|
|
function moveViewUp(){
|
|
|
|
|
var t = parseInt($('.field').css('top'))
|
|
|
|
|
$('.field').css({'top': t-(MOVE_DELTA)})
|
|
|
|
|
}
|
|
|
|
|
function moveViewDown(){
|
|
|
|
|
var t = parseInt($('.field').css('top'))
|
|
|
|
|
$('.field').css({'top': t+(MOVE_DELTA)})
|
|
|
|
|
}
|
|
|
|
|
function moveViewLeft(){
|
|
|
|
|
var l = parseInt($('.field').css('left'))
|
|
|
|
|
$('.field').css({'left': l-(MOVE_DELTA)})
|
|
|
|
|
}
|
|
|
|
|
function moveViewRight(){
|
|
|
|
|
var l = parseInt($('.field').css('left'))
|
|
|
|
|
$('.field').css({'left': l+(MOVE_DELTA)})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-10 18:31:32 +04:00
|
|
|
function centerCurrentImage(){
|
|
|
|
|
$('.field')
|
|
|
|
|
.css({
|
|
|
|
|
'top': 0,
|
|
|
|
|
'left': 0
|
|
|
|
|
})
|
|
|
|
|
// do this after animations are done...
|
|
|
|
|
.one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", centerOrigin)
|
|
|
|
|
// this is repeated intentionally...
|
|
|
|
|
// ...needed for small shifts, while the after-animation event
|
|
|
|
|
// is for large moves.
|
|
|
|
|
centerOrigin()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-04 20:26:38 +04:00
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/******************************************************* Navigation **/
|
|
|
|
|
|
2012-06-08 18:30:54 +04:00
|
|
|
// basic navigation...
|
|
|
|
|
function firstImage(){
|
2012-08-10 19:40:26 +04:00
|
|
|
return $('.current.ribbon').children('.image').first().click()
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
function prevImage(){
|
2012-08-10 19:40:26 +04:00
|
|
|
return $('.current.image').prev('.image').click()
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
function nextImage(){
|
2012-08-10 19:40:26 +04:00
|
|
|
return $('.current.image').next('.image').click()
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
|
|
|
|
function lastImage(){
|
2012-08-10 19:40:26 +04:00
|
|
|
return $('.current.ribbon').children('.image').last().click()
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-07-14 00:00:16 +04:00
|
|
|
|
2012-08-10 19:40:26 +04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// add skip screen images in direction...
|
|
|
|
|
function skipScreenImages(direction){
|
|
|
|
|
// calculate screen width in images...
|
|
|
|
|
var W = $('.viewer').width()
|
|
|
|
|
var w = $('.current.image').width()
|
|
|
|
|
var scale = getElementScale($('.field'))
|
|
|
|
|
var n = Math.max(Math.floor(W/(w*scale))-1, 0)
|
|
|
|
|
|
|
|
|
|
var img = $('.current.image')[direction + 'All']('.image').eq(n)
|
|
|
|
|
if(img.length > 0){
|
|
|
|
|
return img.click()
|
|
|
|
|
} else if(direction == 'next'){
|
|
|
|
|
return lastImage()
|
|
|
|
|
} else if(direction == 'prev'){
|
|
|
|
|
return firstImage()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var nextScreenImages = function(){ return skipScreenImages('next') }
|
|
|
|
|
var prevScreenImages = function(){ return skipScreenImages('prev') }
|
|
|
|
|
|
2012-08-04 18:57:54 +04:00
|
|
|
|
2012-08-02 17:54:40 +04:00
|
|
|
|
2012-08-10 20:25:49 +04:00
|
|
|
function focusRibbon(direction, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
|
|
|
|
var id = get_order($('.current.image'))
|
2012-07-24 14:55:10 +04:00
|
|
|
var prev = getImageBefore(id, $('.current.ribbon')[direction]('.ribbon'))
|
2012-07-14 00:00:16 +04:00
|
|
|
if(prev){
|
|
|
|
|
var next = prev.next()
|
2012-07-14 02:26:06 +04:00
|
|
|
// NOTE: direction is accounted for to make the up/down shifts
|
|
|
|
|
// symmetrical in the general case...
|
|
|
|
|
if(next.length == 0 || direction == 'next'){
|
2012-08-10 19:40:26 +04:00
|
|
|
return prev.click()
|
2012-07-14 00:00:16 +04:00
|
|
|
} else {
|
2012-08-10 19:40:26 +04:00
|
|
|
return next.click()
|
2012-07-14 00:00:16 +04:00
|
|
|
}
|
|
|
|
|
} else {
|
2012-08-10 19:40:26 +04:00
|
|
|
return $('.current.ribbon')[direction]('.ribbon').children('.image').first().click()
|
2012-07-14 00:00:16 +04:00
|
|
|
}
|
|
|
|
|
}
|
2012-08-10 19:40:26 +04:00
|
|
|
var focusAboveRibbon = function(){ return focusRibbon('prev') }
|
|
|
|
|
var focusBelowRibbon = function(){ return focusRibbon('next') }
|
2012-06-08 18:30:54 +04:00
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
|
|
|
|
|
|
2012-08-10 18:31:32 +04:00
|
|
|
/********************************************************** Zooming **/
|
|
|
|
|
|
|
|
|
|
// NOTE: this will only return a single scale value...
|
|
|
|
|
function getElementScale(elem){
|
|
|
|
|
//var transform = elem.css('transform')
|
|
|
|
|
var vendors = ['o', 'moz', 'ms', 'webkit']
|
|
|
|
|
var transform = elem.css('transform')
|
|
|
|
|
var res
|
|
|
|
|
|
|
|
|
|
// go through vendor prefixes... (hate this!)
|
|
|
|
|
if(!transform || transform == 'none'){
|
|
|
|
|
for(var i in vendors){
|
|
|
|
|
transform = elem.css('-' + vendors[i] + '-transform')
|
|
|
|
|
if(transform && transform != 'none'){
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// no transform is set...
|
|
|
|
|
if(!transform || transform == 'none'){
|
|
|
|
|
return 1
|
|
|
|
|
}
|
|
|
|
|
// get the scale value -- first argument of scale/matrix...
|
|
|
|
|
return parseFloat((/(scale|matrix)\(([^,]*),.*\)/).exec(transform)[2])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setElementScale(elem, scale){
|
2012-08-10 19:40:26 +04:00
|
|
|
return elem.css({
|
2012-08-10 18:31:32 +04:00
|
|
|
'transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-moz-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-o-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-ms-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
'-webkit-transform': 'scale('+scale+', '+scale+')',
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function scaleContainerBy(factor){
|
2012-08-10 19:40:26 +04:00
|
|
|
return setContainerScale(getElementScale($('.field'))*factor)
|
2012-08-10 18:31:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setContainerScale(scale){
|
|
|
|
|
return setElementScale($('.field'), scale)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function fitImage(){
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
var W = $('.container').width()
|
|
|
|
|
|
|
|
|
|
var h = $('.image.current').height()
|
|
|
|
|
var w = $('.image.current').width()
|
|
|
|
|
|
|
|
|
|
var f = Math.min(H/h, W/w)
|
|
|
|
|
|
|
|
|
|
setContainerScale(f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function fitThreeImages(){
|
|
|
|
|
var H = $('.container').height()
|
|
|
|
|
var W = $('.container').width()
|
|
|
|
|
|
|
|
|
|
var h = $('.image.current').height()
|
|
|
|
|
// NOTE: this is cheating, need to get actual three widths...
|
|
|
|
|
var w = $('.image.current').width()*3
|
|
|
|
|
|
|
|
|
|
var f = Math.min(H/h, W/w)
|
|
|
|
|
|
|
|
|
|
setContainerScale(f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/********************************************************** Actions **/
|
2012-06-16 21:52:23 +04:00
|
|
|
// basic actions...
|
2012-08-04 19:47:13 +04:00
|
|
|
// NOTE: below 'direction' argument is meant in the html sence,
|
|
|
|
|
// i.e. next/prev...
|
2012-06-16 21:52:23 +04:00
|
|
|
|
2012-06-08 18:30:54 +04:00
|
|
|
// create ribbon above/below helpers...
|
2012-08-04 19:47:13 +04:00
|
|
|
// XXX adding a ribbon above the current is still jumpy, need to devise
|
2012-08-04 20:26:38 +04:00
|
|
|
// a cleaner way to do this...
|
2012-06-16 21:40:36 +04:00
|
|
|
function createRibbon(direction){
|
|
|
|
|
if(direction == 'next'){
|
|
|
|
|
var insert = 'insertAfter'
|
|
|
|
|
} else if(direction == 'prev') {
|
|
|
|
|
var insert = 'insertBefore'
|
|
|
|
|
} else {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-04 19:47:13 +04:00
|
|
|
// adding a new ribbon above the current effectively pushes the
|
|
|
|
|
// whole view down, so we need to compensate for this.
|
|
|
|
|
// NOTE: the problem is partly caused by clicks fiering BEFORE the
|
|
|
|
|
// animation is done...
|
2012-08-04 19:37:40 +04:00
|
|
|
$('.field').addClass('unanimated')
|
|
|
|
|
|
2012-08-04 19:47:13 +04:00
|
|
|
if(direction == 'prev'){
|
|
|
|
|
$('.field').css({
|
2012-08-04 20:26:38 +04:00
|
|
|
'margin-top': parseInt($('.field').css('margin-top')) - $('.ribbon').outerHeight()
|
2012-08-04 19:47:13 +04:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// the actual insert...
|
|
|
|
|
var res = $('<div class="ribbon"></div>')[insert]('.current.ribbon')
|
2012-08-04 19:37:40 +04:00
|
|
|
|
2012-08-04 19:47:13 +04:00
|
|
|
// restore the animated state...
|
2012-08-04 19:37:40 +04:00
|
|
|
$('.field').removeClass('unanimated')
|
2012-08-04 18:57:54 +04:00
|
|
|
|
2012-06-08 18:30:54 +04:00
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-04 20:09:19 +04:00
|
|
|
// merge current and direction ribbon...
|
|
|
|
|
// NOTE: this will take all the elements from direction ribbon and add
|
|
|
|
|
// them to current
|
2012-08-04 19:47:13 +04:00
|
|
|
// XXX this uses jquery animation...
|
2012-08-04 20:26:38 +04:00
|
|
|
// XXX one way to optimise this is to add the lesser ribbon to the
|
|
|
|
|
// greater disregarding their actual order...
|
2012-08-10 20:25:49 +04:00
|
|
|
function mergeRibbons(direction, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-08-04 20:09:19 +04:00
|
|
|
var current_ribbon = $('.current.ribbon')
|
|
|
|
|
var images = $('.current.ribbon')[direction]('.ribbon').children()
|
|
|
|
|
for(var i=0; i < images.length; i++){
|
|
|
|
|
var image = $(images[i])
|
2012-08-04 20:26:38 +04:00
|
|
|
// get previous element after which we need to put the current...
|
2012-08-10 20:25:49 +04:00
|
|
|
var prev_elem = getImageBefore(get_order(image), current_ribbon)
|
2012-08-04 20:26:38 +04:00
|
|
|
// check if we need to be before the first element...
|
2012-08-04 20:09:19 +04:00
|
|
|
if(prev_elem == null){
|
|
|
|
|
image
|
|
|
|
|
.detach()
|
|
|
|
|
.insertBefore(current_ribbon.children('.image').first())
|
|
|
|
|
} else {
|
|
|
|
|
image
|
|
|
|
|
.detach()
|
|
|
|
|
.insertAfter(prev_elem)
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-16 21:52:23 +04:00
|
|
|
// animate...
|
2012-07-24 14:55:10 +04:00
|
|
|
$('.current.ribbon')[direction]('.ribbon')
|
2012-06-08 18:30:54 +04:00
|
|
|
.slideUp(function(){
|
|
|
|
|
$(this).remove()
|
2012-07-24 14:55:10 +04:00
|
|
|
$('.current.image').click()
|
2012-06-08 18:30:54 +04:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-08-05 00:02:17 +04:00
|
|
|
|
|
|
|
|
|
2012-06-17 16:46:46 +04:00
|
|
|
/*************************************************** Editor Actions **/
|
2012-06-16 21:52:23 +04:00
|
|
|
|
|
|
|
|
// now the actual modifiers...
|
2012-08-10 20:25:49 +04:00
|
|
|
function shiftImage(direction, get_order){
|
|
|
|
|
if(get_order == null){
|
|
|
|
|
get_order = getImageOrder
|
|
|
|
|
}
|
2012-07-24 14:55:10 +04:00
|
|
|
if($('.current.ribbon')[direction]('.ribbon').length == 0){
|
2012-06-16 21:40:36 +04:00
|
|
|
createRibbon(direction)
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-07-14 00:00:16 +04:00
|
|
|
|
|
|
|
|
// get previous element after which we need to put the current...
|
|
|
|
|
var prev_elem = getImageBefore(
|
2012-08-10 20:25:49 +04:00
|
|
|
get_order($('.current.image')),
|
2012-07-24 14:55:10 +04:00
|
|
|
$('.current.ribbon')[direction]('.ribbon'))
|
2012-07-14 00:00:16 +04:00
|
|
|
|
|
|
|
|
// last image in ribbon, merge...
|
2012-07-24 14:55:10 +04:00
|
|
|
if($('.current.ribbon').children('.image').length == 1){
|
2012-06-16 21:24:11 +04:00
|
|
|
mergeRibbons(direction)
|
2012-06-08 18:30:54 +04:00
|
|
|
} else {
|
2012-07-24 14:55:10 +04:00
|
|
|
img = $('.current.image')
|
2012-06-08 18:30:54 +04:00
|
|
|
if(img.next('.image').length == 0){
|
|
|
|
|
prevImage()
|
|
|
|
|
} else {
|
|
|
|
|
nextImage()
|
|
|
|
|
}
|
2012-07-14 00:00:16 +04:00
|
|
|
// do the actual move...
|
|
|
|
|
if(prev_elem){
|
|
|
|
|
// insert element after current...
|
|
|
|
|
img
|
|
|
|
|
.detach()
|
|
|
|
|
.insertAfter(prev_elem)
|
|
|
|
|
} else {
|
|
|
|
|
// empty ribbon or fisrt element...
|
|
|
|
|
img
|
|
|
|
|
.detach()
|
2012-07-24 14:55:10 +04:00
|
|
|
.prependTo($('.current.ribbon')[direction]('.ribbon'))
|
2012-07-14 00:00:16 +04:00
|
|
|
}
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-04 19:37:40 +04:00
|
|
|
$('.current.image').click()
|
2012-06-08 18:30:54 +04:00
|
|
|
}
|
2012-08-10 19:40:26 +04:00
|
|
|
var shiftImageDown = function(){ return shiftImage('next') }
|
|
|
|
|
var shiftImageUp = function(){ return shiftImage('prev') }
|
2012-06-08 18:30:54 +04:00
|
|
|
|
2012-06-16 22:12:50 +04:00
|
|
|
|
|
|
|
|
|
2012-08-07 21:12:22 +04:00
|
|
|
|
2012-07-14 00:00:16 +04:00
|
|
|
/*********************************************************************/
|
2012-06-08 18:30:54 +04:00
|
|
|
// vim:set ts=4 sw=4 nowrap :
|