diff --git a/.eslintrc b/.eslintrc
index 2312f48f..d41c0a2a 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -22,7 +22,7 @@
"react/jsx-sort-props": 0,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
- "react/no-did-mount-set-state": 1,
+ "react/no-did-mount-set-state": [1, "allow-in-func"],
"react/no-did-update-set-state": 1,
"react/no-multi-comp": 0,
"react/no-unknown-property": 1,
@@ -58,4 +58,4 @@
"superInFunctions": 1,
"templateStrings": 1
}
-}
\ No newline at end of file
+}
diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js
index 046d8942..165913d4 100644
--- a/js/components/ascribe_detail/edition_container.js
+++ b/js/components/ascribe_detail/edition_container.js
@@ -9,6 +9,9 @@ import Edition from './edition';
import AscribeSpinner from '../ascribe_spinner';
+import { setDocumentTitle } from '../../utils/dom_utils';
+
+
/**
* This is the component that implements resource/data specific functionality
*/
@@ -64,6 +67,8 @@ let EditionContainer = React.createClass({
render() {
if(this.state.edition && this.state.edition.title) {
+ setDocumentTitle([this.state.edition.artist_name, this.state.edition.title].join(', '));
+
return (
` component (because ready is false)
+ * 1) set `state.libraryLoaded = null` (state.libraryLoaded can be in three states: `null`
+ * if we don't know anything about it, `true` if the external library has been loaded,
+ * `false` if we failed to load the external library)
+ * 2) render the cover using the `` component (because libraryLoaded is null)
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
* the `InjectInHeadMixin`, attaching a function to `Promise.then` to change
- * `state.ready` to true
- * 4) when the promise is succesfully resolved, we change `state.ready` triggering
+ * `state.libraryLoaded` to true
+ * 4) when the promise is succesfully resolved, we change `state.libraryLoaded` triggering
* a re-render
* 5) the new render calls `prepareVideoHTML` to get the raw HTML of the video tag
* (that will be later processed and expanded by VideoJS)
@@ -129,18 +132,19 @@ let Video = React.createClass({
mixins: [InjectInHeadMixin],
getInitialState() {
- return { ready: false, videoMounted: false };
+ return { libraryLoaded: null, videoMounted: false };
},
componentDidMount() {
Q.all([
this.inject('//vjs.zencdn.net/4.12/video-js.css'),
- this.inject('//vjs.zencdn.net/4.12/video.js')
- ]).then(this.ready);
+ this.inject('//vjs.zencdn.net/4.12/video.js')])
+ .then(() => this.setState({libraryLoaded: true}))
+ .fail(() => this.setState({libraryLoaded: false}));
},
componentDidUpdate() {
- if (this.state.ready && !this.state.videoMounted) {
+ if (this.state.libraryLoaded && !this.state.videoMounted) {
window.videojs('#mainvideo');
/* eslint-disable */
this.setState({videoMounted: true});
@@ -149,11 +153,9 @@ let Video = React.createClass({
},
componentWillUnmount() {
- window.videojs('#mainvideo').dispose();
- },
-
- ready() {
- this.setState({ready: true, videoMounted: false});
+ if (this.state.videoMounted) {
+ window.videojs('#mainvideo').dispose();
+ }
},
prepareVideoHTML() {
@@ -171,7 +173,7 @@ let Video = React.createClass({
},
render() {
- if (this.state.ready) {
+ if (this.state.libraryLoaded !== null) {
return (
);
diff --git a/js/components/ascribe_settings/account_settings.js b/js/components/ascribe_settings/account_settings.js
index 5f4cfe7a..f337a17b 100644
--- a/js/components/ascribe_settings/account_settings.js
+++ b/js/components/ascribe_settings/account_settings.js
@@ -96,11 +96,15 @@ let AccountSettings = React.createClass({
title={getLangText('Account')}
defaultExpanded={true}>
{content}
-
+
+
+
{profile}
);
}
});
-export default AccountSettings;
\ No newline at end of file
+export default AccountSettings;
diff --git a/js/components/ascribe_settings/contract_settings.js b/js/components/ascribe_settings/contract_settings.js
index 741039ee..71d97542 100644
--- a/js/components/ascribe_settings/contract_settings.js
+++ b/js/components/ascribe_settings/contract_settings.js
@@ -23,6 +23,7 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import AclProxy from '../acl_proxy';
import { getLangText } from '../../utils/lang_utils';
+import { setDocumentTitle } from '../../utils/dom_utils';
import { mergeOptions, truncateTextAtCharIndex } from '../../utils/general_utils';
@@ -86,6 +87,8 @@ let ContractSettings = React.createClass({
let privateContracts = this.getPrivateContracts();
let createPublicContractForm = null;
+ setDocumentTitle(getLangText('Contracts settings'));
+
if(publicContracts.length === 0) {
createPublicContractForm = (
diff --git a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
index c7f62b4c..38ec459a 100644
--- a/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
+++ b/js/components/ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop.js
@@ -12,6 +12,7 @@ import { getLangText } from '../../../utils/lang_utils';
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
let FileDragAndDrop = React.createClass({
propTypes: {
+ className: React.PropTypes.string,
onDrop: React.PropTypes.func.isRequired,
onDragOver: React.PropTypes.func,
onInactive: React.PropTypes.func,
@@ -108,6 +109,7 @@ let FileDragAndDrop = React.createClass({
},
handleOnClick() {
+ let evt;
// when multiple is set to false and the user already uploaded a piece,
// do not propagate event
if(this.props.dropzoneInactive) {
@@ -119,16 +121,18 @@ let FileDragAndDrop = React.createClass({
return;
}
- // Firefox only recognizes the simulated mouse click if bubbles is set to true,
- // but since Google Chrome propagates the event much further than needed, we
- // need to stop propagation as soon as the event is created
- var evt = new MouseEvent('click', {
- view: window,
- bubbles: true,
- cancelable: true
- });
+ try {
+ evt = new MouseEvent('click', {
+ view: window,
+ bubbles: true,
+ cancelable: true
+ });
+ } catch(e) {
+ // For browsers that do not support the new MouseEvent syntax
+ evt = document.createEvent('MouseEvents');
+ evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
+ }
- evt.stopPropagation();
this.refs.fileinput.getDOMNode().dispatchEvent(evt);
},
@@ -160,10 +164,10 @@ let FileDragAndDrop = React.createClass({
{getLangText('Computing hash(es)... This may take a few minutes.')}
@@ -191,12 +195,23 @@ let FileDragAndDrop = React.createClass({
handleResumeFile={this.handleResumeFile}
areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={areAssetsEditable}/>
+ {/*
+ Opera doesn't trigger simulated click events
+ if the targeted input has `display:none` set.
+ Which means we need to set its visibility to hidden
+ instead of using `display:none`.
+
+ See:
+ - http://stackoverflow.com/questions/12880604/jquery-triggerclick-not-working-on-opera-if-the-element-is-not-displayed
+ */}
diff --git a/js/components/login_container.js b/js/components/login_container.js
index 33919cd6..46c14d65 100644
--- a/js/components/login_container.js
+++ b/js/components/login_container.js
@@ -6,6 +6,7 @@ import { Link } from 'react-router';
import LoginForm from './ascribe_forms/form_login';
import { getLangText } from '../utils/lang_utils';
+import { setDocumentTitle } from '../utils/dom_utils';
let LoginContainer = React.createClass({
@@ -26,6 +27,8 @@ let LoginContainer = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Log in'));
+
return (
diff --git a/js/components/password_reset_container.js b/js/components/password_reset_container.js
index 64aaa889..31275a08 100644
--- a/js/components/password_reset_container.js
+++ b/js/components/password_reset_container.js
@@ -11,6 +11,7 @@ import AscribeSpinner from './ascribe_spinner';
import GlobalNotificationModel from '../models/global_notification_model';
import GlobalNotificationActions from '../actions/global_notification_actions';
import { getLangText } from '../utils/lang_utils';
+import { setDocumentTitle } from '../utils/dom_utils';
let PasswordResetContainer = React.createClass({
@@ -76,6 +77,8 @@ let PasswordRequestResetForm = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Reset your password'));
+
return (
;
let AccordionListItemType = this.props.accordionListItemType;
+ setDocumentTitle(getLangText('Collection'));
+
return (
diff --git a/js/components/signup_container.js b/js/components/signup_container.js
index 7c7b5ee7..e6c6fb73 100644
--- a/js/components/signup_container.js
+++ b/js/components/signup_container.js
@@ -6,6 +6,7 @@ import { Link } from 'react-router';
import SignupForm from './ascribe_forms/form_signup';
import { getLangText } from '../utils/lang_utils';
+import { setDocumentTitle } from '../utils/dom_utils';
let SignupContainer = React.createClass({
@@ -28,6 +29,8 @@ let SignupContainer = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Sign up'));
+
if (this.state.submitted){
return (
diff --git a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js b/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js
index f288670d..a5f9838f 100644
--- a/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js
+++ b/js/components/whitelabel/prize/components/ascribe_detail/prize_piece_container.js
@@ -40,6 +40,7 @@ import DetailProperty from '../../../../ascribe_detail/detail_property';
import ApiUrls from '../../../../../constants/api_urls';
import { mergeOptions } from '../../../../../utils/general_utils';
import { getLangText } from '../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../utils/dom_utils';
/**
@@ -112,12 +113,22 @@ let PieceContainer = React.createClass({
// 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) ||
(this.state.currentUser.is_judge && !this.state.piece.selected )) ?
- : this.state.piece.artist_name;
+ null : this.state.piece.artist_name;
// 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 ) ?
: null;
+ if (artistName === null) {
+ setDocumentTitle(this.state.piece.title);
+ } else {
+ setDocumentTitle([artistName, this.state.piece.title].join(', '));
+ }
+
+ if (artistName === null) {
+ artistName = ;
+ }
+
return (
;
diff --git a/js/components/whitelabel/prize/components/prize_signup_container.js b/js/components/whitelabel/prize/components/prize_signup_container.js
index e78e8e6c..884062da 100644
--- a/js/components/whitelabel/prize/components/prize_signup_container.js
+++ b/js/components/whitelabel/prize/components/prize_signup_container.js
@@ -4,6 +4,7 @@ import React from 'react';
import SignupForm from '../../../ascribe_forms/form_signup';
import { getLangText } from '../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../utils/dom_utils';
let SignupContainer = React.createClass({
propTypes: {
@@ -25,6 +26,8 @@ let SignupContainer = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Sign up'));
+
if (this.state.submitted){
return (
diff --git a/js/components/whitelabel/wallet/components/cc/cc_register_piece.js b/js/components/whitelabel/wallet/components/cc/cc_register_piece.js
index fe59f819..5f33126a 100644
--- a/js/components/whitelabel/wallet/components/cc/cc_register_piece.js
+++ b/js/components/whitelabel/wallet/components/cc/cc_register_piece.js
@@ -8,6 +8,7 @@ import LicenseActions from '../../../../../actions/license_actions';
import LicenseStore from '../../../../../stores/license_store';
import { getLangText } from '../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../utils/general_utils';
let CCRegisterPiece = React.createClass({
@@ -81,6 +82,7 @@ let CCRegisterPiece = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Register a new piece'));
return (
diff --git a/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js b/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js
index 5fbb326b..c40b455a 100644
--- a/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js
+++ b/js/components/whitelabel/wallet/components/cyland/cyland_piece_list.js
@@ -9,6 +9,7 @@ import UserStore from '../../../../../stores/user_store';
import CylandAccordionListItem from './cyland_accordion_list/cyland_accordion_list_item';
import { getLangText } from '../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../utils/dom_utils';
let CylandPieceList = React.createClass({
@@ -34,6 +35,8 @@ let CylandPieceList = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Collection'));
+
return (
0) {
@@ -192,4 +195,4 @@ let IkonotvContractNotifications = React.createClass({
}
});
-export default IkonotvContractNotifications;
\ No newline at end of file
+export default IkonotvContractNotifications;
diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js
index b9d90dde..21b52761 100644
--- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js
+++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_detail/ikonotv_piece_container.js
@@ -19,6 +19,7 @@ import WalletPieceContainer from '../../ascribe_detail/wallet_piece_container';
import AscribeSpinner from '../../../../../ascribe_spinner';
import { getLangText } from '../../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../../utils/dom_utils';
import { mergeOptions } from '../../../../../../utils/general_utils';
@@ -95,6 +96,7 @@ let IkonotvPieceContainer = React.createClass({
}
if(this.state.piece && this.state.piece.title) {
+ setDocumentTitle([this.state.piece.artist_name, this.state.piece.title].join(', '));
return (
diff --git a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js
index 48d1f952..0b51bdbd 100644
--- a/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js
+++ b/js/components/whitelabel/wallet/components/ikonotv/ikonotv_piece_list.js
@@ -9,6 +9,7 @@ import UserStore from '../../../../../stores/user_store';
import IkonotvAccordionListItem from './ikonotv_accordion_list/ikonotv_accordion_list_item';
import { getLangText } from '../../../../../utils/lang_utils';
+import { setDocumentTitle } from '../../../../../utils/dom_utils';
let IkonotvPieceList = React.createClass({
@@ -34,6 +35,8 @@ let IkonotvPieceList = React.createClass({
},
render() {
+ setDocumentTitle(getLangText('Register a new piece'));
+
return (
{
+ if (err instanceof TypeError) {
+ throw new Error('For: ' + url + ' - Server did not respond to the request. (Not even displayed a 500)');
+ } else {
+ throw err;
+ }
+ };
}
getUrl(url) {
@@ -118,7 +120,7 @@ class Requests {
merged.method = verb;
return fetch(url, merged)
.then(this.unpackResponse)
- .catch(this.handleError);
+ .catch(this.handleError(url));
}
get(url, params) {
diff --git a/package.json b/package.json
index f962e4a4..3fc839d2 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,7 @@
"react": "0.13.2",
"react-bootstrap": "0.25.1",
"react-datepicker": "^0.12.0",
- "react-router": "^1.0.0-rc1",
+ "react-router": "1.0.0-rc1",
"react-router-bootstrap": "^0.19.0",
"react-star-rating": "~1.3.2",
"react-textarea-autosize": "^2.5.2",