From 3ca05d008fe626f61e7612a74858a43c6f558323 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Mon, 28 Jul 2014 06:36:02 +0400 Subject: [PATCH] started the refactoring of image.js -- mostly merging with ribbons.js (not done yet)... Signed-off-by: Alex A. Naanou --- ui (gen4)/image.js | 59 +---------- ui (gen4)/images.js | 122 +++++++++++++++++++++ ui (gen4)/ribbons.js | 247 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 345 insertions(+), 83 deletions(-) diff --git a/ui (gen4)/image.js b/ui (gen4)/image.js index 20d3db87..b29ee218 100755 --- a/ui (gen4)/image.js +++ b/ui (gen4)/image.js @@ -171,6 +171,8 @@ function _loadImagePreviewURL(image, url){ // 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... +// +// XXX this calls .correctImageProportionsForRotation(..) var updateImage = module.updateImage = function updateImage(image, gid, size, sync){ @@ -317,67 +319,12 @@ function updateImages(list, size, cmp){ } -// 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. var correctImageProportionsForRotation = module.correctImageProportionsForRotation = 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': '', - }) - } - }) + // XXX } diff --git a/ui (gen4)/images.js b/ui (gen4)/images.js index 41838b06..ac10d8f5 100755 --- a/ui (gen4)/images.js +++ b/ui (gen4)/images.js @@ -13,6 +13,49 @@ console.log('>>> images') /*********************************************************************/ +// Calculate relative rotation angle... +// +// Calculate rotation angle relative to from: +// calcRelativeRotation(from, 'cw') +// calcRelativeRotation(from, 'ccw') +// -> 0 | 90 | 180 | 270 +// +// Validate an angle: +// calcRelativeRotation(angle) +// calcRelativeRotation(from, angle) +// -> 0 | 90 | 180 | 270 +// -> null +// +// +module.calcRelativeRotation = function(from, to){ + if(to == null){ + to = from + from = 0 + } + to = to == 'cw' ? 1 + : to == 'ccw' ? -1 + : [0, 90, 180, 270].indexOf(to*1) >= 0 ? to*1 + : [-90, -180, -270].indexOf(to*1) >= 0 ? 360+(to*1) + : null + + // relative rotation... + if(to == 1 || to == -1){ + var res = from + res = res == null ? 0 : res*1 + res += 90*to + res = res < 0 ? 270 + : res > 270 ? 0 + : res + + // explicit direction... + } else { + var res = to + } + + return res +} + + // cmp functions... // XXX is this the right way to seporate these??? @@ -174,6 +217,11 @@ module.ImagesPrototype = { // Image data helpers... + // XXX see: ribbons.js for details... + getBestPreview: function(){ + // XXX + }, + // Get image filename... getImageFileName: function(gid, do_unescape){ do_unescape = do_unescape == null ? true : do_unescape @@ -234,6 +282,80 @@ module.ImagesPrototype = { // XXX see ../ui/sort.js }, + // Actions... + + // Rotate image... + // + // Rotate image clockwise: + // .rotateImage(target, 'cw') + // -> images + // + // Rotate image counterclockwise: + // .rotateImage(target, 'ccw') + // -> images + // + // Set explicit image rotation angle: + // .rotateImage(target, 0|90|180|270) + // .rotateImage(target, -90|-180|-270) + // -> images + // + // NOTE: target can be a gid or a list of gids... + rotateImage: function(gids, direction){ + gids = gids.constructor.name != 'Array' ? [gids] : gids + // validate direction... + if(module.calcRelativeRotation(direction) == null){ + return this + } + + var that = this + gids.forEach(function(key){ + var img = that[key] + var o = direction == 'cw' || direction == 'ccw' + ? module.calcRelativeRotation(img.orientation, direction) + : direction*1 + if(o == 0){ + delete img.orientation + } else { + img.orientation = o + } + // account for proportions... + //that.correctImageProportionsForRotation(img) + // XXX this is a bit of an overkill but it will update the + // preview if needed... + //that.updateImage(img) + }) + return this + }, + + // Flip image... + // + // .flipImage(target, 'horizontal') + // .flipImage(target, 'vertical') + // -> images + // + flipImage: function(gids, direction){ + gids = gids.constructor.name != 'Array' ? [gids] : gids + var that = this + gids.forEach(function(key){ + var img = that[key] + var state = img.flipped + state = state == null ? [] : state + // toggle the specific state... + var i = state.indexOf(direction) + if(i >= 0){ + state.splice(i, 1) + } else { + state.push(direction) + } + if(state.length == 0){ + delete img.flipped + } else { + img.flipped = state + } + }) + return this + }, + // serialization... loadJSON: function(data){ diff --git a/ui (gen4)/ribbons.js b/ui (gen4)/ribbons.js index e7497c43..178c1349 100755 --- a/ui (gen4)/ribbons.js +++ b/ui (gen4)/ribbons.js @@ -18,6 +18,7 @@ require('ext-lib/jquery') var data = require('data') var image = require('image') +var images = require('images') @@ -83,6 +84,7 @@ module.RibbonsClassPrototype = { .replace(/^"(.*)"$/g, '$1')) }, // XXX NOTE: quots removal might render this incompatible with older data formats... + // XXX should this do .updateImage(..) ??? createImage: function(gid){ gid = gid != null ? gid+'' : gid return $('
') @@ -100,6 +102,8 @@ module.RibbonsPrototype = { // // .viewer (jQuery object) // + // .images (Images object) + // // XXX to update images we need to know about images... // Constructors... @@ -333,6 +337,7 @@ module.RibbonsPrototype = { // // XXX interaction animation... // XXX mode is ugly... + // XXX should this be strongly or weakly coupled to images.updateImage(..)??? placeImage: function(target, to, mode){ mode = mode == null ? 'before' : mode var img = this.getImage(target) @@ -384,6 +389,8 @@ module.RibbonsPrototype = { // // NOTE: gids and ribbon must be .getImage(..) and .getRibbon(..) // compatible... + // + // XXX should this be strongly or weakly coupled to images.updateImage(..)??? updateRibbon: function(gids, ribbon){ // get/create the ribbon... var r = this.getRibbon(ribbon) @@ -621,6 +628,196 @@ module.RibbonsPrototype = { }, + getImageMarks: function(){ + // XXX + }, + // XXX this needs: + // IMAGE_UPDATERS -- make it a callback/event (node/jquery)... + updateImageIndicators: function(gid, image){ + gid = gid == null ? this.getElemGID() : gid + image = image == null ? this.getImage() : $(image) + + IMAGE_UPDATERS.forEach(function(update){ + update(gid, image) + }) + + return image + }, + _loadImagePreviewURL: function(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 image{s}... + // + load_img_sync: false, + // + // XXX this needs: + // STUB_IMAGE_DATA + // this.images + // XXX + updateImage: function(image, gid, size, sync){ + image = image == null ? this.getImage() : $(image) + sync = sync == null ? this.load_img_sync : sync + + var that = this + return image.each(function(){ + var image = $(this) + var old_gid = that.getElemGID(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')){ + that.getImageMarks(old_gid).remove() + } + // reset gid... + image + .attr('gid', JSON.stringify(gid) + // this removes the extra quots... + .replace(/^"(.*)"$/g, '$1')) + .css({ + // clear the old preview... + 'background-image': '', + }) + } + size = size == null ? that.getVisibleImageSize('max') : size + + // get the image data... + var img_data = that.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 = that.images.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){ + that._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(){ + that._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... + that.correctImageProportionsForRotation(image) + + // marks and other indicators... + that.updateImageIndicators(gid, image) + }) + }, + + // XXX reread... + // 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. + correctImageProportionsForRotation: function(images){ + // XXX + var W = this.viewer.innerWidth() + var H = this.viewer.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': '', + }) + } + }) + }, + + // Image manipulation... // Rotate an image... @@ -633,45 +830,40 @@ module.RibbonsPrototype = { // .rotateImage(target, 'ccw') // -> image // + // Set explicit image rotation angle: + // .rotateImage(target, 0|90|180|270) + // .rotateImage(target, -90|-180|-270) + // -> image // // NOTE: target must be .getImage(..) compatible. // NOTE: this can be applied in bulk, e.g. // this.rotateImage($('.image'), 'cw') will rotate all the // loaded images clockwise. // - // Rotation tables... - _cw: { - null: 90, - 0: 90, - 90: 180, - 180: 270, - //270: 0, - 270: null, - }, - _ccw: { - null: 270, - 0: 270, - //90: 0, - 90: null, - 180: 90, - 270: 180, - }, - // NOTE: setting a value to null will remove the attribute, 0 will - // set 0 explicitly... + // XXX should this be strongly or weakly coupled to images.updateImage(..) + // or images.correctImageProportionsForRotation(..)??? + // XXX should .correctImageProportionsForRotation(..) be in images??? rotateImage: function(target, direction){ - var r_table = direction == 'cw' ? this._cw : this._ccw target = this.getImage(target) + + // validate direction... + if(images.calcRelativeRotation(direction) == null){ + return target + } + + var that = this target.each(function(i, e){ var img = $(this) - var o = img.attr('orientation') - o = r_table[ o == null ? null : o ] - if(o == null){ + var o = direction == 'cw' || direction == 'ccw' + ? images.calcRelativeRotation(img.attr('orientation'), direction) + : direction*1 + if(o == 0){ img.removeAttr('orientation') } else { img.attr('orientation', o) } // account for proportions... - image.correctImageProportionsForRotation(img) + that.correctImageProportionsForRotation(img) // XXX this is a bit of an overkill but it will update the // preview if needed... //image.updateImage(img) @@ -758,8 +950,9 @@ module.RibbonsPrototype = { }, - _setup: function(viewer){ + _setup: function(viewer, images){ this.viewer = $(viewer) + this.images = images }, } @@ -768,13 +961,13 @@ module.RibbonsPrototype = { // var Ribbons = module.Ribbons = -function Ribbons(viewer){ +function Ribbons(viewer, images){ // in case this is called as a function (without new)... if(this.constructor.name != 'Ribbons'){ return new Ribbons(viewer) } - this._setup(viewer) + this._setup(viewer, images) return this }