mirror of
https://github.com/ascribe/onion.git
synced 2025-02-14 21:10:27 +01:00
q
Merge branch 'master' of bitbucket.org:ascribe/onion
This commit is contained in:
commit
dceb087f20
12
gulpfile.js
12
gulpfile.js
@ -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'))
|
||||||
|
@ -45,7 +45,6 @@ requests.defaults({
|
|||||||
|
|
||||||
|
|
||||||
class AppGateway {
|
class AppGateway {
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
let settings;
|
let settings;
|
||||||
let subdomain = window.location.host.split('.')[0];
|
let subdomain = window.location.host.split('.')[0];
|
||||||
|
@ -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;
|
|
@ -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;
|
|
@ -50,7 +50,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}
|
||||||
|
@ -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
|
||||||
|
@ -206,7 +206,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}
|
||||||
|
@ -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(typeof this.props.onReset === 'function') {
|
||||||
|
this.props.onReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let ref in this.refs) {
|
||||||
|
if(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 + ')');
|
||||||
@ -100,13 +107,14 @@ 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(typeof this.props.getFormData === 'function') {
|
||||||
data = mergeOptionsWithDuplicates(data, this.props.getFormData());
|
data = mergeOptionsWithDuplicates(data, this.props.getFormData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,15 +122,16 @@ let Form = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleChangeChild(){
|
handleChangeChild(){
|
||||||
this.setState({edited: true});
|
this.setState({ edited: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSuccess(response){
|
handleSuccess(response){
|
||||||
if ('handleSuccess' in this.props){
|
if(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] && typeof this.refs[ref].handleSuccess === 'function'){
|
||||||
this.refs[ref].handleSuccess();
|
this.refs[ref].handleSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,15 +143,14 @@ 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])});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let formData = this.getFormData();
|
let formData = this.getFormData();
|
||||||
|
|
||||||
// sentry shouldn't post the user's password
|
// sentry shouldn't post the user's password
|
||||||
@ -164,8 +172,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] && typeof this.refs[ref].clearErrors === 'function'){
|
||||||
this.refs[ref].clearErrors();
|
this.refs[ref].clearErrors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,8 +193,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>
|
||||||
);
|
);
|
||||||
@ -251,6 +267,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()}
|
||||||
|
@ -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);
|
||||||
LoanContractActions.fetchLoanContract(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={
|
||||||
|
@ -7,13 +7,10 @@ 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 { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
import { isReadyForFormSubmission } from '../ascribe_uploader/react_s3_fine_uploader_utils';
|
||||||
@ -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,9 +96,9 @@ 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={isReadyForFormSubmission}
|
isReadyForFormSubmission={isReadyForFormSubmission}
|
||||||
isFineUploaderActive={this.props.isFineUploaderActive}
|
isFineUploaderActive={this.props.isFineUploaderActive}
|
||||||
@ -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
|
|
||||||
}}
|
|
||||||
submitKey={this.props.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 RegisterPieceForm;
|
export default RegisterPieceForm;
|
||||||
|
@ -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>
|
||||||
|
95
js/components/ascribe_forms/input_fineuploader.js
Normal file
95
js/components/ascribe_forms/input_fineuploader.js
Normal 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;
|
@ -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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,11 @@ import ReactAddons from 'react/addons';
|
|||||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||||
|
|
||||||
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { mergeOptions } from '../../utils/general_utils';
|
import { mergeOptions } from '../../utils/general_utils';
|
||||||
|
|
||||||
|
|
||||||
let Property = React.createClass({
|
let Property = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
hidden: React.PropTypes.bool,
|
hidden: React.PropTypes.bool,
|
||||||
@ -29,8 +32,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
|
||||||
@ -87,21 +93,41 @@ let Property = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
|
let input = this.refs.input;
|
||||||
|
|
||||||
// maybe do reset by reload instead of front end state?
|
// maybe do reset by reload instead of front end state?
|
||||||
this.setState({value: this.state.initialValue});
|
this.setState({value: this.state.initialValue});
|
||||||
|
|
||||||
// resets the value of a custom react component input
|
if(input.state && input.state.value) {
|
||||||
this.refs.input.state.value = this.state.initialValue;
|
// resets the value of a custom react component input
|
||||||
|
input.state.value = this.state.initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
// resets the value of a plain HTML5 input
|
// For some reason, if we set the value of a non HTML element (but a custom input),
|
||||||
this.refs.input.getDOMNode().value = this.state.initialValue;
|
// after a reset, the value will be be propagated to this component.
|
||||||
|
//
|
||||||
|
// Therefore we have to make sure only to reset the initial value
|
||||||
|
// of HTML inputs (which we determine by checking if there 'type' attribute matches
|
||||||
|
// the ones included in AppConstants.possibleInputTypes).
|
||||||
|
let inputDOMNode = input.getDOMNode();
|
||||||
|
if(inputDOMNode.type && typeof inputDOMNode.type === 'string' &&
|
||||||
|
AppConstants.possibleInputTypes.indexOf(inputDOMNode.type.toLowerCase()) > -1) {
|
||||||
|
inputDOMNode.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(typeof input.reset === 'function') {
|
||||||
|
input.reset();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleChange(event) {
|
handleChange(event) {
|
||||||
|
|
||||||
this.props.handleChange(event);
|
this.props.handleChange(event);
|
||||||
if ('onChange' in this.props) {
|
if (typeof this.props.onChange === 'function') {
|
||||||
this.props.onChange(event);
|
this.props.onChange(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +143,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(typeof this.props.onClick === 'function') {
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +158,7 @@ let Property = React.createClass({
|
|||||||
isFocused: false
|
isFocused: false
|
||||||
});
|
});
|
||||||
|
|
||||||
if(this.props.onBlur) {
|
if(typeof this.props.onBlur === 'function') {
|
||||||
this.props.onBlur(event);
|
this.props.onBlur(event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -190,6 +216,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) : {};
|
||||||
|
|
||||||
@ -199,7 +226,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">
|
||||||
@ -223,7 +250,7 @@ let Property = React.createClass({
|
|||||||
overlay={tooltip}>
|
overlay={tooltip}>
|
||||||
<div className={'ascribe-settings-property ' + this.props.className}>
|
<div className={'ascribe-settings-property ' + this.props.className}>
|
||||||
{this.state.errors}
|
{this.state.errors}
|
||||||
<span>{ this.props.label}</span>
|
<span>{this.props.label}</span>
|
||||||
{this.renderChildren(style)}
|
{this.renderChildren(style)}
|
||||||
{footer}
|
{footer}
|
||||||
</div>
|
</div>
|
||||||
|
@ -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){
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
@ -200,7 +208,8 @@ let MediaPlayer = React.createClass({
|
|||||||
<br />You can leave this page and check back on the status later.</em>
|
<br />You can leave this page and check back on the status later.</em>
|
||||||
</p>
|
</p>
|
||||||
<ProgressBar now={this.props.encodingStatus}
|
<ProgressBar now={this.props.encodingStatus}
|
||||||
label='%(percent)s%' />
|
label="%(percent)s%"
|
||||||
|
className="ascribe-progress-bar" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -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
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ProgressBar from 'react-progressbar';
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
|
|
||||||
import FileDragAndDropDialog from './file_drag_and_drop_dialog';
|
import FileDragAndDropDialog from './file_drag_and_drop_dialog';
|
||||||
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
|
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
|
||||||
@ -169,12 +169,16 @@ let FileDragAndDrop = React.createClass({
|
|||||||
if(this.props.hashingProgress !== -2) {
|
if(this.props.hashingProgress !== -2) {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<p>{getLangText('Computing hash(es)... This may take a few minutes.')}</p>
|
<div className="file-drag-and-drop-hashing-dialog">
|
||||||
<p>
|
<p>{getLangText('Computing hash(es)... This may take a few minutes.')}</p>
|
||||||
<span>{Math.ceil(this.props.hashingProgress)}%</span>
|
<p>
|
||||||
<a onClick={this.props.handleCancelHashing}> {getLangText('Cancel hashing')}</a>
|
<a onClick={this.props.handleCancelHashing}> {getLangText('Cancel hashing')}</a>
|
||||||
</p>
|
</p>
|
||||||
<ProgressBar completed={this.props.hashingProgress} color="#48DACB"/>
|
<ProgressBar
|
||||||
|
now={Math.ceil(this.props.hashingProgress)}
|
||||||
|
label="%(percent)s%"
|
||||||
|
className="ascribe-progress-bar"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,7 +32,7 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
queryParamsUpload.method = 'upload';
|
queryParamsUpload.method = 'upload';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="file-drag-and-drop-dialog present-options">
|
<div className="file-drag-and-drop-dialog present-options">
|
||||||
<p>{getLangText('Would you rather')}</p>
|
<p>{getLangText('Would you rather')}</p>
|
||||||
<Link
|
<Link
|
||||||
to={this.getPath()}
|
to={this.getPath()}
|
||||||
@ -51,12 +51,12 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
{getLangText('Upload and hash your work')}
|
{getLangText('Upload and hash your work')}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if(this.props.multipleFiles) {
|
if(this.props.multipleFiles) {
|
||||||
return (
|
return (
|
||||||
<span className="file-drag-and-drop-dialog">
|
<div className="file-drag-and-drop-dialog">
|
||||||
<p>{getLangText('Drag files here')}</p>
|
<p>{getLangText('Drag files here')}</p>
|
||||||
<p>{getLangText('or')}</p>
|
<p>{getLangText('or')}</p>
|
||||||
<span
|
<span
|
||||||
@ -64,13 +64,13 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
onClick={this.props.onClick}>
|
onClick={this.props.onClick}>
|
||||||
{getLangText('choose files to upload')}
|
{getLangText('choose files to upload')}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let dialog = queryParams.method === 'hash' ? getLangText('choose a file to hash') : getLangText('choose a file to upload');
|
let dialog = queryParams.method === 'hash' ? getLangText('choose a file to hash') : getLangText('choose a file to upload');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="file-drag-and-drop-dialog">
|
<div className="file-drag-and-drop-dialog">
|
||||||
<p>{getLangText('Drag a file here')}</p>
|
<p>{getLangText('Drag a file here')}</p>
|
||||||
<p>{getLangText('or')}</p>
|
<p>{getLangText('or')}</p>
|
||||||
<span
|
<span
|
||||||
@ -78,7 +78,7 @@ let FileDragAndDropDialog = React.createClass({
|
|||||||
onClick={this.props.onClick}>
|
onClick={this.props.onClick}>
|
||||||
{dialog}
|
{dialog}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ let FileDragAndDropPreview = React.createClass({
|
|||||||
|
|
||||||
if(this.props.areAssetsEditable) {
|
if(this.props.areAssetsEditable) {
|
||||||
removeBtn = (<div className="delete-file">
|
removeBtn = (<div className="delete-file">
|
||||||
<span
|
<span
|
||||||
className="glyphicon glyphicon-remove text-center"
|
className="glyphicon glyphicon-remove text-center"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
title={getLangText('Remove file')}
|
title={getLangText('Remove file')}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ProgressBar from 'react-progressbar';
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
@ -60,7 +60,9 @@ let FileDragAndDropPreviewImage = React.createClass({
|
|||||||
<div
|
<div
|
||||||
className="file-drag-and-drop-preview-image"
|
className="file-drag-and-drop-preview-image"
|
||||||
style={imageStyle}>
|
style={imageStyle}>
|
||||||
<ProgressBar completed={this.props.progress} color="black"/>
|
<ProgressBar
|
||||||
|
now={Math.ceil(this.props.progress)}
|
||||||
|
className="ascribe-progress-bar ascribe-progress-bar-xs"/>
|
||||||
{actionSymbol}
|
{actionSymbol}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import FileDragAndDropPreview from './file_drag_and_drop_preview';
|
import FileDragAndDropPreview from './file_drag_and_drop_preview';
|
||||||
|
import FileDragAndDropPreviewProgress from './file_drag_and_drop_preview_progress';
|
||||||
|
|
||||||
|
import { displayValidFilesFilter } from './react_s3_fine_uploader_utils';
|
||||||
|
|
||||||
|
|
||||||
let FileDragAndDropPreviewIterator = React.createClass({
|
let FileDragAndDropPreviewIterator = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -16,26 +20,37 @@ let FileDragAndDropPreviewIterator = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if(this.props.files) {
|
let {
|
||||||
|
files,
|
||||||
|
handleDeleteFile,
|
||||||
|
handleCancelFile,
|
||||||
|
handlePauseFile,
|
||||||
|
handleResumeFile,
|
||||||
|
areAssetsDownloadable,
|
||||||
|
areAssetsEditable
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
files = files.filter(displayValidFilesFilter);
|
||||||
|
|
||||||
|
if(files && files.length > 0) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="file-drag-and-drop-preview-iterator">
|
||||||
{this.props.files.map((file, i) => {
|
<div className="file-drag-and-drop-preview-iterator-spacing">
|
||||||
if(file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1) {
|
{files.map((file, i) => {
|
||||||
return (
|
return (
|
||||||
<FileDragAndDropPreview
|
<FileDragAndDropPreview
|
||||||
key={i}
|
key={i}
|
||||||
file={file}
|
file={file}
|
||||||
handleDeleteFile={this.props.handleDeleteFile}
|
handleDeleteFile={handleDeleteFile}
|
||||||
handleCancelFile={this.props.handleCancelFile}
|
handleCancelFile={handleCancelFile}
|
||||||
handlePauseFile={this.props.handlePauseFile}
|
handlePauseFile={handlePauseFile}
|
||||||
handleResumeFile={this.props.handleResumeFile}
|
handleResumeFile={handleResumeFile}
|
||||||
areAssetsDownloadable={this.props.areAssetsDownloadable}
|
areAssetsDownloadable={areAssetsDownloadable}
|
||||||
areAssetsEditable={this.props.areAssetsEditable}/>
|
areAssetsEditable={areAssetsEditable}/>
|
||||||
);
|
);
|
||||||
} else {
|
})}
|
||||||
return null;
|
</div>
|
||||||
}
|
<FileDragAndDropPreviewProgress files={files} />
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ProgressBar from 'react-progressbar';
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
import { getLangText } from '../../utils/lang_utils.js';
|
import { getLangText } from '../../utils/lang_utils.js';
|
||||||
@ -55,7 +55,9 @@ let FileDragAndDropPreviewOther = React.createClass({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="file-drag-and-drop-preview">
|
className="file-drag-and-drop-preview">
|
||||||
<ProgressBar completed={this.props.progress} color="black"/>
|
<ProgressBar
|
||||||
|
now={Math.ceil(this.props.progress)}
|
||||||
|
className="ascribe-progress-bar ascribe-progress-bar-xs"/>
|
||||||
<div className="file-drag-and-drop-preview-table-wrapper">
|
<div className="file-drag-and-drop-preview-table-wrapper">
|
||||||
<div className="file-drag-and-drop-preview-other">
|
<div className="file-drag-and-drop-preview-other">
|
||||||
{actionSymbol}
|
{actionSymbol}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||||
|
|
||||||
|
import { displayValidProgressFilesFilter } from './react_s3_fine_uploader_utils';
|
||||||
|
|
||||||
|
|
||||||
|
let FileDragAndDropPreviewProgress = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
files: React.PropTypes.array
|
||||||
|
},
|
||||||
|
|
||||||
|
calcOverallFileSize() {
|
||||||
|
let overallFileSize = 0;
|
||||||
|
let files = this.props.files.filter(displayValidProgressFilesFilter);
|
||||||
|
|
||||||
|
// We just sum up all files' sizes
|
||||||
|
for(let i = 0; i < files.length; i++) {
|
||||||
|
overallFileSize += files[i].size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return overallFileSize;
|
||||||
|
},
|
||||||
|
|
||||||
|
calcOverallProgress() {
|
||||||
|
let overallProgress = 0;
|
||||||
|
let overallFileSize = this.calcOverallFileSize();
|
||||||
|
let files = this.props.files.filter(displayValidProgressFilesFilter);
|
||||||
|
|
||||||
|
// We calculate the overall progress by summing the individuals
|
||||||
|
// files' progresses in relation to their size
|
||||||
|
for(let i = 0; i < files.length; i++) {
|
||||||
|
overallProgress += files[i].size / overallFileSize * files[i].progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return overallProgress;
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let overallProgress = this.calcOverallProgress();
|
||||||
|
let overallFileSize = this.calcOverallFileSize();
|
||||||
|
let style = {
|
||||||
|
visibility: 'hidden'
|
||||||
|
};
|
||||||
|
|
||||||
|
// only visible if overallProgress is over zero
|
||||||
|
// or the overallFileSize is greater than 10MB
|
||||||
|
if(overallProgress !== 0 && overallFileSize > 10000000) {
|
||||||
|
style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProgressBar
|
||||||
|
now={Math.ceil(overallProgress)}
|
||||||
|
label="Overall progress: %(percent)s%"
|
||||||
|
className="ascribe-progress-bar"
|
||||||
|
style={style} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FileDragAndDropPreviewProgress;
|
@ -17,10 +17,9 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
|
|||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import AppConstants from '../../constants/application_constants';
|
||||||
|
|
||||||
import { computeHashOfFile } from '../../utils/file_utils';
|
import { computeHashOfFile, displayValidFilesFilter } 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);
|
||||||
@ -325,11 +340,9 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
completed: false
|
completed: false
|
||||||
};
|
};
|
||||||
|
|
||||||
let newState = React.addons.update(this.state, {
|
let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks });
|
||||||
startedChunks: { $set: chunks }
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState({ startedChunks });
|
||||||
},
|
},
|
||||||
|
|
||||||
onUploadChunkSuccess(id, chunkData, responseJson, xhr) {
|
onUploadChunkSuccess(id, chunkData, responseJson, xhr) {
|
||||||
@ -342,75 +355,65 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
chunks[chunkKey].responseJson = responseJson;
|
chunks[chunkKey].responseJson = responseJson;
|
||||||
chunks[chunkKey].xhr = xhr;
|
chunks[chunkKey].xhr = xhr;
|
||||||
|
|
||||||
let newState = React.addons.update(this.state, {
|
let startedChunks = React.addons.update(this.state.startedChunks, { $set: chunks });
|
||||||
startedChunks: { $set: chunks }
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState({ startedChunks });
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
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 filesToUpload = React.addons.update(this.state.filesToUpload, { $set: files });
|
||||||
|
this.setState({ filesToUpload });
|
||||||
|
|
||||||
// Set the state of the completed file to 'upload successful' in order to
|
// Only after the blob has been created server-side, we can make the form submittable.
|
||||||
// remove it from the GUI
|
this.createBlob(files[id])
|
||||||
files[id].status = 'upload successful';
|
.then(() => {
|
||||||
files[id].key = this.state.uploader.getKey(id);
|
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey
|
||||||
|
// are optional, we'll only trigger them when they're actually defined
|
||||||
let newState = React.addons.update(this.state, {
|
if(this.props.submitKey) {
|
||||||
filesToUpload: { $set: files }
|
this.props.submitKey(files[id].key);
|
||||||
});
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
this.props.setIsUploadReady(false);
|
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader');
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady 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,
|
||||||
.catch((err) => {
|
// the form is ready for submission or not
|
||||||
console.logGlobal(err, false, {
|
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
||||||
files: this.state.filesToUpload,
|
// if so, set uploadstatus to true
|
||||||
chunks: this.state.chunks
|
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);
|
||||||
});
|
});
|
||||||
let notification = new GlobalNotificationModel(err.message, 'danger', 5000);
|
}
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onError(id, name, errorReason) {
|
onError(id, name, errorReason) {
|
||||||
@ -439,7 +442,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');
|
||||||
|
|
||||||
@ -458,16 +460,17 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
} else {
|
} else {
|
||||||
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
console.warn('You didn\'t define the functions isReadyForFormSubmission and/or setIsUploadReady in as a prop in react-s3-fine-uploader');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
onProgress(id, name, uploadedBytes, totalBytes) {
|
onProgress(id, name, uploadedBytes, totalBytes) {
|
||||||
|
let filesToUpload = React.addons.update(this.state.filesToUpload, {
|
||||||
let newState = React.addons.update(this.state, {
|
[id]: {
|
||||||
filesToUpload: { [id]: {
|
progress: { $set: (uploadedBytes / totalBytes) * 100}
|
||||||
progress: { $set: (uploadedBytes / totalBytes) * 100} }
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.setState(newState);
|
this.setState({ filesToUpload });
|
||||||
},
|
},
|
||||||
|
|
||||||
onSessionRequestComplete(response, success) {
|
onSessionRequestComplete(response, success) {
|
||||||
@ -489,8 +492,9 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
return file;
|
return file;
|
||||||
});
|
});
|
||||||
|
|
||||||
let newState = React.addons.update(this.state, {filesToUpload: {$set: updatedFilesToUpload}});
|
let filesToUpload = React.addons.update(this.state.filesToUpload, {$set: updatedFilesToUpload});
|
||||||
this.setState(newState);
|
|
||||||
|
this.setState({filesToUpload });
|
||||||
} else {
|
} else {
|
||||||
// server has to respond with 204
|
// server has to respond with 204
|
||||||
//let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'danger', 10000);
|
//let notification = new GlobalNotificationModel('Could not load attached files (Further data)', 'danger', 10000);
|
||||||
@ -502,13 +506,11 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
|
|
||||||
onDeleteComplete(id, xhr, isError) {
|
onDeleteComplete(id, xhr, isError) {
|
||||||
if(isError) {
|
if(isError) {
|
||||||
let notification = new GlobalNotificationModel(getLangText('Couldn\'t delete file'), 'danger', 10000);
|
this.setStatusOfFile(id, 'online');
|
||||||
|
|
||||||
|
let notification = new GlobalNotificationModel(getLangText('There was an error deleting your file.'), 'danger', 10000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// To hide the file in this component, we need to set it's status to "deleted"
|
|
||||||
this.setStatusOfFile(id, 'deleted');
|
|
||||||
|
|
||||||
let notification = new GlobalNotificationModel(getLangText('File deleted'), 'success', 5000);
|
let notification = new GlobalNotificationModel(getLangText('File deleted'), 'success', 5000);
|
||||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
}
|
}
|
||||||
@ -530,6 +532,13 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleDeleteFile(fileId) {
|
handleDeleteFile(fileId) {
|
||||||
|
// We set the files state to 'deleted' immediately, so that the user is not confused with
|
||||||
|
// the unresponsiveness of the UI
|
||||||
|
//
|
||||||
|
// If there is an error during the deletion, we will just change the status back to 'online'
|
||||||
|
// and display an error message
|
||||||
|
this.setStatusOfFile(fileId, 'deleted');
|
||||||
|
|
||||||
// In some instances (when the file was already uploaded and is just displayed to the user
|
// In some instances (when the file was already uploaded and is just displayed to the user
|
||||||
// - for example in the loan contract or additional files dialog)
|
// - for example in the loan contract or additional files dialog)
|
||||||
// fineuploader does not register an id on the file (we do, don't be confused by this!).
|
// fineuploader does not register an id on the file (we do, don't be confused by this!).
|
||||||
@ -547,8 +556,6 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
// promise
|
// promise
|
||||||
} else {
|
} else {
|
||||||
let fileToDelete = this.state.filesToUpload[fileId];
|
let fileToDelete = this.state.filesToUpload[fileId];
|
||||||
fileToDelete.status = 'deleted';
|
|
||||||
|
|
||||||
S3Fetcher
|
S3Fetcher
|
||||||
.deleteFile(fileToDelete.s3Key, fileToDelete.s3Bucket)
|
.deleteFile(fileToDelete.s3Key, fileToDelete.s3Bucket)
|
||||||
.then(() => this.onDeleteComplete(fileToDelete.id, null, false))
|
.then(() => this.onDeleteComplete(fileToDelete.id, null, false))
|
||||||
@ -580,7 +587,7 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
handleUploadFile(files) {
|
handleUploadFile(files) {
|
||||||
// If multiple set and user already uploaded its work,
|
// If multiple set and user already uploaded its work,
|
||||||
// cancel upload
|
// cancel upload
|
||||||
if(!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0) {
|
if(!this.props.multiple && this.state.filesToUpload.filter(displayValidFilesFilter).length > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,7 +602,7 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
files = validFiles;
|
files = validFiles;
|
||||||
|
|
||||||
// Call this method to signal the outside component that an upload is in progress
|
// Call this method to signal the outside component that an upload is in progress
|
||||||
if(this.props.uploadStarted && typeof this.props.uploadStarted === 'function' && files.length > 0) {
|
if(typeof this.props.uploadStarted === 'function' && files.length > 0) {
|
||||||
this.props.uploadStarted();
|
this.props.uploadStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,6 +743,7 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
synchronizeFileLists(files) {
|
synchronizeFileLists(files) {
|
||||||
let oldFiles = this.state.filesToUpload;
|
let oldFiles = this.state.filesToUpload;
|
||||||
let oldAndNewFiles = this.state.uploader.getUploads();
|
let oldAndNewFiles = this.state.uploader.getUploads();
|
||||||
|
|
||||||
// Add fineuploader specific information to new files
|
// Add fineuploader specific information to new files
|
||||||
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
||||||
for(let j = 0; j < files.length; j++) {
|
for(let j = 0; j < files.length; j++) {
|
||||||
@ -750,6 +758,22 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
// and re-add fineuploader specific information for old files as well
|
// and re-add fineuploader specific information for old files as well
|
||||||
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
||||||
for(let j = 0; j < oldFiles.length; j++) {
|
for(let j = 0; j < oldFiles.length; j++) {
|
||||||
|
|
||||||
|
// EXCEPTION:
|
||||||
|
//
|
||||||
|
// Files do not necessarily come from the user's hard drive but can also be fetched
|
||||||
|
// from Amazon S3. This is handled in onSessionRequestComplete.
|
||||||
|
//
|
||||||
|
// If the user deletes one of those files, then fineuploader will still keep it in his
|
||||||
|
// files array but with key, progress undefined and size === -1 but
|
||||||
|
// status === 'upload successful'.
|
||||||
|
// This poses a problem as we depend on the amount of files that have
|
||||||
|
// status === 'upload successful', therefore once the file is synced,
|
||||||
|
// we need to tag its status as 'deleted' (which basically happens here)
|
||||||
|
if(oldAndNewFiles[i].size === -1 && (!oldAndNewFiles[i].progress || oldAndNewFiles[i].progress === 0)) {
|
||||||
|
oldAndNewFiles[i].status = 'deleted';
|
||||||
|
}
|
||||||
|
|
||||||
if(oldAndNewFiles[i].originalName === oldFiles[j].name) {
|
if(oldAndNewFiles[i].originalName === oldFiles[j].name) {
|
||||||
oldAndNewFiles[i].progress = oldFiles[j].progress;
|
oldAndNewFiles[i].progress = oldFiles[j].progress;
|
||||||
oldAndNewFiles[i].type = oldFiles[j].type;
|
oldAndNewFiles[i].type = oldFiles[j].type;
|
||||||
@ -760,30 +784,23 @@ var ReactS3FineUploader = React.createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set the new file array
|
// set the new file array
|
||||||
let newState = React.addons.update(this.state, {
|
let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles });
|
||||||
filesToUpload: { $set: oldAndNewFiles }
|
|
||||||
});
|
this.setState({ filesToUpload });
|
||||||
this.setState(newState);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setStatusOfFile(fileId, status) {
|
setStatusOfFile(fileId, status) {
|
||||||
// also, sync files from state with the ones from fineuploader
|
let changeSet = {};
|
||||||
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
|
|
||||||
|
|
||||||
filesToUpload[fileId].status = status;
|
|
||||||
|
|
||||||
// is status is set to deleted or canceled, we also need to reset the progress
|
|
||||||
// back to zero
|
|
||||||
if(status === 'deleted' || status === 'canceled') {
|
if(status === 'deleted' || status === 'canceled') {
|
||||||
filesToUpload[fileId].progress = 0;
|
changeSet.progress = { $set: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// set state
|
changeSet.status = { $set: status };
|
||||||
let newState = React.addons.update(this.state, {
|
|
||||||
filesToUpload: { $set: filesToUpload }
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setState(newState);
|
let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet });
|
||||||
|
|
||||||
|
this.setState({ filesToUpload });
|
||||||
},
|
},
|
||||||
|
|
||||||
isDropzoneInactive() {
|
isDropzoneInactive() {
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter function for filtering all deleted and canceled files
|
||||||
|
* @param {object} file A file from filesToUpload that has status as a prop.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function displayValidFilesFilter(file) {
|
||||||
|
return file.status !== 'deleted' && file.status !== 'canceled';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a boolean if there has been at least one file uploaded
|
* Returns a boolean if there has been at least one file uploaded
|
||||||
* successfully without it being deleted or canceled.
|
* successfully without it being deleted or canceled.
|
||||||
@ -7,10 +16,19 @@
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
export function isReadyForFormSubmission(files) {
|
export function isReadyForFormSubmission(files) {
|
||||||
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
|
files = files.filter(displayValidFilesFilter);
|
||||||
if (files.length > 0 && files[0].status === 'upload successful') {
|
if (files.length > 0 && files[0].status === 'upload successful') {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter function for which files to integrate in the progress process
|
||||||
|
* @param {object} file A file from filesToUpload, that has a status as a prop.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function displayValidProgressFilesFilter(file) {
|
||||||
|
return file.status !== 'deleted' && file.status !== 'canceled' && file.status !== 'online';
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2015, Widen Enterprises, Inc. info@fineuploader.com
|
* Copyright 2015, Widen Enterprises, Inc. info@fineuploader.com
|
||||||
*
|
*
|
||||||
* Version: 5.2.2
|
* Version: 5.3.0
|
||||||
*
|
*
|
||||||
* Homepage: http://fineuploader.com
|
* Homepage: http://fineuploader.com
|
||||||
*
|
*
|
||||||
@ -894,7 +894,7 @@ var qq = function(element) {
|
|||||||
}());
|
}());
|
||||||
|
|
||||||
/*global qq */
|
/*global qq */
|
||||||
qq.version = "5.2.2";
|
qq.version = "5.3.0";
|
||||||
|
|
||||||
/* globals qq */
|
/* globals qq */
|
||||||
qq.supportedFeatures = (function() {
|
qq.supportedFeatures = (function() {
|
||||||
@ -1928,6 +1928,10 @@ qq.status = {
|
|||||||
this._endpointStore.set(endpoint, id);
|
this._endpointStore.set(endpoint, id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setForm: function(elementOrId) {
|
||||||
|
this._updateFormSupportAndParams(elementOrId);
|
||||||
|
},
|
||||||
|
|
||||||
setItemLimit: function(newItemLimit) {
|
setItemLimit: function(newItemLimit) {
|
||||||
this._currentItemLimit = newItemLimit;
|
this._currentItemLimit = newItemLimit;
|
||||||
},
|
},
|
||||||
@ -1945,16 +1949,11 @@ qq.status = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
uploadStoredFiles: function() {
|
uploadStoredFiles: function() {
|
||||||
var idToUpload;
|
|
||||||
|
|
||||||
if (this._storedIds.length === 0) {
|
if (this._storedIds.length === 0) {
|
||||||
this._itemError("noFilesError");
|
this._itemError("noFilesError");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while (this._storedIds.length) {
|
this._uploadStoredFiles();
|
||||||
idToUpload = this._storedIds.shift();
|
|
||||||
this._uploadFile(idToUpload);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2038,10 +2037,11 @@ qq.status = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_createStore: function(initialValue, readOnlyValues) {
|
_createStore: function(initialValue, _readOnlyValues_) {
|
||||||
var store = {},
|
var store = {},
|
||||||
catchall = initialValue,
|
catchall = initialValue,
|
||||||
perIdReadOnlyValues = {},
|
perIdReadOnlyValues = {},
|
||||||
|
readOnlyValues = _readOnlyValues_,
|
||||||
copy = function(orig) {
|
copy = function(orig) {
|
||||||
if (qq.isObject(orig)) {
|
if (qq.isObject(orig)) {
|
||||||
return qq.extend({}, orig);
|
return qq.extend({}, orig);
|
||||||
@ -2095,8 +2095,20 @@ qq.status = {
|
|||||||
addReadOnly: function(id, values) {
|
addReadOnly: function(id, values) {
|
||||||
// Only applicable to Object stores
|
// Only applicable to Object stores
|
||||||
if (qq.isObject(store)) {
|
if (qq.isObject(store)) {
|
||||||
perIdReadOnlyValues[id] = perIdReadOnlyValues[id] || {};
|
// If null ID, apply readonly values to all files
|
||||||
qq.extend(perIdReadOnlyValues[id], values);
|
if (id === null) {
|
||||||
|
if (qq.isFunction(values)) {
|
||||||
|
readOnlyValues = values;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
readOnlyValues = readOnlyValues || {};
|
||||||
|
qq.extend(readOnlyValues, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
perIdReadOnlyValues[id] = perIdReadOnlyValues[id] || {};
|
||||||
|
qq.extend(perIdReadOnlyValues[id], values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -2882,7 +2894,7 @@ qq.status = {
|
|||||||
_onBeforeManualRetry: function(id) {
|
_onBeforeManualRetry: function(id) {
|
||||||
var itemLimit = this._currentItemLimit,
|
var itemLimit = this._currentItemLimit,
|
||||||
fileName;
|
fileName;
|
||||||
console.log(this._handler.isValid(id));
|
|
||||||
if (this._preventRetries[id]) {
|
if (this._preventRetries[id]) {
|
||||||
this.log("Retries are forbidden for id " + id, "warn");
|
this.log("Retries are forbidden for id " + id, "warn");
|
||||||
return false;
|
return false;
|
||||||
@ -3005,13 +3017,14 @@ qq.status = {
|
|||||||
this._onSubmit.apply(this, arguments);
|
this._onSubmit.apply(this, arguments);
|
||||||
this._uploadData.setStatus(id, qq.status.SUBMITTED);
|
this._uploadData.setStatus(id, qq.status.SUBMITTED);
|
||||||
this._onSubmitted.apply(this, arguments);
|
this._onSubmitted.apply(this, arguments);
|
||||||
this._options.callbacks.onSubmitted.apply(this, arguments);
|
|
||||||
|
|
||||||
if (this._options.autoUpload) {
|
if (this._options.autoUpload) {
|
||||||
|
this._options.callbacks.onSubmitted.apply(this, arguments);
|
||||||
this._uploadFile(id);
|
this._uploadFile(id);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._storeForLater(id);
|
this._storeForLater(id);
|
||||||
|
this._options.callbacks.onSubmitted.apply(this, arguments);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -3238,6 +3251,23 @@ qq.status = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateFormSupportAndParams: function(formElementOrId) {
|
||||||
|
this._options.form.element = formElementOrId;
|
||||||
|
|
||||||
|
this._formSupport = qq.FormSupport && new qq.FormSupport(
|
||||||
|
this._options.form, qq.bind(this.uploadStoredFiles, this), qq.bind(this.log, this)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this._formSupport && this._formSupport.attachedToForm) {
|
||||||
|
this._paramsStore.addReadOnly(null, this._formSupport.getFormInputsAsObject);
|
||||||
|
|
||||||
|
this._options.autoUpload = this._formSupport.newAutoUpload;
|
||||||
|
if (this._formSupport.newEndpoint) {
|
||||||
|
this.setEndpoint(this._formSupport.newEndpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_upload: function(id, params, endpoint) {
|
_upload: function(id, params, endpoint) {
|
||||||
var name = this.getName(id);
|
var name = this.getName(id);
|
||||||
|
|
||||||
@ -3264,6 +3294,25 @@ qq.status = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_uploadStoredFiles: function() {
|
||||||
|
var idToUpload, stillSubmitting,
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
while (this._storedIds.length) {
|
||||||
|
idToUpload = this._storedIds.shift();
|
||||||
|
this._uploadFile(idToUpload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are still waiting for some files to clear validation, attempt to upload these again in a bit
|
||||||
|
stillSubmitting = this.getUploads({status: qq.status.SUBMITTING}).length;
|
||||||
|
if (stillSubmitting) {
|
||||||
|
qq.log("Still waiting for " + stillSubmitting + " files to clear submit queue. Will re-parse stored IDs array shortly.");
|
||||||
|
setTimeout(function() {
|
||||||
|
self._uploadStoredFiles();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs some internal validation checks on an item, defined in the `validation` option.
|
* Performs some internal validation checks on an item, defined in the `validation` option.
|
||||||
*
|
*
|
||||||
@ -5271,6 +5320,7 @@ qq.XhrUploadHandler = function(spec) {
|
|||||||
*/
|
*/
|
||||||
getResumableFilesData: function() {
|
getResumableFilesData: function() {
|
||||||
var resumableFilesData = [];
|
var resumableFilesData = [];
|
||||||
|
|
||||||
handler._iterateResumeRecords(function(key, uploadData) {
|
handler._iterateResumeRecords(function(key, uploadData) {
|
||||||
handler.moveInProgressToRemaining(null, uploadData.chunking.inProgress, uploadData.chunking.remaining);
|
handler.moveInProgressToRemaining(null, uploadData.chunking.inProgress, uploadData.chunking.remaining);
|
||||||
|
|
||||||
@ -5461,7 +5511,7 @@ qq.XhrUploadHandler = function(spec) {
|
|||||||
_iterateResumeRecords: function(callback) {
|
_iterateResumeRecords: function(callback) {
|
||||||
if (resumeEnabled) {
|
if (resumeEnabled) {
|
||||||
qq.each(localStorage, function(key, item) {
|
qq.each(localStorage, function(key, item) {
|
||||||
if (key.indexOf(qq.format("qq{}resume-", namespace)) === 0) {
|
if (key.indexOf(qq.format("qq{}resume", namespace)) === 0) {
|
||||||
var uploadData = JSON.parse(item);
|
var uploadData = JSON.parse(item);
|
||||||
callback(key, uploadData);
|
callback(key, uploadData);
|
||||||
}
|
}
|
||||||
@ -5728,7 +5778,9 @@ qq.WindowReceiveMessage = function(o) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getItemByFileId: function(id) {
|
getItemByFileId: function(id) {
|
||||||
return this._templating.getFileContainer(id);
|
if (!this._templating.isHiddenForever(id)) {
|
||||||
|
return this._templating.getFileContainer(id);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
@ -6238,11 +6290,6 @@ qq.WindowReceiveMessage = function(o) {
|
|||||||
dontDisplay = this._handler.isProxied(id) && this._options.scaling.hideScaled,
|
dontDisplay = this._handler.isProxied(id) && this._options.scaling.hideScaled,
|
||||||
record;
|
record;
|
||||||
|
|
||||||
// If we don't want this file to appear in the UI, skip all of this UI-related logic.
|
|
||||||
if (dontDisplay) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._options.display.prependFiles) {
|
if (this._options.display.prependFiles) {
|
||||||
if (this._totalFilesInBatch > 1 && this._filesInBatchAddedToUi > 0) {
|
if (this._totalFilesInBatch > 1 && this._filesInBatchAddedToUi > 0) {
|
||||||
prependIndex = this._filesInBatchAddedToUi - 1;
|
prependIndex = this._filesInBatchAddedToUi - 1;
|
||||||
@ -6274,7 +6321,7 @@ qq.WindowReceiveMessage = function(o) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._templating.addFile(id, this._options.formatFileName(name), prependData);
|
this._templating.addFile(id, this._options.formatFileName(name), prependData, dontDisplay);
|
||||||
|
|
||||||
if (canned) {
|
if (canned) {
|
||||||
this._thumbnailUrls[id] && this._templating.updateThumbnail(id, this._thumbnailUrls[id], true);
|
this._thumbnailUrls[id] && this._templating.updateThumbnail(id, this._thumbnailUrls[id], true);
|
||||||
@ -6638,6 +6685,7 @@ qq.Templating = function(spec) {
|
|||||||
HIDE_DROPZONE_ATTR = "qq-hide-dropzone",
|
HIDE_DROPZONE_ATTR = "qq-hide-dropzone",
|
||||||
DROPZPONE_TEXT_ATTR = "qq-drop-area-text",
|
DROPZPONE_TEXT_ATTR = "qq-drop-area-text",
|
||||||
IN_PROGRESS_CLASS = "qq-in-progress",
|
IN_PROGRESS_CLASS = "qq-in-progress",
|
||||||
|
HIDDEN_FOREVER_CLASS = "qq-hidden-forever",
|
||||||
isCancelDisabled = false,
|
isCancelDisabled = false,
|
||||||
generatedThumbnails = 0,
|
generatedThumbnails = 0,
|
||||||
thumbnailQueueMonitorRunning = false,
|
thumbnailQueueMonitorRunning = false,
|
||||||
@ -7273,7 +7321,7 @@ qq.Templating = function(spec) {
|
|||||||
isCancelDisabled = true;
|
isCancelDisabled = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
addFile: function(id, name, prependInfo) {
|
addFile: function(id, name, prependInfo, hideForever) {
|
||||||
var fileEl = qq.toElement(templateHtml.fileTemplate),
|
var fileEl = qq.toElement(templateHtml.fileTemplate),
|
||||||
fileNameEl = getTemplateEl(fileEl, selectorClasses.file),
|
fileNameEl = getTemplateEl(fileEl, selectorClasses.file),
|
||||||
uploaderEl = getTemplateEl(container, selectorClasses.uploader),
|
uploaderEl = getTemplateEl(container, selectorClasses.uploader),
|
||||||
@ -7296,30 +7344,36 @@ qq.Templating = function(spec) {
|
|||||||
fileList.appendChild(fileEl);
|
fileList.appendChild(fileEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
hide(getProgress(id));
|
if (hideForever) {
|
||||||
hide(getSize(id));
|
fileEl.style.display = "none";
|
||||||
hide(getDelete(id));
|
qq(fileEl).addClass(HIDDEN_FOREVER_CLASS);
|
||||||
hide(getRetry(id));
|
|
||||||
hide(getPause(id));
|
|
||||||
hide(getContinue(id));
|
|
||||||
|
|
||||||
if (isCancelDisabled) {
|
|
||||||
this.hideCancel(id);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
hide(getProgress(id));
|
||||||
|
hide(getSize(id));
|
||||||
|
hide(getDelete(id));
|
||||||
|
hide(getRetry(id));
|
||||||
|
hide(getPause(id));
|
||||||
|
hide(getContinue(id));
|
||||||
|
|
||||||
thumb = getThumbnail(id);
|
if (isCancelDisabled) {
|
||||||
if (thumb && !thumb.src) {
|
this.hideCancel(id);
|
||||||
cachedWaitingForThumbnailImg.then(function(waitingImg) {
|
}
|
||||||
thumb.src = waitingImg.src;
|
|
||||||
if (waitingImg.style.maxHeight && waitingImg.style.maxWidth) {
|
|
||||||
qq(thumb).css({
|
|
||||||
maxHeight: waitingImg.style.maxHeight,
|
|
||||||
maxWidth: waitingImg.style.maxWidth
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
show(thumb);
|
thumb = getThumbnail(id);
|
||||||
});
|
if (thumb && !thumb.src) {
|
||||||
|
cachedWaitingForThumbnailImg.then(function(waitingImg) {
|
||||||
|
thumb.src = waitingImg.src;
|
||||||
|
if (waitingImg.style.maxHeight && waitingImg.style.maxWidth) {
|
||||||
|
qq(thumb).css({
|
||||||
|
maxHeight: waitingImg.style.maxHeight,
|
||||||
|
maxWidth: waitingImg.style.maxWidth
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
show(thumb);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -7413,6 +7467,10 @@ qq.Templating = function(spec) {
|
|||||||
icon && qq(icon).addClass(options.classes.editable);
|
icon && qq(icon).addClass(options.classes.editable);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isHiddenForever: function(id) {
|
||||||
|
return qq(getFile(id)).hasClass(HIDDEN_FOREVER_CLASS);
|
||||||
|
},
|
||||||
|
|
||||||
hideEditIcon: function(id) {
|
hideEditIcon: function(id) {
|
||||||
var icon = getEditIcon(id);
|
var icon = getEditIcon(id);
|
||||||
|
|
||||||
@ -7572,13 +7630,17 @@ qq.Templating = function(spec) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
generatePreview: function(id, optFileOrBlob) {
|
generatePreview: function(id, optFileOrBlob) {
|
||||||
thumbGenerationQueue.push({id: id, optFileOrBlob: optFileOrBlob});
|
if (!this.isHiddenForever(id)) {
|
||||||
!thumbnailQueueMonitorRunning && generateNextQueuedPreview();
|
thumbGenerationQueue.push({id: id, optFileOrBlob: optFileOrBlob});
|
||||||
|
!thumbnailQueueMonitorRunning && generateNextQueuedPreview();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateThumbnail: function(id, thumbnailUrl, showWaitingImg) {
|
updateThumbnail: function(id, thumbnailUrl, showWaitingImg) {
|
||||||
thumbGenerationQueue.push({update: true, id: id, thumbnailUrl: thumbnailUrl, showWaitingImg: showWaitingImg});
|
if (!this.isHiddenForever(id)) {
|
||||||
!thumbnailQueueMonitorRunning && generateNextQueuedPreview();
|
thumbGenerationQueue.push({update: true, id: id, thumbnailUrl: thumbnailUrl, showWaitingImg: showWaitingImg});
|
||||||
|
!thumbnailQueueMonitorRunning && generateNextQueuedPreview();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hasDialog: function(type) {
|
hasDialog: function(type) {
|
||||||
@ -9489,12 +9551,6 @@ qq.s3.XhrUploadHandler = function(spec, proxy) {
|
|||||||
result.success,
|
result.success,
|
||||||
|
|
||||||
function failure(reason, xhr) {
|
function failure(reason, xhr) {
|
||||||
console.logGlobal(reason + 'in chunked.combine', false, {
|
|
||||||
uploadId,
|
|
||||||
etagMap,
|
|
||||||
result
|
|
||||||
});
|
|
||||||
|
|
||||||
result.failure(upload.done(id, xhr).response, xhr);
|
result.failure(upload.done(id, xhr).response, xhr);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -12335,7 +12391,7 @@ qq.Scaler = function(spec, log) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
includeReference = spec.sendOriginal,
|
includeOriginal = spec.sendOriginal,
|
||||||
orient = spec.orient,
|
orient = spec.orient,
|
||||||
defaultType = spec.defaultType,
|
defaultType = spec.defaultType,
|
||||||
defaultQuality = spec.defaultQuality / 100,
|
defaultQuality = spec.defaultQuality / 100,
|
||||||
@ -12385,16 +12441,18 @@ qq.Scaler = function(spec, log) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
includeReference && records.push({
|
records.push({
|
||||||
uuid: originalFileUuid,
|
uuid: originalFileUuid,
|
||||||
name: originalFileName,
|
name: originalFileName,
|
||||||
blob: originalBlob
|
size: originalBlob.size,
|
||||||
|
blob: includeOriginal ? originalBlob : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
records.push({
|
records.push({
|
||||||
uuid: originalFileUuid,
|
uuid: originalFileUuid,
|
||||||
name: originalFileName,
|
name: originalFileName,
|
||||||
|
size: originalBlob.size,
|
||||||
blob: originalBlob
|
blob: originalBlob
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -12413,19 +12471,17 @@ qq.Scaler = function(spec, log) {
|
|||||||
proxyGroupId = qq.getUniqueId();
|
proxyGroupId = qq.getUniqueId();
|
||||||
|
|
||||||
qq.each(self.getFileRecords(uuid, name, file), function(idx, record) {
|
qq.each(self.getFileRecords(uuid, name, file), function(idx, record) {
|
||||||
var relatedBlob = file,
|
var blobSize = record.size,
|
||||||
relatedSize = size,
|
|
||||||
id;
|
id;
|
||||||
|
|
||||||
if (record.blob instanceof qq.BlobProxy) {
|
if (record.blob instanceof qq.BlobProxy) {
|
||||||
relatedBlob = record.blob;
|
blobSize = -1;
|
||||||
relatedSize = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id = uploadData.addFile({
|
id = uploadData.addFile({
|
||||||
uuid: record.uuid,
|
uuid: record.uuid,
|
||||||
name: record.name,
|
name: record.name,
|
||||||
size: relatedSize,
|
size: blobSize,
|
||||||
batchId: batchId,
|
batchId: batchId,
|
||||||
proxyGroupId: proxyGroupId
|
proxyGroupId: proxyGroupId
|
||||||
});
|
});
|
||||||
@ -12437,10 +12493,13 @@ qq.Scaler = function(spec, log) {
|
|||||||
originalId = id;
|
originalId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
addFileToHandler(id, relatedBlob);
|
if (record.blob) {
|
||||||
|
addFileToHandler(id, record.blob);
|
||||||
fileList.push({id: id, file: relatedBlob});
|
fileList.push({id: id, file: record.blob});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uploadData.setStatus(id, qq.status.REJECTED);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we are potentially uploading an original file and some scaled versions,
|
// If we are potentially uploading an original file and some scaled versions,
|
||||||
@ -12453,8 +12512,8 @@ qq.Scaler = function(spec, log) {
|
|||||||
qqparentsize: uploadData.retrieve({id: originalId}).size
|
qqparentsize: uploadData.retrieve({id: originalId}).size
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make SURE the UUID for each scaled image is sent with the upload request,
|
// Make sure the UUID for each scaled image is sent with the upload request,
|
||||||
// to be consistent (since we need to ensure it is sent for the original file as well).
|
// to be consistent (since we may need to ensure it is sent for the original file as well).
|
||||||
params[uuidParamName] = uploadData.retrieve({id: scaledId}).uuid;
|
params[uuidParamName] = uploadData.retrieve({id: scaledId}).uuid;
|
||||||
|
|
||||||
uploadData.setParentId(scaledId, originalId);
|
uploadData.setParentId(scaledId, originalId);
|
||||||
@ -14411,4 +14470,4 @@ code.google.com/p/crypto-js/wiki/License
|
|||||||
C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
|
C.HmacSHA1 = Hasher._createHmacHelper(SHA1);
|
||||||
}());
|
}());
|
||||||
|
|
||||||
/*! 2015-06-09 */
|
/*! 2015-08-26 */
|
||||||
|
File diff suppressed because one or more lines are too long
@ -90,14 +90,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}
|
||||||
|
@ -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}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Router from 'react-router';
|
|
||||||
|
|
||||||
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
import AccordionListItemPiece from '../../../../../ascribe_accordion_list/accordion_list_item_piece';
|
||||||
|
|
||||||
@ -17,8 +16,6 @@ import IkonotvSubmitButton from '../ascribe_buttons/ikonotv_submit_button';
|
|||||||
|
|
||||||
import AclProxy from '../../../../../acl_proxy';
|
import AclProxy from '../../../../../acl_proxy';
|
||||||
|
|
||||||
import AclButton from '../../../../../ascribe_buttons/acl_button';
|
|
||||||
|
|
||||||
import { getLangText } from '../../../../../../utils/lang_utils';
|
import { getLangText } from '../../../../../../utils/lang_utils';
|
||||||
import { mergeOptions } from '../../../../../../utils/general_utils';
|
import { mergeOptions } from '../../../../../../utils/general_utils';
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -133,7 +133,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}
|
||||||
|
@ -53,6 +53,10 @@ let constants = {
|
|||||||
'ga': 'UA-60614729-2'
|
'ga': 'UA-60614729-2'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// These are all possible types that are currently supported in HTML5 for the input element
|
||||||
|
// Source: http://www.w3schools.com/tags/att_input_type.asp
|
||||||
|
'possibleInputTypes': ['button', 'checkbox', 'color', 'date', 'datetime', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month', 'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time', 'url', 'week'],
|
||||||
|
|
||||||
// in case of whitelabel customization, we store stuff here
|
// in case of whitelabel customization, we store stuff here
|
||||||
'whitelabel': {},
|
'whitelabel': {},
|
||||||
'raven': {
|
'raven': {
|
||||||
|
1
js/third_party/ga.js
vendored
1
js/third_party/ga.js
vendored
@ -3,7 +3,6 @@
|
|||||||
import alt from '../alt';
|
import alt from '../alt';
|
||||||
import EventActions from '../actions/event_actions';
|
import EventActions from '../actions/event_actions';
|
||||||
|
|
||||||
|
|
||||||
class GoogleAnalyticsHandler {
|
class GoogleAnalyticsHandler {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.bindActions(EventActions);
|
this.bindActions(EventActions);
|
||||||
|
@ -73,9 +73,8 @@
|
|||||||
"q": "^1.4.1",
|
"q": "^1.4.1",
|
||||||
"raven-js": "^1.1.19",
|
"raven-js": "^1.1.19",
|
||||||
"react": "^0.13.2",
|
"react": "^0.13.2",
|
||||||
"react-bootstrap": "^0.24.3",
|
"react-bootstrap": "^0.25.1",
|
||||||
"react-datepicker": "^0.12.0",
|
"react-datepicker": "^0.12.0",
|
||||||
"react-progressbar": "^1.1.0",
|
|
||||||
"react-router": "^0.13.3",
|
"react-router": "^0.13.3",
|
||||||
"react-router-bootstrap": "~0.16.0",
|
"react-router-bootstrap": "~0.16.0",
|
||||||
"react-star-rating": "~1.3.2",
|
"react-star-rating": "~1.3.2",
|
||||||
|
@ -12,8 +12,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.media-other {
|
.media-other {
|
||||||
font-size: 500%;
|
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
|
font-size: 500%;
|
||||||
|
p {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.audiojs {
|
.audiojs {
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
|
|
||||||
padding: 1.5em 0 1.5em 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.inactive-dropzone {
|
.inactive-dropzone {
|
||||||
@ -29,6 +28,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-iterator {
|
||||||
|
margin: 2.5em 0 0 0;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
> div:first-child {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-preview-iterator-spacing {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-dialog {
|
||||||
|
margin: 1.5em 0 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-drag-and-drop-hashing-dialog {
|
||||||
|
margin: 1.5em 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.file-drag-and-drop .file-drag-and-drop-dialog > p:first-child {
|
.file-drag-and-drop .file-drag-and-drop-dialog > p:first-child {
|
||||||
font-size: 1.5em !important;
|
font-size: 1.5em !important;
|
||||||
|
|
||||||
|
0
sass/lib/buttons.scss
Normal file
0
sass/lib/buttons.scss
Normal file
@ -212,31 +212,22 @@ hr {
|
|||||||
border: 1px solid $ascribe-brand-danger;
|
border: 1px solid $ascribe-brand-danger;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.btn-ascribe, .btn-ascribe-inv {
|
.btn-ascribe {
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
font-family: sans-serif !important;
|
font-family: sans-serif !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
|
||||||
|
|
||||||
.btn-ascribe, .btn-ascribe-inv:active, .btn-ascribe-inv:hover {
|
|
||||||
color: #222 !important;
|
color: #222 !important;
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-ascribe:active, .btn-ascribe:hover, .btn-ascribe-inv {
|
.btn-ascribe:active, .btn-ascribe:hover {
|
||||||
color: #FFF !important;
|
color: #FFF !important;
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-ascribe-inv:disabled, .btn-ascribe-inv:focus {
|
|
||||||
color: #444 !important;
|
|
||||||
background-color: #BBB !important;
|
|
||||||
border: 1px solid #444 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-ascribe-sm {
|
.btn-ascribe-sm {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
@ -450,3 +441,14 @@ hr {
|
|||||||
padding-bottom: 30%;
|
padding-bottom: 30%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ascribe-progress-bar {
|
||||||
|
margin-bottom: 0;
|
||||||
|
> .progress-bar {
|
||||||
|
background-color: $ascribe-color-green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ascribe-progress-bar-xs {
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user