Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
399b49e8
Commit
399b49e8
authored
Nov 11, 2019
by
Paul Slaughter
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Merge branch 'mfluharty-clickable-links-in-file-view' into 'master'"
This reverts merge request !18305
parent
4753e8a8
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
68 additions
and
129 deletions
+68
-129
app/assets/javascripts/blob/blob_utils.js
app/assets/javascripts/blob/blob_utils.js
+0
-5
app/assets/javascripts/blob/viewer/index.js
app/assets/javascripts/blob/viewer/index.js
+21
-37
app/assets/javascripts/blob_edit/edit_blob.js
app/assets/javascripts/blob_edit/edit_blob.js
+1
-19
app/assets/stylesheets/framework/files.scss
app/assets/stylesheets/framework/files.scss
+0
-11
app/assets/stylesheets/highlight/common.scss
app/assets/stylesheets/highlight/common.scss
+0
-9
app/assets/stylesheets/highlight/themes/dark.scss
app/assets/stylesheets/highlight/themes/dark.scss
+5
-0
app/assets/stylesheets/highlight/themes/monokai.scss
app/assets/stylesheets/highlight/themes/monokai.scss
+5
-0
app/assets/stylesheets/highlight/themes/none.scss
app/assets/stylesheets/highlight/themes/none.scss
+6
-0
app/assets/stylesheets/highlight/themes/solarized-dark.scss
app/assets/stylesheets/highlight/themes/solarized-dark.scss
+5
-0
app/assets/stylesheets/highlight/themes/solarized-light.scss
app/assets/stylesheets/highlight/themes/solarized-light.scss
+5
-0
app/assets/stylesheets/highlight/white_base.scss
app/assets/stylesheets/highlight/white_base.scss
+5
-0
app/views/shared/_file_highlight.html.haml
app/views/shared/_file_highlight.html.haml
+1
-1
changelogs/unreleased/mfluharty-clickable-links-in-file-view.yml
...ogs/unreleased/mfluharty-clickable-links-in-file-view.yml
+0
-5
spec/features/projects/blobs/edit_spec.rb
spec/features/projects/blobs/edit_spec.rb
+0
-7
spec/javascripts/blob/viewer/index_spec.js
spec/javascripts/blob/viewer/index_spec.js
+14
-35
No files found.
app/assets/javascripts/blob/blob_utils.js
deleted
100644 → 0
View file @
4753e8a8
// capture anything starting with http:// or https://
// up until a disallowed character or whitespace
export
const
blobLinkRegex
=
/https
?
:
\/\/[^
"<>
\\
^`{|}
\s]
+/g
;
export
default
{
blobLinkRegex
};
app/assets/javascripts/blob/viewer/index.js
View file @
399b49e8
...
...
@@ -4,10 +4,6 @@ import Flash from '../../flash';
import
{
handleLocationHash
}
from
'
../../lib/utils/common_utils
'
;
import
axios
from
'
../../lib/utils/axios_utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
blobLinkRegex
}
from
'
~/blob/blob_utils
'
;
const
SIMPLE_VIEWER_NAME
=
'
simple
'
;
const
RICH_VIEWER_NAME
=
'
rich
'
;
export
default
class
BlobViewer
{
constructor
()
{
...
...
@@ -25,7 +21,7 @@ export default class BlobViewer {
}
static
initRichViewer
()
{
const
viewer
=
document
.
querySelector
(
`.blob-viewer[data-type="
${
RICH_VIEWER_NAME
}
"]`
);
const
viewer
=
document
.
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
if
(
!
viewer
||
!
viewer
.
dataset
.
richType
)
return
;
const
initViewer
=
promise
=>
...
...
@@ -65,12 +61,8 @@ export default class BlobViewer {
this
.
switcherBtns
=
document
.
querySelectorAll
(
'
.js-blob-viewer-switch-btn
'
);
this
.
copySourceBtn
=
document
.
querySelector
(
'
.js-copy-blob-source-btn
'
);
this
.
simpleViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
`.blob-viewer[data-type="
${
SIMPLE_VIEWER_NAME
}
"]`
,
);
this
.
richViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
`.blob-viewer[data-type="
${
RICH_VIEWER_NAME
}
"]`
,
);
this
.
simpleViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="simple"]
'
);
this
.
richViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer[data-type="rich"]
'
);
this
.
initBindings
();
...
...
@@ -79,10 +71,10 @@ export default class BlobViewer {
switchToInitialViewer
()
{
const
initialViewer
=
this
.
$fileHolder
[
0
].
querySelector
(
'
.blob-viewer:not(.hidden)
'
);
let
initialViewerName
=
initialViewer
.
dataset
.
type
;
let
initialViewerName
=
initialViewer
.
getAttribute
(
'
data-type
'
)
;
if
(
this
.
switcher
&&
window
.
location
.
hash
.
indexOf
(
'
#L
'
)
===
0
)
{
initialViewerName
=
SIMPLE_VIEWER_NAME
;
initialViewerName
=
'
simple
'
;
}
this
.
switchToViewer
(
initialViewerName
);
...
...
@@ -99,41 +91,35 @@ export default class BlobViewer {
this
.
copySourceBtn
.
addEventListener
(
'
click
'
,
()
=>
{
if
(
this
.
copySourceBtn
.
classList
.
contains
(
'
disabled
'
))
return
this
.
copySourceBtn
.
blur
();
return
this
.
switchToViewer
(
SIMPLE_VIEWER_NAME
);
return
this
.
switchToViewer
(
'
simple
'
);
});
}
}
static
linkifyURLs
(
viewer
)
{
if
(
viewer
.
dataset
.
linkified
)
return
;
document
.
querySelectorAll
(
'
.js-blob-content .code .line
'
).
forEach
(
line
=>
{
// eslint-disable-next-line no-param-reassign
line
.
innerHTML
=
line
.
innerHTML
.
replace
(
blobLinkRegex
,
'
<a href="$&">$&</a>
'
);
});
// eslint-disable-next-line no-param-reassign
viewer
.
dataset
.
linkified
=
true
;
}
switchViewHandler
(
e
)
{
const
target
=
e
.
currentTarget
;
e
.
preventDefault
();
this
.
switchToViewer
(
target
.
dataset
.
viewer
);
this
.
switchToViewer
(
target
.
getAttribute
(
'
data-viewer
'
)
);
}
toggleCopyButtonState
()
{
if
(
!
this
.
copySourceBtn
)
return
;
if
(
this
.
simpleViewer
.
dataset
.
loaded
)
{
this
.
copySourceBtn
.
dataset
.
title
=
__
(
'
Copy file contents
'
);
if
(
this
.
simpleViewer
.
getAttribute
(
'
data-loaded
'
)
)
{
this
.
copySourceBtn
.
setAttribute
(
'
title
'
,
__
(
'
Copy file contents
'
)
);
this
.
copySourceBtn
.
classList
.
remove
(
'
disabled
'
);
}
else
if
(
this
.
activeViewer
===
this
.
simpleViewer
)
{
this
.
copySourceBtn
.
dataset
.
title
=
__
(
'
Wait for the file to load to copy its contents
'
);
this
.
copySourceBtn
.
setAttribute
(
'
title
'
,
__
(
'
Wait for the file to load to copy its contents
'
),
);
this
.
copySourceBtn
.
classList
.
add
(
'
disabled
'
);
}
else
{
this
.
copySourceBtn
.
dataset
.
title
=
__
(
'
Switch to the source to copy the file contents
'
);
this
.
copySourceBtn
.
setAttribute
(
'
title
'
,
__
(
'
Switch to the source to copy the file contents
'
),
);
this
.
copySourceBtn
.
classList
.
add
(
'
disabled
'
);
}
...
...
@@ -173,8 +159,6 @@ export default class BlobViewer {
this
.
$fileHolder
.
trigger
(
'
highlight:line
'
);
handleLocationHash
();
if
(
name
===
SIMPLE_VIEWER_NAME
)
BlobViewer
.
linkifyURLs
(
viewer
);
this
.
toggleCopyButtonState
();
})
.
catch
(()
=>
new
Flash
(
__
(
'
Error loading viewer
'
)));
...
...
@@ -182,17 +166,17 @@ export default class BlobViewer {
static
loadViewer
(
viewerParam
)
{
const
viewer
=
viewerParam
;
const
{
url
,
loaded
,
loading
}
=
viewer
.
dataset
;
const
url
=
viewer
.
getAttribute
(
'
data-url
'
)
;
if
(
!
url
||
loaded
||
loading
)
{
if
(
!
url
||
viewer
.
getAttribute
(
'
data-loaded
'
)
||
viewer
.
getAttribute
(
'
data-loading
'
)
)
{
return
Promise
.
resolve
(
viewer
);
}
viewer
.
dataset
.
loading
=
true
;
viewer
.
setAttribute
(
'
data-loading
'
,
'
true
'
)
;
return
axios
.
get
(
url
).
then
(({
data
})
=>
{
viewer
.
innerHTML
=
data
.
html
;
viewer
.
dataset
.
loaded
=
true
;
viewer
.
setAttribute
(
'
data-loaded
'
,
'
true
'
)
;
return
viewer
;
});
...
...
app/assets/javascripts/blob_edit/edit_blob.js
View file @
399b49e8
...
...
@@ -4,8 +4,7 @@ import $ from 'jquery';
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
createFlash
from
'
~/flash
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
blobLinkRegex
}
from
'
~/blob/blob_utils
'
;
import
TemplateSelectorMediator
from
'
~/blob/file_template_mediator
'
;
import
TemplateSelectorMediator
from
'
../blob/file_template_mediator
'
;
import
getModeByFileExtension
from
'
~/lib/utils/ace_utils
'
;
import
{
addEditorMarkdownListeners
}
from
'
~/lib/utils/text_markdown
'
;
...
...
@@ -18,7 +17,6 @@ export default class EditBlob {
this
.
initModePanesAndLinks
();
this
.
initSoftWrap
();
this
.
initFileSelectors
();
this
.
initBlobContentLinkClickability
();
}
configureAceEditor
()
{
...
...
@@ -91,22 +89,6 @@ export default class EditBlob {
return
this
.
editor
.
focus
();
}
initBlobContentLinkClickability
()
{
this
.
editor
.
renderer
.
on
(
'
afterRender
'
,
()
=>
{
document
.
querySelectorAll
(
'
.ace_text-layer .ace_line > *
'
).
forEach
(
token
=>
{
if
(
token
.
dataset
.
linkified
||
!
token
.
textContent
.
includes
(
'
http
'
))
return
;
// eslint-disable-next-line no-param-reassign
token
.
innerHTML
=
token
.
innerHTML
.
replace
(
blobLinkRegex
,
'
<a target="_blank" href="$&">$&</a>
'
,
);
// eslint-disable-next-line no-param-reassign
token
.
dataset
.
linkified
=
true
;
});
});
}
initSoftWrap
()
{
this
.
isSoftWrapped
=
false
;
this
.
$toggleButton
=
$
(
'
.soft-wrap-toggle
'
);
...
...
app/assets/stylesheets/framework/files.scss
View file @
399b49e8
...
...
@@ -258,17 +258,6 @@
}
}
}
.file-editor
{
.ace_underline
{
text-decoration
:
none
;
}
.ace_line
a
{
pointer-events
:
auto
;
color
:
inherit
;
}
}
}
span
.idiff
{
...
...
app/assets/stylesheets/highlight/common.scss
View file @
399b49e8
...
...
@@ -29,12 +29,3 @@
color
:
$link
;
}
}
// Links to URLs, emails, or dependencies
.code
.line
a
{
color
:
inherit
;
&
:hover
{
text-decoration
:
underline
;
}
}
app/assets/stylesheets/highlight/themes/dark.scss
View file @
399b49e8
...
...
@@ -193,6 +193,11 @@ $dark-il: #de935f;
color
:
$dark-highlight-color
!
important
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$dark-na
;
}
.hll
{
background-color
:
$dark-hll-bg
;
}
.c
{
color
:
$dark-c
;
}
/* Comment */
.err
{
color
:
$dark-err
;
}
/* Error */
...
...
app/assets/stylesheets/highlight/themes/monokai.scss
View file @
399b49e8
...
...
@@ -193,6 +193,11 @@ $monokai-gi: #a6e22e;
color
:
$black
!
important
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$monokai-k
;
}
.hll
{
background-color
:
$monokai-hll
;
}
.c
{
color
:
$monokai-c
;
}
/* Comment */
.err
{
color
:
$monokai-err-color
;
background-color
:
$monokai-err-bg
;
}
/* Error */
...
...
app/assets/stylesheets/highlight/themes/none.scss
View file @
399b49e8
...
...
@@ -143,6 +143,12 @@
background-color
:
$white-normal
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$gl-text-color
;
text-decoration
:
underline
;
}
.hll
{
background-color
:
$white-light
;
}
.gd
{
...
...
app/assets/stylesheets/highlight/themes/solarized-dark.scss
View file @
399b49e8
...
...
@@ -196,6 +196,11 @@ $solarized-dark-il: #2aa198;
background-color
:
$solarized-dark-highlight
!
important
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$solarized-dark-kd
;
}
/* Solarized Dark
For use with Jekyll and Pygments
...
...
app/assets/stylesheets/highlight/themes/solarized-light.scss
View file @
399b49e8
...
...
@@ -204,6 +204,11 @@ $solarized-light-il: #2aa198;
background-color
:
$solarized-light-highlight
!
important
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$solarized-light-kd
;
}
/* Solarized Light
For use with Jekyll and Pygments
...
...
app/assets/stylesheets/highlight/white_base.scss
View file @
399b49e8
...
...
@@ -209,6 +209,11 @@ span.highlight_word {
background-color
:
$white-highlight
!
important
;
}
// Links to URLs, emails, or dependencies
.line
a
{
color
:
$white-nb
;
}
.hll
{
background-color
:
$white-hll-bg
;
}
.c
{
color
:
$white-c
;
...
...
app/views/shared/_file_highlight.html.haml
View file @
399b49e8
...
...
@@ -10,7 +10,7 @@
%a
.diff-line-num
{
href:
"#{link}#L#{i}"
,
id:
"L#{i}"
,
'data-line-number'
=>
i
}
=
link_icon
=
i
.blob-content
.js-blob-content
{
data:
{
blob_id:
blob
.
id
}
}
.blob-content
{
data:
{
blob_id:
blob
.
id
}
}
%pre
.code.highlight
%code
=
blob
.
present
.
highlight
changelogs/unreleased/mfluharty-clickable-links-in-file-view.yml
deleted
100644 → 0
View file @
4753e8a8
---
title
:
Make URLs in blob viewer and blob editor into clickable links
merge_request
:
18305
author
:
type
:
added
spec/features/projects/blobs/edit_spec.rb
View file @
399b49e8
...
...
@@ -62,13 +62,6 @@ describe 'Editing file blob', :js do
expect
(
page
).
to
have_content
'NextFeature'
end
it
'renders a URL in the content of file as a link'
do
project
.
repository
.
create_file
(
user
,
'file.yml'
,
'# go to https://gitlab.com'
,
message:
'testing'
,
branch_name:
branch
)
visit
project_edit_blob_path
(
project
,
tree_join
(
branch
,
'file.yml'
))
expect
(
page
).
to
have_selector
(
'.ace_content .ace_line a'
)
end
context
'from blob file path'
do
before
do
visit
project_blob_path
(
project
,
tree_join
(
branch
,
file_path
))
...
...
spec/javascripts/blob/viewer/index_spec.js
View file @
399b49e8
...
...
@@ -11,13 +11,6 @@ describe('Blob viewer', () => {
preloadFixtures
(
'
snippets/show.html
'
);
const
asyncClick
=
()
=>
new
Promise
(
resolve
=>
{
document
.
querySelector
(
'
.js-blob-viewer-switch-btn[data-viewer="simple"]
'
).
click
();
setTimeout
(
resolve
);
});
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
...
...
@@ -73,12 +66,19 @@ describe('Blob viewer', () => {
});
it
(
'
doesnt reload file if already loaded
'
,
done
=>
{
const
asyncClick
=
()
=>
new
Promise
(
resolve
=>
{
document
.
querySelector
(
'
.js-blob-viewer-switch-btn[data-viewer="simple"]
'
).
click
();
setTimeout
(
resolve
);
});
asyncClick
()
.
then
(()
=>
asyncClick
())
.
then
(()
=>
{
expect
(
document
.
querySelector
(
'
.blob-viewer[data-type="simple"]
'
).
dataset
.
loaded
).
toBe
(
'
true
'
,
);
expect
(
document
.
querySelector
(
'
.blob-viewer[data-type="simple"]
'
).
getAttribute
(
'
data-loaded
'
)
,
)
.
toBe
(
'
true
'
)
;
done
();
})
...
...
@@ -100,7 +100,9 @@ describe('Blob viewer', () => {
});
it
(
'
has tooltip when disabled
'
,
()
=>
{
expect
(
copyButton
.
dataset
.
title
).
toBe
(
'
Switch to the source to copy the file contents
'
);
expect
(
copyButton
.
getAttribute
(
'
data-original-title
'
)).
toBe
(
'
Switch to the source to copy the file contents
'
,
);
});
it
(
'
is blurred when clicked and disabled
'
,
()
=>
{
...
...
@@ -134,7 +136,7 @@ describe('Blob viewer', () => {
document
.
querySelector
(
'
.js-blob-viewer-switch-btn[data-viewer="simple"]
'
).
click
();
setTimeout
(()
=>
{
expect
(
copyButton
.
dataset
.
title
).
toBe
(
'
Copy file contents
'
);
expect
(
copyButton
.
getAttribute
(
'
data-original-title
'
)
).
toBe
(
'
Copy file contents
'
);
done
();
});
...
...
@@ -175,27 +177,4 @@ describe('Blob viewer', () => {
expect
(
axios
.
get
.
calls
.
count
()).
toBe
(
1
);
});
});
describe
(
'
a URL inside the blob content
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
http://test.host/snippets/1.json?viewer=simple
'
).
reply
(
200
,
{
html
:
'
<div class="js-blob-content"><pre class="code"><code><span class="line" lang="yaml"><span class="c1">To install gitlab-shell you also need a Go compiler version 1.8 or newer. https://golang.org/dl/</span></span></code></pre></div>
'
,
});
});
it
(
'
is rendered as a link in simple view
'
,
done
=>
{
asyncClick
()
.
then
(()
=>
{
expect
(
document
.
querySelector
(
'
.blob-viewer[data-type="simple"]
'
).
innerHTML
).
toContain
(
'
<a href="https://golang.org/dl/">https://golang.org/dl/</a>
'
,
);
done
();
})
.
catch
(()
=>
{
fail
();
done
();
});
});
});
});
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