From 34cf9e687140c36981ede34d090f556988d8957c Mon Sep 17 00:00:00 2001 From: vrde Date: Wed, 3 Jun 2015 16:53:27 +0200 Subject: [PATCH 1/6] Add support for videos and images --- js/actions/piece_list_actions.js | 1 - .../ascribe_media/resource_viewer.js | 65 ++++++++++++++++--- js/components/edition.js | 12 +++- js/components/edition_container.js | 3 +- js/mixins/inject_in_head_mixin.js | 44 ++++++++----- 5 files changed, 93 insertions(+), 32 deletions(-) diff --git a/js/actions/piece_list_actions.js b/js/actions/piece_list_actions.js index 4ec237ca..29c8413f 100644 --- a/js/actions/piece_list_actions.js +++ b/js/actions/piece_list_actions.js @@ -14,7 +14,6 @@ class PieceListActions { PieceListFetcher .fetch(page, pageSize, search, orderBy, orderAsc) .then((res) => { - console.log(res); this.actions.updatePieceList({ page, pageSize, diff --git a/js/components/ascribe_media/resource_viewer.js b/js/components/ascribe_media/resource_viewer.js index 4519bcb7..7cc226a8 100644 --- a/js/components/ascribe_media/resource_viewer.js +++ b/js/components/ascribe_media/resource_viewer.js @@ -12,26 +12,73 @@ import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; * - other */ -let resourceMap = { - 'image': 1 -} -let ResourceViewer = React.createClass({ +let Image = React.createClass({ + render() { + return (); + } +}); + +let Video = React.createClass({ propTypes: { - thumbnail: React.PropTypes.string.isRequired, - mimetype: React.PropTypes.oneOf(['image', 'video', 'audio', 'pdf', 'other']).isRequired + 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://antani.com'); + 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.setState({ready: true}); })); }, render() { + if (this.state.ready) { + return ( + + ); + } else { + return( + + ); + } + } +}); + + +let resourceMap = { + 'image': Image, + 'video': Video +} + +let ResourceViewer = React.createClass({ + propTypes: { + mimetype: React.PropTypes.oneOf(['image', 'video', 'audio', 'pdf', 'other']).isRequired, + preview: React.PropTypes.string.isRequired, + url: React.PropTypes.string.isRequired + }, + + render() { + let Component = resourceMap[this.props.mimetype] || Image; + return ( -
- resourceviewer {this.props.thumbnail} {this.props.mimetype} +
+
); } diff --git a/js/components/edition.js b/js/components/edition.js index c1bd4a47..b7f96291 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -10,13 +10,19 @@ 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 } }); + } return (
- +
diff --git a/js/components/edition_container.js b/js/components/edition_container.js index 77d28962..3d847036 100644 --- a/js/components/edition_container.js +++ b/js/components/edition_container.js @@ -37,7 +37,8 @@ let EditionContainer = React.createClass({ if('title' in this.state.edition) { return ( - + ); } else { return ( diff --git a/js/mixins/inject_in_head_mixin.js b/js/mixins/inject_in_head_mixin.js index c230db2c..1671b32d 100644 --- a/js/mixins/inject_in_head_mixin.js +++ b/js/mixins/inject_in_head_mixin.js @@ -1,10 +1,10 @@ let mapAttr = { link: 'href', - source: 'src' + script: 'src' } -let mapExt = { - js: 'source', +let mapTag = { + js: 'script', css: 'link' } @@ -23,33 +23,41 @@ let InjectInHeadMixin = { }, injectTag(tag, src){ - console.log(this.foobar); - if (InjectInHeadMixin.isPresent(tag, src)) - return; + 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; + } + }); - 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(); - let tag = null; - try { - tag = mapAttr(src); - } catch (e) { + 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".`); - } - InjectInHeadMixin.injectTag(tag, src); + + return InjectInHeadMixin.injectTag(tag, src); } }; From 13caad77942b6f70c1e53d728208c3a34b10ab94 Mon Sep 17 00:00:00 2001 From: vrde Date: Thu, 4 Jun 2015 16:15:59 +0200 Subject: [PATCH 2/6] Refactor and restyle --- .../{resource_viewer.js => media_player.js} | 8 +++--- js/components/edition.js | 27 ++++++++++++------- sass/ascribe_media_player.scss | 13 +++++++++ sass/main.scss | 1 + 4 files changed, 36 insertions(+), 13 deletions(-) rename js/components/ascribe_media/{resource_viewer.js => media_player.js} (92%) create mode 100644 sass/ascribe_media_player.scss diff --git a/js/components/ascribe_media/resource_viewer.js b/js/components/ascribe_media/media_player.js similarity index 92% rename from js/components/ascribe_media/resource_viewer.js rename to js/components/ascribe_media/media_player.js index 7cc226a8..ccd74b43 100644 --- a/js/components/ascribe_media/resource_viewer.js +++ b/js/components/ascribe_media/media_player.js @@ -15,7 +15,7 @@ import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; let Image = React.createClass({ render() { - return (); + return (); } }); @@ -64,7 +64,7 @@ let resourceMap = { 'video': Video } -let ResourceViewer = React.createClass({ +let MediaPlayer = React.createClass({ propTypes: { mimetype: React.PropTypes.oneOf(['image', 'video', 'audio', 'pdf', 'other']).isRequired, preview: React.PropTypes.string.isRequired, @@ -75,7 +75,7 @@ let ResourceViewer = React.createClass({ let Component = resourceMap[this.props.mimetype] || Image; return ( -
+
@@ -84,4 +84,4 @@ let ResourceViewer = React.createClass({ } }); -export default ResourceViewer; +export default MediaPlayer; diff --git a/js/components/edition.js b/js/components/edition.js index 767eae0f..6d3d08f8 100644 --- a/js/components/edition.js +++ b/js/components/edition.js @@ -1,5 +1,10 @@ import React from 'react'; -import ResourceViewer from './ascribe_media/resource_viewer'; +import MediaPlayer from './ascribe_media/media_player'; + +import Row from 'react-bootstrap/lib/Row'; +import Col from 'react-bootstrap/lib/Col'; +import Button from 'react-bootstrap/lib/Button'; +import Glyphicon from 'react-bootstrap/lib/Glyphicon'; import EditionActions from '../actions/edition_actions' import AclButton from './acl_button' @@ -18,19 +23,23 @@ let Edition = React.createClass({ } return ( -
-
- + + -
-
+

+ +

+ + -
- -
+ + ); } }); diff --git a/sass/ascribe_media_player.scss b/sass/ascribe_media_player.scss new file mode 100644 index 00000000..bac15ac9 --- /dev/null +++ b/sass/ascribe_media_player.scss @@ -0,0 +1,13 @@ +.ascribe-media-player { + margin-bottom: 1em; + video { + width: 100%; + height: 100%; + } + + img { + width: 100%; + height: 100%; + } +} + diff --git a/sass/main.scss b/sass/main.scss index 2fe60cac..b5b0d7d7 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -9,6 +9,7 @@ @import 'ascribe_accordion_list'; @import 'ascribe_piece_list_bulk_modal'; @import 'ascribe_piece_list_toolbar'; +@import 'ascribe_media_player'; @import 'offset_right'; .hidden { From ff6855970e4ee6290bea58fa95079e9d048ba705 Mon Sep 17 00:00:00 2001 From: vrde Date: Thu, 4 Jun 2015 17:17:39 +0200 Subject: [PATCH 3/6] Still working on image preview --- js/components/ascribe_media/media_player.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index ccd74b43..2997b9b0 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -15,7 +15,11 @@ import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; let Image = React.createClass({ render() { - return (); + return ( + + + + ); } }); From 8f65aa7a2d5ec486338159f8ba2e62316cde36af Mon Sep 17 00:00:00 2001 From: vrde Date: Thu, 4 Jun 2015 17:37:46 +0200 Subject: [PATCH 4/6] Add "other" type display --- js/components/ascribe_media/media_player.js | 22 ++++++++++++++++++--- sass/ascribe_media_player.scss | 5 +++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index 2997b9b0..7df31d8f 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -1,5 +1,6 @@ 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. @@ -13,13 +14,27 @@ import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; */ +let Other = React.createClass({ + render() { + let ext = this.props.url.split('.').pop(); + + return ( + +

+ .{ext} +

+
+ ); + } +}); + let Image = React.createClass({ render() { return ( - ); + ); } }); @@ -65,7 +80,8 @@ let Video = React.createClass({ let resourceMap = { 'image': Image, - 'video': Video + 'video': Video, + 'other': Other } let MediaPlayer = React.createClass({ @@ -76,7 +92,7 @@ let MediaPlayer = React.createClass({ }, render() { - let Component = resourceMap[this.props.mimetype] || Image; + let Component = resourceMap[this.props.mimetype] || Other; return (
diff --git a/sass/ascribe_media_player.scss b/sass/ascribe_media_player.scss index bac15ac9..9dada7f6 100644 --- a/sass/ascribe_media_player.scss +++ b/sass/ascribe_media_player.scss @@ -9,5 +9,10 @@ width: 100%; height: 100%; } + + .media-other { + font-size: 500%; + color: #cccccc; + } } From a4b3438a2db826d56ef07b17357a8eb14d13c98d Mon Sep 17 00:00:00 2001 From: vrde Date: Fri, 5 Jun 2015 13:15:31 +0200 Subject: [PATCH 5/6] Add shmui to handle images --- js/components/ascribe_media/media_player.js | 15 ++++++++++++--- js/mixins/inject_in_head_mixin.js | 5 ++++- package.json | 6 +++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/js/components/ascribe_media/media_player.js b/js/components/ascribe_media/media_player.js index 7df31d8f..6a7d741b 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -29,11 +29,20 @@ let Other = React.createClass({ }); let Image = React.createClass({ + 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(() => { $('.shmui-ascribe').shmui(); })); + }, + render() { return ( - - - + ); } }); diff --git a/js/mixins/inject_in_head_mixin.js b/js/mixins/inject_in_head_mixin.js index 1671b32d..00841726 100644 --- a/js/mixins/inject_in_head_mixin.js +++ b/js/mixins/inject_in_head_mixin.js @@ -22,7 +22,7 @@ let InjectInHeadMixin = { return document.querySelector(query); }, - injectTag(tag, src){ + injectTag(tag, src, extraAttrs) { let promise = new Promise((resolve, reject) => { if (InjectInHeadMixin.isPresent(tag, src)) { resolve(); @@ -37,6 +37,9 @@ let InjectInHeadMixin = { } document.head.appendChild(element); element[attr] = src; + if (tag == 'link') { + element['rel'] = 'stylesheet'; + } } }); diff --git a/package.json b/package.json index 99f8f431..d822c614 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "object-assign": "^2.0.0", "react": "^0.13.2", "react-bootstrap": "~0.22.6", + "react-datepicker": "~0.8.0", "react-router": "^0.13.3", - "uglifyjs": "^2.4.10", - "react-bootstrap": "~0.22.6", - "react-datepicker": "~0.8.0" + "shmui": "^0.1.0", + "uglifyjs": "^2.4.10" }, "jest": { "scriptPreprocessor": "node_modules/babel-jest", From 62cc6b41dffa082ab18f28c934d4b75e0f2e4776 Mon Sep 17 00:00:00 2001 From: vrde Date: Fri, 5 Jun 2015 14:14:59 +0200 Subject: [PATCH 6/6] Lint code --- js/actions/edition_list_actions.js | 2 +- js/components/ascribe_media/media_player.js | 26 +++++++++++++++++---- js/mixins/inject_in_head_mixin.js | 10 ++++---- 3 files changed, 27 insertions(+), 11 deletions(-) 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 index 6a7d741b..55188568 100644 --- a/js/components/ascribe_media/media_player.js +++ b/js/components/ascribe_media/media_player.js @@ -1,3 +1,5 @@ +'use strict'; + import React from 'react'; import InjectInHeadMixin from '../../mixins/inject_in_head_mixin'; import Panel from 'react-bootstrap/lib/Panel'; @@ -15,6 +17,10 @@ import Panel from 'react-bootstrap/lib/Panel'; let Other = React.createClass({ + propTypes: { + url: React.PropTypes.string.isRequired + }, + render() { let ext = this.props.url.split('.').pop(); @@ -29,6 +35,11 @@ let Other = React.createClass({ }); let Image = React.createClass({ + propTypes: { + url: React.PropTypes.string.isRequired, + preview: React.PropTypes.string.isRequired + }, + mixins: [InjectInHeadMixin], componentDidMount() { @@ -37,7 +48,7 @@ let Image = React.createClass({ Promise.all([ this.inject('node_modules/shmui/shmui.css'), this.inject('node_modules/shmui/jquery.shmui.js') - ]).then(() => { $('.shmui-ascribe').shmui(); })); + ]).then(() => { window.jQuery('.shmui-ascribe').shmui(); })); }, render() { @@ -66,7 +77,11 @@ let Video = React.createClass({ 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.setState({ready: true}); })); + ]).then(this.ready)); + }, + + ready() { + this.setState({ready: true}); }, render() { @@ -79,7 +94,7 @@ let Video = React.createClass({ ); } else { - return( + return ( ); } @@ -91,13 +106,14 @@ 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 + url: React.PropTypes.string.isRequired, + extraData: React.PropTypes.array }, render() { diff --git a/js/mixins/inject_in_head_mixin.js b/js/mixins/inject_in_head_mixin.js index a336c25f..b537246b 100644 --- a/js/mixins/inject_in_head_mixin.js +++ b/js/mixins/inject_in_head_mixin.js @@ -2,7 +2,7 @@ let mapAttr = { link: 'href', - source: 'src' + script: 'src' }; let mapTag = { @@ -24,14 +24,14 @@ let InjectInHeadMixin = { return document.querySelector(query); }, - injectTag(tag, src, extraAttrs) { + 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') { + if (tag === 'script') { element.onload = () => resolve(); element.onerror = () => reject(); } else { @@ -39,8 +39,8 @@ let InjectInHeadMixin = { } document.head.appendChild(element); element[attr] = src; - if (tag == 'link') { - element['rel'] = 'stylesheet'; + if (tag === 'link') { + element.rel = 'stylesheet'; } } });