Merge branch 'featuer/image-on-diff' of https://github.com/team-lab/gitbucket into team-lab-featuer/image-on-diff

# Conflicts:
#	src/main/webapp/assets/common/js/gitbucket.js
This commit is contained in:
Naoki Takezoe
2015-05-25 02:06:14 +09:00
6 changed files with 391 additions and 13 deletions

View File

@@ -100,7 +100,8 @@ object JGitUtil {
def isDifferentFromAuthor: Boolean = authorName != committerName || authorEmailAddress != committerEmailAddress
}
case class DiffInfo(changeType: ChangeType, oldPath: String, newPath: String, oldContent: Option[String], newContent: Option[String])
case class DiffInfo(changeType: ChangeType, oldPath: String, newPath: String, oldContent: Option[String], newContent: Option[String],
oldIsImage: Boolean, newIsImage: Boolean, oldObjectId: Option[String], newObjectId: Option[String])
/**
* The file content data for the file content view of the repository viewer.
@@ -492,11 +493,13 @@ object JGitUtil {
treeWalk.addTree(revCommit.getTree)
val buffer = new scala.collection.mutable.ListBuffer[DiffInfo]()
while(treeWalk.next){
val newIsImage = FileUtil.isImage(treeWalk.getPathString)
buffer.append((if(!fetchContent){
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None)
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None, false, newIsImage, None, Option(treeWalk.getObjectId(0)).map(_.name))
} else {
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None,
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray))
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray),
false, newIsImage, None, Option(treeWalk.getObjectId(0)).map(_.name))
}))
}
(buffer.toList, None)
@@ -516,12 +519,15 @@ object JGitUtil {
import scala.collection.JavaConverters._
git.getRepository.getConfig.setString("diff", null, "renames", "copies")
git.diff.setNewTree(newTreeIter).setOldTree(oldTreeIter).call.asScala.map { diff =>
if(!fetchContent || FileUtil.isImage(diff.getOldPath) || FileUtil.isImage(diff.getNewPath)){
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None)
val oldIsImage = FileUtil.isImage(diff.getOldPath)
val newIsImage = FileUtil.isImage(diff.getNewPath)
if(!fetchContent || oldIsImage || newIsImage){
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None, oldIsImage, newIsImage, Option(diff.getOldId).map(_.name), Option(diff.getNewId).map(_.name))
} else {
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath,
JGitUtil.getContentFromId(git, diff.getOldId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray),
JGitUtil.getContentFromId(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray))
JGitUtil.getContentFromId(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray),
oldIsImage, newIsImage, Option(diff.getOldId).map(_.name), Option(diff.getNewId).map(_.name))
}
}.toList
}

View File

@@ -90,13 +90,32 @@
</tr>
<tr>
<td style="padding: 0;">
@if(diff.newContent != None || diff.oldContent != None){
@if(diff.oldObjectId == diff.newObjectId){
<div class="diff-same">File renamed without changes</div>
} else { @if(diff.newContent != None || diff.oldContent != None){
<div id="diffText-@i" class="diffText"></div>
<textarea id="newText-@i" style="display: none;" data-file-name="@diff.oldPath">@diff.newContent.getOrElse("")</textarea>
<textarea id="oldText-@i" style="display: none;" data-file-name="@diff.newPath">@diff.oldContent.getOrElse("")</textarea>
} else { @if(diff.newIsImage || diff.oldIsImage){
<div class="diff-image-render diff2up">
@if(oldCommitId.isDefined && diff.oldIsImage){
<div class="diff-image-frame diff-old"><img src="@url(repository)/blob/@oldCommitId.get/@diff.oldPath?raw=true" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
} else {
@if(diff.changeType != ChangeType.ADD){
Not supported
}
}
@if(newCommitId.isDefined && diff.newIsImage){
<div class="diff-image-frame diff-new"><img src="@url(repository)/blob/@newCommitId.get/@diff.newPath?raw=true" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
} else {
@if(diff.changeType != ChangeType.DELETE){
Not supported
}
}
</div>
} else {
Not supported
} } }
</td>
</tr>
</table>

View File

@@ -107,7 +107,7 @@ $(function(){
$(".no-results").hide();
for(var i=0;i < p.length;i++){
var row = template.clone();
row.find("a").attr("href",pathBase+"/"+p[i].string).html(string_score_hilight(p[i], '<b>'));
row.find("a").attr("href",pathBase+"/"+p[i].string).html(string_score_highlight(p[i], '<b>'));
if(cursor==i){
row.addClass("navigation-focus");
}

View File

@@ -1151,6 +1151,149 @@ table.diff tbody tr.not-diff:hover td{
white-space: nowrap;
letter-spacing: 0px;
}
.diff-same{
background: #DDD;
color: #BBB;
font-size: 16px;
padding: 20px;
font-weight: bold;
text-align: center;
}
/* ------- for imageDiff */
.diff-image-frame{
display: table-cell;
*float: left; /* for ie7 */
vertical-align: middle;
padding: 20px;
background-color: #eee;
}
.diff-image-frame.diff-old{
padding-right: 2px;
}
.diff-image-frame.diff-new{
padding-left: 2px;
}
.diff-image-frame .diff-meta{
margin-top: 12px;
color: #999;
font-family: Helvetica,arial,freesans,clean,sans-serif;
font-size: 12px;
}
.diff-image-frame.diff-old .diff-meta .diff{
color: #bd2c00;
}
.diff-image-frame.diff-new .diff-meta .diff{
color: #55a532;
}
.diff-image-frame img{
max-height: 410px;
max-width: 410px;
background: url(../images/checker.png);
}
.diff-image-render.diff2up{
width:100%;
text-align: center;
display: table;
}
.diff-image-frame.diff-new img{
border: 1px solid #55a532;
}
.diff-image-frame.diff-old img{
border: 1px solid #bd2c00;
}
.diff-image-stack{
position: relative;
background: #EEE;
padding-top: 20px;
}
.diff-image-stack .diff-old,
.diff-image-stack .diff-new{
position:absolute;
overflow: hidden;
margin:0 20px;
}
.diff-image-stack img {
max-width: none;
background: url(../images/checker.png);
}
.diff-image-stack .diff-new{
border: 1px solid #55a532;
background: #EEE;
}
.diff-image-stack .diff-old{
border: 1px solid #bd2c00;
}
.diff-swipe-handle{
position:absolute;
margin-left: 325px;
left: 100px;
}
.diff-silde-bar{
width: 200px;
position: absolute;
left: 325px;
margin: 6px 0 0 7px;
box-sizing: border-box;
border: 1px solid gray;
height: 8px;
}
.image-diff-tools{
text-align: center;
padding: 4px;
background: #f7f7f7;
}
.image-diff-tools{
font-family: 'Helvetica Neue', Helvetica, arial, freesans, clean, sans-serif;
font-size: 12px;
background: #f7f7f7;
margin: 0;
text-align: center;
}
.image-diff-tools li{
background: none;
display: inline;
cursor: pointer;
border-right: 1px solid #ccc;
padding: 0 5px;
position: relative;
color: #666;
}
.image-diff-tools li:last-child{
border-right: none;
}
.image-diff-tools li.active {
font-weight: bold;
}
.no-canvas .need-canvas{
display: none;
}
.diff-image-stack.swipe .diff-new{
border-right: 1px solid #888;
}
.diff-image-stack.swipe .diff-swipe-handle{
margin-left: 15px;
left: 410px;
}
.diff-image-stack.swipe .diff-silde-bar{
display: none;
}
.diff-image-stack.onion .diff-silde-bar{
background: -ms-linear-gradient(left, #bd2c00 0%,#55a532 100%); /* IE10+ */
background: linear-gradient(to right, #bd2c00 0%,#55a532 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#bd2c00', endColorstr='#55a532',GradientType=1 ); /* IE6-9 */
}
.diff-image-stack.blink .diff-silde-bar{
border-style: dotted;
background-image: linear-gradient(to right, #bd2c00, #bd2c00 50%, #55a532 50%, #55a532 100%);
background-size: 2px 2px;
}
.diff-image-stack.difference {
padding-bottom: 18px;
text-align: center;
}
/****************************************************************************/
/* Repository Settings */
/****************************************************************************/

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

View File

@@ -474,12 +474,12 @@ function string_score_sort(word, strings, limit){
return ret;
}
/**
* hilight by result.
* highlight by result.
* @param score {string:"string target string", matchingPositions:"Array[Interger] matchng positions"}
* @param hilight tag ex: '<b>'
* @return array of hilighted html elements.
* @param highlight tag ex: '<b>'
* @return array of highlighted html elements.
*/
function string_score_hilight(result, tag){
function string_score_highlight(result, tag){
var str = result.string, msp=0;
return hilight([], 0, result.matchingPositions[msp]);
function hilight(html, c, mpos){
@@ -493,3 +493,213 @@ function string_score_hilight(result, tag){
}
}
}
/****************************************************************************/
/* Diff */
/****************************************************************************/
// add naturalWidth and naturalHeight for ie 8
function setNatural(img) {
if(typeof img.naturalWidth == 'undefined'){
var tmp = new Image();
tmp.src = img.src;
img.naturalWidth = tmp.width;
img.naturalHeight = tmp.height;
}
}
/**
* onload handler
* @param img <img>
*/
function onLoadedDiffImages(img){
setNatural(img);
img = $(img);
img.show();
var tb = img.parents(".diff-image-render");
// Find images. If the image has not loaded yet, value is undefined.
var old = tb.find(".diff-old img.diff-image:visible")[0];
var neo = tb.find(".diff-new img.diff-image:visible")[0];
imageDiff.appendImageMeta(tb, old, neo);
if(old && neo){
imageDiff.createToolSelector(old, neo).appendTo(tb.parent());
}
}
var imageDiff ={
/** append image meta div after image nodes.
* @param tb <div class="diff-image-2up">
* @param old <img>||undefined
* @param neo <img>||undefined
*/
appendImageMeta:function(tb, old, neo){
old = old || {};
neo = neo || {};
tb.find(".diff-meta").remove();
// before loaded, image is not visible.
tb.find("img.diff-image:visible").each(function(){
var div = $('<p class="diff-meta"><b>W:</b><span class="w"></span> | <b>W:</b><span class="h"></span></p>');
div.find('.w').text(this.naturalWidth+"px").toggleClass("diff", old.naturalWidth != neo.naturalWidth);
div.find('.h').text(this.naturalHeight+"px").toggleClass("diff", old.naturalHeight != neo.naturalHeight);
div.appendTo(this.parentNode);
});
},
/** check this browser can use canvas tag.
*/
hasCanvasSupport:function(){
if(!this.hasCanvasSupport.hasOwnProperty('resultCache')){
this.hasCanvasSupport.resultCache = (typeof $('<canvas>')[0].getContext)=='function';
}
return this.hasCanvasSupport.resultCache;
},
/** create toolbar
* @param old <img>
* @param neo <img>
* @return jQuery(<ul class="image-diff-tools">)
*/
createToolSelector:function(old, neo){
var self = this;
return $('<ul class="image-diff-tools">'+
'<li data-mode="diff2up" class="active">2-up</li>'+
'<li data-mode="swipe">Swipe</li>'+
'<li data-mode="onion">Onion Skin</li>'+
'<li data-mode="difference" class="need-canvas">Difference</li>'+
'<li data-mode="blink">Blink</li>'+
'</ul>')
.toggleClass('no-canvas', !this.hasCanvasSupport())
.on('click', 'li', function(e){
var td = $(this).parents("td");
$(e.delegateTarget).find('li').each(function(){ $(this).toggleClass('active',this == e.target); });
var mode = $(e.target).data('mode');
td.find(".diff-image-render").hide();
// create div if not created yet
if(td.find(".diff-image-render."+mode).show().length===0){
self[mode](old, neo).insertBefore(e.delegateTarget).addClass("diff-image-render");
}
return false;
});
},
/** (private) calc size from images and css (const)
* @param old <img>
* @param neo <img>
*/
calcSizes:function(old, neo){
var maxWidth = 869 - 20 - 20 - 4; // set by css
var h = Math.min(Math.max(old.naturalHeight, neo.naturalHeight),maxWidth);
var w = Math.min(Math.max(old.naturalWidth, neo.naturalWidth),maxWidth);
var oldRate = Math.min(h/old.naturalHeight, w/old.naturalWidth);
var neoRate = Math.min(h/neo.naturalHeight, w/neo.naturalWidth);
var neoW = neo.naturalWidth*neoRate;
var neoH = neo.naturalHeight*neoRate;
var oldW = old.naturalWidth*oldRate;
var oldH = old.naturalHeight*oldRate;
var paddingLeft = (maxWidth/2)-Math.max(neoW,oldW)/2;
return {
height:Math.max(oldH, neoH),
width:w,
padding:paddingLeft,
paddingTop:20, // set by css
barWidth:200, // set by css
old:{ rate:oldRate, width:oldW, height:oldH },
neo:{ rate:neoRate, width:neoW, height:neoH }
};
},
/** (private) create div
* @param old <img>
* @param neo <img>
*/
stack:function(old, neo){
var size = this.calcSizes(old, neo);
var diffNew = $('<div class="diff-new">')
.append($("<img>").attr('src',neo.src).css({width:size.neo.width, height:size.neo.height}));
var diffOld = $('<div class="diff-old">')
.append($("<img>").attr('src',old.src).css({width:size.old.width, height:size.old.height}));
var handle = $('<span class="diff-swipe-handle icon icon-resize-horizontal"></span>')
.css({marginTop:size.height-5});
var bar = $('<hr class="diff-silde-bar">').css({top:size.height+size.paddingTop});
var div = $('<div class="diff-image-stack">')
.css({height:size.height+size.paddingTop, paddingLeft:size.padding})
.append(diffOld, diffNew, bar, handle);
return {
neo:diffNew,
old:diffOld,
size:size,
handle:handle,
bar:bar,
div:div,
/* add event listener 'on mousemove' */
onMoveHandleOnBar:function(callback){
div.on('mousemove',function(e){
var x = Math.max(Math.min((e.pageX - bar.offset().left), size.barWidth), 0);
handle.css({left:x});
callback(x, e);
});
}
};
},
/** create swipe box
* @param old <img>
* @param neo <img>
* @return jQuery(<div class="diff-image-stack swipe">)
*/
swipe:function(old, neo){
var stack = this.stack(old, neo);
function setX(x){
stack.neo.css({width:x});
stack.handle.css({left:x+stack.size.padding});
}
setX(stack.size.neo.width/2);
stack.div.on('mousemove',function(e){
setX(Math.max(Math.min(e.pageX - stack.neo.offset().left, stack.size.neo.width),0));
});
return stack.div.addClass('swipe');
},
/** create blink box
* @param old <img>
* @param neo <img>
* @return jQuery(<div class="diff-image-stack blink">)
*/
blink:function(old, neo){
var stack = this.stack(old, neo);
stack.onMoveHandleOnBar(function(x){
stack.neo.toggle(Math.floor(x) % 2 === 0);
});
return stack.div.addClass('blink');
},
/** create onion skin box
* @param old <img>
* @param neo <img>
* @return jQuery(<div class="diff-image-stack onion">)
*/
onion:function(old, neo){
var stack = this.stack(old, neo);
stack.neo.css({opacity:0.5});
stack.onMoveHandleOnBar(function(x){
stack.neo.css({opacity:x/stack.size.barWidth});
});
return stack.div.addClass('onion');
},
/** create difference box
* @param old <img>
* @param neo <img>
* @return jQuery(<div class="diff-image-stack difference">)
*/
difference:function(old, neo){
var size = this.calcSizes(old,neo);
var canvas = $('<canvas>').attr({width:size.width, height:size.height})[0];
var context = canvas.getContext('2d');
context.clearRect(0, 0, size.width, size.height);
context.drawImage(neo, 0, 0, size.neo.width, size.neo.height);
var neoData = context.getImageData(0, 0, size.neo.width, size.neo.height).data;
context.clearRect(0, 0, size.width, size.height);
context.drawImage(old, 0, 0, size.old.width, size.old.height);
var c = context.getImageData(0, 0, size.neo.width, size.neo.height);
var cData = c.data;
for (var i = cData.length -1; i>0; i --){
cData[i] = (i%4===3) ? Math.max(cData[i], neoData[i]) : Math.abs(cData[i] - neoData[i]);
}
context.putImageData(c, 0, 0);
return $('<div class="diff-image-stack difference">').append(canvas);
}
};