1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

Merge remote-tracking branch 'remotes/origin/master' into AD-456-ikonotv-branded-page-for-registra

Conflicts:
	js/components/ascribe_buttons/button_submit_close.js
	js/components/ascribe_forms/form_loan.js
	js/components/ascribe_forms/form_register_piece.js
	sass/ascribe_media_player.scss
	sass/main.scss
This commit is contained in:
diminator 2015-09-10 17:45:51 +02:00
commit 6013303bd6
25 changed files with 353 additions and 323 deletions

View File

@ -189,17 +189,7 @@ function bundle(watch) {
.pipe(gulpif(!argv.production, sourcemaps.write())) // writes .map file .pipe(gulpif(!argv.production, sourcemaps.write())) // writes .map file
.on('error', notify.onError('Error: <%= error.message %>')) .on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulpif(argv.production, uglify({ .pipe(gulpif(argv.production, uglify({
mangle: true, mangle: true
compress: {
sequences: true,
dead_code: true,
conditionals: true,
booleans: true,
unused: true,
if_return: true,
join_vars: true,
drop_console: true
}
}))) })))
.on('error', notify.onError('Error: <%= error.message %>')) .on('error', notify.onError('Error: <%= error.message %>'))
.pipe(gulp.dest('./build/js')) .pipe(gulp.dest('./build/js'))

View File

@ -1,27 +0,0 @@
'use strict';
import React from 'react';
let ButtonSubmitOrClose = React.createClass({
propTypes: {
submitted: React.PropTypes.bool.isRequired,
text: React.PropTypes.string.isRequired
},
render() {
if (this.props.submitted){
return (
<div className="modal-footer">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
</div>
);
}
return (
<div className="modal-footer">
<button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button>
</div>
);
}
});
export default ButtonSubmitOrClose;

View File

@ -1,32 +0,0 @@
'use strict';
import React from 'react';
import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils.js';
let ButtonSubmitOrClose = React.createClass({
propTypes: {
submitted: React.PropTypes.bool.isRequired,
text: React.PropTypes.string.isRequired,
onClose: React.PropTypes.func.isRequired
},
render() {
if (this.props.submitted){
return (
<div className="modal-footer">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
</div>
);
}
return (
<div className="modal-footer">
<button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button>
<button className="btn btn-ascribe-inv" onClick={this.props.onClose}>{getLangText('CLOSE')}</button>
</div>
);
}
});
export default ButtonSubmitOrClose;

View File

