diff options
author | Phil Hughes <me@iamphill.com> | 2017-05-05 21:43:13 +0300 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2017-05-05 21:43:13 +0300 |
commit | ecaa68a7095750ef7575fdefdb200ef01ef88748 (patch) | |
tree | 8728d073f15d64f8b5254ab8126804f6855aa4e4 /app | |
parent | 1bf9f012ccd744f415ffcb32c6af36dc04d7e07a (diff) | |
parent | 165bcec30506380efa912b3dba25815aeea9c92f (diff) |
Merge branch 'balsalmiq-support' into 'master'
Initial balsamiq support
See merge request !10564
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js | 114 | ||||
-rw-r--r-- | app/assets/javascripts/blob/balsamiq_viewer.js | 6 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/files.scss | 12 | ||||
-rw-r--r-- | app/models/blob.rb | 1 | ||||
-rw-r--r-- | app/models/blob_viewer/balsamiq.rb | 12 | ||||
-rw-r--r-- | app/views/projects/blob/viewers/_balsamiq.html.haml | 4 |
6 files changed, 149 insertions, 0 deletions
diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js new file mode 100644 index 00000000000..cdbfe36ca1c --- /dev/null +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -0,0 +1,114 @@ +/* global Flash */ + +import sqljs from 'sql.js'; +import { template as _template } from 'underscore'; + +const PREVIEW_TEMPLATE = _template(` + <div class="panel panel-default"> + <div class="panel-heading"><%- name %></div> + <div class="panel-body"> + <img class="img-thumbnail" src="data:image/png;base64,<%- image %>"/> + </div> + </div> +`); + +class BalsamiqViewer { + constructor(viewer) { + this.viewer = viewer; + this.endpoint = this.viewer.dataset.endpoint; + } + + loadFile() { + const xhr = new XMLHttpRequest(); + + xhr.open('GET', this.endpoint, true); + xhr.responseType = 'arraybuffer'; + + xhr.onload = this.renderFile.bind(this); + xhr.onerror = BalsamiqViewer.onError; + + xhr.send(); + } + + renderFile(loadEvent) { + const container = document.createElement('ul'); + + this.initDatabase(loadEvent.target.response); + + const previews = this.getPreviews(); + previews.forEach((preview) => { + const renderedPreview = this.renderPreview(preview); + + container.appendChild(renderedPreview); + }); + + container.classList.add('list-inline'); + container.classList.add('previews'); + + this.viewer.appendChild(container); + } + + initDatabase(data) { + const previewBinary = new Uint8Array(data); + + this.database = new sqljs.Database(previewBinary); + } + + getPreviews() { + const thumbnails = this.database.exec('SELECT * FROM thumbnails'); + + return thumbnails[0].values.map(BalsamiqViewer.parsePreview); + } + + getResource(resourceID) { + const resources = this.database.exec(`SELECT * FROM resources WHERE id = '${resourceID}'`); + + return resources[0]; + } + + renderPreview(preview) { + const previewElement = document.createElement('li'); + + previewElement.classList.add('preview'); + previewElement.innerHTML = this.renderTemplate(preview); + + return previewElement; + } + + renderTemplate(preview) { + const resource = this.getResource(preview.resourceID); + const name = BalsamiqViewer.parseTitle(resource); + const image = preview.image; + + const template = PREVIEW_TEMPLATE({ + name, + image, + }); + + return template; + } + + static parsePreview(preview) { + return JSON.parse(preview[1]); + } + + /* + * resource = { + * columns: ['ID', 'BRANCHID', 'ATTRIBUTES', 'DATA'], + * values: [['id', 'branchId', 'attributes', 'data']], + * } + * + * 'attributes' being a JSON string containing the `name` property. + */ + static parseTitle(resource) { + return JSON.parse(resource.values[0][2]).name; + } + + static onError() { + const flash = new Flash('Balsamiq file could not be loaded.'); + + return flash; + } +} + +export default BalsamiqViewer; diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js new file mode 100644 index 00000000000..1dacf84470f --- /dev/null +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -0,0 +1,6 @@ +import BalsamiqViewer from './balsamiq/balsamiq_viewer'; + +document.addEventListener('DOMContentLoaded', () => { + const balsamiqViewer = new BalsamiqViewer(document.getElementById('js-balsamiq-viewer')); + balsamiqViewer.loadFile(); +}); diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index c197bf6b9f5..1dd0e5ab581 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -162,6 +162,18 @@ &.code { padding: 0; } + + .list-inline.previews { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-content: flex-start; + align-items: baseline; + + .preview { + padding: $gl-padding; + } + } } } diff --git a/app/models/blob.rb b/app/models/blob.rb index a4fae22a0c4..eaf0b713122 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -26,6 +26,7 @@ class Blob < SimpleDelegator BlobViewer::Image, BlobViewer::Sketch, + BlobViewer::Balsamiq, BlobViewer::Video, diff --git a/app/models/blob_viewer/balsamiq.rb b/app/models/blob_viewer/balsamiq.rb new file mode 100644 index 00000000000..f982521db99 --- /dev/null +++ b/app/models/blob_viewer/balsamiq.rb @@ -0,0 +1,12 @@ +module BlobViewer + class Balsamiq < Base + include Rich + include ClientSide + + self.partial_name = 'balsamiq' + self.extensions = %w(bmpr) + self.binary = true + self.switcher_icon = 'file-image-o' + self.switcher_title = 'preview' + end +end diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml new file mode 100644 index 00000000000..28670e7de97 --- /dev/null +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -0,0 +1,4 @@ +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('balsamiq_viewer') + +.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_url } } |