1
0
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:
vrde 2015-06-05 14:21:25 +02:00
commit 25181db07e
9 changed files with 212 additions and 77 deletions

View File

@ -35,4 +35,4 @@ class EditionListActions {
}
}
export default alt.createActions(EditionListActions);
export default alt.createActions(EditionListActions);

View 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;

View File

@ -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;

View File

@ -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>
);
}
});

View File

@ -39,9 +39,8 @@ let EditionContainer = React.createClass({
render() {
if('title' in this.state.edition) {
return (
<Edition
edition={this.state.edition}
currentUser={this.state.currentUser} />
<Edition edition={this.state.edition}
currentUser={this.state.currentUser} />
);
} else {
return (

View File

@ -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);
}
};

View File

@ -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"
},

View 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;
}
}

View File

@ -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 {