summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/blob
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2017-04-06 22:00:00 +0200
committerKamil Trzcinski <ayufan@ayufan.eu>2017-04-06 22:05:22 +0200
commit37a7b099d6eafc87209368a9fc7f608282b99e08 (patch)
treea2712e439cec8547d84bed2d57d339cb1cd5c502 /app/assets/javascripts/blob
parent57c353fca7121a120142161b253004f33d815766 (diff)
parent73cb71e41c0ade92b9673a5d74c7dd78679fae91 (diff)
downloadgitlab-ce-optimise-pipelines.tar.gz
Merge remote-tracking branch 'origin/master' into optimise-pipelinesoptimise-pipelines
Diffstat (limited to 'app/assets/javascripts/blob')
-rw-r--r--app/assets/javascripts/blob/3d_viewer/index.js147
-rw-r--r--app/assets/javascripts/blob/3d_viewer/mesh_object.js49
-rw-r--r--app/assets/javascripts/blob/blob_fork_suggestion.js15
-rw-r--r--app/assets/javascripts/blob/pdf/index.js62
-rw-r--r--app/assets/javascripts/blob/pdf_viewer.js3
-rw-r--r--app/assets/javascripts/blob/sketch/index.js73
-rw-r--r--app/assets/javascripts/blob/sketch_viewer.js8
-rw-r--r--app/assets/javascripts/blob/stl_viewer.js19
8 files changed, 376 insertions, 0 deletions
diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js
new file mode 100644
index 00000000000..68d4ddad551
--- /dev/null
+++ b/app/assets/javascripts/blob/3d_viewer/index.js
@@ -0,0 +1,147 @@
+import * as THREE from 'three/build/three.module';
+import STLLoaderClass from 'three-stl-loader';
+import OrbitControlsClass from 'three-orbit-controls';
+import MeshObject from './mesh_object';
+
+const STLLoader = STLLoaderClass(THREE);
+const OrbitControls = OrbitControlsClass(THREE);
+
+export default class Renderer {
+ constructor(container) {
+ this.renderWrapper = this.render.bind(this);
+ this.objects = [];
+
+ this.container = container;
+ this.width = this.container.offsetWidth;
+ this.height = 500;
+
+ this.loader = new STLLoader();
+
+ this.fov = 45;
+ this.camera = new THREE.PerspectiveCamera(
+ this.fov,
+ this.width / this.height,
+ 1,
+ 1000,
+ );
+
+ this.scene = new THREE.Scene();
+
+ this.scene.add(this.camera);
+
+ // Setup the viewer
+ this.setupRenderer();
+ this.setupGrid();
+ this.setupLight();
+
+ // Setup OrbitControls
+ this.controls = new OrbitControls(
+ this.camera,
+ this.renderer.domElement,
+ );
+ this.controls.minDistance = 5;
+ this.controls.maxDistance = 30;
+ this.controls.enableKeys = false;
+
+ this.loadFile();
+ }
+
+ setupRenderer() {
+ this.renderer = new THREE.WebGLRenderer({
+ antialias: true,
+ });
+
+ this.renderer.setClearColor(0xFFFFFF);
+ this.renderer.setPixelRatio(window.devicePixelRatio);
+ this.renderer.setSize(
+ this.width,
+ this.height,
+ );
+ }
+
+ setupLight() {
+ // Point light illuminates the object
+ const pointLight = new THREE.PointLight(
+ 0xFFFFFF,
+ 2,
+ 0,
+ );
+
+ pointLight.castShadow = true;
+
+ this.camera.add(pointLight);
+
+ // Ambient light illuminates the scene
+ const ambientLight = new THREE.AmbientLight(
+ 0xFFFFFF,
+ 1,
+ );
+ this.scene.add(ambientLight);
+ }
+
+ setupGrid() {
+ this.grid = new THREE.GridHelper(
+ 20,
+ 20,
+ 0x000000,
+ 0x000000,
+ );
+
+ this.scene.add(this.grid);
+ }
+
+ loadFile() {
+ this.loader.load(this.container.dataset.endpoint, (geo) => {
+ const obj = new MeshObject(geo);
+
+ this.objects.push(obj);
+ this.scene.add(obj);
+
+ this.start();
+ this.setDefaultCameraPosition();
+ });
+ }
+
+ start() {
+ // Empty the container first
+ this.container.innerHTML = '';
+
+ // Add to DOM
+ this.container.appendChild(this.renderer.domElement);
+
+ // Make controls visible
+ this.container.parentNode.classList.remove('is-stl-loading');
+
+ this.render();
+ }
+
+ render() {
+ this.renderer.render(
+ this.scene,
+ this.camera,
+ );
+
+ requestAnimationFrame(this.renderWrapper);
+ }
+
+ changeObjectMaterials(type) {
+ this.objects.forEach((obj) => {
+ obj.changeMaterial(type);
+ });
+ }
+
+ setDefaultCameraPosition() {
+ const obj = this.objects[0];
+ const radius = (obj.geometry.boundingSphere.radius / 1.5);
+ const dist = radius / (Math.sin((this.fov * (Math.PI / 180)) / 2));
+
+ this.camera.position.set(
+ 0,
+ dist + 1,
+ dist,
+ );
+
+ this.camera.lookAt(this.grid);
+ this.controls.update();
+ }
+}
diff --git a/app/assets/javascripts/blob/3d_viewer/mesh_object.js b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
new file mode 100644
index 00000000000..96758884abf
--- /dev/null
+++ b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
@@ -0,0 +1,49 @@
+import {
+ Matrix4,
+ MeshLambertMaterial,
+ Mesh,
+} from 'three/build/three.module';
+
+const defaultColor = 0xE24329;
+const materials = {
+ default: new MeshLambertMaterial({
+ color: defaultColor,
+ }),
+ wireframe: new MeshLambertMaterial({
+ color: defaultColor,
+ wireframe: true,
+ }),
+};
+
+export default class MeshObject extends Mesh {
+ constructor(geo) {
+ super(
+ geo,
+ materials.default,
+ );
+
+ this.geometry.computeBoundingSphere();
+
+ this.rotation.set(-Math.PI / 2, 0, 0);
+
+ if (this.geometry.boundingSphere.radius > 4) {
+ const scale = 4 / this.geometry.boundingSphere.radius;
+
+ this.geometry.applyMatrix(
+ new Matrix4().makeScale(
+ scale,
+ scale,
+ scale,
+ ),
+ );
+ this.geometry.computeBoundingSphere();
+
+ this.position.x = -this.geometry.boundingSphere.center.x;
+ this.position.z = this.geometry.boundingSphere.center.y;
+ }
+ }
+
+ changeMaterial(type) {
+ this.material = materials[type];
+ }
+}
diff --git a/app/assets/javascripts/blob/blob_fork_suggestion.js b/app/assets/javascripts/blob/blob_fork_suggestion.js
new file mode 100644
index 00000000000..aa9a4e1c99a
--- /dev/null
+++ b/app/assets/javascripts/blob/blob_fork_suggestion.js
@@ -0,0 +1,15 @@
+function BlobForkSuggestion(openButton, cancelButton, suggestionSection) {
+ if (openButton) {
+ openButton.addEventListener('click', () => {
+ suggestionSection.classList.remove('hidden');
+ });
+ }
+
+ if (cancelButton) {
+ cancelButton.addEventListener('click', () => {
+ suggestionSection.classList.add('hidden');
+ });
+ }
+}
+
+export default BlobForkSuggestion;
diff --git a/app/assets/javascripts/blob/pdf/index.js b/app/assets/javascripts/blob/pdf/index.js
new file mode 100644
index 00000000000..a74c2db9a61
--- /dev/null
+++ b/app/assets/javascripts/blob/pdf/index.js
@@ -0,0 +1,62 @@
+/* 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');
+
+ return 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>
+ `,
+ });
+};
diff --git a/app/assets/javascripts/blob/pdf_viewer.js b/app/assets/javascripts/blob/pdf_viewer.js
new file mode 100644
index 00000000000..91abe9dd699
--- /dev/null
+++ b/app/assets/javascripts/blob/pdf_viewer.js
@@ -0,0 +1,3 @@
+import renderPDF from './pdf';
+
+document.addEventListener('DOMContentLoaded', renderPDF);
diff --git a/app/assets/javascripts/blob/sketch/index.js b/app/assets/javascripts/blob/sketch/index.js
new file mode 100644
index 00000000000..0799991aa40
--- /dev/null
+++ b/app/assets/javascripts/blob/sketch/index.js
@@ -0,0 +1,73 @@
+import JSZip from 'jszip';
+import JSZipUtils from 'jszip-utils';
+
+export default class SketchLoader {
+ constructor(container) {
+ this.container = container;
+ this.loadingIcon = this.container.querySelector('.js-loading-icon');
+
+ this.load();
+ }
+
+ load() {
+ return this.getZipFile()
+ .then(data => JSZip.loadAsync(data))
+ .then(asyncResult => asyncResult.files['previews/preview.png'].async('uint8array'))
+ .then((content) => {
+ const url = window.URL || window.webkitURL;
+ const blob = new Blob([new Uint8Array(content)], {
+ type: 'image/png',
+ });
+ const previewUrl = url.createObjectURL(blob);
+
+ this.render(previewUrl);
+ })
+ .catch(this.error.bind(this));
+ }
+
+ getZipFile() {
+ return new JSZip.external.Promise((resolve, reject) => {
+ JSZipUtils.getBinaryContent(this.container.dataset.endpoint, (err, data) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve(data);
+ }
+ });
+ });
+ }
+
+ render(previewUrl) {
+ const previewLink = document.createElement('a');
+ const previewImage = document.createElement('img');
+
+ previewLink.href = previewUrl;
+ previewLink.target = '_blank';
+ previewImage.src = previewUrl;
+ previewImage.className = 'img-responsive';
+
+ previewLink.appendChild(previewImage);
+ this.container.appendChild(previewLink);
+
+ this.removeLoadingIcon();
+ }
+
+ error() {
+ const errorMsg = document.createElement('p');
+
+ errorMsg.className = 'prepend-top-default append-bottom-default text-center';
+ errorMsg.textContent = `
+ Cannot show preview. For previews on sketch files, they must have the file format
+ introduced by Sketch version 43 and above.
+ `;
+ this.container.appendChild(errorMsg);
+
+ this.removeLoadingIcon();
+ }
+
+ removeLoadingIcon() {
+ if (this.loadingIcon) {
+ this.loadingIcon.remove();
+ }
+ }
+}
diff --git a/app/assets/javascripts/blob/sketch_viewer.js b/app/assets/javascripts/blob/sketch_viewer.js
new file mode 100644
index 00000000000..0640dd26855
--- /dev/null
+++ b/app/assets/javascripts/blob/sketch_viewer.js
@@ -0,0 +1,8 @@
+/* eslint-disable no-new */
+import SketchLoader from './sketch';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const el = document.getElementById('js-sketch-viewer');
+
+ new SketchLoader(el);
+});
diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js
new file mode 100644
index 00000000000..f611c4fe640
--- /dev/null
+++ b/app/assets/javascripts/blob/stl_viewer.js
@@ -0,0 +1,19 @@
+import Renderer from './3d_viewer';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const viewer = new Renderer(document.getElementById('js-stl-viewer'));
+
+ [].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => {
+ el.addEventListener('click', (e) => {
+ const target = e.target;
+
+ e.preventDefault();
+
+ document.querySelector('.js-material-changer.active').classList.remove('active');
+ target.classList.add('active');
+ target.blur();
+
+ viewer.changeObjectMaterials(target.dataset.type);
+ });
+ });
+});