@ -61,7 +61,7 @@ let EditionContainer = React.createClass({
}, },
render() { render() {
if('title' in this.state.edition) { if(this.state.edition && this.state.edition.title) {
return ( return (
<Edition <Edition
edition={this.state.edition} edition={this.state.edition}

View File

@ -39,7 +39,7 @@ let FurtherDetailsFileuploader = React.createClass({
return null; return null;
} }
let otherDataIds = this.props.otherData ? this.props.otherData.map((data)=>{return data.id; }).join() : null; let otherDataIds = this.props.otherData ? this.props.otherData.map((data) => data.id).join() : null;
return ( return (
<Property <Property

View File

@ -205,7 +205,7 @@ let PieceContainer = React.createClass({
}, },
render() { render() {
if('title' in this.state.piece) { if(this.state.piece && this.state.piece.title) {
return ( return (
<Piece <Piece
piece={this.state.piece} piece={this.state.piece}

View File

@ -41,7 +41,9 @@ let Form = React.createClass({
// It will make use of the GlobalNotification // It will make use of the GlobalNotification
isInline: React.PropTypes.bool, isInline: React.PropTypes.bool,
autoComplete: React.PropTypes.string autoComplete: React.PropTypes.string,
onReset: React.PropTypes.func
}, },
getDefaultProps() { getDefaultProps() {
@ -60,9 +62,15 @@ let Form = React.createClass({
}; };
}, },
reset(){ reset() {
for (let ref in this.refs){ // If onReset prop is defined from outside,
if (typeof this.refs[ref].reset === 'function'){ // notify component that a form reset is happening.
if(this.props.onReset && typeof this.props.onReset === 'function') {
this.props.onReset();
}
for(let ref in this.refs) {
if (this.refs[ref].reset && typeof this.refs[ref].reset === 'function'){
this.refs[ref].reset(); this.refs[ref].reset();
} }
} }
@ -70,7 +78,6 @@ let Form = React.createClass({
}, },
submit(event){ submit(event){
if(event) { if(event) {
event.preventDefault(); event.preventDefault();
} }
@ -79,7 +86,7 @@ let Form = React.createClass({
this.clearErrors(); this.clearErrors();
// selecting http method based on props // selecting http method based on props
if(this[this.props.method]) { if(this[this.props.method] && typeof this[this.props.method] === 'function') {
window.setTimeout(() => this[this.props.method](), 100); window.setTimeout(() => this[this.props.method](), 100);
} else { } else {
throw new Error('This HTTP method is not supported by form.js (' + this.props.method + ')'); throw new Error('This HTTP method is not supported by form.js (' + this.props.method + ')');
@ -114,13 +121,13 @@ let Form = React.createClass({
.catch(this.handleError); .catch(this.handleError);
}, },
getFormData(){ getFormData() {
let data = {}; let data = {};
for (let ref in this.refs){ for(let ref in this.refs){
data[this.refs[ref].props.name] = this.refs[ref].state.value; data[this.refs[ref].props.name] = this.refs[ref].state.value;
} }
if ('getFormData' in this.props){ if (this.props.getFormData && typeof this.props.getFormData === 'function'){
data = mergeOptionsWithDuplicates(data, this.props.getFormData()); data = mergeOptionsWithDuplicates(data, this.props.getFormData());
} }
@ -132,11 +139,12 @@ let Form = React.createClass({
}, },
handleSuccess(response){ handleSuccess(response){
if ('handleSuccess' in this.props){ if(this.props.handleSuccess && typeof this.props.handleSuccess === 'function') {
this.props.handleSuccess(response); this.props.handleSuccess(response);
} }
for (var ref in this.refs){
if ('handleSuccess' in this.refs[ref]){ for(let ref in this.refs) {
if(this.refs[ref] && this.refs[ref].handleSuccess && typeof this.refs[ref].handleSuccess === 'function'){
this.refs[ref].handleSuccess(); this.refs[ref].handleSuccess();
} }
} }
@ -148,9 +156,9 @@ let Form = React.createClass({
handleError(err){ handleError(err){
if (err.json) { if (err.json) {
for (var input in err.json.errors){ for (let input in err.json.errors){
if (this.refs && this.refs[input] && this.refs[input].state) { if (this.refs && this.refs[input] && this.refs[input].state) {
this.refs[input].setErrors( err.json.errors[input]); this.refs[input].setErrors(err.json.errors[input]);
} else { } else {
this.setState({errors: this.state.errors.concat(err.json.errors[input])}); this.setState({errors: this.state.errors.concat(err.json.errors[input])});
} }
@ -178,8 +186,8 @@ let Form = React.createClass({
}, },
clearErrors(){ clearErrors(){
for (var ref in this.refs){ for(let ref in this.refs){
if ('clearErrors' in this.refs[ref]){ if (this.refs[ref] && this.refs[ref].clearErrors && typeof this.refs[ref].clearErrors === 'function'){
this.refs[ref].clearErrors(); this.refs[ref].clearErrors();
} }
} }
@ -199,8 +207,16 @@ let Form = React.createClass({
buttons = ( buttons = (
<div className="row" style={{margin: 0}}> <div className="row" style={{margin: 0}}>
<p className="pull-right"> <p className="pull-right">
<Button className="btn btn-default btn-sm ascribe-margin-1px" type="submit">{this.props.buttonSubmitText}</Button> <Button
<Button className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.reset}>CANCEL</Button> className="btn btn-default btn-sm ascribe-margin-1px"
type="submit">
{this.props.buttonSubmitText}
</Button>
<Button
className="btn btn-danger btn-delete btn-sm ascribe-margin-1px"
type="reset">
CANCEL
</Button>
</p> </p>
</div> </div>
); );
@ -265,6 +281,7 @@ let Form = React.createClass({
role="form" role="form"
className={className} className={className}
onSubmit={this.submit} onSubmit={this.submit}
onReset={this.reset}
autoComplete={this.props.autoComplete}> autoComplete={this.props.autoComplete}>
{this.getFakeAutocompletableInputs()} {this.getFakeAutocompletableInputs()}
{this.getErrors()} {this.getErrors()}

View File

@ -69,10 +69,11 @@ let LoanForm = React.createClass({
}, },
handleOnChange(event) { handleOnChange(event) {
let potentialEmail = event.target.value; // event.target.value is the submitted email of the loanee
if(event && event.target && event.target.value && event.target.value.match(/.*@.*/)) {
if(potentialEmail.match(/.*@.*/)) { LoanContractActions.fetchLoanContract(event.target.value);
ContractActions.fetchContract(potentialEmail); } else {
LoanContractActions.flushLoanContract();
} }
}, },
@ -143,6 +144,7 @@ let LoanForm = React.createClass({
ref='form' ref='form'
url={this.props.url} url={this.props.url}
getFormData={this.getFormData} getFormData={this.getFormData}
onReset={this.handleOnChange}
handleSuccess={this.props.handleSuccess} handleSuccess={this.props.handleSuccess}
buttons={this.getButtons()} buttons={this.getButtons()}
spinner={ spinner={

View File

@ -7,16 +7,13 @@ import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import InputFineUploader from './input_fineuploader';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({ let RegisterPieceForm = React.createClass({
@ -45,7 +42,6 @@ let RegisterPieceForm = React.createClass({
getInitialState(){ getInitialState(){
return mergeOptions( return mergeOptions(
{ {
digitalWorkKey: null,
isUploadReady: false isUploadReady: false
}, },
UserStore.getState() UserStore.getState()
@ -65,18 +61,6 @@ let RegisterPieceForm = React.createClass({
this.setState(state); this.setState(state);
}, },
getFormData(){
return {
digital_work_key: this.state.digitalWorkKey
};
},
submitKey(key){
this.setState({
digitalWorkKey: key
});
},
setIsUploadReady(isReady) { setIsUploadReady(isReady) {
this.setState({ this.setState({
isUploadReady: isReady isUploadReady: isReady
@ -94,14 +78,15 @@ let RegisterPieceForm = React.createClass({
className="ascribe-form-bordered" className="ascribe-form-bordered"
ref='form' ref='form'
url={ApiUrls.pieces_list} url={ApiUrls.pieces_list}
getFormData={this.getFormData}
handleSuccess={this.props.handleSuccess} handleSuccess={this.props.handleSuccess}
buttons={<button buttons={
type="submit" <button
className="btn ascribe-btn ascribe-btn-login" type="submit"
disabled={!this.state.isUploadReady || this.props.disabled}> className="btn ascribe-btn ascribe-btn-login"
{this.props.submitMessage} disabled={!this.state.isUploadReady || this.props.disabled}>
</button>} {this.props.submitMessage}
</button>
}
spinner={ spinner={
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner"> <span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" /> <img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
@ -111,11 +96,11 @@ let RegisterPieceForm = React.createClass({
<h3>{this.props.headerMessage}</h3> <h3>{this.props.headerMessage}</h3>
</div> </div>
<Property <Property
name="digital_work_key"
ignoreFocus={true}> ignoreFocus={true}>
<FileUploader <InputFineUploader
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={isReadyForFormSubmission}
isFineUploaderActive={this.props.isFineUploaderActive} isFineUploaderActive={this.props.isFineUploaderActive}
onLoggedOut={this.props.onLoggedOut} onLoggedOut={this.props.onLoggedOut}
editable={this.props.isFineUploaderEditable} editable={this.props.isFineUploaderEditable}
@ -152,73 +137,4 @@ let RegisterPieceForm = React.createClass({
} }
}); });
let FileUploader = React.createClass({
propTypes: {
setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func,
onClick: React.PropTypes.func,
// isFineUploaderActive is used to lock react fine uploader in case
// a user is actually not logged in already to prevent him from droping files
// before login in
isFineUploaderActive: React.PropTypes.bool,
onLoggedOut: React.PropTypes.func,
editable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
// provided by Property
disabled: React.PropTypes.bool
},
render() {
let editable = this.props.isFineUploaderActive;
// if disabled is actually set by property, we want to override
// isFineUploaderActive
if(typeof this.props.disabled !== 'undefined') {
editable = !this.props.disabled;
}
return (
<ReactS3FineUploader
onClick={this.props.onClick}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork'
}}
createBlobRoutine={{
url: ApiUrls.blob_digitalworks
}}
validation={{
itemLimit: 100000,
sizeLimit: '25000000000'
}}
submitKey={this.props.submitKey}
setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
areAssetsDownloadable={false}
areAssetsEditable={editable}
signature={{
endpoint: AppConstants.serverUrl + 's3/signature/',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
onInactive={this.props.onLoggedOut}
enableLocalHashing={this.props.enableLocalHashing} />
);
}
});
export default RegisterPieceForm; export default RegisterPieceForm;

View File

@ -18,7 +18,8 @@ let InputDate = React.createClass({
getInitialState() { getInitialState() {
return { return {
value: null value: null,
value_moment: null
}; };
}, },
@ -45,6 +46,10 @@ let InputDate = React.createClass({
}); });
}, },
reset() {
this.setState(this.getInitialState());
},
render() { render() {
return ( return (
<div> <div>

View File

@ -0,0 +1,95 @@
'use strict';
import React from 'react';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls';
import { getCookie } from '../../utils/fetch_api_utils';
let InputFileUploader = React.createClass({
propTypes: {
setIsUploadReady: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func,
onClick: React.PropTypes.func,
// isFineUploaderActive is used to lock react fine uploader in case
// a user is actually not logged in already to prevent him from droping files
// before login in
isFineUploaderActive: React.PropTypes.bool,
onLoggedOut: React.PropTypes.func,
editable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
// provided by Property
disabled: React.PropTypes.bool
},
getInitialState() {
return {
value: null
};
},
submitKey(key){
this.setState({
value: key
});
},
reset() {
this.refs.fineuploader.reset();
},
render() {
let editable = this.props.isFineUploaderActive;
// if disabled is actually set by property, we want to override
// isFineUploaderActive
if(typeof this.props.disabled !== 'undefined') {
editable = !this.props.disabled;
}
return (
<ReactS3FineUploader
ref="fineuploader"
onClick={this.props.onClick}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork'
}}
createBlobRoutine={{
url: ApiUrls.blob_digitalworks
}}
submitKey={this.submitKey}
validation={{
itemLimit: 100000,
sizeLimit: '25000000000'
}}
setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
areAssetsDownloadable={false}
areAssetsEditable={editable}
signature={{
endpoint: AppConstants.serverUrl + 's3/signature/',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
onInactive={this.props.onLoggedOut}
enableLocalHashing={this.props.enableLocalHashing} />
);
}
});
export default InputFileUploader;

View File

@ -4,6 +4,7 @@ import React from 'react';
import TextareaAutosize from 'react-textarea-autosize'; import TextareaAutosize from 'react-textarea-autosize';
let InputTextAreaToggable = React.createClass({ let InputTextAreaToggable = React.createClass({
propTypes: { propTypes: {
editable: React.PropTypes.bool.isRequired, editable: React.PropTypes.bool.isRequired,
@ -17,14 +18,17 @@ let InputTextAreaToggable = React.createClass({
value: this.props.defaultValue value: this.props.defaultValue
}; };
}, },
handleChange(event) { handleChange(event) {
this.setState({value: event.target.value}); this.setState({value: event.target.value});
this.props.onChange(event); this.props.onChange(event);
}, },
render() { render() {
let className = 'form-control ascribe-textarea'; let className = 'form-control ascribe-textarea';
let textarea = null; let textarea = null;
if (this.props.editable){
if(this.props.editable) {
className = className + ' ascribe-textarea-editable'; className = className + ' ascribe-textarea-editable';
textarea = ( textarea = (
<TextareaAutosize <TextareaAutosize
@ -37,10 +41,10 @@ let InputTextAreaToggable = React.createClass({
onBlur={this.props.onBlur} onBlur={this.props.onBlur}
placeholder={this.props.placeholder} /> placeholder={this.props.placeholder} />
); );
} } else {
else{
textarea = <pre className="ascribe-pre">{this.state.value}</pre>; textarea = <pre className="ascribe-pre">{this.state.value}</pre>;
} }
return textarea; return textarea;
} }
}); });

View File

@ -29,8 +29,11 @@ let Property = React.createClass({
handleChange: React.PropTypes.func, handleChange: React.PropTypes.func,
ignoreFocus: React.PropTypes.bool, ignoreFocus: React.PropTypes.bool,
className: React.PropTypes.string, className: React.PropTypes.string,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
onChange: React.PropTypes.func, onChange: React.PropTypes.func,
onBlur: React.PropTypes.func,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element React.PropTypes.element
@ -98,12 +101,20 @@ let Property = React.createClass({
// resets the value of a plain HTML5 input // resets the value of a plain HTML5 input
this.refs.input.getDOMNode().value = this.state.initialValue; this.refs.input.getDOMNode().value = this.state.initialValue;
// For some inputs, reseting state.value is not enough to visually reset the
// component.
//
// So if the input actually needs a visual reset, it needs to implement
// a dedicated reset method.
if(this.refs.input.reset && typeof this.refs.input.reset === 'function') {
this.refs.input.reset();
}
}, },
handleChange(event) { handleChange(event) {
this.props.handleChange(event); this.props.handleChange(event);
if ('onChange' in this.props) { if (this.props.onChange && typeof this.props.onChange === 'function') {
this.props.onChange(event); this.props.onChange(event);
} }
@ -119,7 +130,7 @@ let Property = React.createClass({
// if onClick is defined from the outside, // if onClick is defined from the outside,
// just call it // just call it
if(this.props.onClick) { if(this.props.onClick && typeof this.props.onClick === 'function') {
this.props.onClick(); this.props.onClick();
} }
@ -134,7 +145,7 @@ let Property = React.createClass({
isFocused: false isFocused: false
}); });
if(this.props.onBlur) { if(this.props.onBlur && typeof this.props.onBlur === 'function') {
this.props.onBlur(event); this.props.onBlur(event);
} }
}, },
@ -192,6 +203,7 @@ let Property = React.createClass({
}, },
render() { render() {
let footer = null;
let tooltip = <span/>; let tooltip = <span/>;
let style = this.props.style ? mergeOptions({}, this.props.style) : {}; let style = this.props.style ? mergeOptions({}, this.props.style) : {};
@ -201,7 +213,7 @@ let Property = React.createClass({
{this.props.tooltip} {this.props.tooltip}
</Tooltip>); </Tooltip>);
} }
let footer = null;
if(this.props.footer){ if(this.props.footer){
footer = ( footer = (
<div className="ascribe-property-footer"> <div className="ascribe-property-footer">

View File

@ -42,6 +42,13 @@ let PropertyCollapsile = React.createClass({
} }
}, },
reset() {
// If the child input is a native HTML element, it will be reset automatically
// by the DOM.
// However, we need to collapse this component again.
this.setState(this.getInitialState());
},
render() { render() {
let tooltip = <span/>; let tooltip = <span/>;
if (this.props.tooltip){ if (this.props.tooltip){

View File

@ -28,12 +28,20 @@ let Other = React.createClass({
}, },
render() { render() {
let ext = this.props.url.split('.').pop(); let filename = this.props.url.split('/').pop();
let tokens = filename.split('.');
let preview;
if (tokens.length > 1) {
preview = '.' + tokens.pop();
} else {
preview = 'file';
}
return ( return (
<Panel className="media-other"> <Panel className="media-other">
<p className="text-center"> <p className="text-center">
.{ext} {preview}
</p> </p>
</Panel> </Panel>
); );

View File

@ -4,13 +4,12 @@ import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import ReactAddons from 'react/addons'; import ReactAddons from 'react/addons';
import Col from 'react-bootstrap/lib/Col';
import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs'; import SlidesContainerBreadcrumbs from './slides_container_breadcrumbs';
let State = Router.State; let State = Router.State;
let Navigation = Router.Navigation; let Navigation = Router.Navigation;
let SlidesContainer = React.createClass({ let SlidesContainer = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.arrayOf(React.PropTypes.element), children: React.PropTypes.arrayOf(React.PropTypes.element),
@ -30,12 +29,15 @@ let SlidesContainer = React.createClass({
let slideNum = -1; let slideNum = -1;
let startFrom = -1; let startFrom = -1;
// We can actually need to check if slide_num is present as a key in queryParams.
// We do not really care about its value though...
if(queryParams && 'slide_num' in queryParams) { if(queryParams && 'slide_num' in queryParams) {
slideNum = parseInt(queryParams.slide_num, 10); slideNum = parseInt(queryParams.slide_num, 10);
} }
// if slide_num is not set, this will be done in componentDidMount // if slide_num is not set, this will be done in componentDidMount
// the query param 'start_from' removes all slide children before the respective number // the query param 'start_from' removes all slide children before the respective number
// Also, we use the 'in' keyword for the same reason as above in 'slide_num'
if(queryParams && 'start_from' in queryParams) { if(queryParams && 'start_from' in queryParams) {
startFrom = parseInt(queryParams.start_from, 10); startFrom = parseInt(queryParams.start_from, 10);
} }
@ -51,6 +53,9 @@ let SlidesContainer = React.createClass({
componentDidMount() { componentDidMount() {
// check if slide_num was defined, and if not then default to 0 // check if slide_num was defined, and if not then default to 0
let queryParams = this.getQuery(); let queryParams = this.getQuery();
// We use 'in' to check if the key is present in the user's browser url bar,
// we do not really care about its value at this point
if(!('slide_num' in queryParams)) { if(!('slide_num' in queryParams)) {
// we're first requiring all the other possible queryParams and then set // we're first requiring all the other possible queryParams and then set
@ -241,6 +246,16 @@ let SlidesContainer = React.createClass({
}, },
render() { render() {
let spacing = this.state.containerWidth * this.state.slideNum;
let translateXValue = 'translateX(' + (-1) * spacing + 'px)';
/*
According to the react documentation,
all browser vendor prefixes need to be upper cases in the beginning except for
the Microsoft one *bigfuckingsurprise*
https://facebook.github.io/react/tips/inline-styles.html
*/
return ( return (
<div <div
className="container ascribe-sliding-container-wrapper" className="container ascribe-sliding-container-wrapper"
@ -250,7 +265,11 @@ let SlidesContainer = React.createClass({
className="container ascribe-sliding-container" className="container ascribe-sliding-container"
style={{ style={{
width: this.state.containerWidth * this.customChildrenCount(), width: this.state.containerWidth * this.customChildrenCount(),
transform: 'translateX(' + (-1) * this.state.containerWidth * this.state.slideNum + 'px)' transform: translateXValue,
WebkitTransform: translateXValue,
MozTransform: translateXValue,
OTransform: translateXValue,
mstransform: translateXValue
}}> }}>
<div className="row"> <div className="row">
{this.renderChildren()} {this.renderChildren()}

View File

@ -20,7 +20,6 @@ import AppConstants from '../../constants/application_constants';
import { computeHashOfFile } from '../../utils/file_utils'; import { computeHashOfFile } from '../../utils/file_utils';
var ReactS3FineUploader = React.createClass({ var ReactS3FineUploader = React.createClass({
propTypes: { propTypes: {
keyRoutine: React.PropTypes.shape({ keyRoutine: React.PropTypes.shape({
url: React.PropTypes.string, url: React.PropTypes.string,
@ -125,6 +124,7 @@ var ReactS3FineUploader = React.createClass({
bucket: 'ascribe0' bucket: 'ascribe0'
}, },
request: { request: {
//endpoint: 'https://www.ascribe.io.global.prod.fastly.net',
endpoint: 'https://ascribe0.s3.amazonaws.com', endpoint: 'https://ascribe0.s3.amazonaws.com',
accessKey: 'AKIAIVCZJ33WSCBQ3QDA' accessKey: 'AKIAIVCZJ33WSCBQ3QDA'
}, },
@ -235,6 +235,21 @@ var ReactS3FineUploader = React.createClass({
}; };
}, },
// Resets the whole react fineuploader component to its initial state
reset() {
// Cancel all currently ongoing uploads
this.state.uploader.cancelAll();
// and reset component in general
this.state.uploader.reset();
// proclaim that upload is not ready
this.props.setIsUploadReady(false);
// reset internal data structures of component
this.setState(this.getInitialState());
},
requestKey(fileId) { requestKey(fileId) {
let filename = this.state.uploader.getName(fileId); let filename = this.state.uploader.getName(fileId);
let uuid = this.state.uploader.getUuid(fileId); let uuid = this.state.uploader.getUuid(fileId);
@ -356,64 +371,59 @@ var ReactS3FineUploader = React.createClass({
onComplete(id, name, res, xhr) { onComplete(id, name, res, xhr) {
// there has been an issue with the server's connection // there has been an issue with the server's connection
if(xhr.status === 0) { if((xhr && xhr.status === 0) || res.error) {
console.logGlobal(new Error(res.error || 'Complete was called but there wasn\t a success'), false, {
console.logGlobal(new Error('Complete was called but there wasn\t a success'), false, {
files: this.state.filesToUpload, files: this.state.filesToUpload,
chunks: this.state.chunks chunks: this.state.chunks
}); });
} else {
let files = this.state.filesToUpload;
return; // Set the state of the completed file to 'upload successful' in order to
} // remove it from the GUI
files[id].status = 'upload successful';
files[id].key = this.state.uploader.getKey(id);
let files = this.state.filesToUpload; let newState = React.addons.update(this.state, {
filesToUpload: { $set: files }
// Set the state of the completed file to 'upload successful' in order to
// remove it from the GUI
files[id].status = 'upload successful';
files[id].key = this.state.uploader.getKey(id);
let newState = React.addons.update(this.state, {
filesToUpload: { $set: files }
});
this.setState(newState);
// Only after the blob has been created server-side, we can make the form submittable.
this.createBlob(files[id])
.then(() => {
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey
// are optional, we'll only trigger them when they're actually defined
if(this.props.submitKey) {
this.props.submitKey(files[id].key);
} else {
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader');
}
// for explanation, check comment of if statement above
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
// also, lets check if after the completion of this upload,
// the form is ready for submission or not
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
// if so, set uploadstatus to true
this.props.setIsUploadReady(true);
} else {
this.props.setIsUploadReady(false);
}
} else {
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
}
})
.catch((err) => {
console.logGlobal(err, false, {
files: this.state.filesToUpload,
chunks: this.state.chunks
});
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
}); });
this.setState(newState);
// Only after the blob has been created server-side, we can make the form submittable.
this.createBlob(files[id])
.then(() => {
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey
// are optional, we'll only trigger them when they're actually defined
if(this.props.submitKey) {
this.props.submitKey(files[id].key);
} else {
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader');
}
// for explanation, check comment of if statement above
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
// also, lets check if after the completion of this upload,
// the form is ready for submission or not
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
// if so, set uploadstatus to true
this.props.setIsUploadReady(true);
} else {
this.props.setIsUploadReady(false);
}
} else {
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
}
})
.catch((err) => {
console.logGlobal(err, false, {
files: this.state.filesToUpload,
chunks: this.state.chunks
});
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
});
}
}, },
onError(id, name, errorReason) { onError(id, name, errorReason) {
@ -442,7 +452,6 @@ var ReactS3FineUploader = React.createClass({
}, },
onCancel(id) { onCancel(id) {
// when a upload is canceled, we need to update this components file array // when a upload is canceled, we need to update this components file array
this.setStatusOfFile(id, 'canceled'); this.setStatusOfFile(id, 'canceled');
@ -464,7 +473,6 @@ var ReactS3FineUploader = React.createClass({
}, },
onProgress(id, name, uploadedBytes, totalBytes) { onProgress(id, name, uploadedBytes, totalBytes) {
let newState = React.addons.update(this.state, { let newState = React.addons.update(this.state, {
filesToUpload: { [id]: { filesToUpload: { [id]: {
progress: { $set: (uploadedBytes / totalBytes) * 100} } progress: { $set: (uploadedBytes / totalBytes) * 100} }

View File

@ -102,14 +102,23 @@ let PieceContainer = React.createClass({
}, },
render() { render() {
if('title' in this.state.piece) { if(this.state.piece && this.state.piece.title) {
/*
This really needs a refactor!
- Tim
*/
// Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted // Only show the artist name if you are the participant or if you are a judge and the piece is shortlisted
let artistName = ((this.state.currentUser.is_jury && !this.state.currentUser.is_judge) || let artistName = ((this.state.currentUser.is_jury && !this.state.currentUser.is_judge) ||
(this.state.currentUser.is_judge && !this.state.piece.selected )) ? (this.state.currentUser.is_judge && !this.state.piece.selected )) ?
<span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : this.state.piece.artist_name; <span className="glyphicon glyphicon-eye-close" aria-hidden="true"/> : this.state.piece.artist_name;
// Only show the artist email if you are a judge and the piece is shortlisted // Only show the artist email if you are a judge and the piece is shortlisted
let artistEmail = (this.state.currentUser.is_judge && this.state.piece.selected ) ? let artistEmail = (this.state.currentUser.is_judge && this.state.piece.selected ) ?
<DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } /> : null; <DetailProperty label={getLangText('REGISTREE')} value={ this.state.piece.user_registered } /> : null;
return ( return (
<Piece <Piece
piece={this.state.piece} piece={this.state.piece}

View File

@ -68,7 +68,7 @@ let CylandPieceContainer = React.createClass({
}, },
render() { render() {
if('title' in this.state.piece) { if(this.state.piece && this.state.piece.title) {
return ( return (
<Piece <Piece
piece={this.state.piece} piece={this.state.piece}

View File

@ -72,6 +72,9 @@ let CylandRegisterPiece = React.createClass({
// we may need to enter the process at step 1 or 2. // we may need to enter the process at step 1 or 2.
// If this is the case, we'll need the piece number to complete submission. // If this is the case, we'll need the piece number to complete submission.
// It is encoded in the URL as a queryParam and we're checking for it here. // It is encoded in the URL as a queryParam and we're checking for it here.
//
// We're using 'in' here as we want to know if 'piece_id' is present in the url,
// we don't care about the value.
if(queryParams && 'piece_id' in queryParams) { if(queryParams && 'piece_id' in queryParams) {
PieceActions.fetchOne(queryParams.piece_id); PieceActions.fetchOne(queryParams.piece_id);
} }

View File

@ -58,7 +58,7 @@ let IkonotvSubmitButton = React.createClass({
<InputCheckbox> <InputCheckbox>
<span> <span>
{' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '} {' ' + getLangText('I agree to the Terms of Service of IkonoTV Archive') + ' '}
(<a href="https://d1qjsxua1o9x03.cloudfront.net/live/743394beff4b1282ba735e5e3723ed74/contract/bbc92f1d-4504-49f8-818c-8dd7113c6e06.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}> (<a href="https://s3-us-west-2.amazonaws.com/ascribe0/whitelabel/ikonotv/ikono-tos.pdf" target="_blank" style={{fontSize: '0.9em', color: 'rgba(0,0,0,0.7)'}}>
{getLangText('read')} {getLangText('read')}
</a>) </a>)
</span> </span>

View File

@ -132,7 +132,7 @@ let IkonotvPieceContainer = React.createClass({
}, },
render() { render() {
if('title' in this.state.piece) { if(this.state.piece && this.state.piece.title) {
return ( return (
<Piece <Piece
piece={this.state.piece} piece={this.state.piece}

View File

@ -3,35 +3,40 @@
video, video,
img { img {
display: block;
height: auto;
margin: 0 auto;
max-height: 640px;
max-width: 100%; max-width: 100%;
max-height: 640px;
width: auto; width: auto;
height: auto;
display: block;
margin: 0 auto;
} }
.media-other { .media-other {
color: #cccccc; color: #cccccc;
font-size: 500%; font-size: 500%;
p {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
} }
.audiojs { .audiojs {
background-image: none;
margin: 50px auto; margin: 50px auto;
* { background-image: none;
box-sizing: content-box; }
}
.loaded { .audiojs * {
background-color: $ascribe-color-green; box-sizing: content-box;
background-image: none; }
}
.progress { .audiojs .loaded {
background-color: rgba(255, 255, 255, .8); background-color: $ascribe-color-green;
background-image: none; background-image: none;
} }
.audiojs .progress {
background-color: rgba(255,255,255,0.8);
background-image: none;
} }
.video-js, .video-js,
@ -44,13 +49,12 @@
} }
.vjs-fullscreen { .vjs-fullscreen {
padding-top: 0; padding-top: 0px;
video {
max-height: 100%;
}
} }
.vjs-fullscreen video {
max-height: 100%;
}
.vjs-default-skin .vjs-play-progress, .vjs-default-skin .vjs-play-progress,
.vjs-default-skin .vjs-volume-level { .vjs-default-skin .vjs-volume-level {
@ -58,35 +62,41 @@
} }
.vjs-default-skin .vjs-big-play-button { .vjs-default-skin .vjs-big-play-button {
background-color: rgba(0, 0, 0, .8);
border: 0;
-moz-border-radius: 6px;
-o-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px; border-radius: 6px;
-moz-box-shadow: none; -o-border-radius: 6px;
-o-box-shadow: none; -moz-border-radius: 6px;
-webkit-box-shadow: none; -webkit-border-radius: 6px;
box-shadow: none; box-shadow: none;
-o-box-shadow: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
width: 100px;
height: 60px; height: 60px;
top: 50%;
left: 50%; left: 50%;
margin: -30px -50px; margin: -30px -50px;
top: 50%;
width: 100px; border: none;
background-color: rgba(0,0,0,.8);
} }
.vjs-default-skin:hover .vjs-big-play-button, .vjs-default-skin:hover .vjs-big-play-button,
.vjs-default-skin .vjs-big-play-button:focus { .vjs-default-skin .vjs-big-play-button:focus {
background-color: rgba(0, 0, 0, .9);
border-color: #fff; border-color: #fff;
-moz-box-shadow: none; background-color: rgba(0,0,0,.9);
-o-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
-moz-transition: all 0s; -o-box-shadow: none;
-o-transition: all 0s; -moz-box-shadow: none;
-webkit-transition: all 0s; -webkit-box-shadow: none;
transition: all 0s; transition: all 0s;
-o-transition: all 0s;
-moz-transition: all 0s;
-webkit-transition: all 0s;
} }
.vjs-default-skin .vjs-big-play-button:before { .vjs-default-skin .vjs-big-play-button:before {
@ -103,7 +113,7 @@
} }
.vjs-default-skin .vjs-control-bar { .vjs-default-skin .vjs-control-bar {
background-color: rgba(0, 0, 0, .7); background-color: rgba(0,0,0,.7);
} }
} }

0
sass/lib/buttons.scss Normal file
View File

View File

@ -209,36 +209,20 @@ hr {
border: 1px solid $ascribe-brand-danger; border: 1px solid $ascribe-brand-danger;
} }
} }
.btn-ascribe {
.btn-ascribe,
.btn-ascribe-inv {
border: 1px solid #444; border: 1px solid #444;
border-radius: 0 !important; border-radius: 0 !important;
font-family: sans-serif !important; font-family: sans-serif !important;
line-height: 2em; line-height: 2em;
margin-left: 0 !important; margin-left: 0 !important;
margin-right: 1px; font-family: sans-serif !important;
} border-radius: 0 !important;
.btn-ascribe,
.btn-ascribe-inv:active,
.btn-ascribe-inv:hover {
background-color: #fff;
color: #222 !important; color: #222 !important;
} }
.btn-ascribe:active, .btn-ascribe:active, .btn-ascribe:hover {
.btn-ascribe:hover, color: #FFF !important;
.btn-ascribe-inv {
background-color: #444; background-color: #444;
color: #fff !important;
}
.btn-ascribe-inv:disabled,
.btn-ascribe-inv:focus {
background-color: #BBB !important;
border: 1px solid #444 !important;
color: #444 !important;
} }
.btn-ascribe-sm { .btn-ascribe-sm {