Browse Source

Ipython notebook support (#4070)

* added marked and notebookjs javascript libraries

* added ipython notebook render support using javascript libraries

* recompiled gogs.css to include ipynb-related css

* removed superflous javascript library files
pull/3750/merge
Herbert 8 years ago committed by 无闻
parent
commit
9af0dd23dd
  1. 119
      public/css/gogs.css
  2. 89
      public/less/_repository.less
  3. 19
      public/plugins/marked-0.3.6/LICENSE
  4. 6
      public/plugins/marked-0.3.6/marked.min.js
  5. 21
      public/plugins/notebookjs-0.2.6/LICENSE.txt
  6. 1
      public/plugins/notebookjs-0.2.6/notebook.min.js
  7. 2
      routers/repo/view.go
  8. 6
      templates/base/head.tmpl
  9. 20
      templates/repo/view_file.tmpl

119
public/css/gogs.css

@ -812,7 +812,7 @@ footer .ui.language .menu {
border: solid 1px #ccc; border: solid 1px #ccc;
border-bottom-color: #bbb; border-bottom-color: #bbb;
border-radius: 3px; border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb; box-shadow: inset 0 -1px 0 #bbbbbb;
} }
.markdown:not(code) input[type="checkbox"] { .markdown:not(code) input[type="checkbox"] {
vertical-align: middle !important; vertical-align: middle !important;
@ -883,7 +883,7 @@ footer .ui.language .menu {
} }
.install form label { .install form label {
text-align: right; text-align: right;
width: 320px !important; width: 320px;
} }
.install form input { .install form input {
width: 35% !important; width: 35% !important;
@ -892,7 +892,7 @@ footer .ui.language .menu {
text-align: left; text-align: left;
} }
.install form .field .help { .install form .field .help {
margin-left: 335px !important; margin-left: 335px;
} }
.install form .field.optional .title { .install form .field.optional .title {
margin-left: 38%; margin-left: 38%;
@ -928,18 +928,18 @@ footer .ui.language .menu {
text-align: center; text-align: center;
} }
#create-page-form form .header { #create-page-form form .header {
padding-left: 280px !important; padding-left: 280px;
} }
#create-page-form form .inline.field > label { #create-page-form form .inline.field > label {
text-align: right; text-align: right;
width: 250px !important; width: 250px;
word-wrap: break-word; word-wrap: break-word;
} }
#create-page-form form .help { #create-page-form form .help {
margin-left: 265px !important; margin-left: 265px;
} }
#create-page-form form .optional .title { #create-page-form form .optional .title {
margin-left: 250px !important; margin-left: 250px;
} }
#create-page-form form input, #create-page-form form input,
#create-page-form form textarea { #create-page-form form textarea {
@ -965,7 +965,7 @@ footer .ui.language .menu {
.user.reset.password form .header, .user.reset.password form .header,
.user.signin form .header, .user.signin form .header,
.user.signup form .header { .user.signup form .header {
padding-left: 280px !important; padding-left: 280px;
} }
.user.activate form .inline.field > label, .user.activate form .inline.field > label,
.user.forgot.password form .inline.field > label, .user.forgot.password form .inline.field > label,
@ -973,7 +973,7 @@ footer .ui.language .menu {
.user.signin form .inline.field > label, .user.signin form .inline.field > label,
.user.signup form .inline.field > label { .user.signup form .inline.field > label {
text-align: right; text-align: right;
width: 250px !important; width: 250px;
word-wrap: break-word; word-wrap: break-word;
} }
.user.activate form .help, .user.activate form .help,
@ -981,14 +981,14 @@ footer .ui.language .menu {
.user.reset.password form .help, .user.reset.password form .help,
.user.signin form .help, .user.signin form .help,
.user.signup form .help { .user.signup form .help {
margin-left: 265px !important; margin-left: 265px;
} }
.user.activate form .optional .title, .user.activate form .optional .title,
.user.forgot.password form .optional .title, .user.forgot.password form .optional .title,
.user.reset.password form .optional .title, .user.reset.password form .optional .title,
.user.signin form .optional .title, .user.signin form .optional .title,
.user.signup form .optional .title { .user.signup form .optional .title {
margin-left: 250px !important; margin-left: 250px;
} }
.user.activate form input, .user.activate form input,
.user.forgot.password form input, .user.forgot.password form input,
@ -1014,14 +1014,14 @@ footer .ui.language .menu {
.user.reset.password form .header, .user.reset.password form .header,
.user.signin form .header, .user.signin form .header,
.user.signup form .header { .user.signup form .header {
padding-left: 230px !important; padding-left: 230px;
} }
.user.activate form .inline.field > label, .user.activate form .inline.field > label,
.user.forgot.password form .inline.field > label, .user.forgot.password form .inline.field > label,
.user.reset.password form .inline.field > label, .user.reset.password form .inline.field > label,
.user.signin form .inline.field > label, .user.signin form .inline.field > label,
.user.signup form .inline.field > label { .user.signup form .inline.field > label {
width: 200px !important; width: 200px;
} }
.repository.new.repo form, .repository.new.repo form,
.repository.new.migrate form, .repository.new.migrate form,
@ -1037,24 +1037,24 @@ footer .ui.language .menu {
.repository.new.repo form .header, .repository.new.repo form .header,
.repository.new.migrate form .header, .repository.new.migrate form .header,
.repository.new.fork form .header { .repository.new.fork form .header {
padding-left: 280px !important; padding-left: 280px;
} }
.repository.new.repo form .inline.field > label, .repository.new.repo form .inline.field > label,
.repository.new.migrate form .inline.field > label, .repository.new.migrate form .inline.field > label,
.repository.new.fork form .inline.field > label { .repository.new.fork form .inline.field > label {
text-align: right; text-align: right;
width: 250px !important; width: 250px;
word-wrap: break-word; word-wrap: break-word;
} }
.repository.new.repo form .help, .repository.new.repo form .help,
.repository.new.migrate form .help, .repository.new.migrate form .help,
.repository.new.fork form .help { .repository.new.fork form .help {
margin-left: 265px !important; margin-left: 265px;
} }
.repository.new.repo form .optional .title, .repository.new.repo form .optional .title,
.repository.new.migrate form .optional .title, .repository.new.migrate form .optional .title,
.repository.new.fork form .optional .title { .repository.new.fork form .optional .title {
margin-left: 250px !important; margin-left: 250px;
} }
.repository.new.repo form input, .repository.new.repo form input,
.repository.new.migrate form input, .repository.new.migrate form input,
@ -1083,7 +1083,7 @@ footer .ui.language .menu {
width: 50%!important; width: 50%!important;
} }
.repository.new.repo .ui.form #auto-init { .repository.new.repo .ui.form #auto-init {
margin-left: 265px !important; margin-left: 265px;
} }
.new.webhook form .help { .new.webhook form .help {
margin-left: 25px; margin-left: 25px;
@ -1306,6 +1306,81 @@ footer .ui.language .menu {
.repository.file.list #file-content .view-raw img { .repository.file.list #file-content .view-raw img {
margin-bottom: -5px; margin-bottom: -5px;
} }
.repository.file.list #file-content #ipython-notebook {
margin-left: 80px;
}
.repository.file.list #file-content #ipython-notebook .nb-notebook {
line-height: 1.5;
}
.repository.file.list #file-content #ipython-notebook .nb-stdout,
.repository.file.list #file-content #ipython-notebook .nb-stderr {
white-space: pre-wrap;
margin: 1em 0;
padding: 0.1em 0.5em;
}
.repository.file.list #file-content #ipython-notebook .nb-stderr {
background-color: #FAA;
}
.repository.file.list #file-content #ipython-notebook .nb-cell + .nb-cell {
margin-top: 0.5em;
}
.repository.file.list #file-content #ipython-notebook .nb-output table {
border: 1px solid #000;
border-collapse: collapse;
}
.repository.file.list #file-content #ipython-notebook .nb-output th {
font-weight: bold;
}
.repository.file.list #file-content #ipython-notebook .nb-output th,
.repository.file.list #file-content #ipython-notebook .nb-output td {
border: 1px solid #000;
padding: 0.25em;
text-align: left;
vertical-align: middle;
border-collapse: collapse;
}
.repository.file.list #file-content #ipython-notebook .nb-cell {
position: relative;
}
.repository.file.list #file-content #ipython-notebook .nb-raw-cell {
white-space: pre-wrap;
background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
padding: 1em;
margin: .5em 0;
}
.repository.file.list #file-content #ipython-notebook .nb-output {
min-height: 1em;
width: 100%;
overflow-x: scroll;
border-right: 1px dotted #CCC;
}
.repository.file.list #file-content #ipython-notebook .nb-output img {
max-width: 100%;
}
.repository.file.list #file-content #ipython-notebook .nb-output:before,
.repository.file.list #file-content #ipython-notebook .nb-input:before {
position: absolute;
font-family: monospace;
color: #999;
left: -7.5em;
width: 7em;
text-align: right;
}
.repository.file.list #file-content #ipython-notebook .nb-input:before {
content: "In [" attr(data-prompt-number) "]:";
}
.repository.file.list #file-content #ipython-notebook .nb-output:before {
content: "Out [" attr(data-prompt-number) "]:";
}
.repository.file.list #file-content #ipython-notebook .nb-markdown-cell {
background-color: #eee;
margin-left: -80px;
padding: 11.5px 10px 19.5px 80px;
}
.repository.file.list #file-content #ipython-notebook div[style="max-height:1000px;max-width:1500px;overflow:auto;"] {
max-height: none !important;
}
.repository.file.list #file-content .plain-text { .repository.file.list #file-content .plain-text {
font-size: 14px; font-size: 14px;
padding: 10px 15px; padding: 10px 15px;
@ -2539,18 +2614,18 @@ footer .ui.language .menu {
text-align: center; text-align: center;
} }
.organization.new.org form .header { .organization.new.org form .header {
padding-left: 280px !important; padding-left: 280px;
} }
.organization.new.org form .inline.field > label { .organization.new.org form .inline.field > label {
text-align: right; text-align: right;
width: 250px !important; width: 250px;
word-wrap: break-word; word-wrap: break-word;
} }
.organization.new.org form .help { .organization.new.org form .help {
margin-left: 265px !important; margin-left: 265px;
} }
.organization.new.org form .optional .title { .organization.new.org form .optional .title {
margin-left: 250px !important; margin-left: 250px;
} }
.organization.new.org form input, .organization.new.org form input,
.organization.new.org form textarea { .organization.new.org form textarea {

89
public/less/_repository.less

@ -251,6 +251,95 @@
} }
} }
#ipython-notebook {
margin-left: 80px;
.nb-notebook {
line-height: 1.5;
}
.nb-stdout, .nb-stderr {
white-space: pre-wrap;
margin: 1em 0;
padding: 0.1em 0.5em;
}
.nb-stderr {
background-color: #FAA;
}
.nb-cell + .nb-cell {
margin-top: 0.5em;
}
.nb-output table {
border: 1px solid #000;
border-collapse: collapse;
}
.nb-output th {
font-weight: bold;
}
.nb-output th, .nb-output td {
border: 1px solid #000;
padding: 0.25em;
text-align: left;
vertical-align: middle;
border-collapse: collapse;
}
.nb-cell {
position: relative;
}
.nb-raw-cell {
white-space: pre-wrap;
background-color: #f5f2f0;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
padding: 1em;
margin: .5em 0;
}
.nb-output {
min-height: 1em;
width: 100%;
overflow-x: scroll;
border-right: 1px dotted #CCC;
}
.nb-output img {
max-width: 100%;
}
.nb-output:before, .nb-input:before {
position: absolute;
font-family: monospace;
color: #999;
left: -7.5em;
width: 7em;
text-align: right;
}
.nb-input:before {
content: "In [" attr(data-prompt-number) "]:";
}
.nb-output:before {
content: "Out [" attr(data-prompt-number) "]:";
}
.nb-markdown-cell {
background-color: #eee;
margin-left: -80px;
padding: 11.5px 10px 19.5px 80px;
}
// Fix pandas dataframe formatting
div[style="max-height:1000px;max-width:1500px;overflow:auto;"] {
max-height: none !important;
}
}
.plain-text { .plain-text {
font-size: 14px; font-size: 14px;
padding: 10px 15px; padding: 10px 15px;

19
public/plugins/marked-0.3.6/LICENSE

@ -0,0 +1,19 @@
Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

6
public/plugins/marked-0.3.6/marked.min.js vendored

File diff suppressed because one or more lines are too long

21
public/plugins/notebookjs-0.2.6/LICENSE.txt

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014, Jeremy Singer-Vine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
public/plugins/notebookjs-0.2.6/notebook.min.js vendored

File diff suppressed because one or more lines are too long

2
routers/repo/view.go

@ -156,6 +156,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["IsMarkdown"] = isMarkdown ctx.Data["IsMarkdown"] = isMarkdown
ctx.Data["ReadmeExist"] = isMarkdown && markdown.IsReadmeFile(blob.Name()) ctx.Data["ReadmeExist"] = isMarkdown && markdown.IsReadmeFile(blob.Name())
ctx.Data["IsIPyNB"] = strings.HasSuffix(blob.Name(), ".ipynb")
if isMarkdown { if isMarkdown {
ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas())) ctx.Data["FileContent"] = string(markdown.Render(buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
} else { } else {

6
templates/base/head.tmpl

@ -21,6 +21,12 @@
<link rel="stylesheet" href="{{AppSubUrl}}/assets/font-awesome-4.6.3/css/font-awesome.min.css"> <link rel="stylesheet" href="{{AppSubUrl}}/assets/font-awesome-4.6.3/css/font-awesome.min.css">
<link rel="stylesheet" href="{{AppSubUrl}}/assets/octicons-4.3.0/octicons.min.css"> <link rel="stylesheet" href="{{AppSubUrl}}/assets/octicons-4.3.0/octicons.min.css">
<!-- notebook.js for rendering ipython notebooks and marked.js for rendering markdown in notebooks -->
{{if .IsIPyNB }}
<script src="{{AppSubUrl}}/plugins/notebookjs-0.2.6/notebook.min.js"></script>
<script src="{{AppSubUrl}}/plugins/marked-0.3.6/marked.min.js"></script>
{{end}}
{{if .RequireSimpleMDE}} {{if .RequireSimpleMDE}}
<link rel="stylesheet" href="{{AppSubUrl}}/plugins/simplemde-1.10.1/simplemde.min.css"> <link rel="stylesheet" href="{{AppSubUrl}}/plugins/simplemde-1.10.1/simplemde.min.css">
<script src="{{AppSubUrl}}/plugins/simplemde-1.10.1/simplemde.min.js"></script> <script src="{{AppSubUrl}}/plugins/simplemde-1.10.1/simplemde.min.js"></script>

20
templates/repo/view_file.tmpl

@ -39,6 +39,26 @@
<div class="file-view {{if .IsMarkdown}}markdown{{else if .ReadmeInList}}plain-text{{else if .IsTextFile}}code-view{{end}} has-emoji"> <div class="file-view {{if .IsMarkdown}}markdown{{else if .ReadmeInList}}plain-text{{else if .IsTextFile}}code-view{{end}} has-emoji">
{{if or .IsMarkdown .ReadmeInList}} {{if or .IsMarkdown .ReadmeInList}}
{{if .FileContent}}{{.FileContent | Str2html}}{{end}} {{if .FileContent}}{{.FileContent | Str2html}}{{end}}
{{else if .IsIPyNB}}
{{if .FileContent}}
<div id="ipython-notebook"></div>
<script>
var rendered = null;
$.getJSON("{{.RawFileLink}}", null, function(notebook_json) {
var notebook = nb.parse(notebook_json);
rendered = notebook.render();
$("#ipython-notebook").append(rendered);
$("#ipython-notebook code").each(function(i, block) {
$(block).addClass("py").addClass("python");
hljs.highlightBlock(block);
});
$("#ipython-notebook .nb-markdown-cell").each(function(i, markdown) {
$(markdown).html(marked($(markdown).html()));
});
});
</script>
{{end}}
{{else if not .IsTextFile}} {{else if not .IsTextFile}}
<div class="view-raw ui center"> <div class="view-raw ui center">
{{if .IsImageFile}} {{if .IsImageFile}}

Loading…
Cancel
Save