Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
gpt
large_projects
gitlabhq1
Commits
b54203f0
Commit
b54203f0
authored
7 years ago
by
Felipe Artur
Committed by
Jacob Schatz
7 years ago
Browse files
Options
Download
Email Patches
Plain Diff
Commenting on image diffs
parent
b4f9dc48
Changes
77
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
563 additions
and
41 deletions
+563
-41
app/assets/images/icon_image_comment.svg
app/assets/images/icon_image_comment.svg
+1
-0
app/assets/images/icon_image_comment@2x.svg
app/assets/images/icon_image_comment@2x.svg
+1
-0
app/assets/javascripts/commit.js
app/assets/javascripts/commit.js
+0
-12
app/assets/javascripts/commit/file.js
app/assets/javascripts/commit/file.js
+0
-14
app/assets/javascripts/commit/image_file.js
app/assets/javascripts/commit/image_file.js
+5
-8
app/assets/javascripts/diff.js
app/assets/javascripts/diff.js
+4
-1
app/assets/javascripts/diff_notes/components/jump_to_discussion.js
...s/javascripts/diff_notes/components/jump_to_discussion.js
+8
-1
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+0
-2
app/assets/javascripts/image_diff/helpers/badge_helper.js
app/assets/javascripts/image_diff/helpers/badge_helper.js
+38
-0
app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
...avascripts/image_diff/helpers/comment_indicator_helper.js
+58
-0
app/assets/javascripts/image_diff/helpers/dom_helper.js
app/assets/javascripts/image_diff/helpers/dom_helper.js
+44
-0
app/assets/javascripts/image_diff/helpers/index.js
app/assets/javascripts/image_diff/helpers/index.js
+25
-0
app/assets/javascripts/image_diff/helpers/utils_helper.js
app/assets/javascripts/image_diff/helpers/utils_helper.js
+95
-0
app/assets/javascripts/image_diff/image_badge.js
app/assets/javascripts/image_diff/image_badge.js
+23
-0
app/assets/javascripts/image_diff/image_diff.js
app/assets/javascripts/image_diff/image_diff.js
+143
-0
app/assets/javascripts/image_diff/init_discussion_tab.js
app/assets/javascripts/image_diff/init_discussion_tab.js
+12
-0
app/assets/javascripts/image_diff/replaced_image_diff.js
app/assets/javascripts/image_diff/replaced_image_diff.js
+92
-0
app/assets/javascripts/image_diff/view_types.js
app/assets/javascripts/image_diff/view_types.js
+9
-0
app/assets/javascripts/lib/utils/image_utility.js
app/assets/javascripts/lib/utils/image_utility.js
+5
-0
app/assets/javascripts/main.js
app/assets/javascripts/main.js
+0
-3
No files found.
app/assets/images/icon_image_comment.svg
0 → 100644
View file @
b54203f0
<svg
width=
"24"
height=
"30"
viewBox=
"0 0 24 30"
xmlns=
"http://www.w3.org/2000/svg"
><title>
cursor
</title><g
fill=
"none"
fill-rule=
"evenodd"
><path
d=
"M24 12.105c0 6.686-5.74 11.58-12 17.895C5.74 23.684 0 18.79 0 12.105 0 5.42 5.373 0 12 0s12 5.42 12 12.105z"
fill=
"#1F78D1"
fill-rule=
"nonzero"
/><path
d=
"M15.28 25.249c1.458-1.475 2.539-2.635 3.474-3.747 2.851-3.394 4.203-6.265 4.203-9.397 0-6.111-4.908-11.062-10.957-11.062-6.05 0-10.957 4.951-10.957 11.062 0 3.132 1.352 6.003 4.203 9.397.935 1.112 2.016 2.272 3.474 3.747.511.517 2.216 2.213 3.28 3.275 1.064-1.062 2.769-2.758 3.28-3.275z"
fill=
"#FFF"
/><path
d=
"M14.551 8.256A6.874 6.874 0 0 0 12 7.787c-.91 0-1.763.156-2.558.469-.79.308-1.42.725-1.888 1.252-.465.527-.697 1.096-.697 1.708 0 .5.159.977.476 1.433.321.45.772.841 1.352 1.172l.583.334-.181.643c-.107.407-.263.79-.469 1.152a6.604 6.604 0 0 0 1.842-1.145l.288-.254.381.04c.309.035.599.053.871.053.91 0 1.761-.154 2.551-.462.795-.312 1.424-.732 1.889-1.259.468-.526.703-1.096.703-1.707 0-.612-.235-1.181-.703-1.708-.465-.527-1.094-.944-1.889-1.252zm2.645.81c.536.656.804 1.373.804 2.15 0 .776-.268 1.495-.804 2.156-.535.656-1.263 1.176-2.183 1.56-.92.38-1.924.57-3.013.57a9.16 9.16 0 0 1-.971-.054 7.32 7.32 0 0 1-3.08 1.62 5.044 5.044 0 0 1-.764.148h-.033a.26.26 0 0 1-.181-.074.324.324 0 0 1-.107-.18v-.007c-.014-.018-.016-.045-.007-.08.014-.037.018-.059.014-.068 0-.009.01-.031.033-.067a.645.645 0 0 0 .04-.06 1.73 1.73 0 0 0 .047-.054l.054-.06a53.034 53.034 0 0 1 .435-.489c.049-.049.118-.136.207-.26.094-.126.168-.24.221-.342.054-.103.114-.235.181-.395.067-.161.125-.33.174-.51-.7-.397-1.254-.888-1.66-1.473A3.261 3.261 0 0 1 6 11.216c0-.777.268-1.494.804-2.15.535-.66 1.263-1.18 2.183-1.56.92-.384 1.924-.576 3.013-.576 1.09 0 2.094.192 3.013.576.92.38 1.648.9 2.183 1.56z"
fill=
"#1F78D1"
fill-rule=
"nonzero"
/></g></svg>
This diff is collapsed.
Click to expand it.
app/assets/images/icon_image_comment@2x.svg
0 → 100644
View file @
b54203f0
<svg
width=
"48"
height=
"60"
viewBox=
"0 0 48 60"
xmlns=
"http://www.w3.org/2000/svg"
><title>
cursor_2x
</title><g
fill=
"none"
fill-rule=
"evenodd"
><path
d=
"M48 24.21C48 37.583 36.522 47.369 24 60 11.478 47.368 0 37.582 0 24.21 0 10.84 10.745 0 24 0s24 10.84 24 24.21z"
fill=
"#1F78D1"
fill-rule=
"nonzero"
/><path
d=
"M30.56 50.497c2.915-2.95 5.078-5.268 6.947-7.493 5.703-6.788 8.406-12.53 8.406-18.793 0-12.223-9.815-22.124-21.913-22.124S2.087 11.988 2.087 24.211c0 6.263 2.703 12.005 8.406 18.793 1.87 2.225 4.032 4.544 6.947 7.493 1.022 1.035 4.432 4.426 6.56 6.55 2.128-2.124 5.538-5.515 6.56-6.55z"
fill=
"#FFF"
/><path
d=
"M29.103 16.512c-1.58-.625-3.282-.938-5.103-.938-1.821 0-3.527.313-5.116.938-1.58.616-2.84 1.45-3.777 2.504-.928 1.054-1.393 2.192-1.393 3.415 0 1 .317 1.956.951 2.866.643.902 1.545 1.684 2.706 2.344l1.165.67-.362 1.286a9.603 9.603 0 0 1-.937 2.303 13.208 13.208 0 0 0 3.683-2.29l.576-.509.763.08c.616.072 1.196.108 1.741.108 1.821 0 3.522-.308 5.103-.925 1.589-.625 2.848-1.464 3.776-2.517.938-1.054 1.407-2.192 1.407-3.416 0-1.223-.469-2.361-1.407-3.415-.928-1.053-2.187-1.888-3.776-2.504zm5.29 1.62c1.071 1.313 1.607 2.746 1.607 4.3 0 1.553-.536 2.99-1.607 4.312-1.072 1.312-2.527 2.353-4.366 3.12-1.84.76-3.848 1.139-6.027 1.139a18.32 18.32 0 0 1-1.942-.107c-1.768 1.562-3.821 2.643-6.16 3.24-.438.126-.947.224-1.527.295h-.067a.521.521 0 0 1-.362-.147.649.649 0 0 1-.214-.362v-.013c-.027-.036-.032-.09-.014-.16.027-.072.036-.117.027-.135 0-.017.022-.062.067-.133a1.29 1.29 0 0 0 .08-.121c.01-.009.04-.045.094-.107a106.068 106.068 0 0 1 .522-.59c.215-.232.367-.401.456-.508.098-.099.236-.273.415-.523.188-.25.335-.477.442-.683.107-.205.228-.468.362-.79.134-.321.25-.66.348-1.018-1.402-.794-2.51-1.777-3.322-2.946C12.402 25.025 12 23.77 12 22.43c0-1.553.536-2.986 1.607-4.299 1.072-1.321 2.527-2.361 4.366-3.12 1.84-.768 3.848-1.152 6.027-1.152 2.179 0 4.188.384 6.027 1.152 1.84.759 3.294 1.799 4.366 3.12z"
fill=
"#1F78D1"
fill-rule=
"nonzero"
/></g></svg>
This diff is collapsed.
Click to expand it.
app/assets/javascripts/commit.js
deleted
100644 → 0
View file @
b4f9dc48
/* eslint-disable func-names, space-before-function-paren, wrap-iife */
/* global CommitFile */
window
.
Commit
=
(
function
()
{
function
Commit
()
{
$
(
'
.files .diff-file
'
).
each
(
function
()
{
return
new
CommitFile
(
this
);
});
}
return
Commit
;
})();
This diff is collapsed.
Click to expand it.
app/assets/javascripts/commit/file.js
deleted
100644 → 0
View file @
b4f9dc48
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new */
/* global ImageFile */
(
function
()
{
this
.
CommitFile
=
(
function
()
{
function
CommitFile
(
file
)
{
if
(
$
(
'
.image
'
,
file
).
length
)
{
new
gl
.
ImageFile
(
file
);
}
}
return
CommitFile
;
})();
}).
call
(
window
);
This diff is collapsed.
Click to expand it.
app/assets/javascripts/commit/image_file.js
View file @
b54203f0
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
import
'
vendor/jquery.waitforimages
'
;
(
function
()
{
gl
.
ImageFile
=
(
function
()
{
var
prepareFrames
;
...
...
@@ -17,15 +19,10 @@
// Load two-up view after images are loaded
// so that we can display the correct width and height information
const
images
=
$
(
'
.two-up.view img
'
,
_this
.
file
);
let
loadedCount
=
0
;
images
.
on
(
'
load
'
,
()
=>
{
loadedCount
+=
1
;
const
$images
=
$
(
'
.two-up.view img
'
,
_this
.
file
);
if
(
loadedCount
===
images
.
length
)
{
_this
.
initView
(
'
two-up
'
);
}
$images
.
waitForImages
(
function
()
{
_this
.
initView
(
'
two-up
'
);
});
});
};
...
...
This diff is collapsed.
Click to expand it.
app/assets/javascripts/diff.js
View file @
b54203f0
...
...
@@ -3,6 +3,7 @@
import
'
./lib/utils/url_utility
'
;
import
FilesCommentButton
from
'
./files_comment_button
'
;
import
SingleFileDiff
from
'
./single_file_diff
'
;
import
imageDiffHelper
from
'
./image_diff/helpers/index
'
;
const
UNFOLD_COUNT
=
20
;
let
isBound
=
false
;
...
...
@@ -20,7 +21,9 @@ class Diff {
const
tab
=
document
.
getElementById
(
'
diffs
'
);
if
(
!
tab
||
(
tab
&&
tab
.
dataset
&&
tab
.
dataset
.
isLocked
!==
''
))
FilesCommentButton
.
init
(
$diffFile
);
$diffFile
.
each
((
index
,
file
)
=>
new
gl
.
ImageFile
(
file
));
const
firstFile
=
$
(
'
.files
'
).
first
().
get
(
0
);
const
canCreateNote
=
firstFile
&&
firstFile
.
hasAttribute
(
'
data-can-create-note
'
);
$diffFile
.
each
((
index
,
file
)
=>
imageDiffHelper
.
initImageDiff
(
file
,
canCreateNote
));
if
(
!
isBound
)
{
$
(
document
)
...
...
This diff is collapsed.
Click to expand it.
app/assets/javascripts/diff_notes/components/jump_to_discussion.js
View file @
b54203f0
...
...
@@ -171,7 +171,14 @@ const JumpToDiscussion = Vue.extend({
// When jumping between unresolved discussions on the diffs tab, we show them.
$target
.
closest
(
"
.content
"
).
show
();
$target
=
$target
.
closest
(
"
tr.notes_holder
"
);
const
$notesHolder
=
$target
.
closest
(
"
tr.notes_holder
"
);
// Image diff discussions does not use notes_holder
// so we should keep original $target value in those cases
if
(
$notesHolder
.
length
>
0
)
{
$target
=
$notesHolder
;
}
$target
.
show
();
// If we are on the diffs tab, we don't scroll to the discussion itself, but to
...
...
This diff is collapsed.
Click to expand it.
app/assets/javascripts/dispatcher.js
View file @
b54203f0
...
...
@@ -7,7 +7,6 @@
/* global IssuableForm */
/* global LabelsSelect */
/* global MilestoneSelect */
/* global Commit */
/* global CommitsList */
/* global NewBranchForm */
/* global NotificationsForm */
...
...
@@ -316,7 +315,6 @@ import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils';
new
gl
.
Activities
();
break
;
case
'
projects:commit:show
'
:
new
Commit
();
new
gl
.
Diff
();
new
ZenMode
();
shortcut_handler
=
new
ShortcutsNavigation
();
...
...
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/helpers/badge_helper.js
0 → 100644
View file @
b54203f0
export
function
createImageBadge
(
noteId
,
{
x
,
y
},
classNames
=
[])
{
const
buttonEl
=
document
.
createElement
(
'
button
'
);
const
classList
=
classNames
.
concat
([
'
js-image-badge
'
]);
classList
.
forEach
(
className
=>
buttonEl
.
classList
.
add
(
className
));
buttonEl
.
setAttribute
(
'
type
'
,
'
button
'
);
buttonEl
.
setAttribute
(
'
disabled
'
,
true
);
buttonEl
.
dataset
.
noteId
=
noteId
;
buttonEl
.
style
.
left
=
`
${
x
}
px`
;
buttonEl
.
style
.
top
=
`
${
y
}
px`
;
return
buttonEl
;
}
export
function
addImageBadge
(
containerEl
,
{
coordinate
,
badgeText
,
noteId
})
{
const
buttonEl
=
createImageBadge
(
noteId
,
coordinate
,
[
'
badge
'
]);
buttonEl
.
innerText
=
badgeText
;
containerEl
.
appendChild
(
buttonEl
);
}
export
function
addImageCommentBadge
(
containerEl
,
{
coordinate
,
noteId
})
{
const
buttonEl
=
createImageBadge
(
noteId
,
coordinate
,
[
'
image-comment-badge
'
,
'
inverted
'
]);
const
iconEl
=
document
.
createElement
(
'
i
'
);
iconEl
.
className
=
'
fa fa-comment-o
'
;
iconEl
.
setAttribute
(
'
aria-label
'
,
'
comment
'
);
buttonEl
.
appendChild
(
iconEl
);
containerEl
.
appendChild
(
buttonEl
);
}
export
function
addAvatarBadge
(
el
,
event
)
{
const
{
noteId
,
badgeNumber
}
=
event
.
detail
;
// Add badge to new comment
const
avatarBadgeEl
=
el
.
querySelector
(
`#
${
noteId
}
.badge`
);
avatarBadgeEl
.
innerText
=
badgeNumber
;
avatarBadgeEl
.
classList
.
remove
(
'
hidden
'
);
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
0 → 100644
View file @
b54203f0
export
function
addCommentIndicator
(
containerEl
,
{
x
,
y
})
{
const
buttonEl
=
document
.
createElement
(
'
button
'
);
buttonEl
.
classList
.
add
(
'
btn-transparent
'
);
buttonEl
.
classList
.
add
(
'
comment-indicator
'
);
buttonEl
.
setAttribute
(
'
type
'
,
'
button
'
);
buttonEl
.
style
.
left
=
`
${
x
}
px`
;
buttonEl
.
style
.
top
=
`
${
y
}
px`
;
buttonEl
.
innerHTML
=
gl
.
utils
.
spriteIcon
(
'
image-comment-dark
'
);
containerEl
.
appendChild
(
buttonEl
);
}
export
function
removeCommentIndicator
(
imageFrameEl
)
{
const
commentIndicatorEl
=
imageFrameEl
.
querySelector
(
'
.comment-indicator
'
);
const
imageEl
=
imageFrameEl
.
querySelector
(
'
img
'
);
const
willRemove
=
!!
commentIndicatorEl
;
let
meta
=
{};
if
(
willRemove
)
{
meta
=
{
x
:
parseInt
(
commentIndicatorEl
.
style
.
left
,
10
),
y
:
parseInt
(
commentIndicatorEl
.
style
.
top
,
10
),
image
:
{
width
:
imageEl
.
width
,
height
:
imageEl
.
height
,
},
};
commentIndicatorEl
.
remove
();
}
return
Object
.
assign
({},
meta
,
{
removed
:
willRemove
,
});
}
export
function
showCommentIndicator
(
imageFrameEl
,
coordinate
)
{
const
{
x
,
y
}
=
coordinate
;
const
commentIndicatorEl
=
imageFrameEl
.
querySelector
(
'
.comment-indicator
'
);
if
(
commentIndicatorEl
)
{
commentIndicatorEl
.
style
.
left
=
`
${
x
}
px`
;
commentIndicatorEl
.
style
.
top
=
`
${
y
}
px`
;
}
else
{
addCommentIndicator
(
imageFrameEl
,
coordinate
);
}
}
export
function
commentIndicatorOnClick
(
event
)
{
// Prevent from triggering onAddImageDiffNote in notes.js
event
.
stopPropagation
();
const
buttonEl
=
event
.
currentTarget
;
const
diffViewerEl
=
buttonEl
.
closest
(
'
.diff-viewer
'
);
const
textareaEl
=
diffViewerEl
.
querySelector
(
'
.note-container .note-textarea
'
);
textareaEl
.
focus
();
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/helpers/dom_helper.js
0 → 100644
View file @
b54203f0
export
function
setPositionDataAttribute
(
el
,
options
)
{
// Update position data attribute so that the
// new comment form can use this data for ajax request
const
{
x
,
y
,
width
,
height
}
=
options
;
const
position
=
el
.
dataset
.
position
;
const
positionObject
=
Object
.
assign
({},
JSON
.
parse
(
position
),
{
x
,
y
,
width
,
height
,
});
el
.
setAttribute
(
'
data-position
'
,
JSON
.
stringify
(
positionObject
));
}
export
function
updateDiscussionAvatarBadgeNumber
(
discussionEl
,
newBadgeNumber
)
{
const
avatarBadgeEl
=
discussionEl
.
querySelector
(
'
.image-diff-avatar-link .badge
'
);
avatarBadgeEl
.
innerText
=
newBadgeNumber
;
}
export
function
updateDiscussionBadgeNumber
(
discussionEl
,
newBadgeNumber
)
{
const
discussionBadgeEl
=
discussionEl
.
querySelector
(
'
.badge
'
);
discussionBadgeEl
.
innerText
=
newBadgeNumber
;
}
export
function
toggleCollapsed
(
event
)
{
const
toggleButtonEl
=
event
.
currentTarget
;
const
discussionNotesEl
=
toggleButtonEl
.
closest
(
'
.discussion-notes
'
);
const
formEl
=
discussionNotesEl
.
querySelector
(
'
.discussion-form
'
);
const
isCollapsed
=
discussionNotesEl
.
classList
.
contains
(
'
collapsed
'
);
if
(
isCollapsed
)
{
discussionNotesEl
.
classList
.
remove
(
'
collapsed
'
);
}
else
{
discussionNotesEl
.
classList
.
add
(
'
collapsed
'
);
}
// Override the inline display style set in notes.js
if
(
formEl
&&
!
isCollapsed
)
{
formEl
.
style
.
display
=
'
none
'
;
}
else
if
(
formEl
&&
isCollapsed
)
{
formEl
.
style
.
display
=
'
block
'
;
}
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/helpers/index.js
0 → 100644
View file @
b54203f0
import
*
as
badgeHelper
from
'
./badge_helper
'
;
import
*
as
commentIndicatorHelper
from
'
./comment_indicator_helper
'
;
import
*
as
domHelper
from
'
./dom_helper
'
;
import
*
as
utilsHelper
from
'
./utils_helper
'
;
export
default
{
addCommentIndicator
:
commentIndicatorHelper
.
addCommentIndicator
,
removeCommentIndicator
:
commentIndicatorHelper
.
removeCommentIndicator
,
showCommentIndicator
:
commentIndicatorHelper
.
showCommentIndicator
,
commentIndicatorOnClick
:
commentIndicatorHelper
.
commentIndicatorOnClick
,
addImageBadge
:
badgeHelper
.
addImageBadge
,
addImageCommentBadge
:
badgeHelper
.
addImageCommentBadge
,
addAvatarBadge
:
badgeHelper
.
addAvatarBadge
,
setPositionDataAttribute
:
domHelper
.
setPositionDataAttribute
,
updateDiscussionAvatarBadgeNumber
:
domHelper
.
updateDiscussionAvatarBadgeNumber
,
updateDiscussionBadgeNumber
:
domHelper
.
updateDiscussionBadgeNumber
,
toggleCollapsed
:
domHelper
.
toggleCollapsed
,
resizeCoordinatesToImageElement
:
utilsHelper
.
resizeCoordinatesToImageElement
,
generateBadgeFromDiscussionDOM
:
utilsHelper
.
generateBadgeFromDiscussionDOM
,
getTargetSelection
:
utilsHelper
.
getTargetSelection
,
initImageDiff
:
utilsHelper
.
initImageDiff
,
};
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/helpers/utils_helper.js
0 → 100644
View file @
b54203f0
import
ImageBadge
from
'
../image_badge
'
;
import
ImageDiff
from
'
../image_diff
'
;
import
ReplacedImageDiff
from
'
../replaced_image_diff
'
;
import
'
../../commit/image_file
'
;
export
function
resizeCoordinatesToImageElement
(
imageEl
,
meta
)
{
const
{
x
,
y
,
width
,
height
}
=
meta
;
const
imageWidth
=
imageEl
.
width
;
const
imageHeight
=
imageEl
.
height
;
const
widthRatio
=
imageWidth
/
width
;
const
heightRatio
=
imageHeight
/
height
;
return
{
x
:
Math
.
round
(
x
*
widthRatio
),
y
:
Math
.
round
(
y
*
heightRatio
),
width
:
imageWidth
,
height
:
imageHeight
,
};
}
export
function
generateBadgeFromDiscussionDOM
(
imageFrameEl
,
discussionEl
)
{
const
position
=
JSON
.
parse
(
discussionEl
.
dataset
.
position
);
const
firstNoteEl
=
discussionEl
.
querySelector
(
'
.note
'
);
const
badge
=
new
ImageBadge
({
actual
:
position
,
imageEl
:
imageFrameEl
.
querySelector
(
'
img
'
),
noteId
:
firstNoteEl
.
id
,
discussionId
:
discussionEl
.
dataset
.
discussionId
,
});
return
badge
;
}
export
function
getTargetSelection
(
event
)
{
const
containerEl
=
event
.
currentTarget
;
const
imageEl
=
containerEl
.
querySelector
(
'
img
'
);
const
x
=
event
.
offsetX
;
const
y
=
event
.
offsetY
;
const
width
=
imageEl
.
width
;
const
height
=
imageEl
.
height
;
const
actualWidth
=
imageEl
.
naturalWidth
;
const
actualHeight
=
imageEl
.
naturalHeight
;
const
widthRatio
=
actualWidth
/
width
;
const
heightRatio
=
actualHeight
/
height
;
// Browser will include the frame as a clickable target,
// which would result in potential 1px out of bounds value
// This bound the coordinates to inside the frame
const
normalizedX
=
Math
.
max
(
0
,
x
)
&&
Math
.
min
(
x
,
width
);
const
normalizedY
=
Math
.
max
(
0
,
y
)
&&
Math
.
min
(
y
,
height
);
return
{
browser
:
{
x
:
normalizedX
,
y
:
normalizedY
,
width
,
height
,
},
actual
:
{
// Round x, y so that we don't need to deal with decimals
x
:
Math
.
round
(
normalizedX
*
widthRatio
),
y
:
Math
.
round
(
normalizedY
*
heightRatio
),
width
:
actualWidth
,
height
:
actualHeight
,
},
};
}
export
function
initImageDiff
(
fileEl
,
canCreateNote
,
renderCommentBadge
)
{
const
options
=
{
canCreateNote
,
renderCommentBadge
,
};
let
diff
;
// ImageFile needs to be invoked before initImageDiff so that badges
// can mount to the correct location
new
gl
.
ImageFile
(
fileEl
);
// eslint-disable-line no-new
if
(
fileEl
.
querySelector
(
'
.diff-file .js-single-image
'
))
{
diff
=
new
ImageDiff
(
fileEl
,
options
);
diff
.
init
();
}
else
if
(
fileEl
.
querySelector
(
'
.diff-file .js-replaced-image
'
))
{
diff
=
new
ReplacedImageDiff
(
fileEl
,
options
);
diff
.
init
();
}
return
diff
;
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/image_badge.js
0 → 100644
View file @
b54203f0
import
imageDiffHelper
from
'
./helpers/index
'
;
const
defaultMeta
=
{
x
:
0
,
y
:
0
,
width
:
0
,
height
:
0
,
};
export
default
class
ImageBadge
{
constructor
(
options
)
{
const
{
noteId
,
discussionId
}
=
options
;
this
.
actual
=
options
.
actual
||
defaultMeta
;
this
.
browser
=
options
.
browser
||
defaultMeta
;
this
.
noteId
=
noteId
;
this
.
discussionId
=
discussionId
;
if
(
options
.
imageEl
&&
!
options
.
browser
)
{
this
.
browser
=
imageDiffHelper
.
resizeCoordinatesToImageElement
(
options
.
imageEl
,
this
.
actual
);
}
}
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/image_diff.js
0 → 100644
View file @
b54203f0
import
imageDiffHelper
from
'
./helpers/index
'
;
import
ImageBadge
from
'
./image_badge
'
;
import
{
isImageLoaded
}
from
'
../lib/utils/image_utility
'
;
export
default
class
ImageDiff
{
constructor
(
el
,
options
)
{
this
.
el
=
el
;
this
.
canCreateNote
=
!!
(
options
&&
options
.
canCreateNote
);
this
.
renderCommentBadge
=
!!
(
options
&&
options
.
renderCommentBadge
);
this
.
$noteContainer
=
$
(
'
.note-container
'
,
this
.
el
);
this
.
imageBadges
=
[];
}
init
()
{
this
.
imageFrameEl
=
this
.
el
.
querySelector
(
'
.diff-file .js-image-frame
'
);
this
.
imageEl
=
this
.
imageFrameEl
.
querySelector
(
'
img
'
);
this
.
bindEvents
();
}
bindEvents
()
{
this
.
imageClickedWrapper
=
this
.
imageClicked
.
bind
(
this
);
this
.
imageBlurredWrapper
=
imageDiffHelper
.
removeCommentIndicator
.
bind
(
null
,
this
.
imageFrameEl
);
this
.
addBadgeWrapper
=
this
.
addBadge
.
bind
(
this
);
this
.
removeBadgeWrapper
=
this
.
removeBadge
.
bind
(
this
);
this
.
renderBadgesWrapper
=
this
.
renderBadges
.
bind
(
this
);
// Render badges
if
(
isImageLoaded
(
this
.
imageEl
))
{
this
.
renderBadges
();
}
else
{
this
.
imageEl
.
addEventListener
(
'
load
'
,
this
.
renderBadgesWrapper
);
}
// jquery makes the event delegation here much simpler
this
.
$noteContainer
.
on
(
'
click
'
,
'
.js-diff-notes-toggle
'
,
imageDiffHelper
.
toggleCollapsed
);
$
(
this
.
el
).
on
(
'
click
'
,
'
.comment-indicator
'
,
imageDiffHelper
.
commentIndicatorOnClick
);
if
(
this
.
canCreateNote
)
{
this
.
el
.
addEventListener
(
'
click.imageDiff
'
,
this
.
imageClickedWrapper
);
this
.
el
.
addEventListener
(
'
blur.imageDiff
'
,
this
.
imageBlurredWrapper
);
this
.
el
.
addEventListener
(
'
addBadge.imageDiff
'
,
this
.
addBadgeWrapper
);
this
.
el
.
addEventListener
(
'
removeBadge.imageDiff
'
,
this
.
removeBadgeWrapper
);
}
}
imageClicked
(
event
)
{
const
customEvent
=
event
.
detail
;
const
selection
=
imageDiffHelper
.
getTargetSelection
(
customEvent
);
const
el
=
customEvent
.
currentTarget
;
imageDiffHelper
.
setPositionDataAttribute
(
el
,
selection
.
actual
);
imageDiffHelper
.
showCommentIndicator
(
this
.
imageFrameEl
,
selection
.
browser
);
}
renderBadges
()
{
const
discussionsEls
=
this
.
el
.
querySelectorAll
(
'
.note-container .discussion-notes .notes
'
);
[...
discussionsEls
].
forEach
(
this
.
renderBadge
.
bind
(
this
));
}
renderBadge
(
discussionEl
,
index
)
{
const
imageBadge
=
imageDiffHelper
.
generateBadgeFromDiscussionDOM
(
this
.
imageFrameEl
,
discussionEl
);
this
.
imageBadges
.
push
(
imageBadge
);
const
options
=
{
coordinate
:
imageBadge
.
browser
,
noteId
:
imageBadge
.
noteId
,
};
if
(
this
.
renderCommentBadge
)
{
imageDiffHelper
.
addImageCommentBadge
(
this
.
imageFrameEl
,
options
);
}
else
{
const
numberBadgeOptions
=
Object
.
assign
({},
options
,
{
badgeText
:
index
+
1
,
});
imageDiffHelper
.
addImageBadge
(
this
.
imageFrameEl
,
numberBadgeOptions
);
}
}
addBadge
(
event
)
{
const
{
x
,
y
,
width
,
height
,
noteId
,
discussionId
}
=
event
.
detail
;
const
badgeText
=
this
.
imageBadges
.
length
+
1
;
const
imageBadge
=
new
ImageBadge
({
actual
:
{
x
,
y
,
width
,
height
,
},
imageEl
:
this
.
imageFrameEl
.
querySelector
(
'
img
'
),
noteId
,
discussionId
,
});
this
.
imageBadges
.
push
(
imageBadge
);
imageDiffHelper
.
addImageBadge
(
this
.
imageFrameEl
,
{
coordinate
:
imageBadge
.
browser
,
badgeText
,
noteId
,
});
imageDiffHelper
.
addAvatarBadge
(
this
.
el
,
{
detail
:
{
noteId
,
badgeNumber
:
badgeText
,
},
});
const
discussionEl
=
this
.
el
.
querySelector
(
`#discussion_
${
discussionId
}
`
);
imageDiffHelper
.
updateDiscussionBadgeNumber
(
discussionEl
,
badgeText
);
}
removeBadge
(
event
)
{
const
{
badgeNumber
}
=
event
.
detail
;
const
indexToRemove
=
badgeNumber
-
1
;
const
imageBadgeEls
=
this
.
imageFrameEl
.
querySelectorAll
(
'
.badge
'
);
if
(
this
.
imageBadges
.
length
!==
badgeNumber
)
{
// Cascade badges count numbers for (avatar badges + image badges)
this
.
imageBadges
.
forEach
((
badge
,
index
)
=>
{
if
(
index
>
indexToRemove
)
{
const
{
discussionId
}
=
badge
;
const
updatedBadgeNumber
=
index
;
const
discussionEl
=
this
.
el
.
querySelector
(
`#discussion_
${
discussionId
}
`
);
imageBadgeEls
[
index
].
innerText
=
updatedBadgeNumber
;
imageDiffHelper
.
updateDiscussionBadgeNumber
(
discussionEl
,
updatedBadgeNumber
);
imageDiffHelper
.
updateDiscussionAvatarBadgeNumber
(
discussionEl
,
updatedBadgeNumber
);
}
});
}
this
.
imageBadges
.
splice
(
indexToRemove
,
1
);
const
imageBadgeEl
=
imageBadgeEls
[
indexToRemove
];
imageBadgeEl
.
remove
();
}
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/init_discussion_tab.js
0 → 100644
View file @
b54203f0
import
imageDiffHelper
from
'
./helpers/index
'
;
export
default
()
=>
{
// Always pass can-create-note as false because a user
// cannot place new badge markers on discussion tab
const
canCreateNote
=
false
;
const
renderCommentBadge
=
true
;
const
diffFileEls
=
document
.
querySelectorAll
(
'
.timeline-content .diff-file.js-image-file
'
);
[...
diffFileEls
].
forEach
(
diffFileEl
=>
imageDiffHelper
.
initImageDiff
(
diffFileEl
,
canCreateNote
,
renderCommentBadge
));
};
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/replaced_image_diff.js
0 → 100644
View file @
b54203f0
import
imageDiffHelper
from
'
./helpers/index
'
;
import
{
viewTypes
,
isValidViewType
}
from
'
./view_types
'
;
import
ImageDiff
from
'
./image_diff
'
;
export
default
class
ReplacedImageDiff
extends
ImageDiff
{
init
(
defaultViewType
=
viewTypes
.
TWO_UP
)
{
this
.
imageFrameEls
=
{
[
viewTypes
.
TWO_UP
]:
this
.
el
.
querySelector
(
'
.two-up .js-image-frame
'
),
[
viewTypes
.
SWIPE
]:
this
.
el
.
querySelector
(
'
.swipe .js-image-frame
'
),
[
viewTypes
.
ONION_SKIN
]:
this
.
el
.
querySelector
(
'
.onion-skin .js-image-frame
'
),
};
const
viewModesEl
=
this
.
el
.
querySelector
(
'
.view-modes-menu
'
);
this
.
viewModesEls
=
{
[
viewTypes
.
TWO_UP
]:
viewModesEl
.
querySelector
(
'
.two-up
'
),
[
viewTypes
.
SWIPE
]:
viewModesEl
.
querySelector
(
'
.swipe
'
),
[
viewTypes
.
ONION_SKIN
]:
viewModesEl
.
querySelector
(
'
.onion-skin
'
),
};
this
.
currentView
=
defaultViewType
;
this
.
generateImageEls
();
this
.
bindEvents
();
}
generateImageEls
()
{
this
.
imageEls
=
{};
const
viewTypeNames
=
Object
.
getOwnPropertyNames
(
viewTypes
);
viewTypeNames
.
forEach
((
viewType
)
=>
{
this
.
imageEls
[
viewType
]
=
this
.
imageFrameEls
[
viewType
].
querySelector
(
'
img
'
);
});
}
bindEvents
()
{
super
.
bindEvents
();
this
.
changeToViewTwoUp
=
this
.
changeView
.
bind
(
this
,
viewTypes
.
TWO_UP
);
this
.
changeToViewSwipe
=
this
.
changeView
.
bind
(
this
,
viewTypes
.
SWIPE
);
this
.
changeToViewOnionSkin
=
this
.
changeView
.
bind
(
this
,
viewTypes
.
ONION_SKIN
);
this
.
viewModesEls
[
viewTypes
.
TWO_UP
].
addEventListener
(
'
click
'
,
this
.
changeToViewTwoUp
);
this
.
viewModesEls
[
viewTypes
.
SWIPE
].
addEventListener
(
'
click
'
,
this
.
changeToViewSwipe
);
this
.
viewModesEls
[
viewTypes
.
ONION_SKIN
].
addEventListener
(
'
click
'
,
this
.
changeToViewOnionSkin
);
}
get
imageEl
()
{
return
this
.
imageEls
[
this
.
currentView
];
}
get
imageFrameEl
()
{
return
this
.
imageFrameEls
[
this
.
currentView
];
}
changeView
(
newView
)
{
if
(
!
isValidViewType
(
newView
))
{
return
;
}
const
indicator
=
imageDiffHelper
.
removeCommentIndicator
(
this
.
imageFrameEl
);
this
.
currentView
=
newView
;
// Clear existing badges on new view
const
existingBadges
=
this
.
imageFrameEl
.
querySelectorAll
(
'
.badge
'
);
[...
existingBadges
].
map
(
badge
=>
badge
.
remove
());
// Remove existing references to old view image badges
this
.
imageBadges
=
[];
// Image_file.js has a fade animation of 200ms for loading the view
// Need to wait an additional 250ms for the images to be displayed
// on window in order to re-normalize their dimensions
setTimeout
(
this
.
renderNewView
.
bind
(
this
,
indicator
),
250
);
}
renderNewView
(
indicator
)
{
// Generate badge coordinates on new view
this
.
renderBadges
();
// Re-render indicator in new view
if
(
indicator
.
removed
)
{
const
normalizedIndicator
=
imageDiffHelper
.
resizeCoordinatesToImageElement
(
this
.
imageEl
,
{
x
:
indicator
.
x
,
y
:
indicator
.
y
,
width
:
indicator
.
image
.
width
,
height
:
indicator
.
image
.
height
,
});
imageDiffHelper
.
showCommentIndicator
(
this
.
imageFrameEl
,
normalizedIndicator
);
}
}
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/image_diff/view_types.js
0 → 100644
View file @
b54203f0
export
const
viewTypes
=
{
TWO_UP
:
'
TWO_UP
'
,
SWIPE
:
'
SWIPE
'
,
ONION_SKIN
:
'
ONION_SKIN
'
,
};
export
function
isValidViewType
(
validate
)
{
return
!!
Object
.
getOwnPropertyNames
(
viewTypes
).
find
(
viewType
=>
viewType
===
validate
);
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/lib/utils/image_utility.js
0 → 100644
View file @
b54203f0
/* eslint-disable import/prefer-default-export */
export
function
isImageLoaded
(
element
)
{
return
element
.
complete
&&
element
.
naturalHeight
!==
0
;
}
This diff is collapsed.
Click to expand it.
app/assets/javascripts/main.js
View file @
b54203f0
...
...
@@ -35,8 +35,6 @@ import './shortcuts_network';
import
'
./templates/issuable_template_selector
'
;
import
'
./templates/issuable_template_selectors
'
;
// commit
import
'
./commit/file
'
;
import
'
./commit/image_file
'
;
// lib/utils
...
...
@@ -70,7 +68,6 @@ import './build';
import
'
./build_artifacts
'
;
import
'
./build_variables
'
;
import
'
./ci_lint_editor
'
;
import
'
./commit
'
;
import
'
./commits
'
;
import
'
./compare
'
;
import
'
./compare_autocomplete
'
;
...
...
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment