ImageGrid/ui/image.js
Alex A. Naanou ece95ff30f some refacoring and preparations for gen4...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
2014-07-18 20:48:40 +04:00

350 lines
8.4 KiB
JavaScript
Executable File

/**********************************************************************
*
*
*
**********************************************************************/
// A stub image, also here for documentation...
var STUB_IMAGE_DATA = {
// Entity GID...
id: 'STUB-GID',
// Entity type
//
// can be:
// - 'image'
// - 'group'
type: 'image',
// Entity state
//
// can be:
// - 'single'
// - 'grouped'
// - 'hidden'
// - ...
state: 'single',
// Creation time...
ctime: 0,
// Original path...
path: './images/sizes/900px/SIZE.jpg',
// Previews...
// NOTE: the actual values depend on specific image and can be
// any size...
preview: {
'150px': './images/sizes/150px/SIZE.jpg',
'350px': './images/sizes/350px/SIZE.jpg',
'900px': './images/sizes/900px/SIZE.jpg',
},
// Classes
// XXX currently unused...
classes: '',
// Image orientation (optional)
//
// can be:
// - null/undefined - same as 0
// - 0 (default) - load as-is
// - 90 - rotate 90deg CW
// - 180 - rotate 180deg CW
// - 270 - rotate 270deg CW (90deg CCW)
//
// NOTE: use orientationExif2ImageGrid(..) to convert from EXIF
// orientation format to ImageGrid format...
orientation: 0,
// Image flip state (optional)
//
// can be:
// - null/undefined
// - array
//
// can contain:
// - 'vertical'
// - 'horizontal'
//
// NOTE: use orientationExif2ImageGrid(..) to convert from EXIF
// orientation format to ImageGrid format...
flipped: null,
// Image comment (optional)
//
// can be:
// - null/undefined
// - string
comment: null,
// List of image tags (optional)
//
// can be:
// - null/undefined
// - array
tags: null,
}
// List of function that update image state...
//
// these are called by updateImage(..) after the image is created.
//
// each function must be of the form:
// updateImage(gid, image) -> image
//
var IMAGE_UPDATERS = []
/*********************************************************************/
// XXX Constructors...
/*********************************************************************/
// Run all the image update functions registered in IMAGE_UPDATERS, on
// an image...
//
function updateImageIndicators(gid, image){
gid = gid == null ? getImageGID() : gid
image = image == null ? getImage() : $(image)
IMAGE_UPDATERS.forEach(function(update){
update(gid, image)
})
return image
}
// helper...
function _loadImagePreviewURL(image, url){
// pre-cache and load image...
// NOTE: this will make images load without a blackout...
var img = new Image()
img.onload = function(){
image.css({
'background-image': 'url("'+ url +'")',
})
}
img.src = url
return img
}
// Update an image element
//
// NOTE: care must be taken to reset ALL attributes an image can have,
// a common bug if this is not done correctly, is that some settings
// may leak to newly loaded images...
function updateImage(image, gid, size, sync){
image = image == null ? getImage() : $(image)
sync = sync == null ? CONFIG.load_img_sync : sync
var old_gid = getImageGID(image)
// same image -- update...
if(old_gid == gid || gid == null){
gid = old_gid
// reuse for different image -- reconstruct...
} else {
// remove old marks...
if(typeof(old_gid) == typeof('str')){
getImageMarks(old_gid).remove()
}
// reset gid...
image
.attr('gid', JSON.stringify(gid))
.css({
// clear the old preview...
'background-image': '',
})
}
size = size == null ? getVisibleImageSize('max') : size
// get the image data...
var img_data = IMAGES[gid]
if(img_data == null){
img_data = STUB_IMAGE_DATA
}
/* XXX does not seem to be needing this...
// set the current class...
if(gid == DATA.current){
image.addClass('current')
} else {
image.removeClass('current')
}
*/
// preview...
var p_url = getBestPreview(gid, size).url
// update the preview if it's a new image or...
if(old_gid != gid
// the new preview (purl) is different to current...
|| image.css('background-image').indexOf(encodeURI(p_url)) < 0){
// sync load...
if(sync){
_loadImagePreviewURL(image, p_url)
// async load...
} else {
// NOTE: storing the url in .data() makes the image load the
// last requested preview and in a case when we manage to
// call updateImage(...) on the same element multiple times
// before the previews get loaded...
// ...setting the data().loading is sync while loading an
// image is not, and if several loads are done in sequence
// there is no guarantee that they will happen in the same
// order as requested...
image.data().loading = p_url
setTimeout(function(){
_loadImagePreviewURL(image, image.data().loading)
}, 0)
}
}
// main attrs...
image
.attr({
order: DATA.order.indexOf(gid),
orientation: img_data.orientation == null ? 0 : img_data.orientation,
})
// flip...
setImageFlipState(image, img_data.flipped == null ? [] : img_data.flipped)
// NOTE: this only has effect on non-square image blocks...
correctImageProportionsForRotation(image)
// marks and other indicators...
updateImageIndicators(gid, image)
return image
}
// Same as updateImage(...) but will update all loaded images.
//
// If list is passed this will update only the images in the list. The
// list can contain either gids or image elements.
//
// If CONFIG.update_sort_enabled is set, this will prioritize images by
// distance from current image, loading the closest images first...
//
// If CONFIG.update_sync is set, this will run asynchronously.
function updateImages(list, size, cmp){
var deferred = $.Deferred()
function _worker(){
list = list == null ? $('.image') : $(list)
size = size == null ? getVisibleImageSize('max') : size
function _update(_, e){
var img = typeof(e) == typeof('str') ? getImage(e) : $(e)
if(img.length > 0){
updateImage(img, null, size)
}
}
// sorted run...
if(CONFIG.update_sort_enabled && cmp != false){
cmp = cmp == null ?
makeGIDDistanceCmp(getImageGID(), function(e){
return typeof(e) == typeof('str') ? e : getImageGID(e)
})
// XXX this is more correct but is slow...
//makeGIDRibbonDistanceCmp(getImageGID(), getImageGID)
: cmp
deferred.resolve(list
// sort images by distance from current, so as to update what
// the user is looking at first...
.sort(cmp)
.map(_update))
// do a fast run w.o. sorting images...
} else {
deferred.resolve(list.map(_update))
}
}
if(CONFIG.update_sync){
_worker()
} else {
setTimeout(_worker, 0)
}
return deferred
}
// Compensate for viewer proportioned and rotated images.
//
// This will set the margins so as to make the rotated image offset the
// same space as it is occupying visually...
//
// NOTE: this is not needed for square image blocks.
// NOTE: if an image block is square, this will remove the margins.
function correctImageProportionsForRotation(images, container){
container = container == null ? $('.viewer') : container
var W = container.innerWidth()
var H = container.innerHeight()
var viewer_p = W > H ? 'landscape' : 'portrait'
return $(images).each(function(i, e){
var image = $(this)
// orientation...
var o = image.attr('orientation')
o = o == null ? 0 : o
var w = image.outerWidth()
var h = image.outerHeight()
// non-square image...
if(w != h){
var image_p = w > h ? 'landscape' : 'portrait'
// when the image is turned 90deg/270deg and its
// proportions are the same as the screen...
if((o == 90 || o == 270) && image_p == viewer_p){
image.css({
width: h,
height: w,
})
image.css({
'margin-top': -((w - h)/2),
'margin-bottom': -((w - h)/2),
'margin-left': (w - h)/2,
'margin-right': (w - h)/2,
})
} else if((o == 0 || o == 180) && image_p != viewer_p){
image.css({
width: h,
height: w,
})
image.css({
'margin': '',
})
}
// square image...
} else {
image.css({
'margin': '',
})
}
})
}
/**********************************************************************
* vim:set ts=4 sw=4 : */