Commit dad30d6b authored by Sam Rose's avatar Sam Rose Committed by Jacob Schatz

Use PDFLab to render PDFs in GitLab

parent 97667b83
/* eslint-disable no-new */
import Vue from 'vue';
import PDFLab from 'vendor/pdflab';
import workerSrc from 'vendor/pdf.worker';
Vue.use(PDFLab, {
workerSrc,
});
export default () => {
const el = document.getElementById('js-pdf-viewer');
new Vue({
el,
data() {
return {
error: false,
loadError: false,
loading: true,
pdf: el.dataset.endpoint,
};
},
methods: {
onLoad() {
this.loading = false;
},
onError(error) {
this.loading = false;
this.loadError = true;
this.error = error;
},
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="PDF loading">
</i>
</div>
<pdf-lab
v-if="!loadError"
:pdf="pdf"
@pdflabload="onLoad"
@pdflaberror="onError" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occured whilst loading the file. Please try again later.
</span>
<span v-else>
An error occured whilst decoding the file.
</span>
</p>
</div>
`,
});
};
import renderPDF from './pdf';
document.addEventListener('DOMContentLoaded', renderPDF);
......@@ -46,6 +46,10 @@ class Blob < SimpleDelegator
text? && language && language.name == 'SVG'
end
def pdf?
name && File.extname(name) == '.pdf'
end
def ipython_notebook?
text? && language&.name == 'Jupyter Notebook'
end
......@@ -71,6 +75,8 @@ class Blob < SimpleDelegator
end
elsif image? || svg?
'image'
elsif pdf?
'pdf'
elsif ipython_notebook?
'notebook'
elsif sketch?
......
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('pdf_viewer')
.file-content#js-pdf-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
......@@ -38,6 +38,7 @@ var config = {
network: './network/network_bundle.js',
notebook_viewer: './blob/notebook_viewer.js',
sketch_viewer: './blob/sketch_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
profile: './profile/profile_bundle.js',
protected_branches: './protected_branches/protected_branches_bundle.js',
snippet: './snippet/snippet_bundle.js',
......@@ -65,7 +66,11 @@ var config = {
{
test: /\.svg$/,
use: 'raw-loader'
}
}, {
test: /\.(worker.js|pdf)$/,
exclude: /node_modules/,
loader: 'file-loader',
},
]
},
......@@ -107,6 +112,7 @@ var config = {
'issuable',
'merge_conflicts',
'notebook_viewer',
'pdf_viewer',
'vue_pipelines',
],
minChunks: function(module, count) {
......
import renderPDF from '~/blob/pdf';
import testPDF from './test.pdf';
describe('PDF renderer', () => {
let viewer;
preloadFixtures('static/pdf_viewer.html.raw');
beforeEach(() => {
loadFixtures('static/pdf_viewer.html.raw');
viewer = document.getElementById('js-pdf-viewer');
viewer.dataset.endpoint = testPDF;
});
it('shows loading icon', () => {
renderPDF();
expect(
document.querySelector('.loading'),
).not.toBeNull();
});
describe('successful response', () => {
beforeEach((done) => {
renderPDF();
setTimeout(() => {
done();
}, 500);
});
it('does not show loading icon', () => {
expect(
document.querySelector('.loading'),
).toBeNull();
});
it('renders the PDF', () => {
expect(
document.querySelector('.pdf-viewer'),
).not.toBeNull();
});
it('renders the PDF page', () => {
expect(
document.querySelector('.pdf-page'),
).not.toBeNull();
});
});
describe('error getting file', () => {
beforeEach((done) => {
viewer.dataset.endpoint = 'invalid/endpoint';
renderPDF();
setTimeout(() => {
done();
}, 500);
});
it('does not show loading icon', () => {
expect(
document.querySelector('.loading'),
).toBeNull();
});
it('shows error message', () => {
expect(
document.querySelector('.md').textContent.trim(),
).toBe('An error occured whilst loading the file. Please try again later.');
});
});
});
.file-content#js-pdf-viewer{ data: { endpoint: '/test' } }
......@@ -53,6 +53,20 @@ describe Blob do
end
end
describe '#pdf?' do
it 'is falsey when file extension is not .pdf' do
git_blob = double(name: 'git_blob.txt')
expect(described_class.decorate(git_blob)).not_to be_pdf
end
it 'is truthy when file extension is .pdf' do
git_blob = double(name: 'git_blob.pdf')
expect(described_class.decorate(git_blob)).to be_pdf
end
end
describe '#ipython_notebook?' do
it 'is falsey when language is not Jupyter Notebook' do
git_blob = double(text?: true, language: double(name: 'JSON'))
......@@ -102,6 +116,7 @@ describe Blob do
def stubbed_blob(overrides = {})
overrides.reverse_merge!(
name: nil,
image?: false,
language: nil,
lfs_pointer?: false,
......@@ -146,6 +161,11 @@ describe Blob do
expect(blob.to_partial_path(project)).to eq 'download'
end
it 'handles PDFs' do
blob = stubbed_blob(name: 'blob.pdf', pdf?: true)
expect(blob.to_partial_path(project)).to eq 'pdf'
end
it 'handles iPython notebooks' do
blob = stubbed_blob(text?: true, ipython_notebook?: true)
expect(blob.to_partial_path(project)).to eq 'notebook'
......
This diff is collapsed.
This diff is collapsed.
......@@ -1942,6 +1942,12 @@ file-entry-cache@^2.0.0:
flat-cache "^1.2.1"
object-assign "^4.0.1"
file-loader@^0.11.1:
version "0.11.1"
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.1.tgz#6b328ee1234a729e4e47d36375dd6d35c0e1db84"
dependencies:
loader-utils "^1.0.2"
filename-regex@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775"
......@@ -2923,6 +2929,14 @@ loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5:
json5 "^0.5.0"
object-assign "^4.0.1"
loader-utils@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
dependencies:
big.js "^3.1.3"
emojis-list "^2.0.0"
json5 "^0.5.0"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment