From 75a907eeabb2983bda2bd1081eacb8a7d94db63b Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Mon, 24 Nov 2014 09:34:46 -0500 Subject: [PATCH] Add .bat and .cmd to the pretty print extensions --- src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js | 157 +++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 136 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js index 27cca8c..6157513 100644 --- a/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js +++ b/src/main/java/com/gitblit/wicket/pages/scripts/imgdiff.js @@ -22,7 +22,7 @@ * * The styling of the slider is to be done in CSS. Currently recognized options: * - initial: <float> clipped to [0..1], default 0 - * - handleClass: <string> to assign to the handle div element created. + * - handleClass: <string> to assign to the handle span element created. * If no handleClass is specified, a very plain default style is assigned. */ function rangeSlider(elem, options) { @@ -30,7 +30,7 @@ options.initial = Math.min(1.0, Math.max(0, options.initial)); var $elem = $(elem); - var $handle = $('<div></div>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); + var $handle = $('<span></span>').css({ position: 'absolute', left: 0, cursor: 'ew-resize' }); var $root = $(document.documentElement); var $doc = $(document); var lastRatio = options.initial; @@ -38,8 +38,8 @@ /** Mousemove event handler to track the mouse and move the slider. Generates slider:pos events. */ function track(e) { var pos = $elem.offset().left; - var width = $elem.width(); - var handleWidth = $handle.width(); + var width = $elem.innerWidth(); + var handleWidth = $handle.outerWidth(false); var range = width - handleWidth; if (range <= 0) return; var delta = Math.min(range, Math.max (0, e.pageX - pos - handleWidth / 2)); @@ -57,44 +57,64 @@ /** Snaps the slider to the given ratio and generates a slider:pos event with the new ratio. */ function setTo(ratio) { - var w = $elem.width(); + var w = $elem.innerWidth(); if (w <= 0 || $elem.is(':hidden')) return; lastRatio = Math.min( 1.0, Math.max(0, ratio)); - $handle.css('left', "" + Math.max(0, 100 * (lastRatio * (w - $handle.width())) / w) + '%'); + $handle.css('left', "" + Math.max(0, 100 * (lastRatio * (w - $handle.outerWidth(false))) / w) + '%'); $elem.trigger('slider:pos', { ratio: lastRatio, handle: $handle[0] }); } /** * Moves the slider to the given ratio, clipped to [0..1], in duration milliseconds. - * Generates slider:pos events during the animation. If duration === 0, same as setTo. - * Default duration is 500ms. + * Generates slider:pos events during the animation. If duration <= 30, same as setTo. + * Default duration is 500ms. If a callback is given, it's called once the animation + * has completed. */ - function moveTo(ratio, duration) { + function moveTo(ratio, duration, callback) { ratio = Math.min(1.0, Math.max(0, ratio)); - if (ratio === lastRatio) return; + if (ratio === lastRatio) { + if (typeof callback == 'function') callback(); + return; + } if (typeof duration == 'undefined') duration = 500; - if (duration === 0) { + if (duration <= 30) { + // Cinema is 24 or 48 frames/sec, so 20-40ms per frame. Makes no sense to animate for such a short duration. setTo(ratio); + if (typeof callback == 'function') callback(); } else { - var target = ratio * ($elem.width() - $handle.width()); + var target = ratio * ($elem.innerWidth() - $handle.outerWidth(false)); if (ratio > lastRatio) target--; else target++; $handle.stop().animate({left: target}, { 'duration' : duration, 'step' : function() { - lastRatio = Math.min(1.0, Math.max(0, $handle.position().left / ($elem.width() - $handle.width()))); + lastRatio = Math.min(1.0, Math.max(0, $handle.position().left / ($elem.innerWidth() - $handle.outerWidth(false)))); $elem.trigger('slider:pos', { ratio : lastRatio, handle : $handle[0] }); }, - 'complete' : function() { setTo(ratio); } // Last step gives us a % value again. + 'complete' : function() { setTo(ratio); if (typeof callback == 'function') callback(); } // Ensure we have again a % value } ); } } + /** + * As moveTo, but determines an appropriate duration in the range [0..maxDuration] on its own, + * depending on the distance the handle would move. If no maxDuration is given it defaults + * to 1500ms. + */ + function moveAuto(ratio, maxDuration, callback) { + if (typeof maxDuration == 'undefined') maxDuration = 1500; + var delta = ratio - lastRatio; + if (delta < 0) delta = -delta; + var speed = $elem.innerWidth() * delta * 2; + if (speed > maxDuration) speed = maxDuration; + moveTo(ratio, speed, callback); + } + /** Returns the current ratio. */ function getValue() { return lastRatio; } - + $elem.append($handle); if (options.handleClass) { $handle.addClass(options.handleClass); @@ -108,9 +128,11 @@ $root.addClass('no-select'); $doc.on('mousemove', track); $doc.on('mouseup', end); + e.stopPropagation(); + e.preventDefault(); }); - return { setRatio: setTo, moveRatio: moveTo, getRatio: getValue, handle: $handle[0] }; + return { setRatio: setTo, moveRatio: moveTo, 'moveAuto': moveAuto, getRatio: getValue, handle: $handle[0] }; } function setup() { @@ -119,14 +141,15 @@ var $overlaySlider = $this.find('.imgdiff-ovr-slider').first(); var $opacitySlider = $this.find('.imgdiff-opa-slider').first(); var overlayAccess = rangeSlider($overlaySlider, {handleClass: 'imgdiff-ovr-handle'}); - rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); + var opacityAccess = rangeSlider($opacitySlider, {handleClass: 'imgdiff-opa-handle'}); var $img = $('#' + this.id.substr(this.id.indexOf('-')+1)); // Here we change opacity var $div = $img.parent(); // This controls visibility: here we change width. + var blinking = false; $overlaySlider.on('slider:pos', function(e, data) { var pos = $(data.handle).offset().left; var imgLeft = $img.offset().left; // Global - var imgW = $img.outerWidth(); + var imgW = $img.outerWidth(true); var imgOff = $img.position().left; // From left edge of $div if (pos <= imgLeft) { $div.width(0); @@ -136,10 +159,102 @@ $div.width(imgW + imgOff); } }); - $opacitySlider.on('slider:pos', function(e, data) { - if ($div.width() <= 0) overlayAccess.moveRatio(1.0, 500); // Make old image visible in a nice way - $img.css('opacity', 1.0 - data.ratio); + $overlaySlider.css('cursor', 'pointer'); + $overlaySlider.on('mousedown', function(e) { + var newRatio = (e.pageX - $overlaySlider.offset().left) / $overlaySlider.innerWidth(); + var oldRatio = overlayAccess.getRatio(); + if (newRatio !== oldRatio) { + overlayAccess.moveAuto(newRatio); + } }); + + var autoShowing = false; + $opacitySlider.on('slider:pos', function(e, data) { + if ($div.width() <= 0 && !blinking) { + // Make old image visible in a nice way, *then* adjust opacity + autoShowing = true; + overlayAccess.moveAuto(1.0, 500, function() { + $img.stop().animate( + {opacity: 1.0 - opacityAccess.getRatio()}, + {duration: 400, + complete: function () { + // In case the opacity handle was moved while we were trying to catch up + $img.css('opacity', 1.0 - opacityAccess.getRatio()); + autoShowing = false; + } + } + ); + }); + } else if (!autoShowing) { + $img.css('opacity', 1.0 - data.ratio); + } + }); + $opacitySlider.on('click', function(e) { + var newRatio = (e.pageX - $opacitySlider.offset().left) / $opacitySlider.innerWidth(); + var oldRatio = opacityAccess.getRatio(); + if (newRatio !== oldRatio) { + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, function() {opacityAccess.moveAuto(newRatio);}); // Make old image visible in a nice way + } else { + opacityAccess.moveAuto(newRatio) + } + } + e.preventDefault(); + }); + + // Blinking before and after images is a good way for the human eye to catch differences. + var $blinker = $this.find('.imgdiff-blink'); + var initialOpacity = null; + $blinker.on('click', function(e) { + if (blinking) { + window.clearTimeout(blinking); + $blinker.children('img').first().css('border', '1px solid transparent'); + opacityAccess.setRatio(initialOpacity); + blinking = null; + } else { + $blinker.children('img').first().css('border', '1px solid #AAA'); + initialOpacity = opacityAccess.getRatio(); + var currentOpacity = 1.0; + function blink() { + opacityAccess.setRatio(currentOpacity); + currentOpacity = 1.0 - currentOpacity; + // Keep frequeny below 2Hz (i.e., delay above 500ms) + blinking = window.setTimeout(blink, 600); + } + if ($div.width() <= 0) { + overlayAccess.moveRatio(1.0, 500, blink); + } else { + blink(); + } + } + e.preventDefault(); + }); + + // Subtracting before and after images is another good way to detect differences. Result will be + // black where identical. + if (typeof $img[0].style.mixBlendMode != 'undefined') { + // Feature test: does the browser support the mix-blend-mode CSS property from the Compositing + // and Blending Level 1 spec (http://dev.w3.org/fxtf/compositing-1/#mix-blend-mode )? + // As of 2014-11, only Firefox >= 32 and Safari >= 7.1 support this. Other browsers will have to + // make do with the blink comparator only. + var $sub = $this.find('.imgdiff-subtract'); + $sub.css('display', 'inline-block'); + $sub.on('click', function (e) { + var curr = $img.css('mix-blend-mode'); + if (curr != 'difference') { + curr = 'difference'; + $sub.children('img').first().css('border', '1px solid #AAA'); + if ($div.width() <= 0) overlayAccess.moveRatio(1.0, 500); + opacityAccess.setRatio(0); + } else { + curr = 'normal'; + $sub.children('img').first().css('border', '1px solid transparent'); + + } + $img.css('mix-blend-mode', curr); + e.preventDefault(); + }); + } }); } -- Gitblit v1.9.1