// XXX need a uniform way to address images (filename?) function setDefaultInitialState(){ if($('.current.ribbon').length == 0){ $('.ribbon').first().addClass('current') } if($('.current.image').length == 0){ $('.current.ribbon').children('.image').first().addClass('current') } } function setupKeyboard(){ $(document) .keydown(handleKeys) } function setupGestures(){ $('.viewer') // XXX this is flaky and breaks some of my code... .swipe({ swipeLeft: nextImage, swipeRight: prevImage, swipeUp: shiftImageUp, swipeDown: shiftImageDown, }) /* XXX jquery.mobile handlers... (with this I'm getting way too much bling) .bind('swipeleft', function(e){ nextImage() e.preventDefault() return false }) .bind('swiperight', function(e){ prevImage() e.preventDefault() return false }) */ } function setupControlElements(){ // images... $(".image").click(setCurrentImage) // buttons... $('.next-image').click(nextImage) $('.prev-image').click(prevImage) $('.demote').click(shiftImageUp) $('.promote').click(shiftImageDown) $('.toggle-wide').click(toggleWideView) // XXX need to update this to real zooming... $('.toggle-single').click(toggleRibbonView) } function loadImages(json){ var images = json.images var ribbon = $('.ribbon').last() $('.image').remove() for(var i = 0; i < images.length; i++){ $('
') .css({ 'background-image': 'url('+images[i]+')' }) // set a unique id for each image... .attr({'id': i}) .click(setCurrentImage) .appendTo(ribbon) } ribbon.children().first().click() } /*************************************************** Event Handlers **/ function setCurrentImage(){ // set classes... $('.current').removeClass('current') $(this) .addClass('current') .parents('.ribbon') .addClass('current') // position the field and ribbons... centerSquare() // center other ribbons relative to current image... // XXX } // XXX depricated... /* function handleImageClick(e) { var cur = $(this) // switch classes... cur.parents().siblings().children(".image").removeClass("current") cur.siblings(".image").removeClass("current") cur.siblings().children(".image").removeClass("current") cur.parents().siblings(".ribbon").removeClass("current") cur.addClass("current") cur.parents(".ribbon").addClass("current") 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 ZOOM_FACTOR = 2 // key configuration... // XXX need to make this handle modifiers gracefully... var keys = { toggleHelp: [72], toggleRibbonView: [70], close: [27, 88, 67], // zooming... zoomIn: [187], zoomOut: [189], // zoom presets... zoomOriginal: [48], fitOne: [49], fitThree: [51], first: [36], last: [35], previous: [37, 80, 188, 8], next: [39, 78, 190, 32], // these work with ctrl and shift modifiers... down: [40], up: [38], // these work with ctrl modifier... promote: [45], demote: [46], ignore: [16, 17, 18], helpShowOnUnknownKey: true } // XXX revise... function handleKeys(event){ var code = event.keyCode, fn = $.inArray; var _ = (fn(code, keys.close) >= 0) ? function(){}() : (fn(code, keys.first) >= 0) ? firstImage() : (fn(code, keys.next) >= 0) ? nextImage() : (fn(code, keys.previous) >= 0) ? prevImage() : (fn(code, keys.last) >= 0) ? lastImage() : (fn(code, keys.promote) >= 0) ? function(){ if(event.ctrlKey){ createRibbon('next') } shiftImageDown() }() : (fn(code, keys.demote) >= 0) ? function(){ if(event.ctrlKey){ createRibbon('prev') } shiftImageUp() }() : (fn(code, keys.down) >= 0) ? function(){ if(event.shiftKey){ if(event.ctrlKey){ createRibbon('next') } shiftImageDown() } else { focusBelowRibbon() } }() : (fn(code, keys.up) >= 0) ? function(){ if(event.shiftKey){ if(event.ctrlKey){ createRibbon('prev') } shiftImageUp() } else { focusAboveRibbon() } }() // zooming... : (fn(code, keys.zoomIn) >= 0) ? zoomContainerBy(ZOOM_FACTOR) : (fn(code, keys.zoomOut) >= 0) ? zoomContainerBy(1/ZOOM_FACTOR) // zoom presets... : (fn(code, keys.zoomOriginal) >= 0) ? setContainerZoom(1) : (fn(code, keys.fitOne) >= 0) ? fitImage() : (fn(code, keys.fitThree) >= 0) ? fitThreeImages() : (fn(code, keys.toggleRibbonView) >= 0) ? toggleRibbonView() : (fn(code, keys.ignore) >= 0) ? false // XXX : (keys.helpShowOnUnknownKey) ? function(){alert(code)}() : false; return false; } /************************************************************ Modes **/ // mode switchers... function unsetViewerMode(mode){ $('.' + mode) .removeClass(mode) // animation... .one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){ $('.current.image').click() return true }); } function setViewerMode(mode){ $('.viewer').not('.' + mode) .addClass(mode) // animation... .one("webkitTransitionEnd oTransitionEnd msTransitionEnd transitionend", function(){ $('.current.image').click() return true }); } // ribbon/single view modes... function toggleRibbonView(){ if($('.single-image-mode').length > 0){ unsetViewerMode('single-image-mode') } else { setViewerMode('single-image-mode') } } // wide view mode toggle... // XXX replace this with adequate zooming... // XXX broken... function toggleWideView(){ if($('.wide-view-mode').length > 0){ setContainerZoom(0.1) //$('.viewer').addClass('wide-view-more') //unsetViewerMode('wide-view-mode') } else { setContainerZoom(1) //$('.viewer').removeClass('wide-view-more') //unsetViewerMode('single-image-mode') //setViewerMode('wide-view-mode') } } /******************************************************* Navigation **/ // 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 for the above two functions to be stable we will need to jump up // to the next and down to the prev element... function focusRibbon(direction){ var id = $('.current.image').attr('id') var prev = getImageBefore(id, $('.current.ribbon')[direction]('.ribbon')) if(prev){ var next = prev.next() // NOTE: direction is accounted for to make the up/down shifts // symmetrical in the general case... if(next.length == 0 || direction == 'next'){ prev.click() } else { next.click() } } else { $('.current.ribbon')[direction]('.ribbon').children('.image').first().click() } } function focusAboveRibbon(){ focusRibbon('prev') } function focusBelowRibbon(){ focusRibbon('next') } /********************************************************** Helpers **/ // 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? // XXX might be better to make this binary search in very large sets of data function getImageBefore(id, ribbon){ // walk the ribbon till we find two images one with an ID less and // another greater that id... id = parseInt(id) var images = ribbon.children('.image') var prev for(var i=0; i < images.length; i++){ if(parseInt($(images[i]).attr('id')) > id){ return prev } prev = $(images[i]) } return prev } // center a ribbon horizontally... // if id exists in ribbon make it the center, else center between the // two images that id came from. function centerRibbonHorizontally(id, ribbon){ // XXX } // center the ribbon in the middle of the container vertically... function centerRibbonVertically(ribbon){ // XXX } /********************************************************** Actions **/ // basic actions... // NOTE: below 'direction' is meant in the html sence, i.e. next/prev... // create ribbon above/below helpers... function createRibbon(direction){ if(direction == 'next'){ var insert = 'insertAfter' } else if(direction == 'prev') { var insert = 'insertBefore' } else { return false } var res = $('')[insert]('.current.ribbon') // HACK: without this, the class change below will not animate... .show() .addClass('ribbon') .removeClass('new-ribbon') // XXX need to account for increased top when creating a ribbon above... // i.e. shift the content upward... /* if(direction == 'prev'){ $('.field').css({ top: $('.field').position().top - $('.current.ribbon').outerHeight() }) }*/ return res } // XXX sort elements correctly... function mergeRibbons(direction){ $('.current.ribbon')[direction]('.ribbon') .children() .detach() .insertAfter('.current.image') // animate... $('.current.ribbon')[direction]('.ribbon') .slideUp(function(){ $(this).remove() $('.current.image').click() }) } /*************************************************** Editor Actions **/ // now the actual modifiers... function shiftImage(direction){ if($('.current.ribbon')[direction]('.ribbon').length == 0){ createRibbon(direction) } // get previous element after which we need to put the current... var prev_elem = getImageBefore( $('.current.image').attr('id'), $('.current.ribbon')[direction]('.ribbon')) // last image in ribbon, merge... if($('.current.ribbon').children('.image').length == 1){ mergeRibbons(direction) } else { img = $('.current.image') if(img.next('.image').length == 0){ prevImage() } else { nextImage() } // do the actual move... if(prev_elem){ // insert element after current... img .detach() .insertAfter(prev_elem) } else { // empty ribbon or fisrt element... img .detach() .prependTo($('.current.ribbon')[direction]('.ribbon')) } } // XXX this has to know aout animations... $('.current.image').click() } function shiftImageDown(){ return shiftImage('next') } // XXX this has problems, when creating a new ribbon this does not settle // into a correct spot... function shiftImageUp(){ return shiftImage('prev') } /*********************************************************************/ // vim:set ts=4 sw=4 nowrap :