diff --git a/index.html b/index.html
index 8d16b099..320dfa89 100644
--- a/index.html
+++ b/index.html
@@ -9,7 +9,7 @@
-
+
diff --git a/js/actions/edition_list_actions.js b/js/actions/edition_list_actions.js
index 83b9abfe..ca653323 100644
--- a/js/actions/edition_list_actions.js
+++ b/js/actions/edition_list_actions.js
@@ -35,4 +35,4 @@ class EditionListActions {
}
}
-export default alt.createActions(EditionListActions);
\ No newline at end of file
+export default alt.createActions(EditionListActions);
diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js
new file mode 100644
index 00000000..55188568
--- /dev/null
+++ b/js/components/ascribe_media/media_player.js
@@ -0,0 +1,132 @@
+'use strict';
+
+import React from 'react';
+import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
+import Panel from 'react-bootstrap/lib/Panel';
+
+/**
+ * This is the component that implements display-specific functionality.
+ *
+ * ResourceViewer handles the following mimetypes:
+ * - image
+ * - video
+ * - audio
+ * - pdf
+ * - other
+ */
+
+
+let Other = React.createClass({
+ propTypes: {
+ url: React.PropTypes.string.isRequired
+ },
+
+ render() {
+ let ext = this.props.url.split('.').pop();
+
+ return (
+
+
+ .{ext}
+
+
+ );
+ }
+});
+
+let Image = React.createClass({
+ propTypes: {
+ url: React.PropTypes.string.isRequired,
+ preview: React.PropTypes.string.isRequired
+ },
+
+ mixins: [InjectInHeadMixin],
+
+ componentDidMount() {
+ this.inject('http://code.jquery.com/jquery-2.1.4.min.js')
+ .then(() =>
+ Promise.all([
+ this.inject('node_modules/shmui/shmui.css'),
+ this.inject('node_modules/shmui/jquery.shmui.js')
+ ]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
+ },
+
+ render() {
+ return (
+
+ );
+ }
+});
+
+let Video = React.createClass({
+ propTypes: {
+ preview: React.PropTypes.string.isRequired,
+ url: React.PropTypes.string.isRequired,
+ extraData: React.PropTypes.array.isRequired
+ },
+
+ mixins: [InjectInHeadMixin],
+
+ getInitialState() {
+ return { ready: false };
+ },
+
+ componentDidMount() {
+ this.inject('http://code.jquery.com/jquery-2.1.4.min.js')
+ .then(() =>
+ Promise.all([
+ this.inject('https://cdnjs.cloudflare.com/ajax/libs/mediaelement/2.17.0/mediaelement-and-player.min.js'),
+ this.inject('https://cdnjs.cloudflare.com/ajax/libs/mediaelement/2.17.0/mediaelementplayer.min.css')
+ ]).then(this.ready));
+ },
+
+ ready() {
+ this.setState({ready: true});
+ },
+
+ render() {
+ if (this.state.ready) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+ }
+});
+
+
+let resourceMap = {
+ 'image': Image,
+ 'video': Video,
+ 'other': Other
+};
+
+let MediaPlayer = React.createClass({
+ propTypes: {
+ mimetype: React.PropTypes.oneOf(['image', 'video', 'audio', 'pdf', 'other']).isRequired,
+ preview: React.PropTypes.string.isRequired,
+ url: React.PropTypes.string.isRequired,
+ extraData: React.PropTypes.array
+ },
+
+ render() {
+ let Component = resourceMap[this.props.mimetype] || Other;
+
+ return (
+
+
+
+ );
+ }
+});
+
+export default MediaPlayer;
diff --git a/js/components/ascribe_media/resource_viewer.js b/js/components/ascribe_media/resource_viewer.js
deleted file mode 100644
index 012228c6..00000000
--- a/js/components/ascribe_media/resource_viewer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-'use strict';
-
-import React from 'react';
-import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
-
-/**
- * This is the component that implements display-specific functionality.
- *
- * ResourceViewer handles the following mimetypes:
- * - image
- * - video
- * - audio
- * - pdf
- * - other
- */
-
-let resourceMap = {
- 'image': 1
-};
-
-let ResourceViewer = React.createClass({
- propTypes: {
- thumbnail: React.PropTypes.string.isRequired,
- mimetype: React.PropTypes.oneOf(['image', 'video', 'audio', 'pdf', 'other']).isRequired
- },
-
- mixins: [InjectInHeadMixin],
-
- componentDidMount() {
- //this.inject('http://antani.com');
- },
-
- render() {
- return (
-
- resourceviewer {this.props.thumbnail} {this.props.mimetype}
-
- );
- }
-});
-
-export default ResourceViewer;
diff --git a/js/components/edition.js b/js/components/edition.js
index 56f2b472..8574b3dc 100644
--- a/js/components/edition.js
+++ b/js/components/edition.js
@@ -1,11 +1,13 @@
'use strict';
import React from 'react';
+import MediaPlayer from './ascribe_media/media_player';
import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin';
+import Row from 'react-bootstrap/lib/Row';
+import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
-
-import ResourceViewer from './ascribe_media/resource_viewer';
+import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import EditionActions from '../actions/edition_actions';
import AclButtonList from './ascribe_buttons/acl_button_list';
@@ -25,6 +27,11 @@ let Edition = React.createClass({
render() {
let thumbnail = this.props.edition.thumbnail;
let mimetype = this.props.edition.digital_work.mime;
+ let extraData = null;
+
+ if (this.props.edition.digital_work.encoding_urls) {
+ extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label } });
+ }
let bitcoinIdValue = (
{this.props.edition.bitcoin_id}
@@ -35,12 +42,19 @@ let Edition = React.createClass({
);
return (
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
);
}
});
diff --git a/js/mixins/inject_in_head_mixin.js b/js/mixins/inject_in_head_mixin.js
index 42e3b6dc..b537246b 100644
--- a/js/mixins/inject_in_head_mixin.js
+++ b/js/mixins/inject_in_head_mixin.js
@@ -2,11 +2,11 @@
let mapAttr = {
link: 'href',
- source: 'src'
+ script: 'src'
};
-let mapExt = {
- js: 'source',
+let mapTag = {
+ js: 'script',
css: 'link'
};
@@ -24,35 +24,46 @@ let InjectInHeadMixin = {
return document.querySelector(query);
},
- injectTag(tag, src){
- if (InjectInHeadMixin.isPresent(tag, src)) {
- return;
- }
+ injectTag(tag, src) {
+ let promise = new Promise((resolve, reject) => {
+ if (InjectInHeadMixin.isPresent(tag, src)) {
+ resolve();
+ } else {
+ let attr = mapAttr[tag];
+ let element = document.createElement(tag);
+ if (tag === 'script') {
+ element.onload = () => resolve();
+ element.onerror = () => reject();
+ } else {
+ resolve();
+ }
+ document.head.appendChild(element);
+ element[attr] = src;
+ if (tag === 'link') {
+ element.rel = 'stylesheet';
+ }
+ }
+ });
- let attr = mapAttr[tag];
- let element = document.createElement(tag);
- document.head.appendChild(element);
- element[attr] = src;
+ return promise;
},
injectStylesheet(src) {
- this.injectTag('link', src);
+ return InjectInHeadMixin.injectTag('link', src);
},
injectScript(src) {
- this.injectTag('source', src);
+ return InjectInHeadMixin.injectTag('source', src);
},
inject(src) {
let ext = src.split('.').pop();
- try {
- let tag = mapAttr(src);
- } catch (e) {
- throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension ${ext}. Valid extensions are "js" and "css".`);
+ let tag = mapTag[ext];
+ if (!tag) {
+ throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
}
- // ES6Lint says tag is not defined, pls fix
- // - Tim
- InjectInHeadMixin.injectTag(tag, src);
+
+ return InjectInHeadMixin.injectTag(tag, src);
}
};
diff --git a/package.json b/package.json
index 5de03b4e..2e9d596b 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,9 @@
"object-assign": "^2.0.0",
"react": "^0.13.2",
"react-bootstrap": "~0.22.6",
+ "react-datepicker": "~0.8.0",
"react-router": "^0.13.3",
+ "shmui": "^0.1.0",
"uglifyjs": "^2.4.10",
"react-datepicker": "~0.8.0"
},
diff --git a/sass/ascribe_media_player.scss b/sass/ascribe_media_player.scss
new file mode 100644
index 00000000..9dada7f6
--- /dev/null
+++ b/sass/ascribe_media_player.scss
@@ -0,0 +1,18 @@
+.ascribe-media-player {
+ margin-bottom: 1em;
+ video {
+ width: 100%;
+ height: 100%;
+ }
+
+ img {
+ width: 100%;
+ height: 100%;
+ }
+
+ .media-other {
+ font-size: 500%;
+ color: #cccccc;
+ }
+}
+
diff --git a/sass/main.scss b/sass/main.scss
index f8fa5791..5dd95ade 100644
--- a/sass/main.scss
+++ b/sass/main.scss
@@ -10,6 +10,7 @@
@import 'ascribe_piece_list_bulk_modal';
@import 'ascribe_piece_list_toolbar';
@import 'ascribe_edition';
+@import 'ascribe_media_player';
@import 'offset_right';
.hidden {