mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
Merge branch 'AD-412-mediaplayer'
This commit is contained in:
commit
25181db07e
132
js/components/ascribe_media/media_player.js
Normal file
132
js/components/ascribe_media/media_player.js
Normal file
@ -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 (
|
||||
<Panel className="media-other">
|
||||
<p className="text-center">
|
||||
.{ext}
|
||||
</p>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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 (
|
||||
<img className="shmui-ascribe" src={this.props.preview} data-large-src={this.props.url}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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 (
|
||||
<video poster={this.props.preview} controls="controls" preload="none">
|
||||
{this.props.extraData.map((data, i) =>
|
||||
<source key={i} type={'video/' + data.type} src={data.url} />
|
||||
)}
|
||||
</video>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Image src={this.props.preview} />
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
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 (
|
||||
<div className="ascribe-media-player">
|
||||
<Component preview={this.props.preview}
|
||||
url={this.props.url}
|
||||
extraData={this.props.extraData} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default MediaPlayer;
|
@ -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 (
|
||||
<div>
|
||||
resourceviewer {this.props.thumbnail} {this.props.mimetype}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ResourceViewer;
|
@ -1,8 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import MediaPlayer from './ascribe_media/media_player';
|
||||
|
||||
import ResourceViewer from './ascribe_media/resource_viewer';
|
||||
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';
|
||||
@ -19,20 +23,30 @@ 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 (
|
||||
<div>
|
||||
<div className="col-md-7">
|
||||
<ResourceViewer thumbnail={thumbnail}
|
||||
mimetype={mimetype}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<Row>
|
||||
<Col md={7}>
|
||||
<MediaPlayer mimetype={mimetype}
|
||||
preview={thumbnail}
|
||||
url={this.props.edition.digital_work.url}
|
||||
extraData={extraData} />
|
||||
<p className="text-center">
|
||||
<Button bsSize="xsmall" href={this.props.edition.digital_work.url} target="_blank">
|
||||
Download <Glyphicon glyph="cloud-download" />
|
||||
</Button>
|
||||
</p>
|
||||
</Col>
|
||||
<Col md={5}>
|
||||
<EditionHeader edition={this.props.edition}/>
|
||||
<EditionDetails edition={this.props.edition} currentUser={ this.props.currentUser }/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -39,8 +39,7 @@ let EditionContainer = React.createClass({
|
||||
render() {
|
||||
if('title' in this.state.edition) {
|
||||
return (
|
||||
<Edition
|
||||
edition={this.state.edition}
|
||||
<Edition edition={this.state.edition}
|
||||
currentUser={this.state.currentUser} />
|
||||
);
|
||||
} else {
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
let mapAttr = {
|
||||
link: 'href',
|
||||
source: 'src'
|
||||
script: 'src'
|
||||
};
|
||||
|
||||
let mapExt = {
|
||||
js: 'source',
|
||||
let mapTag = {
|
||||
js: 'script',
|
||||
css: 'link'
|
||||
};
|
||||
|
||||
@ -25,34 +25,45 @@ let InjectInHeadMixin = {
|
||||
},
|
||||
|
||||
injectTag(tag, src) {
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
if (InjectInHeadMixin.isPresent(tag, src)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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"
|
||||
},
|
||||
|
18
sass/ascribe_media_player.scss
Normal file
18
sass/ascribe_media_player.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user