- {docs.map(({ title, bibcode, author, totalAuthors }) => (
+ {docs.map(({ title, bibcode, author, totalAuthors }, index) => (
onPaperSelect(docs[index], index)}
/>
))}
From 0340e0610e799e08b4d03141d6a1216682fbbe4a Mon Sep 17 00:00:00 2001
From: Tim Hostetler <6970899+thostetler@users.noreply.github.com>
Date: Wed, 6 May 2020 11:48:04 -0400
Subject: [PATCH 19/46] move query params into redux and add action
---
src/js/react/Recommender/actions.js | 20 +++----
.../components/RecommendedList.jsx.js | 10 +---
src/js/react/Recommender/middleware.js | 59 +++++++++++++------
src/js/react/Recommender/reducer.js | 20 ++++++-
4 files changed, 67 insertions(+), 42 deletions(-)
diff --git a/src/js/react/Recommender/actions.js b/src/js/react/Recommender/actions.js
index bff665a48..62f3ad9ed 100644
--- a/src/js/react/Recommender/actions.js
+++ b/src/js/react/Recommender/actions.js
@@ -9,22 +9,12 @@ define([], function() {
EMIT_ANALYTICS: 'EMIT_ANALYTICS',
SET_TAB: 'SET_TAB',
SET_ORACLE_TARGET: 'SET_ORACLE_TARGET',
+ SET_QUERY_PARAMS: 'SET_QUERY_PARAMS',
};
const actionCreators = {
- getRecommendations: ({ func, sort, numDocs, cutOffDays, topNReads }) => ({
- type: 'API_REQUEST',
- scope: actions.GET_RECOMMENDATIONS,
- options: {
- type: 'POST',
- data: {
- function: func,
- sort,
- num_docs: numDocs,
- cutoff_days: cutOffDays,
- top_n_reads: topNReads,
- },
- },
+ getRecommendations: () => ({
+ type: actions.GET_RECOMMENDATIONS,
}),
getDocs: (query) => ({
type: 'API_REQUEST',
@@ -43,6 +33,10 @@ define([], function() {
type: actions.SET_QUERY,
payload: query,
}),
+ setQueryParams: (payload) => ({
+ type: actions.SET_QUERY_PARAMS,
+ payload,
+ }),
updateSearchBar: (text) => ({
type: actions.UPDATE_SEARCH_BAR,
payload: text,
diff --git a/src/js/react/Recommender/components/RecommendedList.jsx.js b/src/js/react/Recommender/components/RecommendedList.jsx.js
index d3db7bac7..c6d9c866e 100644
--- a/src/js/react/Recommender/components/RecommendedList.jsx.js
+++ b/src/js/react/Recommender/components/RecommendedList.jsx.js
@@ -87,15 +87,7 @@ define([
);
React.useEffect(() => {
if (docs.length === 0) {
- dispatch(
- getRecommendations({
- function: 'similar',
- sort: 'entry_date',
- numDocs: 5,
- cutoffDays: 5,
- topNReads: 10,
- })
- );
+ dispatch(getRecommendations());
}
}, [docs]);
diff --git a/src/js/react/Recommender/middleware.js b/src/js/react/Recommender/middleware.js
index e1d7ad80b..19e2299b9 100644
--- a/src/js/react/Recommender/middleware.js
+++ b/src/js/react/Recommender/middleware.js
@@ -22,31 +22,52 @@ define(['../shared/helpers', './actions'], function(
next(action);
});
- const getRecommendations = middleware(({ next, action, dispatch }) => {
- next(action);
+ const getRecommendations = middleware(
+ ({ next, action, dispatch, getState }) => {
+ next(action);
- if (action.type === apiSuccess(GET_RECOMMENDATIONS)) {
- dispatch(setQuery(action.result.query));
- dispatch(
- getDocs({
- fl: 'bibcode,title,author,[fields author=3],author_count',
- q: action.result.query,
- })
- );
- }
+ if (action.type === GET_RECOMMENDATIONS) {
+ const { queryParams } = getState();
+ const { func, sort, numDocs, cutOffDays, topNReads } = queryParams;
+ dispatch({
+ type: 'API_REQUEST',
+ scope: GET_RECOMMENDATIONS,
+ options: {
+ type: 'POST',
+ data: {
+ function: func,
+ sort,
+ num_docs: numDocs,
+ cutoff_days: cutOffDays,
+ top_n_reads: topNReads,
+ },
+ },
+ });
+ }
- if (action.type === apiFailure(GET_RECOMMENDATIONS)) {
- if (action.result && action.result.query) {
- const { scope } = parseScope(action.type);
- dispatch({ type: `${scope}_RESET` });
+ if (action.type === apiSuccess(GET_RECOMMENDATIONS)) {
dispatch(setQuery(action.result.query));
+ dispatch(
+ getDocs({
+ fl: 'bibcode,title,author,[fields author=3],author_count',
+ q: action.result.query,
+ })
+ );
}
- }
- if (action.type === apiSuccess(GET_DOCS)) {
- dispatch(setDocs(action.result.response.docs));
+ if (action.type === apiFailure(GET_RECOMMENDATIONS)) {
+ if (action.result && action.result.query) {
+ const { scope } = parseScope(action.type);
+ dispatch({ type: `${scope}_RESET` });
+ dispatch(setQuery(action.result.query));
+ }
+ }
+
+ if (action.type === apiSuccess(GET_DOCS)) {
+ dispatch(setDocs(action.result.response.docs));
+ }
}
- });
+ );
const updateSearchBar = middleware(({ action, next, trigger }) => {
next(action);
diff --git a/src/js/react/Recommender/reducer.js b/src/js/react/Recommender/reducer.js
index 3f4bb2814..796482f76 100644
--- a/src/js/react/Recommender/reducer.js
+++ b/src/js/react/Recommender/reducer.js
@@ -1,6 +1,6 @@
define(['redux', './actions'], function(
{ combineReducers },
- { SET_DOCS, SET_QUERY, SET_TAB, SET_ORACLE_TARGET }
+ { SET_DOCS, SET_QUERY, SET_TAB, SET_ORACLE_TARGET, SET_QUERY_PARAMS }
) {
const requestState = {
GET_RECOMMENDATIONS: { status: null, result: null, error: null },
@@ -66,11 +66,29 @@ define(['redux', './actions'], function(
return state;
};
+ const queryParamsState = {
+ function: 'similar',
+ sort: 'entry_date',
+ numDocs: 5,
+ cutoffDays: 5,
+ topNReads: 10,
+ };
+ const queryParams = (state = queryParamsState, action) => {
+ if (action.type === SET_QUERY_PARAMS && action.payload) {
+ return {
+ ...state,
+ ...action.payload,
+ };
+ }
+ return state;
+ };
+
return combineReducers({
requests,
docs,
query,
tab,
oracleTarget,
+ queryParams,
});
});
From 85b419cf090ae5ce577c9d71333d9f13bc0ad36c Mon Sep 17 00:00:00 2001
From: Tim Hostetler <6970899+thostetler@users.noreply.github.com>
Date: Wed, 6 May 2020 11:53:03 -0400
Subject: [PATCH 20/46] add extra type check on dispatch
---
src/js/react/BumblebeeWidget.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/js/react/BumblebeeWidget.js b/src/js/react/BumblebeeWidget.js
index 2ef54541d..f56baf7f9 100644
--- a/src/js/react/BumblebeeWidget.js
+++ b/src/js/react/BumblebeeWidget.js
@@ -73,7 +73,9 @@ define([
}
},
dispatch({ type, ...args }) {
- this._store.dispatch({ type, ...args });
+ if (this._store && typeof this._store.dispatch === 'function') {
+ this._store.dispatch({ type, ...args });
+ }
},
getState() {
return this._store.getState();
From 8ae86e3befe41a95b5b20250630b599bf12fe719 Mon Sep 17 00:00:00 2001
From: Tim Hostetler <6970899+thostetler@users.noreply.github.com>
Date: Wed, 25 Sep 2019 20:38:00 +0000
Subject: [PATCH 21/46] initial boilerplate
Please enter the commit message for your changes. Lines starting
---
grunt/check-release-version.js | 71 +-
npm-shrinkwrap.json | 1651 ++---------------
src/config/discovery.config.js | 11 +-
src/index.html | 2 +-
src/js/apps/discovery/navigator.js | 87 +-
src/js/apps/discovery/router.js | 545 +++---
src/js/components/api_targets.js | 1 +
.../templates/results-page-layout.html | 1 +
src/js/react/BumblebeeWidget.js | 50 +-
src/js/react/MyAdsDashboard/actions.js | 130 ++
.../MyAdsDashboard/components/App.jsx.js | 121 ++
.../components/ArxivClassList.jsx.js | 347 ++++
.../components/ArxivForm.jsx.js | 194 ++
.../components/AuthorsForm.jsx.js | 209 +++
.../components/CitationsEntry.jsx.js | 210 +++
.../components/CitationsForm.jsx.js | 207 +++
.../components/ClassicLoginForm.jsx.js | 263 +++
.../components/Dashboard.jsx.js | 431 +++++
.../components/GeneralForm.jsx.js | 247 +++
.../components/ImportNotificationsForm.jsx.js | 155 ++
.../components/KeywordForm.jsx.js | 175 ++
.../components/SelectTemplate.jsx.js | 65 +
.../components/TemplatePill.jsx.js | 54 +
src/js/react/MyAdsDashboard/constants.js | 19 +
.../MyAdsDashboard/containers/ArxivForm.js | 29 +
.../MyAdsDashboard/containers/AuthorsForm.js | 32 +
.../containers/CitationsForm.js | 32 +
.../containers/ClassicLoginForm.js | 27 +
.../MyAdsDashboard/containers/Dashboard.js | 39 +
.../MyAdsDashboard/containers/GeneralForm.js | 30 +
.../containers/ImportNotificationsForm.js | 19 +
.../MyAdsDashboard/containers/KeywordForm.js | 32 +
.../containers/SelectTemplate.js | 22 +
src/js/react/MyAdsDashboard/index.js | 45 +
src/js/react/MyAdsDashboard/middleware.js | 154 ++
.../MyAdsDashboard/models/arxivClasses.js | 887 +++++++++
src/js/react/MyAdsDashboard/reducer.js | 106 ++
src/js/react/MyAdsDashboard/typedefs.js | 27 +
src/js/react/MyAdsFreeform/actions.js | 56 +
.../react/MyAdsFreeform/components/App.jsx.js | 123 ++
.../components/CollapsePanel.jsx.js | 96 +
.../components/SaveQueryForm.jsx.js | 162 ++
src/js/react/MyAdsFreeform/constants.js | 10 +
.../MyAdsFreeform/containers/SaveQueryForm.js | 17 +
src/js/react/MyAdsFreeform/index.js | 45 +
src/js/react/MyAdsFreeform/middleware.js | 112 ++
src/js/react/MyAdsFreeform/reducer.js | 67 +
.../react/shared/components/ApiMessage.jsx.js | 41 +
src/js/react/shared/middleware/api.js | 24 +-
.../query_info/query_info_template.html | 33 +-
.../widgets/query_info/query_info_widget.js | 31 +-
.../abstract_page_library_add/template.html | 127 +-
.../user-settings-layout.html | 13 +-
.../user_settings_page_manager/user_nav.html | 97 +-
.../user_page_manager.js | 5 +
src/styles/img/how_to_add_general_query.gif | Bin 0 -> 4077163 bytes
src/styles/img/myADS.gif | Bin 0 -> 2037458 bytes
.../sass/ads-sass/results-page-widgets.scss | 41 +-
src/styles/sass/manifest.scss | 3 +-
59 files changed, 5752 insertions(+), 2078 deletions(-)
create mode 100644 src/js/react/MyAdsDashboard/actions.js
create mode 100644 src/js/react/MyAdsDashboard/components/App.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/ArxivClassList.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/ArxivForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/AuthorsForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/CitationsEntry.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/CitationsForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/ClassicLoginForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/Dashboard.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/GeneralForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/KeywordForm.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/SelectTemplate.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/components/TemplatePill.jsx.js
create mode 100644 src/js/react/MyAdsDashboard/constants.js
create mode 100644 src/js/react/MyAdsDashboard/containers/ArxivForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/AuthorsForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/CitationsForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/ClassicLoginForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/Dashboard.js
create mode 100644 src/js/react/MyAdsDashboard/containers/GeneralForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/ImportNotificationsForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/KeywordForm.js
create mode 100644 src/js/react/MyAdsDashboard/containers/SelectTemplate.js
create mode 100644 src/js/react/MyAdsDashboard/index.js
create mode 100644 src/js/react/MyAdsDashboard/middleware.js
create mode 100644 src/js/react/MyAdsDashboard/models/arxivClasses.js
create mode 100644 src/js/react/MyAdsDashboard/reducer.js
create mode 100644 src/js/react/MyAdsDashboard/typedefs.js
create mode 100644 src/js/react/MyAdsFreeform/actions.js
create mode 100644 src/js/react/MyAdsFreeform/components/App.jsx.js
create mode 100644 src/js/react/MyAdsFreeform/components/CollapsePanel.jsx.js
create mode 100644 src/js/react/MyAdsFreeform/components/SaveQueryForm.jsx.js
create mode 100644 src/js/react/MyAdsFreeform/constants.js
create mode 100644 src/js/react/MyAdsFreeform/containers/SaveQueryForm.js
create mode 100644 src/js/react/MyAdsFreeform/index.js
create mode 100644 src/js/react/MyAdsFreeform/middleware.js
create mode 100644 src/js/react/MyAdsFreeform/reducer.js
create mode 100644 src/js/react/shared/components/ApiMessage.jsx.js
create mode 100644 src/styles/img/how_to_add_general_query.gif
create mode 100644 src/styles/img/myADS.gif
diff --git a/grunt/check-release-version.js b/grunt/check-release-version.js
index 4205cffd6..b793d85ab 100644
--- a/grunt/check-release-version.js
+++ b/grunt/check-release-version.js
@@ -4,45 +4,50 @@
*
* @module grunt/check-release-version
*/
-module.exports = function (grunt) {
- grunt.registerMultiTask('check-release-version', 'Get release version', async function () {
- const done = this.async();
- const axios = require('axios');
- const fs = require('fs');
- const githubReleasesPath = 'https://api.github.com/repos/adsabs/bumblebee/releases/latest';
- const releaseFilePath = '/release';
+module.exports = function(grunt) {
+ grunt.registerMultiTask(
+ 'check-release-version',
+ 'Get release version',
+ async function() {
+ const done = this.async();
+ const axios = require('axios');
+ const fs = require('fs');
+ const githubReleasesPath =
+ 'https://api.github.com/repos/adsabs/bumblebee/releases/latest';
+ const releaseFilePath = '/release';
- try {
- let version;
+ try {
+ let version;
- if (fs.existsSync(releaseFilePath)) {
+ if (fs.existsSync(releaseFilePath)) {
+ // check for the existence of release file
+ console.log('found release file!');
+ const buffer = fs.readFileSync(releaseFilePath);
+ version = buffer.toString().trim();
+ } else {
+ // otherwise, get the latest version from github
+ console.log(
+ 'did not find release file, fetching latest release from github'
+ );
+ const response = await axios.get(githubReleasesPath);
+ version = response.data.tag_name;
+ }
- // check for the existence of release file
- console.log('found release file!');
- const buffer = fs.readFileSync(releaseFilePath);
- version = buffer.toString();
- } else {
-
- // otherwise, get the latest version from github
- console.log('did not find release file, fetching latest release from github');
- const response = await axios.get(githubReleasesPath);
- version = response.data.tag_name;
- }
-
- if (version) {
- console.log('version found: ', version);
- grunt.config('appVersion', version);
+ if (version) {
+ console.log('version found: ', version);
+ grunt.config('appVersion', version);
+ }
+ } catch (e) {
+ console.error(e);
+ } finally {
+ done();
}
- } catch (e) {
- console.error(e);
- } finally {
- done();
}
- });
+ );
return {
release: {
- options: {}
- }
- }
+ options: {},
+ },
+ };
};
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 16577e3c8..a150a38e1 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1004,6 +1004,7 @@
"integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
"dev": true
},
+<<<<<<< HEAD
"@types/color-name": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -1014,6 +1015,8 @@
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"@types/q": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz",
@@ -1064,12 +1067,6 @@
"integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
"dev": true
},
- "acorn-jsx": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
- "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
- "dev": true
- },
"acorn-node": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz",
@@ -1128,11 +1125,14 @@
"integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
"dev": true
},
+<<<<<<< HEAD
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@@ -1206,24 +1206,6 @@
"sprintf-js": "~1.0.2"
}
},
- "aria-query": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
- "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
- "dev": true,
- "requires": {
- "ast-types-flow": "0.0.7",
- "commander": "^2.11.0"
- },
- "dependencies": {
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true
- }
- }
- },
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -1265,70 +1247,6 @@
"integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
"dev": true
},
- "array-includes": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
- "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
- "is-string": "^1.0.5"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
"array-map": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
@@ -1362,69 +1280,6 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
- "array.prototype.flat": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
- "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -1494,18 +1349,6 @@
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true
},
- "ast-types-flow": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
- "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
- "dev": true
- },
- "astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
- "dev": true
- },
"async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
@@ -1608,12 +1451,6 @@
}
}
},
- "axobject-query": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.2.tgz",
- "integrity": "sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ==",
- "dev": true
- },
"backbone": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz",
@@ -3135,11 +2972,14 @@
"integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
"dev": true
},
+<<<<<<< HEAD
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"camel-case": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
@@ -3252,12 +3092,6 @@
}
}
},
- "chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
- },
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -3270,12 +3104,6 @@
"integrity": "sha1-4JIVodUVQtsqJXaWl2W89hJVg+s=",
"dev": true
},
- "ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "dev": true
- },
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@@ -3359,6 +3187,7 @@
"timers-ext": "0.1"
}
},
+<<<<<<< HEAD
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -3407,6 +3236,8 @@
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -3553,12 +3384,6 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0="
},
- "compare-versions": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz",
- "integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==",
- "dev": true
- },
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@@ -3871,12 +3696,6 @@
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
"dev": true
},
- "contains-path": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
- "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
- "dev": true
- },
"content-disposition": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz",
@@ -3963,6 +3782,7 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
+<<<<<<< HEAD
"cosmiconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
@@ -3993,6 +3813,8 @@
}
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"coveralls": {
"version": "2.13.3",
"resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.13.3.tgz",
@@ -4280,12 +4102,6 @@
"es5-ext": "~0.10.2"
}
},
- "damerau-levenshtein": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
- "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==",
- "dev": true
- },
"dash-ast": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz",
@@ -4775,15 +4591,6 @@
"path-type": "^3.0.0"
}
},
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
"dom-helpers": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
@@ -5238,200 +5045,6 @@
}
}
},
- "eslint": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz",
- "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.0.0",
- "ajv": "^6.10.0",
- "chalk": "^2.1.0",
- "cross-spawn": "^6.0.5",
- "debug": "^4.0.1",
- "doctrine": "^3.0.0",
- "eslint-scope": "^5.0.0",
- "eslint-utils": "^1.3.1",
- "eslint-visitor-keys": "^1.0.0",
- "espree": "^6.0.0",
- "esquery": "^1.0.1",
- "esutils": "^2.0.2",
- "file-entry-cache": "^5.0.1",
- "functional-red-black-tree": "^1.0.1",
- "glob-parent": "^5.0.0",
- "globals": "^11.7.0",
- "ignore": "^4.0.6",
- "import-fresh": "^3.0.0",
- "imurmurhash": "^0.1.4",
- "inquirer": "^6.4.1",
- "is-glob": "^4.0.0",
- "js-yaml": "^3.13.1",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.3.0",
- "lodash": "^4.17.14",
- "minimatch": "^3.0.4",
- "mkdirp": "^0.5.1",
- "natural-compare": "^1.4.0",
- "optionator": "^0.8.2",
- "progress": "^2.0.0",
- "regexpp": "^2.0.1",
- "semver": "^6.1.2",
- "strip-ansi": "^5.2.0",
- "strip-json-comments": "^3.0.1",
- "table": "^5.2.3",
- "text-table": "^0.2.0",
- "v8-compile-cache": "^2.0.3"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "dependencies": {
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- }
- }
- },
- "esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true
- },
- "figures": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
- "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^1.0.5"
- }
- },
- "glob-parent": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
- "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "ignore": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
- "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
- "dev": true
- },
- "inquirer": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz",
- "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^3.2.0",
- "chalk": "^2.4.2",
- "cli-cursor": "^2.1.0",
- "cli-width": "^2.0.0",
- "external-editor": "^3.0.3",
- "figures": "^2.0.0",
- "lodash": "^4.17.12",
- "mute-stream": "0.0.7",
- "run-async": "^2.2.0",
- "rxjs": "^6.4.0",
- "string-width": "^2.1.0",
- "strip-ansi": "^5.1.0",
- "through": "^2.3.6"
- }
- },
- "js-yaml": {
- "version": "3.13.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
- "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
- "dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- },
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
- },
- "mute-stream": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
- "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
- "dev": true
- },
- "progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "dev": true
- },
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- },
- "strip-json-comments": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
- "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
- "dev": true
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
"eslint-config-airbnb": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.0.1.tgz",
@@ -5454,489 +5067,54 @@
"object.entries": "^1.1.0"
}
},
- "eslint-config-prettier": {
- "version": "6.10.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz",
- "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==",
+ "eslint-plugin-requirejs": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-requirejs/-/eslint-plugin-requirejs-4.0.0.tgz",
+ "integrity": "sha512-0RUXMrQ7elTtTZT6dwP6HozoAUzUBnMawBmdOKyQAWqulrLnKg8WGKJIpo7cDisjAr91SXDUIhCOrIqgbz9ETQ==",
"dev": true,
"requires": {
- "get-stdin": "^6.0.0"
+ "ignore": "5.0.5"
},
"dependencies": {
- "get-stdin": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
- "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+ "ignore": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz",
+ "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==",
"dev": true
}
}
},
- "eslint-import-resolver-node": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz",
- "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==",
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ },
+ "etag": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
+ "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=",
+ "dev": true
+ },
+ "event-emitter": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
"dev": true,
"requires": {
- "debug": "^2.6.9",
- "resolve": "^1.13.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- }
- }
- },
- "eslint-module-utils": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz",
- "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==",
- "dev": true,
- "requires": {
- "debug": "^2.6.9",
- "pkg-dir": "^2.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- }
- }
- },
- "eslint-plugin-import": {
- "version": "2.20.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz",
- "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==",
- "dev": true,
- "requires": {
- "array-includes": "^3.0.3",
- "array.prototype.flat": "^1.2.1",
- "contains-path": "^0.1.0",
- "debug": "^2.6.9",
- "doctrine": "1.5.0",
- "eslint-import-resolver-node": "^0.3.2",
- "eslint-module-utils": "^2.4.1",
- "has": "^1.0.3",
- "minimatch": "^3.0.4",
- "object.values": "^1.1.0",
- "read-pkg-up": "^2.0.0",
- "resolve": "^1.12.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "doctrine": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
- "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2",
- "isarray": "^1.0.0"
- }
- },
- "find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
- "dev": true,
- "requires": {
- "locate-path": "^2.0.0"
- }
- },
- "graceful-fs": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
- "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
- "dev": true
- },
- "load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "strip-bom": "^3.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
- "path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
- "dev": true,
- "requires": {
- "pify": "^2.0.0"
- }
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- },
- "read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
- "dev": true,
- "requires": {
- "load-json-file": "^2.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
- }
- },
- "read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
- "dev": true,
- "requires": {
- "find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
- }
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- },
- "strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
- "dev": true
- }
- }
- },
- "eslint-plugin-jsx-a11y": {
- "version": "6.2.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
- "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
- "dev": true,
- "requires": {
- "@babel/runtime": "^7.4.5",
- "aria-query": "^3.0.0",
- "array-includes": "^3.0.3",
- "ast-types-flow": "^0.0.7",
- "axobject-query": "^2.0.2",
- "damerau-levenshtein": "^1.0.4",
- "emoji-regex": "^7.0.2",
- "has": "^1.0.3",
- "jsx-ast-utils": "^2.2.1"
- },
- "dependencies": {
- "@babel/runtime": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz",
- "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==",
- "dev": true,
- "requires": {
- "regenerator-runtime": "^0.13.2"
- }
- }
- }
- },
- "eslint-plugin-prettier": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz",
- "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==",
- "dev": true,
- "requires": {
- "prettier-linter-helpers": "^1.0.0"
- }
- },
- "eslint-plugin-react": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.18.3.tgz",
- "integrity": "sha512-Bt56LNHAQCoou88s8ViKRjMB2+36XRejCQ1VoLj716KI1MoE99HpTVvIThJ0rvFmG4E4Gsq+UgToEjn+j044Bg==",
- "dev": true,
- "requires": {
- "array-includes": "^3.1.1",
- "doctrine": "^2.1.0",
- "has": "^1.0.3",
- "jsx-ast-utils": "^2.2.3",
- "object.entries": "^1.1.1",
- "object.fromentries": "^2.0.2",
- "object.values": "^1.1.1",
- "prop-types": "^15.7.2",
- "resolve": "^1.14.2",
- "string.prototype.matchall": "^4.0.2"
- },
- "dependencies": {
- "doctrine": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
- "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "object.values": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
- "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
- "resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
- "dev": true,
- "requires": {
- "path-parse": "^1.0.6"
- }
- }
- }
- },
- "eslint-plugin-react-hooks": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz",
- "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==",
- "dev": true
- },
- "eslint-plugin-requirejs": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-requirejs/-/eslint-plugin-requirejs-4.0.0.tgz",
- "integrity": "sha512-0RUXMrQ7elTtTZT6dwP6HozoAUzUBnMawBmdOKyQAWqulrLnKg8WGKJIpo7cDisjAr91SXDUIhCOrIqgbz9ETQ==",
- "dev": true,
- "requires": {
- "ignore": "5.0.5"
- },
- "dependencies": {
- "ignore": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.5.tgz",
- "integrity": "sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA==",
- "dev": true
- }
- }
- },
- "eslint-scope": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
- "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
- "dev": true,
- "requires": {
- "esrecurse": "^4.1.0",
- "estraverse": "^4.1.1"
- },
- "dependencies": {
- "estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true
- }
- }
- },
- "eslint-utils": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
- "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^1.1.0"
- }
- },
- "eslint-visitor-keys": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
- "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
- "dev": true
- },
- "espree": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
- "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
- "dev": true,
- "requires": {
- "acorn": "^7.1.0",
- "acorn-jsx": "^5.1.0",
- "eslint-visitor-keys": "^1.1.0"
- },
- "dependencies": {
- "acorn": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
- "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
- "dev": true
- }
- }
- },
- "esprima": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
- "dev": true
- },
- "esquery": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
- "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
- "dev": true,
- "requires": {
- "estraverse": "^4.0.0"
- },
- "dependencies": {
- "estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true
- }
- }
- },
- "esrecurse": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
- "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
- "dev": true,
- "requires": {
- "estraverse": "^4.1.0"
- },
- "dependencies": {
- "estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true
- }
- }
- },
- "estraverse": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
- "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
- "dev": true
- },
- "esutils": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
- },
- "etag": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz",
- "integrity": "sha1-A9MLX2fdbmMtKUXTDWZScxo01dg=",
- "dev": true
- },
- "event-emitter": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
- "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
- "dev": true,
- "requires": {
- "d": "1",
- "es5-ext": "~0.10.14"
+ "d": "1",
+ "es5-ext": "~0.10.14"
},
"dependencies": {
"d": {
@@ -6243,28 +5421,6 @@
}
}
},
- "external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "dev": true,
- "requires": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
- "dependencies": {
- "tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.2"
- }
- }
- }
- },
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -6441,12 +5597,6 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
},
- "fast-diff": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
- "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
- "dev": true
- },
"fast-glob": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz",
@@ -6515,15 +5665,6 @@
"object-assign": "^4.1.0"
}
},
- "file-entry-cache": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
- "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
- "dev": true,
- "requires": {
- "flat-cache": "^2.0.1"
- }
- },
"file-saver": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
@@ -6751,34 +5892,6 @@
}
}
},
- "flat-cache": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
- "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
- "dev": true,
- "requires": {
- "flatted": "^2.0.0",
- "rimraf": "2.6.3",
- "write": "1.0.3"
- },
- "dependencies": {
- "rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "dev": true,
- "requires": {
- "glob": "^7.1.3"
- }
- }
- }
- },
- "flatted": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
- "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
- "dev": true
- },
"follow-redirects": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
@@ -6921,12 +6034,6 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
- "functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
- "dev": true
- },
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -8984,6 +8091,7 @@
}
}
},
+<<<<<<< HEAD
"human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -9132,6 +8240,8 @@
}
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -9219,6 +8329,7 @@
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
"integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
},
+<<<<<<< HEAD
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@@ -9228,6 +8339,8 @@
"resolve-from": "^4.0.0"
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"import-lazy": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz",
@@ -9235,12 +8348,6 @@
"dev": true,
"optional": true
},
- "imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
- "dev": true
- },
"in-publish": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
@@ -9487,77 +8594,13 @@
"integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=",
"dev": true
},
- "tough-cookie": {
- "version": "0.12.1",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz",
- "integrity": "sha1-giDH4hq9WxPZaAQlS9WoHr8sfWI=",
- "dev": true,
- "requires": {
- "punycode": ">=0.2.0"
- }
- }
- }
- },
- "internal-slot": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz",
- "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==",
- "dev": true,
- "requires": {
- "es-abstract": "^1.17.0-next.1",
- "has": "^1.0.3",
- "side-channel": "^1.0.2"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "tough-cookie": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz",
+ "integrity": "sha1-giDH4hq9WxPZaAQlS9WoHr8sfWI=",
"dev": true,
"requires": {
- "has": "^1.0.3"
+ "punycode": ">=0.2.0"
}
}
}
@@ -9830,11 +8873,14 @@
"dev": true,
"optional": true
},
+<<<<<<< HEAD
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
@@ -9873,12 +8919,6 @@
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
- "is-string": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
- "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
- "dev": true
- },
"is-svg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
@@ -10262,11 +9302,14 @@
"dev": true,
"optional": true
},
+<<<<<<< HEAD
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -10286,12 +9329,6 @@
"jsonify": "~0.0.0"
}
},
- "json-stable-stringify-without-jsonify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
- "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
- "dev": true
- },
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -10386,16 +9423,6 @@
}
}
},
- "jsx-ast-utils": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
- "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==",
- "dev": true,
- "requires": {
- "array-includes": "^3.0.3",
- "object.assign": "^4.1.0"
- }
- },
"junk": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/junk/-/junk-1.0.3.tgz",
@@ -10521,6 +9548,7 @@
"integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=",
"dev": true
},
+<<<<<<< HEAD
"lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
@@ -10927,6 +9955,8 @@
}
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"load-grunt-config": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/load-grunt-config/-/load-grunt-config-0.19.2.tgz",
@@ -12301,6 +11331,7 @@
"integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==",
"dev": true
},
+<<<<<<< HEAD
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -12312,6 +11343,8 @@
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M="
},
+=======
+>>>>>>> initial boilerplate
"needle": {
"version": "0.7.11",
"resolved": "https://registry.npmjs.org/needle/-/needle-0.7.11.tgz",
@@ -15607,12 +14640,6 @@
}
}
},
- "object-inspect": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
- "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
- "dev": true
- },
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -15641,133 +14668,15 @@
}
},
"object.entries": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz",
- "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
- "object.fromentries": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz",
- "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
+ "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
+ "es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has": "^1.0.3"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
}
},
"object.getownpropertydescriptors": {
@@ -15794,6 +14703,7 @@
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
"dev": true,
+ "optional": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
@@ -15836,12 +14746,6 @@
"integrity": "sha1-XeRqCFi59J+fIRqo8mYoVQZX8mI=",
"dev": true
},
- "opencollective-postinstall": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
- "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==",
- "dev": true
- },
"opn": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/opn/-/opn-1.0.2.tgz",
@@ -15928,12 +14832,6 @@
"win-release": "^1.0.0"
}
},
- "os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
- "dev": true
- },
"osenv": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.0.tgz",
@@ -16098,6 +14996,7 @@
"no-case": "^2.2.0"
}
},
+<<<<<<< HEAD
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -16106,6 +15005,8 @@
"callsites": "^3.0.0"
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"parents": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@@ -16425,26 +15326,6 @@
"pinkie": "^2.0.0"
}
},
- "pkg-dir": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
- "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
- "dev": true,
- "requires": {
- "find-up": "^2.1.0"
- },
- "dependencies": {
- "find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
- "dev": true,
- "requires": {
- "locate-path": "^2.0.0"
- }
- }
- }
- },
"pkg-up": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz",
@@ -16454,6 +15335,7 @@
"find-up": "^1.0.0"
}
},
+<<<<<<< HEAD
"please-upgrade-node": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
@@ -16462,6 +15344,8 @@
"semver-compare": "^1.0.0"
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"plur": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz",
@@ -16490,21 +15374,6 @@
"dev": true,
"optional": true
},
- "prettier": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
- "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
- "dev": true
- },
- "prettier-linter-helpers": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
- "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
- "dev": true,
- "requires": {
- "fast-diff": "^1.1.2"
- }
- },
"pretty-bytes": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.2.0.tgz",
@@ -17249,75 +16118,6 @@
"integrity": "sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w==",
"dev": true
},
- "regexp.prototype.flags": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
- "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
- "regexpp": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
- "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
- "dev": true
- },
"regexpu-core": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz",
@@ -17510,11 +16310,14 @@
"path-parse": "^1.0.6"
}
},
+<<<<<<< HEAD
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -17549,6 +16352,7 @@
"lowercase-keys": "^1.0.0"
}
},
+<<<<<<< HEAD
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -17568,6 +16372,8 @@
}
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -17619,21 +16425,13 @@
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=",
"dev": true
},
- "run-async": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
- "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
- "dev": true,
- "requires": {
- "is-promise": "^2.1.0"
- }
- },
"rx": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz",
"integrity": "sha1-Ia3H2A8CACr1Da6X/Z2/JIdV9WY=",
"dev": true
},
+<<<<<<< HEAD
"rxjs": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
@@ -17642,6 +16440,8 @@
"tslib": "^1.9.0"
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -17879,11 +16679,14 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA=="
},
+<<<<<<< HEAD
"semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"semver-diff": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-0.1.0.tgz",
@@ -17905,7 +16708,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz",
"integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"semver-truncate": {
"version": "1.1.2",
@@ -18198,69 +17002,6 @@
"integrity": "sha1-kEktcv/MgVmXa6umL7D2iE8MM3g=",
"dev": true
},
- "side-channel": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz",
- "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==",
- "dev": true,
- "requires": {
- "es-abstract": "^1.17.0-next.1",
- "object-inspect": "^1.7.0"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
"sigmund": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
@@ -18278,6 +17019,11 @@
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=",
"dev": true
},
+ "simple-react-validator": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/simple-react-validator/-/simple-react-validator-1.3.2.tgz",
+ "integrity": "sha512-MJujXCgf+Fa0GFTUNA4zgV0NqLSnnRxoaRdUxLY5rwE7Y3Hs/dqMoigCKTvBRCCnX83eXt38gBbV09FRWn2SkA=="
+ },
"sinon": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.2.tgz",
@@ -18299,25 +17045,6 @@
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
"dev": true
},
- "slice-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
- },
- "dependencies": {
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
- }
- }
- },
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -18842,93 +17569,6 @@
}
}
},
- "string.prototype.matchall": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz",
- "integrity": "sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0",
- "has-symbols": "^1.0.1",
- "internal-slot": "^1.0.2",
- "regexp.prototype.flags": "^1.3.0",
- "side-channel": "^1.0.2"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.17.4",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
- "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
- "dev": true,
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.1.5",
- "is-regex": "^1.0.5",
- "object-inspect": "^1.7.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimleft": "^2.1.1",
- "string.prototype.trimright": "^2.1.1"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "has-symbols": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
- "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
- "dev": true
- },
- "is-callable": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
- "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
- "dev": true
- },
- "is-regex": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
- "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- }
- }
- },
- "string.prototype.trimleft": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
- "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
- "string.prototype.trimright": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
- "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
- "dev": true,
- "requires": {
- "define-properties": "^1.1.3",
- "function-bind": "^1.1.1"
- }
- },
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
@@ -19119,58 +17759,6 @@
"acorn-node": "^1.2.0"
}
},
- "table": {
- "version": "5.4.6",
- "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
- "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
- "dev": true,
- "requires": {
- "ajv": "^6.10.2",
- "lodash": "^4.17.14",
- "slice-ansi": "^2.1.0",
- "string-width": "^3.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
- "dev": true
- },
- "lodash": {
- "version": "4.17.15",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
- "dev": true
- },
- "string-width": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
- "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
- "dev": true,
- "requires": {
- "emoji-regex": "^7.0.1",
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^5.1.0"
- }
- },
- "strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "dev": true,
- "requires": {
- "ansi-regex": "^4.1.0"
- }
- }
- }
- },
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
@@ -19523,11 +18111,14 @@
"glob": "^7.1.2"
}
},
+<<<<<<< HEAD
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"tsscmp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz",
@@ -20001,12 +18592,6 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
- "v8-compile-cache": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
- "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
- "dev": true
- },
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -20080,12 +18665,6 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
- "which-pm-runs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
- "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=",
- "dev": true
- },
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
@@ -20149,15 +18728,6 @@
"integrity": "sha1-f1I+/bcbAQDnfc6DTAZSPL49VOA=",
"dev": true
},
- "write": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
- "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
- "dev": true,
- "requires": {
- "mkdirp": "^0.5.1"
- }
- },
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
@@ -20192,6 +18762,7 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
},
+<<<<<<< HEAD
"yaml": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz",
@@ -20210,6 +18781,8 @@
}
}
},
+=======
+>>>>>>> 1195dba1... initial boilerplate
"yargs": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
diff --git a/src/config/discovery.config.js b/src/config/discovery.config.js
index e153bff67..159ca5752 100644
--- a/src/config/discovery.config.js
+++ b/src/config/discovery.config.js
@@ -5,11 +5,12 @@ require.config({
deps: (function() {
if (typeof window !== 'undefined' && window.skipMain) {
return ['common.config'];
+ } else {
+ return ['config/common.config', 'js/apps/discovery/main'];
}
- return ['config/common.config', 'js/apps/discovery/main'];
})(),
- // this will be overridden in the compiled file
+ //this will be overridden in the compiled file
waitSeconds: 30,
// Configuration we want to make available to modules of ths application
@@ -87,6 +88,7 @@ require.config({
SearchWidget: 'js/widgets/search_bar/search_bar_widget',
PaperSearchForm: 'js/widgets/paper_search_form/widget',
Results: 'js/widgets/results/widget',
+ MyAdsFreeform: 'reactify!js/react/BumblebeeWidget?MyAdsFreeform',
QueryInfo: 'js/widgets/query_info/query_info_widget',
QueryDebugInfo: 'js/widgets/api_query/widget',
ExportWidget: 'es6!js/widgets/export/widget.jsx',
@@ -97,6 +99,7 @@ require.config({
PaperNetwork: 'js/wraps/paper_network',
ConceptCloud: 'js/widgets/wordcloud/widget',
BubbleChart: 'js/widgets/bubble_chart/widget',
+ MyAdsDashboard: 'reactify!js/react/BumblebeeWidget?MyAdsDashboard',
AuthorAffiliationTool:
'es6!js/widgets/author_affiliation_tool/widget.jsx',
@@ -129,7 +132,7 @@ require.config({
ShowCoreads: 'js/wraps/coreads',
ShowSimilar: 'js/wraps/similar',
MetaTagsWidget: 'js/widgets/meta_tags/widget',
- // can't camel case because router only capitalizes first letter
+ //can't camel case because router only capitalizes first letter
ShowToc: 'js/wraps/table_of_contents',
ShowResources: 'es6!js/widgets/resources/widget.jsx',
ShowAssociated: 'es6!js/widgets/associated/widget.jsx',
@@ -174,7 +177,7 @@ require.config({
async: 'libs/requirejs-plugins/async',
babel: 'libs/requirejs-babel-plugin/babel-5.8.34.min',
backbone: [
- // '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min',
+ //'//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min',
'libs/backbone/backbone',
],
'backbone-validation': [
diff --git a/src/index.html b/src/index.html
index c58985827..55c39aa32 100644
--- a/src/index.html
+++ b/src/index.html
@@ -63,7 +63,7 @@
diff --git a/src/js/apps/discovery/navigator.js b/src/js/apps/discovery/navigator.js
index 4e016e8d5..c1bc6aa73 100644
--- a/src/js/apps/discovery/navigator.js
+++ b/src/js/apps/discovery/navigator.js
@@ -60,6 +60,7 @@ define([
var searchPageAlwaysVisible = [
'Results',
+ 'MyAdsFreeform',
'QueryInfo',
'AffiliationFacet',
'AuthorFacet',
@@ -209,7 +210,6 @@ define([
});
this.set('LibraryImport', function(page, data) {
- var that = this;
var defer = $.Deferred();
var that = this;
if (redirectIfNotSignedIn(that.endpoint)) {
@@ -284,6 +284,30 @@ define([
)
);
+ this.set('MyAdsDashboard', function() {
+ var defer = $.Deferred();
+ var that = this;
+ if (redirectIfNotSignedIn(that.endpoint)) {
+ defer.resolve();
+ return defer.promise();
+ }
+
+ app
+ .getObject('MasterPageManager')
+ .show('SettingsPage', ['MyAdsDashboard', 'UserNavbarWidget'])
+ .then(function() {
+ app.getWidget('SettingsPage').done(function(widget) {
+ widget.setActive('MyAdsDashboard');
+ that.route = '#user/settings/myads';
+ that.title = 'myADS Notifications';
+ publishPageChange('settings-page');
+ defer.resolve();
+ });
+ });
+
+ return defer.promise();
+ });
+
this.set('LibraryActionsWidget', function() {
var $dd = $.Deferred();
var that = this;
@@ -399,8 +423,8 @@ define([
app
.getWidget('LibraryListWidget', 'IndividualLibraryWidget')
.then(function(w) {
- w.LibraryListWidget.setData(data);
- w.IndividualLibraryWidget.setSubView(data);
+ w['LibraryListWidget'].setData(data);
+ w['IndividualLibraryWidget'].setSubView(data);
if (pub) publishPageChange('libraries-page');
defer.resolve();
@@ -533,13 +557,13 @@ define([
this.set('authentication-page', function(page, data) {
var defer = $.Deferred();
- var data = data || {};
- var subView = data.subView || 'login';
- var loggedIn = app
- .getBeeHive()
- .getObject('User')
- .isLoggedIn();
- var that = this;
+ var data = data || {},
+ subView = data.subView || 'login',
+ loggedIn = app
+ .getBeeHive()
+ .getObject('User')
+ .isLoggedIn(),
+ that = this;
if (loggedIn) {
// redirect to index
@@ -794,8 +818,7 @@ define([
'BubbleChart',
];
- var widgetName;
- var pages;
+ var widgetName, pages;
// convention is that a navigate command for search page widget starts with "show-"
// waits for the navigate to results page emitted by the discovery_mediator
@@ -843,7 +866,7 @@ define([
that.route += '&__tb=1';
}
- const q = query;
+ let q = query;
if (q instanceof ApiQuery) {
var update = {};
var par = function(str) {
@@ -945,14 +968,14 @@ define([
});
this.set('user-action', function(endPoint, data) {
- var failMessage = '';
- var failTitle = '';
- var route;
- var done;
- var defer = $.Deferred();
+ var failMessage = '',
+ failTitle = '',
+ route,
+ done,
+ defer = $.Deferred();
- var token = data.token;
- var subView = data.subView;
+ var token = data.token,
+ subView = data.subView;
function fail(jqXHR, status, errorThrown) {
self
@@ -1153,8 +1176,7 @@ define([
});
});
return;
- }
- if (orcidApi.hasAccess()) {
+ } else if (orcidApi.hasAccess()) {
// XXX:rca = this block is async; showing modals even if the page under may be
// changing; likely not intended to be doing that but not sure...
@@ -1219,8 +1241,8 @@ define([
* */
function showResultsPageWidgetWithUniqueUrl(command, options) {
- var defer = $.Deferred();
- var that = this;
+ var defer = $.Deferred(),
+ that = this;
options = options || {};
var q = app.getObject('AppStorage').getCurrentQuery();
if (!q && options.q) {
@@ -1316,10 +1338,10 @@ define([
this.set('verify-abstract', function() {
// XXX:rca - moved from router; not in a working state
// check we are using the canonical bibcode and redirect to it if necessary
- var q;
- var req;
- var defer = $.Deferred;
- var that = this;
+ var q,
+ req,
+ defer = $.Deferred,
+ that = this;
q = new ApiQuery({
q: 'identifier:' + this.queryUpdater.quoteIfNecessary(bibcode),
@@ -1330,8 +1352,7 @@ define([
target: ApiTargets.SEARCH,
options: {
done: function(resp) {
- var navigateString;
- var href;
+ var navigateString, href;
if (!subPage) {
navigateString = 'ShowAbstract';
@@ -1340,7 +1361,7 @@ define([
'Show' + subPage[0].toUpperCase() + subPage.slice(1);
href = '#abs/' + bibcode + '/' + subPage;
}
- // self.routerNavigate(navigateString, { href: href });
+ //self.routerNavigate(navigateString, { href: href });
if (
resp.response &&
@@ -1648,8 +1669,8 @@ define([
});
this.set('show-author-affiliation-tool', function(id, options) {
- var defer = $.Deferred();
- var that = this;
+ var defer = $.Deferred(),
+ that = this;
var q = app.getObject('AppStorage').getCurrentQuery();
app
.getObject('MasterPageManager')
diff --git a/src/js/apps/discovery/router.js b/src/js/apps/discovery/router.js
index 68493e50d..94f3ecce3 100644
--- a/src/js/apps/discovery/router.js
+++ b/src/js/apps/discovery/router.js
@@ -1,312 +1,295 @@
define([
- 'underscore',
- 'jquery',
- 'backbone',
- 'js/components/api_query',
- 'js/mixins/dependon',
- 'js/components/api_feedback',
- 'js/components/api_request',
- 'js/components/api_targets',
- 'js/mixins/api_access',
- 'js/components/api_query_updater',
-], function(
- _,
- $,
- Backbone,
- ApiQuery,
- Dependon,
- ApiFeedback,
- ApiRequest,
- ApiTargets,
- ApiAccessMixin,
- ApiQueryUpdater
-) {
- var Router = Backbone.Router.extend({
- initialize: function(options) {
- options = options || {};
- this.queryUpdater = new ApiQueryUpdater('Router');
- },
-
- execute: function(callback, args) {
- // only perform actions if history has started
- if (Backbone.History.started) {
- var route = Backbone.history.getFragment();
- route = route === '' ? 'index' : route;
-
- // Workaround for issue where hitting back button from the index page
- // goes to an empty `search/` route, so capture that here and go back 2
- if (route === 'search/' && _.isEmpty(_.reject(args, _.isUndefined))) {
- return Backbone.history.history.go(-2);
+ 'underscore',
+ 'jquery',
+ 'backbone',
+ 'js/components/api_query',
+ 'js/mixins/dependon',
+ 'js/components/api_feedback',
+ 'js/components/api_request',
+ 'js/components/api_targets',
+ 'js/mixins/api_access',
+ 'js/components/api_query_updater'
+
+ ],
+ function (
+ _,
+ $,
+ Backbone,
+ ApiQuery,
+ Dependon,
+ ApiFeedback,
+ ApiRequest,
+ ApiTargets,
+ ApiAccessMixin,
+ ApiQueryUpdater
+ ) {
+ var Router = Backbone.Router.extend({
+
+ initialize: function (options) {
+ options = options || {};
+ this.queryUpdater = new ApiQueryUpdater('Router');
+ },
+
+ execute: function (callback, args) {
+ // only perform actions if history has started
+ if (Backbone.History.started) {
+ var route = Backbone.history.getFragment();
+ route = route === '' ? 'index' : route;
+
+ // Workaround for issue where hitting back button from the index page
+ // goes to an empty `search/` route, so capture that here and go back 2
+ if (route === 'search/' && _.isEmpty(_.reject(args, _.isUndefined))) {
+ return Backbone.history.history.go(-2);
+ }
}
- }
- if (_.isFunction(callback)) {
- callback.apply(this, args);
- }
- },
-
- activate: function(beehive) {
- this.setBeeHive(beehive);
- if (!this.hasPubSub()) {
- throw new Error(
- 'Ooops! Who configured this #@$%! There is no PubSub service!'
- );
- }
- },
-
- /*
- * if you don't want the navigator to duplicate the route in history,
- * use this function instead of pubsub.publish(pubsub.NAVIGATE ...)
- * */
-
- routerNavigate: function(route, options) {
- var options = options || {};
- this.getPubSub().publish(this.getPubSub().NAVIGATE, route, options);
- },
-
- routes: {
- '/': 'index',
- '': 'index',
- 'classic-form(/)': 'classicForm',
- 'paper-form(/)': 'paperForm',
- 'index/(:query)': 'index',
- 'search/(:query)(/)(:widgetName)': 'search',
- 'search(?:query)': 'search',
- 'execute-query/(:query)': 'executeQuery',
- 'abs/*path': 'view',
+ if (_.isFunction(callback)) {
+ callback.apply(this, args);
+ }
+ },
+
+ activate: function (beehive) {
+ this.setBeeHive(beehive);
+ if (!this.hasPubSub()) {
+ throw new Error('Ooops! Who configured this #@$%! There is no PubSub service!');
+ }
+ },
+
/*
- * user endpoints require user to be logged in, either
- * to orcid or to ads
+ * if you don't want the navigator to duplicate the route in history,
+ * use this function instead of pubsub.publish(pubsub.NAVIGATE ...)
* */
- 'user/orcid*(:subView)': 'orcidPage',
- 'user/account(/)(:subView)': 'authenticationPage',
- 'user/account/verify/(:subView)/(:token)': 'routeToVerifyPage',
- 'user/settings(/)(:subView)(/)': 'settingsPage',
- 'user/libraries(/)(:id)(/)(:subView)(/)(:subData)(/)': 'librariesPage',
- 'user/home(/)': 'homePage',
- /* end user routes */
-
- 'orcid-instructions(/)': 'orcidInstructions',
-
- 'public-libraries/(:id)(/)': 'publicLibraryPage',
- '*invalidRoute': 'noPageFound',
- },
-
- index: function(query) {
- this.routerNavigate('index-page');
- },
-
- classicForm: function() {
- this.routerNavigate('ClassicSearchForm');
- },
-
- paperForm: function() {
- this.routerNavigate('PaperSearchForm');
- },
-
- search: function(query, widgetName) {
- if (query) {
- try {
- var q = new ApiQuery().load(query);
- this.routerNavigate('search-page', {
- q: q,
- page: widgetName && 'show-' + widgetName,
- replace: true,
- });
- } catch (e) {
- console.error('Error parsing query from a string: ', query, e);
- this.getPubSub().publish(this.getPubSub().NAVIGATE, 'index-page');
- this.getPubSub().publish(
- this.getPubSub().BIG_FIRE,
- new ApiFeedback({
+
+ routerNavigate: function (route, options) {
+ var options = options || {};
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, route, options);
+ },
+
+ routes: {
+ '/': 'index',
+ '': 'index',
+ 'classic-form(/)': 'classicForm',
+ 'paper-form(/)': 'paperForm',
+ 'index/(:query)': 'index',
+ 'search/(:query)(/)(:widgetName)': 'search',
+ 'search(?:query)': 'search',
+ 'execute-query/(:query)': 'executeQuery',
+ 'abs/*path': 'view',
+ /*
+ * user endpoints require user to be logged in, either
+ * to orcid or to ads
+ * */
+ 'user/orcid*(:subView)': 'orcidPage',
+ 'user/account(/)(:subView)': 'authenticationPage',
+ 'user/account/verify/(:subView)/(:token)': 'routeToVerifyPage',
+ 'user/settings(/)(:subView)(/)': 'settingsPage',
+ 'user/libraries(/)(:id)(/)(:subView)(/)(:subData)(/)': 'librariesPage',
+ 'user/home(/)': 'homePage',
+ /* end user routes */
+
+ 'orcid-instructions(/)': 'orcidInstructions',
+
+ 'public-libraries/(:id)(/)': 'publicLibraryPage',
+ '*invalidRoute': 'noPageFound'
+ },
+
+ index: function (query) {
+ this.routerNavigate('index-page');
+ },
+
+ classicForm: function () {
+ this.routerNavigate('ClassicSearchForm');
+ },
+
+ paperForm: function () {
+ this.routerNavigate('PaperSearchForm');
+ },
+
+ search: function (query, widgetName) {
+
+ if (query) {
+ try {
+ var q = new ApiQuery().load(query);
+ this.routerNavigate('search-page', {
+ q: q,
+ page: widgetName && 'show-' + widgetName,
+ replace: true
+ });
+ } catch (e) {
+ console.error('Error parsing query from a string: ', query, e);
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'index-page');
+ this.getPubSub().publish(this.getPubSub().BIG_FIRE, new ApiFeedback({
code: ApiFeedback.CODES.CANNOT_ROUTE,
reason: 'Cannot parse query',
- query: query,
- })
- );
- return this.getPubSub().publish(
- this.getPubSub().ALERT,
- new ApiFeedback({
+ query: query
+ }));
+ return this.getPubSub().publish(this.getPubSub().ALERT, new ApiFeedback({
code: ApiFeedback.CODES.ALERT,
msg: 'unable parse query',
type: 'danger',
- modal: true,
- })
- );
+ modal: true
+ }));
+ }
+ } else {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'index-page');
}
- } else {
- this.getPubSub().publish(this.getPubSub().NAVIGATE, 'index-page');
- }
- },
-
- executeQuery: function(queryId) {
- this.getPubSub().publish(
- this.getPubSub().NAVIGATE,
- 'execute-query',
- queryId
- );
- },
-
- view: function(path) {
- if (!path) {
- return this.routerNavigate('404');
- }
+ },
- // break apart the path
- const parts = path.split('/');
- // check for a subpage
- let subPage;
- const subPageRegex = /^(abstract|citations|references|coreads|similar|toc|graphics|metrics|exportcitation)$/;
- if (parts[parts.length - 1].match(subPageRegex)) {
- subPage = parts.pop();
- }
+ executeQuery: function (queryId) {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'execute-query', queryId);
+ },
- // take the rest and combine into the identifier
- const id = parts.join('/');
-
- var navigateString;
- var href;
- if (!subPage) {
- navigateString = 'ShowAbstract';
- href = '#abs/' + encodeURIComponent(id) + '/abstract';
- } else {
- navigateString = 'Show' + subPage[0].toUpperCase() + subPage.slice(1);
- href = '#abs/' + encodeURIComponent(id) + '/' + subPage;
- }
- this.routerNavigate(navigateString, {
- href: href,
- bibcode: id,
- });
- },
-
- routeToVerifyPage: function(subView, token) {
- this.getPubSub().publish(this.getPubSub().NAVIGATE, 'user-action', {
- subView: subView,
- token: token,
- });
- },
-
- orcidPage: function() {
- this.getPubSub().publish(this.getPubSub().NAVIGATE, 'orcid-page');
- },
-
- orcidInstructions: function() {
- this.getPubSub().publish(this.getPubSub().NAVIGATE, 'orcid-instructions');
- },
-
- authenticationPage: function(subView) {
- // possible subViews: "login", "register", "reset-password"
- if (
- subView &&
- !_.contains(
- ['login', 'register', 'reset-password-1', 'reset-password-2'],
- subView
- )
- ) {
- throw new Error(
- "that isn't a subview that the authentication page knows about"
- );
- }
- this.routerNavigate('authentication-page', {
- subView: subView,
- });
- },
-
- settingsPage: function(subView) {
- // possible subViews: "token", "password", "email", "preferences"
- if (_.contains(['token', 'password', 'email', 'delete'], subView)) {
- this.routerNavigate('UserSettings', {
- subView: subView,
+ view: function (path) {
+ if (!path) {
+ return this.routerNavigate('404');
+ }
+
+ // break apart the path
+ const parts = path.split('/');
+
+ // check for a subpage
+ let subPage;
+ const subPageRegex = /^(abstract|citations|references|coreads|similar|toc|graphics|metrics|exportcitation)$/
+ if (parts[parts.length - 1].match(subPageRegex)) {
+ subPage = parts.pop();
+ }
+
+ // take the rest and combine into the identifier
+ const id = parts.join('/');
+
+ var navigateString, href;
+ if (!subPage) {
+ navigateString = 'ShowAbstract';
+ href = '#abs/' + encodeURIComponent(id) + '/abstract';
+ } else {
+ navigateString = 'Show' + subPage[0].toUpperCase() + subPage.slice(1);
+ href = '#abs/' + encodeURIComponent(id) + '/' + subPage;
+ }
+ this.routerNavigate(navigateString, {
+ href: href,
+ bibcode: id
});
- } else if (_.contains(['librarylink', 'orcid', 'application'], subView)) {
- // show preferences if no subview provided
- this.routerNavigate('UserPreferences', {
+ },
+
+
+ routeToVerifyPage: function (subView, token) {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'user-action', {
subView: subView,
+ token: token
});
- } else if (_.contains(['libraryimport'], subView)) {
- this.routerNavigate('LibraryImport');
- } else {
- // just default to showing the library link page for now
- this.routerNavigate('UserPreferences', {
- subView: undefined,
- });
- }
- },
+ },
+
+ orcidPage: function () {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'orcid-page');
+ },
- librariesPage: function(id, subView, subData) {
- if (id) {
- if (id === 'actions') {
- return this.routerNavigate('LibraryActionsWidget', 'libraries');
+ orcidInstructions: function () {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'orcid-instructions');
+ },
+
+ authenticationPage: function (subView) {
+ // possible subViews: "login", "register", "reset-password"
+ if (subView && !_.contains(['login', 'register', 'reset-password-1', 'reset-password-2'], subView)) {
+ throw new Error('that isn\'t a subview that the authentication page knows about');
}
+ this.routerNavigate('authentication-page', {
+ subView: subView
+ });
+ },
- // individual libraries view
- var subView = subView || 'library';
- if (_.contains(['library', 'admin'], subView)) {
- this.routerNavigate('IndividualLibraryWidget', {
- subView: subView,
- id: id,
+ settingsPage: function (subView) {
+ // possible subViews: "token", "password", "email", "preferences"
+ if (_.contains(['token', 'password', 'email', 'delete'], subView)) {
+ this.routerNavigate('UserSettings', {
+ subView: subView
});
- } else if (
- _.contains(['export', 'metrics', 'visualization'], subView)
- ) {
- subView = 'library-' + subView;
-
- if (subView == 'library-export') {
- this.routerNavigate(subView, {
- subView: subData || 'bibtex',
- id: id,
- });
- } else if (subView == 'library-metrics') {
- this.routerNavigate(subView, {
- id: id,
+ } else if (_.contains(['librarylink', 'orcid', 'application'], subView)) {
+ // show preferences if no subview provided
+ this.routerNavigate('UserPreferences', {
+ subView: subView
+ });
+ } else if (_.contains(['libraryimport'], subView)) {
+ this.routerNavigate('LibraryImport');
+
+ } else if (_.contains(['myads'], subView)) {
+ this.routerNavigate('MyAdsDashboard');
+ } else {
+ // just default to showing the library link page for now
+ this.routerNavigate('UserPreferences', {
+ subView: undefined
+ });
+ }
+ },
+
+ librariesPage: function (id, subView, subData) {
+ if (id) {
+ if (id === 'actions') {
+ return this.routerNavigate('LibraryActionsWidget', 'libraries');
+ }
+
+ // individual libraries view
+ var subView = subView || 'library';
+ if (_.contains(['library', 'admin'], subView)) {
+ this.routerNavigate('IndividualLibraryWidget', {
+ subView: subView,
+ id: id
});
+ } else if (_.contains(['export', 'metrics', 'visualization'], subView)) {
+ subView = 'library-' + subView;
+
+ if (subView == 'library-export') {
+ this.routerNavigate(subView, {
+ subView: subData || 'bibtex',
+ id: id
+ });
+ } else if (subView == 'library-metrics') {
+ this.routerNavigate(subView, {
+ id: id
+ });
+ }
+ } else {
+ throw new Error('did not recognize subview for library view');
}
} else {
- throw new Error('did not recognize subview for library view');
+ // main libraries view
+ this.routerNavigate('AllLibrariesWidget', 'libraries');
}
- } else {
+ },
+
+ publicLibraryPage: function (id) {
// main libraries view
- this.routerNavigate('AllLibrariesWidget', 'libraries');
- }
- },
-
- publicLibraryPage: function(id) {
- // main libraries view
- this.getPubSub().publish(
- this.getPubSub().NAVIGATE,
- 'IndividualLibraryWidget',
- {
+ this.getPubSub().publish(this.getPubSub().NAVIGATE, 'IndividualLibraryWidget', {
id: id,
publicView: true,
- subView: 'library',
- }
- );
- },
-
- homePage: function(subView) {
- this.routerNavigate('home-page', {
- subView: subView,
- });
- },
-
- noPageFound: function() {
- this.routerNavigate('404');
- },
-
- _extractParameters: function(route, fragment) {
- return _.map(route.exec(fragment).slice(1), function(param) {
- // do not decode api queries
- if (/q\=/.test(param)) {
- return param;
- }
+ subView: 'library'
+ });
+ },
- return param ? decodeURIComponent(param) : param;
- });
- },
- });
+ homePage: function (subView) {
+ this.routerNavigate('home-page', {
+ subView: subView
+ });
+ },
+
+ noPageFound: function () {
+ this.routerNavigate('404');
+ },
- _.extend(Router.prototype, Dependon.BeeHive, ApiAccessMixin);
- return Router;
-});
+ _extractParameters: function (route, fragment) {
+ return _.map(route.exec(fragment).slice(1), function (param) {
+ // do not decode api queries
+ if (/q\=/.test(param)) {
+ return param;
+ }
+
+ return param ? decodeURIComponent(param) : param;
+ });
+ }
+ });
+
+ _.extend(Router.prototype, Dependon.BeeHive, ApiAccessMixin);
+
+ return Router;
+ });
diff --git a/src/js/components/api_targets.js b/src/js/components/api_targets.js
index bf26557e3..e05895f48 100644
--- a/src/js/components/api_targets.js
+++ b/src/js/components/api_targets.js
@@ -21,6 +21,7 @@ define(['underscore', 'backbone'], function(_, Backbone) {
SERVICE_CITATION_HELPER: 'citation_helper',
SERVICE_AUTHOR_AFFILIATION_EXPORT: 'authoraff',
MYADS_STORAGE: 'vault',
+ MYADS_NOTIFICATIONS: 'vault/notifications',
AUTHOR_AFFILIATION_SEARCH: 'author-affiliation/search',
AUTHOR_AFFILIATION_EXPORT: 'author-affiliation/export',
RESOLVER: 'resolver',
diff --git a/src/js/page_managers/templates/results-page-layout.html b/src/js/page_managers/templates/results-page-layout.html
index c5138056e..2e541ec5a 100644
--- a/src/js/page_managers/templates/results-page-layout.html
+++ b/src/js/page_managers/templates/results-page-layout.html
@@ -76,6 +76,7 @@
+
diff --git a/src/js/react/BumblebeeWidget.js b/src/js/react/BumblebeeWidget.js
index 2ef54541d..ed234bbcb 100644
--- a/src/js/react/BumblebeeWidget.js
+++ b/src/js/react/BumblebeeWidget.js
@@ -6,28 +6,7 @@ define([
'js/widgets/base/base_widget',
'js/components/api_request',
'js/components/api_query',
- 'analytics',
-], function(_, BaseWidget, ApiRequest, ApiQuery, analytics) {
- const getBeeHive = () => {
- return window.bbb.getBeeHive();
- };
-
- const getPubSub = () => {
- const beehive = getBeeHive();
- const ps = beehive.getService('PubSub');
- return ps;
- };
-
- const subscribe = (...args) => {
- const ps = getPubSub();
- ps.subscribe(ps.pubSubKey, ...args);
- };
-
- const publish = (...args) => {
- const ps = getPubSub();
- ps.publish(ps.pubSubKey, ...args);
- };
-
+], function(_, BaseWidget, ApiRequest, ApiQuery) {
const BumblebeeWidget = BaseWidget.extend({
/**
* @override
@@ -52,7 +31,6 @@ define([
getCurrentQuery: _.bind(this.onGetCurrentQuery, this),
isLoggedIn: _.bind(this.isLoggedIn, this),
getInitialData: _.bind(this.getInitialData, this),
- analyticsEvent: _.bind(this.analyticsEvent, this),
});
this.listenTo(this, 'page-manager-message', (ev, data) => {
@@ -83,31 +61,33 @@ define([
cb(this.initialData);
}
},
- activate() {
- const ps = getPubSub();
- subscribe(ps.USER_ANNOUNCEMENT, this.handleUserAnnouncement.bind(this));
+ activate(beehive) {
+ this.setBeeHive(beehive);
+ const ps = this.getPubSub();
+ ps.subscribe(
+ ps.USER_ANNOUNCEMENT,
+ this.handleUserAnnouncement.bind(this)
+ );
},
handleUserAnnouncement() {},
isLoggedIn(cb) {
const user = this.getBeeHive().getObject('User');
- if (typeof cb === 'function') {
- cb(user.isLoggedIn());
- }
+ cb(user.isLoggedIn());
},
onGetCurrentQuery(callback) {
callback(this.getCurrentQuery());
},
subscribeToPubSub(event, callback) {
- const ps = getPubSub();
- subscribe(ps[event], callback);
+ const ps = this.getPubSub();
+ ps.subscribe(ps[event], callback);
},
publishToPubSub(event, ...args) {
- const ps = getPubSub();
- publish(ps[event], ...args);
+ const ps = this.getPubSub();
+ ps.publish(ps[event], ...args);
},
doSearch(queryParams) {
const query = new ApiQuery();
- if (_.isString(queryParams)) {
+ if (typeof queryParams === 'string') {
query.load(queryParams);
} else {
query.set({ ...queryParams });
@@ -117,7 +97,7 @@ define([
});
},
onSendRequest({ options, target, query }) {
- const ps = getPubSub();
+ const ps = this.getPubSub();
const request = new ApiRequest({
target,
query: new ApiQuery(query),
diff --git a/src/js/react/MyAdsDashboard/actions.js b/src/js/react/MyAdsDashboard/actions.js
new file mode 100644
index 000000000..dc4e6c288
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/actions.js
@@ -0,0 +1,130 @@
+define([], function() {
+ const actions = {
+ // requests
+ ADD_NOTIFICATION: 'ADD_NOTIFICATION',
+ UPDATE_NOTIFICATION: 'UPDATE_NOTIFICATION',
+ REMOVE_NOTIFICATION: 'REMOVE_NOTIFICATION',
+ GET_NOTIFICATIONS: 'GET_NOTIFICATIONS',
+ GET_NOTIFICATION: 'GET_NOTIFICATION',
+ FETCH_CLASSIC_MIRRORS: 'FETCH_CLASSIC_MIRRORS',
+ LOGIN_CLASSIC: 'LOGIN_CLASSIC',
+ IMPORT_CLASSIC: 'IMPORT_CLASSIC',
+ LOGIN_CLASSIC_CHECK: 'LOGIN_CLASSIC_CHECK',
+
+ // notifications state management
+ SET_NOTIFICATIONS: 'SET_NOTIFICATIONS',
+ EDIT_NOTIFICATION: 'EDIT_NOTIFICATION',
+ SET_EDITING_NOTIFICATION: 'SET_EDITING_NOTIFICATION',
+ RESET_EDITING_NOTIFICATION: 'RESET_EDITING_NOTIFICATION',
+ TOGGLE_ACTIVE: 'TOGGLE_ACTIVE',
+
+ // paging
+ GOTO: 'GOTO',
+
+ // imports
+ IMPORT_NOTIFICATIONS: 'IMPORT_NOTIFICATIONS',
+
+ // searching
+ GET_QUERY: 'GET_QUERY',
+ RUN_QUERY: 'RUN_QUERY',
+ };
+
+ const actionCreators = {
+ addNotification: (notification) => ({
+ type: 'API_REQUEST',
+ scope: actions.ADD_NOTIFICATION,
+ options: {
+ type: 'POST',
+ target: 'vault/notifications',
+ data: notification,
+ },
+ }),
+ getNotifications: () => ({
+ type: 'API_REQUEST',
+ scope: actions.GET_NOTIFICATIONS,
+ options: {
+ type: 'GET',
+ target: 'vault/notifications',
+ },
+ }),
+ getNotification: (id) => ({
+ type: 'API_REQUEST',
+ scope: actions.GET_NOTIFICATION,
+ options: {
+ type: 'GET',
+ target: `vault/notifications/${id}`,
+ },
+ }),
+ updateNotification: (notification) => ({
+ type: 'API_REQUEST',
+ scope: actions.UPDATE_NOTIFICATION,
+ options: {
+ type: 'PUT',
+ target: `vault/notifications/${notification.id}`,
+ data: notification,
+ },
+ }),
+ removeNotification: (id) => ({
+ type: 'API_REQUEST',
+ scope: actions.REMOVE_NOTIFICATION,
+ options: {
+ type: 'DELETE',
+ target: `vault/notifications/${id}`,
+ },
+ }),
+ fetchClassicMirrors: () => ({
+ type: 'API_REQUEST',
+ scope: actions.FETCH_CLASSIC_MIRRORS,
+ options: {
+ type: 'GET',
+ target: 'harbour/mirrors',
+ },
+ }),
+ loginClassic: (data) => ({
+ type: 'API_REQUEST',
+ scope: actions.LOGIN_CLASSIC,
+ options: {
+ type: 'POST',
+ target: 'harbour/auth/classic',
+ data: data,
+ },
+ }),
+ loginClassicCheck: () => ({
+ type: 'API_REQUEST',
+ scope: actions.LOGIN_CLASSIC_CHECK,
+ options: {
+ type: 'GET',
+ target: 'harbour/user',
+ },
+ }),
+ importClassic: () => ({
+ type: 'API_REQUEST',
+ scope: actions.IMPORT_CLASSIC,
+ options: {
+ type: 'GET',
+ target: 'vault/myads-import',
+ },
+ }),
+ importNotifications: () => ({ type: actions.IMPORT_NOTIFICATIONS }),
+ goTo: (payload) => ({ type: actions.GOTO, payload }),
+ editNotification: (id) => ({ type: actions.EDIT_NOTIFICATION, id }),
+ toggleActive: (id) => ({ type: actions.TOGGLE_ACTIVE, id }),
+ getQuery: (qid) => ({
+ type: 'API_REQUEST',
+ scope: 'GET_QUERY',
+ options: {
+ type: 'GET',
+ target: `vault/query/${qid}`,
+ },
+ }),
+ runQuery: (result) => ({
+ type: 'RUN_QUERY',
+ result,
+ }),
+ };
+
+ return {
+ ...actions,
+ ...actionCreators,
+ };
+});
diff --git a/src/js/react/MyAdsDashboard/components/App.jsx.js b/src/js/react/MyAdsDashboard/components/App.jsx.js
new file mode 100644
index 000000000..2bc643e67
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/App.jsx.js
@@ -0,0 +1,121 @@
+define([
+ 'react',
+ '../constants',
+ '../containers/Dashboard',
+ '../containers/SelectTemplate',
+ '../containers/ArxivForm',
+ '../containers/CitationsForm',
+ '../containers/AuthorsForm',
+ '../containers/KeywordForm',
+ '../containers/GeneralForm',
+ '../containers/ImportNotificationsForm',
+], function(
+ React,
+ { page: PAGE },
+ Dashboard,
+ SelectTemplate,
+ ArxivForm,
+ CitationsForm,
+ AuthorsForm,
+ KeywordForm,
+ GeneralForm,
+ ImportNotificationsForm
+) {
+ const getComponent = (page) => {
+ switch (page) {
+ case PAGE.DASHBOARD:
+ return ;
+ case PAGE.ARXIV_FORM:
+ return ;
+ case PAGE.CITATIONS_FORM:
+ return ;
+ case PAGE.AUTHORS_FORM:
+ return ;
+ case PAGE.KEYWORD_FORM:
+ return ;
+ case PAGE.GENERAL_FORM:
+ return ;
+ case PAGE.SELECT_TEMPLATE:
+ return ;
+ case PAGE.IMPORT_NOTIFICATIONS:
+ return ;
+ }
+ };
+
+ const TITLES = {
+ [PAGE.ARXIV_FORM]: 'arXiv',
+ [PAGE.CITATIONS_FORM]: 'Citations',
+ [PAGE.AUTHORS_FORM]: 'Authors',
+ [PAGE.KEYWORD_FORM]: 'Keywords',
+ [PAGE.GENERAL_FORM]: 'General',
+ [PAGE.SELECT_TEMPLATE]: 'Create New',
+ [PAGE.IMPORT_NOTIFICATIONS]: 'Import',
+ };
+
+ const getMiddleMessage = (page, isEditing) => {
+ let msg = '';
+ if (
+ page !== PAGE.SELECT_TEMPLATE &&
+ page !== PAGE.IMPORT_NOTIFICATIONS &&
+ page !== PAGE.DASHBOARD
+ ) {
+ msg = ` ${isEditing ? 'Editing' : 'Create New'} |`;
+ }
+ return msg;
+ };
+
+ const getPageTitle = (page, isEditing) => {
+ let title = TITLES[page];
+
+ if (title) {
+ return `myADS |${getMiddleMessage(page, isEditing)} ${title}`;
+ }
+ return 'myADS';
+ };
+
+ const getBackButton = (page, onClick) => {
+ if (page === PAGE.DASHBOARD) {
+ return null;
+ }
+
+ let onBack;
+ switch (page) {
+ case PAGE.ARXIV_FORM:
+ case PAGE.CITATIONS_FORM:
+ case PAGE.KEYWORD_FORM:
+ case PAGE.AUTHORS_FORM:
+ case PAGE.GENERAL_FORM:
+ onBack = onClick.bind(null, PAGE.SELECT_TEMPLATE);
+ break;
+ default:
+ onBack = onClick.bind(null, PAGE.DASHBOARD);
+ }
+
+ return (
+
+ );
+ };
+
+ class App extends React.Component {
+ render() {
+ return (
+
+
+ {getBackButton(
+ this.props.editingNotification ? null : this.props.page,
+ this.props.goTo
+ )}{' '}
+ {getPageTitle(this.props.page, this.props.editingNotification)}
+
+
+ {getComponent(this.props.page)}
+
+
+ );
+ }
+ }
+
+ return App;
+});
diff --git a/src/js/react/MyAdsDashboard/components/ArxivClassList.jsx.js b/src/js/react/MyAdsDashboard/components/ArxivClassList.jsx.js
new file mode 100644
index 000000000..82648de48
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ArxivClassList.jsx.js
@@ -0,0 +1,347 @@
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ '../models/arxivClasses',
+], function(
+ _,
+ React,
+ { Checkbox, ListGroup, ListGroupItem, Button },
+ ArxivClassesModel
+) {
+ const initialState = {
+ groups: {
+ ...ArxivClassesModel,
+ },
+ all: false,
+ };
+
+ /**
+ *
+ */
+ class ArxivClassList extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ ...initialState,
+ };
+ this.onSelect = _.debounce(this._onSelect.bind(this), 100);
+ this.props.onSelection(this._groupsToArray());
+ if (this.props.initialSelected && this.props.initialSelected.length > 0) {
+ try {
+ this.selectGroups(this.props.initialSelected, true);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
+
+ _isIndeterminate(children) {
+ // only return true if SOME of the children are selected, not all
+ const keys = Object.keys(children);
+ const selected = keys.filter((k) => children[k].selected);
+ return selected.length > 0 && selected.length < keys.length;
+ }
+
+ _allSelected(items) {
+ return Object.keys(items).every((k) => items[k].selected);
+ }
+
+ _noneSelected(items) {
+ return !Object.keys(items).some((k) => items[k].selected);
+ }
+
+ _groupsToArray() {
+ let arr = [];
+ Object.keys(this.state.groups).forEach((parentKey) => {
+ const item = this.state.groups[parentKey];
+
+ // if it is selected, add it and move on
+ if (item.selected) {
+ arr = [...arr, item.key];
+
+ // if it is indeterminate, then go into the children and grab them instead
+ } else if (item.indeterminate) {
+ arr = [
+ ...arr,
+ ...Object.keys(item.children)
+ .filter((k) => item.children[k].selected)
+ .map((k) => item.children[k].key),
+ ];
+ }
+ });
+ return arr;
+ }
+
+ selectGroups(groups, value) {
+ if (!groups || groups.length <= 0) {
+ return;
+ }
+
+ const _groups = this.state.groups;
+ const newGroups = groups.reduce(
+ (acc, key) => {
+ // checking parent keys
+ if (acc[key]) {
+ acc[key] = {
+ ...acc[key],
+ selected: value,
+ indeterminate: false,
+ children: Object.keys(acc[key].children).reduce((a, k) => {
+ a[k] = { ...acc[key].children[k], selected: value };
+ return a;
+ }, {}),
+ };
+ } else {
+ // the key is a child, we need to find the entry
+ Object.keys(acc).forEach((k) => {
+ if (acc[k].children && acc[k].children[key]) {
+ const children = {
+ ...acc[k].children,
+ [key]: {
+ ...acc[k].children[key],
+ selected: value,
+ },
+ };
+ acc[k] = {
+ ...acc[k],
+ indeterminate: this._isIndeterminate(children),
+ selected: this._allSelected(children),
+ children: children,
+ };
+ }
+ });
+ }
+ return acc;
+ },
+ { ..._groups }
+ );
+ setTimeout(() => this.setState({ groups: newGroups },
+ () => this.props.onSelection(this._groupsToArray())), 0);
+ }
+
+ _onSelect(key, value) {
+ const valueDefined = typeof value !== 'undefined';
+ const _groups = this.state.groups;
+
+ if (_groups[key]) {
+ const selected = valueDefined ? value : !_groups[key].selected;
+ const newGroups = {
+ ..._groups,
+ [key]: {
+ ..._groups[key],
+ selected: selected,
+ indeterminate: false,
+
+ // if we are selecting the parent, then select all children too
+ children: Object.keys(_groups[key].children).reduce((acc, k) => {
+ acc[k] = {
+ ..._groups[key].children[k],
+ selected: selected,
+ };
+ return acc;
+ }, {}),
+ },
+ };
+
+ this.setState({ groups: newGroups }, () => {
+ this.props.onSelection(this._groupsToArray());
+ });
+ } else {
+ const newGroups = Object.keys(_groups).reduce((acc, k) => {
+ if (_groups[k].children && _groups[k].children[key]) {
+ const selected = valueDefined
+ ? value
+ : !_groups[k].children[key].selected;
+
+ const children = {
+ ..._groups[k].children,
+ [key]: {
+ ..._groups[k].children[key],
+ selected: selected,
+ },
+ };
+
+ acc[k] = {
+ ..._groups[k],
+ indeterminate: this._isIndeterminate(children),
+ selected: this._allSelected(children),
+ children: children,
+ };
+ } else {
+ acc[k] = { ..._groups[k] };
+ }
+ return acc;
+ }, {});
+
+ this.setState(
+ {
+ groups: newGroups,
+ },
+ () => this.props.onSelection(this._groupsToArray())
+ );
+ }
+ }
+
+ onSelectAll() {
+ const selectAll = !this.state.all;
+ const { groups } = this.state;
+ this.setState(
+ {
+ all: selectAll,
+ groups: Object.keys(groups).reduce((acc, k) => {
+ acc[k] = {
+ ...groups[k],
+ selected: selectAll,
+ indeterminate: false,
+ children: Object.keys(groups[k].children).map((childKey) => ({
+ ...groups[k].children[childKey],
+ selected: selectAll,
+ })),
+ };
+ return acc;
+ }, {}),
+ },
+ () => {
+ this.props.onSelection(this._groupsToArray());
+ }
+ );
+ }
+
+ render() {
+ return (
+
+
+ {Object.keys(this.state.groups).map((k) => (
+
+ ))}
+
+ this.onSelectAll()}
+ >
+ Select All
+
+
+ );
+ }
+ }
+
+ class Item extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ expanded: false,
+ hasChildren: Object.keys(props.item.children).length > 0,
+ };
+
+ this.onSelect = (e, key, value) => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.props.onSelect(key, value);
+ };
+ }
+
+ expand(e) {
+ if (this.state.hasChildren) {
+ this.setState({
+ expanded: !this.state.expanded,
+ });
+ } else {
+ this.onSelect(e, this.props.item.key);
+ }
+ }
+
+ render() {
+ const { key, label, selected, children, indeterminate } = this.props.item;
+ const childrenKeys = Object.keys(children);
+ const hasChildren = this.state.hasChildren;
+
+ return (
+
+ this.expand(e)}
+ style={{
+ paddingLeft: hasChildren ? 'auto' : 37,
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ }}
+ >
+ {hasChildren && (
+
+
+
+ )}
+ this.onSelect(e, key)}
+ inputRef={(el) => el && (el.indeterminate = indeterminate)}
+ >
+ {selected || indeterminate ? (
+ {`${key}: ${label}`}
+ ) : (
+ `${key}: ${label}`
+ )}
+
+
+
+ {this.state.expanded && hasChildren && (
+
+ {childrenKeys.map((childKey) => {
+ const { key, label, selected } = children[childKey];
+ return (
+ this.onSelect(e, key)}
+ >
+ this.onSelect(e, key)}
+ style={{
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ }}
+ >
+ {selected ? (
+ {`${key}: ${label}`}
+ ) : (
+ `${key}: ${label}`
+ )}
+
+
+ );
+ })}
+
+ )}
+
+ );
+ }
+ }
+
+ return ArxivClassList;
+});
diff --git a/src/js/react/MyAdsDashboard/components/ArxivForm.jsx.js b/src/js/react/MyAdsDashboard/components/ArxivForm.jsx.js
new file mode 100644
index 000000000..782911801
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ArxivForm.jsx.js
@@ -0,0 +1,194 @@
+define(['react', 'react-bootstrap', 'es6!./ArxivClassList.jsx'], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock },
+ ArxivClassList
+) {
+ const getStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error});
+ case 'success':
+ return Notification Created!;
+ }
+ };
+
+ class ArxivForm extends React.Component {
+ constructor(props) {
+ super(props);
+
+ let updatedState = {};
+ if (this.props.editingNotification) {
+ updatedState = {
+ groups: this.props.editingNotification.classes,
+ keywords: this.props.editingNotification.data,
+ name: this.props.editingNotification.name,
+ editing: true,
+ };
+ }
+
+ this.state = {
+ groups: [],
+ keywords: '',
+ name: '',
+ message: null,
+ editing: false,
+ pending: false,
+ ...updatedState,
+ };
+ this.onClassSelection = this.onClassSelection.bind(this);
+ }
+
+ onClassSelection(groups) {
+ this.setState({
+ groups,
+ });
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ const { keywords, groups, name, pending } = this.state;
+
+ if (pending) {
+ return;
+ }
+
+ if (groups.length <= 0) {
+ return this.showMessage('must select at least one group');
+ }
+
+ const payload = {
+ data: keywords,
+ classes: groups,
+ name: name
+ };
+
+ if (this.state.editing) {
+ this.props.updateNotification({
+ ...this.props.editingNotification,
+ ...payload,
+ });
+ } else {
+ this.props.addNotification(payload);
+ }
+ }
+
+ componentWillReceiveProps(next) {
+ const addStatus = next.addNotificationRequest.status;
+ const updateStatus = next.updateNotificationRequest.status;
+
+ // fires success handler if our request was successful
+ if (
+ addStatus === 'success' || updateStatus === 'success'
+ ) {
+ setTimeout(() => this.props.onSuccess(), 1000);
+ }
+
+ // won't allow a request to go through if we're pending or had just failed
+ if (
+ addStatus === 'pending' || updateStatus === 'pending' ||
+ addStatus === 'failure' || updateStatus === 'failure'
+ ) {
+ this.setState({ pending: true });
+ } else if (!addStatus && !updateStatus) {
+ this.setState({ pending: false });
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ return ArxivForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/AuthorsForm.jsx.js b/src/js/react/MyAdsDashboard/components/AuthorsForm.jsx.js
new file mode 100644
index 000000000..a4458ab1a
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/AuthorsForm.jsx.js
@@ -0,0 +1,209 @@
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ 'es6!./CitationsEntry.jsx',
+ 'js/react/shared/helpers',
+], function(
+ _,
+ React,
+ { Form, Alert, FormGroup, FormControl, ControlLabel, HelpBlock },
+ CitationsEntry,
+ { escape, unescape }
+) {
+ const getStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return
Request failed. ({error});
+ case 'success':
+ return
Notification Created!;
+ }
+ };
+
+ const AuthorsFormInitialState = {
+ message: null,
+ name: '',
+ orcid: '',
+ editing: false,
+ notificationName: '',
+ entries: [],
+ pending: false,
+ };
+ class AuthorsForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.onSubmit = this.onSubmit.bind(this);
+ this.entriesUpdated = this.entriesUpdated.bind(this);
+ let updatedState = {};
+ if (this.props.editingNotification) {
+ updatedState = {
+ editing: true,
+ ...this.parseQueryString(this.props.editingNotification.data),
+ notificationName: this.props.editingNotification.name,
+ };
+ }
+ this.state = { ...AuthorsFormInitialState, ...updatedState };
+ }
+
+ parseQueryString(query) {
+ try {
+ const parts = query.split(' OR ');
+ let entries = {};
+ if (parts.length > 0) {
+ entries = parts.map((str) => {
+ const [p, field, value] = /^(author|orcid):"(.*)"$/.exec(str);
+ return {
+ field: field === 'author' ? 'name' : 'orcid',
+ value,
+ };
+ });
+ }
+ return { entries };
+ } catch (e) {
+ return { editing: false };
+ }
+ }
+
+ entriesUpdated(entries) {
+ this.setState({ entries });
+ }
+
+ createQueryString() {
+ return this.state.entries
+ .map(({ field, value }) => {
+ return `${field === 'name' ? 'author' : 'orcid'}:"${value}"`;
+ })
+ .join(' OR ');
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+
+ if (this.state.pending) {
+ return;
+ }
+
+ const data = this.createQueryString();
+ if (data === '') {
+ return this.showMessage('Must add an author name or orcid ID');
+ }
+
+ const payload = { data, name: this.state.notificationName };
+
+ if (this.state.editing) {
+ this.props.updateNotification({
+ ...this.props.editingNotification,
+ ...payload,
+ });
+ } else {
+ this.props.addNotification(payload);
+ this.setState({ reset: true });
+ }
+ }
+
+ onChange({ type, value }) {
+ // set the value and clear the other
+ this.setState({
+ [type]: value,
+ [type === 'name' ? 'orcid' : 'name']: '',
+ });
+ }
+
+ componentWillReceiveProps(next) {
+ const addStatus = next.addNotificationRequest.status;
+ const updateStatus = next.updateNotificationRequest.status;
+
+ // fires success handler if our request was successful
+ if (addStatus === 'success' || updateStatus === 'success') {
+ setTimeout(() => this.props.onSuccess(), 1000);
+ }
+
+ // won't allow a request to go through if we're pending or had just failed
+ if (
+ addStatus === 'pending' ||
+ updateStatus === 'pending' ||
+ addStatus === 'failure' ||
+ updateStatus === 'failure'
+ ) {
+ this.setState({ pending: true });
+ } else if (!addStatus && !updateStatus) {
+ this.setState({ pending: false });
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ return AuthorsForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/CitationsEntry.jsx.js b/src/js/react/MyAdsDashboard/components/CitationsEntry.jsx.js
new file mode 100644
index 000000000..44bd9619e
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/CitationsEntry.jsx.js
@@ -0,0 +1,210 @@
+define(['underscore', 'react', 'react-bootstrap'], function(
+ { escape },
+ React,
+ { Alert, FormGroup, ControlLabel, FormControl, HelpBlock, Radio }
+) {
+ const TYPES = {
+ name: 'Author Name',
+ orcid: 'ORCiD',
+ };
+
+ const INPUTS = {
+ name: {
+ label: 'Author Name (Last, First)',
+ placeholder: 'Huchra, John',
+ helpText: 'Enter author name (ex. Huchra, John)',
+ },
+ orcid: {
+ label: 'ORCiD ID',
+ placeholder: '0000-0002-3843-3472',
+ helpText: 'Enter ORCiD id (ex. 0000-0002-3843-3472)',
+ },
+ };
+
+ const renderTable = (entries, onDelete) => {
+ return (
+
+ {entries.length ? (
+
+ {entries.map(({ field, value }, idx) => {
+ return (
+ -
+ {idx + 1}
+ {' '}
+ {escape(value)}
+ {' '}
+
+
+ );
+ })}
+
+ ) : (
+
Please add at least one entry
+ )}
+
+ );
+ };
+
+ const initialState = {
+ field: 'name',
+ value: '',
+ error: null,
+ entries: [],
+ };
+ class CitationsEntry extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ ...initialState,
+ ...props.initialState,
+ };
+ this.input = null;
+ this.onDelete = this.onDelete.bind(this);
+ }
+
+ onChangeField(field) {
+ this.setState({ field }, () => {
+ this.input.focus();
+ });
+ }
+
+ setError(msg) {
+ this.setState({ error: msg });
+ }
+
+ onChangeValue(value) {
+ this.setState({ value });
+ this.props.onChange({ field: this.state.field, value });
+ }
+
+ shouldAdd() {
+ const { value, field } = this.state;
+ if (field === 'orcid') {
+ if (value.trim().match(/^\d{4}-\d{4}-\d{4}-\d{4}$/)) {
+ return true;
+ }
+ this.setError('Not a valid ORCiD id');
+ } else if (field === 'name') {
+ return value.trim() !== '';
+ }
+ return false;
+ }
+
+ onAdd(e) {
+ e.preventDefault();
+ if (this.shouldAdd()) {
+ const entries = [
+ ...this.state.entries,
+ { field: this.state.field, value: this.state.value },
+ ];
+ this.setState({
+ entries,
+ value: '',
+ });
+ this.props.entriesUpdated(entries);
+ }
+ }
+
+ onDelete(e, idx) {
+ e.preventDefault();
+ const entries = [
+ ...this.state.entries.slice(0, idx),
+ ...this.state.entries.slice(idx + 1),
+ ];
+ this.setState({
+ entries,
+ });
+ this.props.entriesUpdated(entries);
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ if (
+ this.state.error !== null &&
+ (prevState.field !== this.state.field ||
+ prevState.value !== this.state.value)
+ ) {
+ this.setState({ error: null });
+ }
+ }
+
+ render() {
+ return (
+
+
+ Type
+ {Object.keys(TYPES).map((field) => (
+ this.onChangeField(field)}
+ >
+ {TYPES[field]}
+
+ ))}
+
+ {Object.keys(INPUTS)
+ .filter((k) => k === this.state.field)
+ .map((field) => {
+ const { label, placeholder, helpText } = INPUTS[field];
+ return (
+
+ {label}
+ this.onChangeValue(e.target.value)}
+ inputRef={(ref) => {
+ this.input = ref;
+ this.props.inputRef(ref);
+ }}
+ placeholder={placeholder}
+ autofocus
+ />
+ {helpText}
+ {this.state.error && (
+ {this.state.error}
+ )}
+
+ );
+ })}
+ {this.props.multiple && (
+
+
+
+ {renderTable(this.state.entries, this.onDelete)}
+
+
+ )}
+
+ );
+ }
+ }
+
+ CitationsEntry.defaultProps = {
+ initialState: {},
+ multiple: false,
+ inputRef: () => {},
+ entriesUpdated: [],
+ onChange: () => {}
+ };
+
+ return CitationsEntry;
+});
diff --git a/src/js/react/MyAdsDashboard/components/CitationsForm.jsx.js b/src/js/react/MyAdsDashboard/components/CitationsForm.jsx.js
new file mode 100644
index 000000000..c716eb9db
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/CitationsForm.jsx.js
@@ -0,0 +1,207 @@
+define([
+ 'react',
+ 'react-bootstrap',
+ 'es6!./CitationsEntry.jsx',
+ 'js/react/shared/helpers',
+], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock },
+ CitationsEntry,
+ { escape, unescape }
+) {
+ const getStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return
Request failed. ({error});
+ case 'success':
+ return
Notification Created!;
+ }
+ };
+
+ const CitationsFormInitialState = {
+ message: null,
+ name: '',
+ orcid: '',
+ editing: false,
+ notificationName: '',
+ entries: [],
+ pending: false,
+ };
+ class CitationsForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.onSubmit = this.onSubmit.bind(this);
+ this.entriesUpdated = this.entriesUpdated.bind(this);
+ let updatedState = {};
+ if (this.props.editingNotification) {
+ updatedState = {
+ editing: true,
+ ...this.parseQueryString(this.props.editingNotification.data),
+ notificationName: this.props.editingNotification.name,
+ };
+ }
+ this.state = { ...CitationsFormInitialState, ...updatedState };
+ }
+
+ parseQueryString(query) {
+ try {
+ const parts = query.split(' OR ');
+ let entries = {};
+ if (parts.length > 0) {
+ entries = parts.map((str) => {
+ const [p, field, value] = /^(author|orcid):"(.*)"$/.exec(str);
+ return {
+ field: field === 'author' ? 'name' : 'orcid',
+ value,
+ };
+ });
+ }
+ return { entries };
+ } catch (e) {
+ return { editing: false };
+ }
+ }
+
+ entriesUpdated(entries) {
+ this.setState({ entries });
+ }
+
+ createQueryString() {
+ return this.state.entries
+ .map(({ field, value }) => {
+ return `${field === 'name' ? 'author' : 'orcid'}:"${value}"`;
+ })
+ .join(' OR ');
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+
+ if (this.state.pending) {
+ return;
+ }
+
+ const data = this.createQueryString();
+ if (data === '') {
+ return this.showMessage('Must add an author name or orcid ID');
+ }
+
+ const payload = { data, name: this.state.notificationName };
+
+ if (this.state.editing) {
+ this.props.updateNotification({
+ ...this.props.editingNotification,
+ ...payload,
+ });
+ } else {
+ this.props.addNotification(payload);
+ this.setState({ reset: true });
+ }
+ }
+
+ onChange(data) {
+ // set the value and clear the other
+ this.setState({
+ [type]: value,
+ [type === 'name' ? 'orcid' : 'name']: '',
+ });
+ }
+
+ componentWillReceiveProps(next) {
+ const addStatus = next.addNotificationRequest.status;
+ const updateStatus = next.updateNotificationRequest.status;
+
+ // fires success handler if our request was successful
+ if (addStatus === 'success' || updateStatus === 'success') {
+ setTimeout(() => this.props.onSuccess(), 1000);
+ }
+
+ // won't allow a request to go through if we're pending or had just failed
+ if (
+ addStatus === 'pending' ||
+ updateStatus === 'pending' ||
+ addStatus === 'failure' ||
+ updateStatus === 'failure'
+ ) {
+ this.setState({ pending: true });
+ } else if (!addStatus && !updateStatus) {
+ this.setState({ pending: false });
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ return CitationsForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/ClassicLoginForm.jsx.js b/src/js/react/MyAdsDashboard/components/ClassicLoginForm.jsx.js
new file mode 100644
index 000000000..41fbf02ec
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ClassicLoginForm.jsx.js
@@ -0,0 +1,263 @@
+define(['react', 'react-bootstrap'], function(
+ React,
+ { Form, FormGroup, FormControl, ControlLabel, HelpBlock, Button, Alert }
+) {
+ const loginStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return (
+
+ {error ? error : 'Login failed, try changing the mirror site.'}
+
+ );
+ case 'success':
+ return
Login Successful!;
+ }
+ };
+
+ const initialState = {
+ email: '',
+ password: '',
+ mirror: '',
+ mirrors: [],
+ mirrorsFail: false,
+ loginSuccessful: false,
+ message: null,
+ showCancel: false,
+ };
+
+ class ClassicLoginForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = initialState;
+ this.onSubmit = this.onSubmit.bind(this);
+ this.onChangeUser = this.onChangeUser.bind(this);
+ this.onCancel = this.onCancel.bind(this);
+ }
+
+ onChange(prop) {
+ return (e) => {
+ this.setState({ [prop]: e.target.value });
+ };
+ }
+
+ showMessage(message, timing) {
+ this.setState({ message }, () => {
+ if (timing) {
+ setTimeout(() => this.setState({ message: null }), timing);
+ }
+ });
+ }
+
+ componentDidMount() {
+ this.props.loginClassicCheck();
+ this.props.fetchClassicMirrors();
+ }
+
+ onChangeUser(e) {
+ e.preventDefault();
+ this.setState({
+ loginSuccessful: null,
+ showCancel: true,
+ });
+ this.props.onChangeUser();
+ }
+
+ onCancel(e) {
+ e.preventDefault();
+ this.setState({
+ loginSuccessful: true,
+ showCancel: false,
+ });
+ this.props.onLogin();
+ }
+
+ componentWillReceiveProps(next) {
+ if (
+ this.props.classicMirrorsRequest.status !==
+ next.classicMirrorsRequest.status &&
+ next.classicMirrorsRequest.status === 'success'
+ ) {
+ this.setState({
+ mirrors: next.classicMirrorsRequest.result,
+ mirror: 'adsabs.harvard.edu',
+ });
+ } else if (
+ this.props.classicMirrorsRequest.status !==
+ next.classicMirrorsRequest.status &&
+ next.classicMirrorsRequest.status === 'failure'
+ ) {
+ this.setState({
+ mirrorsFail: true,
+ });
+ }
+
+ if (
+ this.props.loginClassicCheckRequest.status !==
+ next.loginClassicCheckRequest.status &&
+ next.loginClassicCheckRequest.status === 'success'
+ ) {
+ const {
+ classic_email,
+ classic_mirror,
+ } = next.loginClassicCheckRequest.result;
+ this.setState({
+ loginSuccessful: true,
+ email: classic_email,
+ mirror: classic_mirror,
+ });
+ next.onLogin();
+ }
+
+ if (
+ this.props.loginClassicRequest.status !==
+ next.loginClassicRequest.status &&
+ next.loginClassicRequest.status === 'success'
+ ) {
+ this.setState({
+ loginSuccessful: true,
+ });
+ next.onLogin();
+ }
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ this.props.loginClassic({
+ classic_email: this.state.email,
+ classic_mirror: this.state.mirror,
+ classic_password: this.state.password,
+ });
+ }
+
+ render() {
+ if (this.props.loginClassicCheckRequest.status === 'pending') {
+ return
loading...
;
+ } else if (this.state.loginSuccessful) {
+ return (
+
+ logged in as
{this.state.email} on the{' '}
+
{this.state.mirror} mirror site.{' '}
+
+ Change user?
+
+
+ );
+ }
+
+ return (
+
+ );
+ }
+ }
+
+ ClassicLoginForm.defaultProps = {
+ classicMirrorsRequest: {},
+ loginClassicRequest: {},
+ loginClassicCheckRequest: {},
+ fetchClassicMirrors: () => {},
+ loginClassic: () => {},
+ loginClassicCheck: () => {},
+ onSubmit: () => {},
+ onLogin: () => {},
+ onChangeUser: () => {},
+ };
+
+ return ClassicLoginForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/Dashboard.jsx.js b/src/js/react/MyAdsDashboard/components/Dashboard.jsx.js
new file mode 100644
index 000000000..02686537c
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/Dashboard.jsx.js
@@ -0,0 +1,431 @@
+define([
+ 'underscore',
+ 'react',
+ 'es6!./TemplatePill.jsx',
+ 'moment',
+ 'react-bootstrap',
+], function(
+ _,
+ React,
+ TemplatePill,
+ moment,
+ { Dropdown, MenuItem, ButtonGroup, Button }
+) {
+ const getFriendlyDateString = (dateStr) => {
+ return moment(dateStr).format('lll');
+ };
+
+ const SortableHeader = ({ children, onClick, direction, active }) => {
+ if (!active) {
+ return (
+
onClick('asc')}>
+ {children}
+ |
+ );
+ } else {
+ const caret = direction === 'desc' ? 'down' : 'up';
+ return (
+
onClick(direction === 'desc' ? 'asc' : 'desc')}
+ >
+ {children}{' '}
+
+ |
+ );
+ }
+ };
+
+ /**
+ * @typedef {import('../typedefs.js').Notification} Notification
+ */
+
+ class MyAdsDashboard extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeItem: null,
+ searchValue: '',
+ filterText: null,
+ sortCol: null,
+ sortDir: null,
+ };
+ this.onFilter = _.debounce(this.onFilter, 100);
+ }
+
+ /**
+ * @param {Notification} item
+ */
+ onEdit(item) {
+ this.props.editNotification(item.id);
+ }
+
+ /**
+ * @param {Notification} item
+ */
+ onDelete(item) {
+ if (confirm('Are you sure?')) {
+ this.props.removeNotification(item.id);
+ }
+ }
+
+ onCreateNewNotification() {
+ this.props.createNewNotification();
+ }
+
+ onImportNotifications() {
+ this.props.importNotifications();
+ }
+
+ /**
+ *
+ * @param {Notification} item
+ */
+ onToggleActive(item) {
+ this.props.toggleActive(item.id);
+ }
+
+ /**
+ * @param {string} id
+ */
+ onEnterItem(id) {
+ requestAnimationFrame(() => this.setState({ activeItem: id }));
+ }
+
+ onLeaveItem() {
+ requestAnimationFrame(() => this.setState({ activeItem: null }));
+ }
+
+ componentDidMount() {
+ if (Object.keys(this.props.notifications).length === 0) {
+ this.props.getNotifications();
+ }
+ }
+
+ onFilter(filterText) {
+ requestAnimationFrame(() => this.setState({ filterText }));
+ }
+
+ /**
+ *
+ * @param {string} value
+ */
+ onSearch(value) {
+ this.setState({ searchValue: value });
+ this.onFilter(value);
+ }
+
+ onSort(sortCol) {
+ return (sortDir) => {
+ this.setState({ sortCol, sortDir });
+ };
+ }
+
+ onRunQuery({ id }) {
+ this.props.runQuery(true);
+ this.props.editNotification(id);
+ }
+
+ render() {
+ let ids = Object.keys(this.props.notifications);
+ if (this.state.filterText && this.state.filterText.length > 0) {
+ const regx = new RegExp('.*' + this.state.filterText + '.*', 'ig');
+ ids = Object.keys(this.props.notifications).filter((k) => {
+ if (!this.state.filterText) {
+ return true;
+ }
+
+ return _.values(this.props.notifications[k])
+ .join(' ')
+ .match(regx);
+ });
+ }
+
+ if (this.state.sortCol && this.state.sortDir) {
+ ids = ids.sort((a, b) => {
+ const { [a]: left, [b]: right } = this.props.notifications;
+ const prop = this.state.sortCol === '#' ? 'id' : this.state.sortCol;
+ const dir = this.state.sortDir;
+ const leftVal = left[prop];
+ const rightVal = right[prop];
+ if (prop === 'updated') {
+ return moment(dir === 'asc' ? leftVal : rightVal).diff(
+ dir === 'asc' ? rightVal : leftVal
+ );
+ }
+
+ if (leftVal < rightVal) {
+ return dir === 'asc' ? -1 : 1;
+ }
+ if (leftVal > rightVal) {
+ return dir === 'asc' ? 1 : -1;
+ }
+ return 0;
+ });
+ }
+ const getRequest = this.props.getNotificationsRequest;
+ const updateRequest = this.props.updateNotificationRequest;
+ const removeRequest = this.props.removeNotificationRequest;
+ const disable =
+ removeRequest.status === 'pending' ||
+ updateRequest.status === 'pending' ||
+ getRequest.status === 'pending';
+
+ if (ids.length === 0 && getRequest.status === 'pending') {
+ return (
+
+
+ {' '}
+ Loading...
+
+
+ );
+ } else if (ids.length === 0 && getRequest.status === 'failure') {
+ return (
+
+
Error: {getRequest.error}
+
+ );
+ }
+
+ return (
+
+
+
+
+ Email Notifications
+
+
+
+ this.onSearch(e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #
+
+
+ Name
+
+
+ Type
+
+
+ Frequency
+
+
+
+ Updated
+
+ | Actions |
+
+
+
+ {ids.map((id, i) => {
+ /** @type {Notification} */
+ const item = this.props.notifications[id];
+ const isGeneral = item.type === 'query';
+
+ return (
+
+ |
+ {i + 1}
+ |
+
+ {item.name}
+ |
+
+
+ |
+
+ {item.frequency}
+ |
+
+ {getFriendlyDateString(item.updated)}
+ |
+
+
+
+ {' '}
+ Actions
+
+
+
+ {isGeneral && (
+
+ )}
+
+
+
+
+ |
+
+ );
+ })}
+
+
+ {ids.length === 0 ? (
+ this.state.searchValue && (
+
Your search is not matching any notifications.
+ )
+ ) : (
+
You don't have any notifications yet!
+ )}
+
+ );
+ }
+ }
+
+ return MyAdsDashboard;
+});
diff --git a/src/js/react/MyAdsDashboard/components/GeneralForm.jsx.js b/src/js/react/MyAdsDashboard/components/GeneralForm.jsx.js
new file mode 100644
index 000000000..5ca68ec41
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/GeneralForm.jsx.js
@@ -0,0 +1,247 @@
+define(['react', 'react-bootstrap', 'js/react/shared/helpers'], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, Checkbox, Radio, Button },
+ { isEmpty }
+) {
+ const getStatusMessage = ({ status, error, noSuccess }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return
Request failed. ({error});
+ case 'success':
+ return (
+
+ {noSuccess ? '' : 'Notification Created!'}
+
+ );
+ }
+ };
+
+ const GeneralFormInitialState = {
+ stateful: false,
+ frequency: 'daily',
+ message: '',
+ name: '',
+ editing: false,
+ pending: false,
+ };
+ class GeneralForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.onSubmit = this.onSubmit.bind(this);
+ this.onFormChange = this.onFormChange.bind(this);
+ this.onGotoResults = this.onGotoResults.bind(this);
+ let updatedState = {};
+ if (this.props.editingNotification) {
+ const { stateful, frequency, name } = this.props.editingNotification;
+ updatedState = {
+ editing: true,
+ stateful,
+ frequency,
+ name,
+ };
+ }
+ this.state = { ...GeneralFormInitialState, ...updatedState };
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ onGotoResults() {
+ if (!this.state.pending) {
+ this.props.getQuery(this.props.editingNotification.qid);
+ }
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ const { name, frequency, stateful, pending } = this.state;
+
+ if (pending) {
+ return;
+ }
+
+ if (isEmpty(name)) {
+ return this.showMessage('Notification name cannot be empty');
+ }
+ const payload = { name, frequency, stateful };
+
+ this.props.updateNotification({
+ ...this.props.editingNotification,
+ ...payload,
+ });
+ }
+
+ componentWillReceiveProps(next) {
+ const updateStatus = next.requests.updateNotification.status;
+ const getQueryStatus = next.requests.getQuery.status;
+
+ // fires success handler if our request was successful
+ if (updateStatus === 'success') {
+ setTimeout(() => this.props.onSuccess(), 1000);
+ }
+
+ // won't allow a request to go through if we're pending or had just failed
+ if (
+ updateStatus === 'pending' ||
+ updateStatus === 'failure' ||
+ getQueryStatus === 'pending' ||
+ getQueryStatus === 'failure'
+ ) {
+ this.setState({ pending: true });
+ }
+
+ if (!updateStatus && !getQueryStatus) {
+ this.setState({ pending: false });
+ }
+ }
+
+ onFormChange(prop) {
+ return (e) => {
+ const value =
+ e.target.type === 'checkbox' ? e.target.checked : e.target.value;
+ this.setState({
+ [prop]: value,
+ updated: true,
+ });
+ };
+ }
+
+ render() {
+ return (
+
+ {this.state.editing ? (
+
+ ) : (
+
+
+
+ How to create a new general notification:
+
+
+ -
+ 1. Perform a new search
+
+ -
+ 2. While on results page, expand "Create
+ email notification" menu
+
+ -
+ 3. Add a name and frequency
+
+ -
+ 4. Click "Create"
+
+
+
+ Check
+ out the Gif on the right for an example.{' '}
+
+
+
+
+ Start a new search
+
+
+
+
+

+
+
+ )}
+
+ );
+ }
+ }
+
+ return GeneralForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx.js b/src/js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx.js
new file mode 100644
index 000000000..119fce919
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx.js
@@ -0,0 +1,155 @@
+// @ts-nocheck
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ '../containers/ClassicLoginForm',
+], function(
+ { debounce },
+ React,
+ {
+ Form,
+ FormGroup,
+ FormControl,
+ ControlLabel,
+ HelpBlock,
+ Button,
+ Alert,
+ Modal,
+ },
+ ClassicLoginForm
+) {
+ const getStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return (
+
+ {error ? error : 'Unable to import'}
+
+ );
+ }
+ };
+
+ const initialState = {
+ isLoggedIn: false,
+ showModal: false,
+ new: 0,
+ existing: 0,
+ };
+
+ class ImportNotificationsForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = initialState;
+ this.onSubmit = this.onSubmit.bind(this);
+ this.onLogin = this.onLogin.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onChangeUser = this.onChangeUser.bind(this);
+ this.beginImport = debounce(this.beginImport, 1000);
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ this.beginImport();
+ }
+
+ beginImport() {
+ this.props.importClassic();
+ }
+
+ onLogin() {
+ this.setState({ isLoggedIn: true });
+ }
+
+ onChangeUser() {
+ this.setState({ isLoggedIn: false });
+ }
+
+ onHide() {
+ this.setState({ showModal: false });
+ this.props.onSuccess();
+ }
+
+ componentWillReceiveProps(next) {
+ if (
+ this.props.importClassicRequest.status !==
+ next.importClassicRequest.status &&
+ next.importClassicRequest.status === 'success'
+ ) {
+ this.setState({
+ showModal: true,
+ new: next.importClassicRequest.result.new.length,
+ existing: next.importClassicRequest.result.existing.length,
+ });
+ }
+ }
+
+ render() {
+ return (
+
+
this.onLogin()}
+ onChangeUser={this.onChangeUser}
+ />
+ {this.state.isLoggedIn && (
+
+ )}
+ {this.state.showModal && (
+
+
+ Import Successful
+
+
+
+
+ We successfully imported {this.state.new} new
+ notification{this.state.new !== 1 ? 's' : ''}
+
+
+ and found {this.state.existing} existing
+ notification{this.state.existing !== 1 ? 's' : ''}
+
+
+
+
+
+
+
+ )}
+
+ );
+ }
+ }
+
+ ImportNotificationsForm.defaultProps = {
+ onSuccess: () => {},
+ importClassic: () => {},
+ };
+
+ return ImportNotificationsForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/KeywordForm.jsx.js b/src/js/react/MyAdsDashboard/components/KeywordForm.jsx.js
new file mode 100644
index 000000000..d871b441f
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/KeywordForm.jsx.js
@@ -0,0 +1,175 @@
+define(['react', 'react-bootstrap'], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock }
+) {
+ const getStatusMessage = ({ status, error }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return
Request failed. ({error});
+ case 'success':
+ return
Notification Created!;
+ }
+ };
+
+ const KeywordFormInitialState = {
+ keywords: '',
+ message: '',
+ name: '',
+ editing: false,
+ pending: false,
+ };
+ class KeywordForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.onSubmit = this.onSubmit.bind(this);
+ this.onChange = this.onChange.bind(this);
+ let updatedState = {};
+ if (this.props.editingNotification) {
+ updatedState = {
+ editing: true,
+ keywords: this.props.editingNotification.data,
+ name: this.props.editingNotification.name,
+ };
+ }
+ this.state = { ...KeywordFormInitialState, ...updatedState };
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ createQueryString() {
+ const { keywords } = this.state;
+ return keywords.trim();
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ const data = this.createQueryString();
+ const { name, pending } = this.state;
+
+ if (pending) {
+ return;
+ }
+
+ if (data === '') {
+ return this.showMessage('Must add at least one keyword');
+ }
+ const payload = { data, name };
+
+ if (this.state.editing) {
+ this.props.updateNotification({
+ ...this.props.editingNotification,
+ ...payload,
+ });
+ } else {
+ this.props.addNotification(payload);
+ }
+ }
+
+ componentWillReceiveProps(next) {
+ const addStatus = next.addNotificationRequest.status;
+ const updateStatus = next.updateNotificationRequest.status;
+
+ // fires success handler if our request was successful
+ if (
+ addStatus === 'success' || updateStatus === 'success'
+ ) {
+ setTimeout(() => this.props.onSuccess(), 1000);
+ }
+
+ // won't allow a request to go through if we're pending or had just failed
+ if (
+ addStatus === 'pending' || updateStatus === 'pending' ||
+ addStatus === 'failure' || updateStatus === 'failure'
+ ) {
+ this.setState({ pending: true });
+ } else if (!addStatus && !updateStatus) {
+ this.setState({ pending: false });
+ }
+ }
+
+ onChange(e) {
+ this.setState({
+ keywords: e.target.value,
+ });
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ return KeywordForm;
+});
diff --git a/src/js/react/MyAdsDashboard/components/SelectTemplate.jsx.js b/src/js/react/MyAdsDashboard/components/SelectTemplate.jsx.js
new file mode 100644
index 000000000..680a12a1c
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/SelectTemplate.jsx.js
@@ -0,0 +1,65 @@
+define(['react', '../constants'], function(React, { page }) {
+ class SelectTemplate extends React.Component {
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ return SelectTemplate;
+});
diff --git a/src/js/react/MyAdsDashboard/components/TemplatePill.jsx.js b/src/js/react/MyAdsDashboard/components/TemplatePill.jsx.js
new file mode 100644
index 000000000..e0eefe6f9
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/TemplatePill.jsx.js
@@ -0,0 +1,54 @@
+define(['react'], function(React) {
+ /**
+ * @typedef TemplateType
+ * @property {string} color
+ * @property {string} label
+ */
+
+ /** @type {Object.
} */
+ const templateTypeConstants = {
+ arxiv: { color: 'primary', label: 'arXiv' },
+ citations: { color: 'info', label: 'Citations' },
+ authors: { color: 'warning', label: 'Authors' },
+ keyword: { color: 'success', label: 'Keyword' },
+ general: { color: '#AA5535', label: 'General' },
+ };
+
+ /**
+ *
+ * @param {string} shortName the name of the template type
+ * @returns {string}
+ */
+ const getTemplateLabel = (shortName) => {
+ return templateTypeConstants[shortName].label;
+ };
+
+ /**
+ *
+ * @param {Object} props
+ * @param {string} props.name the name of the template type
+ */
+ const TemplatePill = ({ name, disabled }) => {
+ let shortName = name || 'general';
+ let isHex = templateTypeConstants[shortName].color.startsWith('#');
+ return (
+
+ {getTemplateLabel(shortName)}
+
+ );
+ };
+
+ return TemplatePill;
+});
diff --git a/src/js/react/MyAdsDashboard/constants.js b/src/js/react/MyAdsDashboard/constants.js
new file mode 100644
index 000000000..d59e0b2af
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/constants.js
@@ -0,0 +1,19 @@
+define([], function() {
+ const page = {
+ // pages
+ DASHBOARD: 'dashboard',
+ SELECT_TEMPLATE: 'select-template',
+ IMPORT_NOTIFICATIONS: 'import-notitifications',
+
+ // forms
+ ARXIV_FORM: 'arxiv-form',
+ CITATIONS_FORM: 'citations-form',
+ KEYWORD_FORM: 'keyword-form',
+ AUTHORS_FORM: 'authors-form',
+ GENERAL_FORM: 'general-form'
+ };
+
+ return {
+ page,
+ };
+});
diff --git a/src/js/react/MyAdsDashboard/containers/ArxivForm.js b/src/js/react/MyAdsDashboard/containers/ArxivForm.js
new file mode 100644
index 000000000..4aac20c02
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/ArxivForm.js
@@ -0,0 +1,29 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/ArxivForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(ArxivForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({
+ requests,
+ notifications,
+ editingNotification,
+ }) => ({
+ addNotificationRequest: requests.ADD_NOTIFICATION,
+ updateNotificationRequest: requests.UPDATE_NOTIFICATION,
+ editingNotification,
+ notifications,
+ });
+
+ const { addNotification, goTo, updateNotification } = actions;
+
+ const actionCreators = {
+ addNotification: (notification) =>
+ addNotification({ ...notification, template: 'arxiv', type: 'template' }),
+ updateNotification,
+ onSuccess: () => goTo(page.DASHBOARD),
+ onCancel: () => goTo(page.DASHBOARD),
+ };
+
+ return connect(mapStateToProps, actionCreators)(ArxivForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/AuthorsForm.js b/src/js/react/MyAdsDashboard/containers/AuthorsForm.js
new file mode 100644
index 000000000..e4513e1f2
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/AuthorsForm.js
@@ -0,0 +1,32 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/AuthorsForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(KeywordForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({
+ requests,
+ notifications,
+ editingNotification,
+ }) => ({
+ addNotificationRequest: requests.ADD_NOTIFICATION,
+ updateNotificationRequest: requests.UPDATE_NOTIFICATION,
+ editingNotification,
+ notifications,
+ });
+
+ const { addNotification, updateNotification, goTo } = actions;
+
+ const actionCreators = {
+ addNotification: (notification) =>
+ addNotification({
+ ...notification,
+ template: 'authors',
+ type: 'template',
+ }),
+ updateNotification,
+ onSuccess: () => goTo(page.DASHBOARD),
+ onCancel: () => goTo(page.DASHBOARD),
+ };
+ return connect(mapStateToProps, actionCreators)(KeywordForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/CitationsForm.js b/src/js/react/MyAdsDashboard/containers/CitationsForm.js
new file mode 100644
index 000000000..ec7fdbc9c
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/CitationsForm.js
@@ -0,0 +1,32 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/CitationsForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(CitationsForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({
+ requests,
+ notifications,
+ editingNotification,
+ }) => ({
+ addNotificationRequest: requests.ADD_NOTIFICATION,
+ updateNotificationRequest: requests.UPDATE_NOTIFICATION,
+ editingNotification,
+ notifications,
+ });
+
+ const { addNotification, updateNotification, goTo } = actions;
+
+ const actionCreators = {
+ addNotification: (notification) =>
+ addNotification({
+ ...notification,
+ template: 'citations',
+ type: 'template',
+ }),
+ updateNotification,
+ onSuccess: () => goTo(page.DASHBOARD),
+ onCancel: () => goTo(page.DASHBOARD),
+ };
+ return connect(mapStateToProps, actionCreators)(CitationsForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/ClassicLoginForm.js b/src/js/react/MyAdsDashboard/containers/ClassicLoginForm.js
new file mode 100644
index 000000000..f56956070
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/ClassicLoginForm.js
@@ -0,0 +1,27 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/ClassicLoginForm.jsx',
+ 'react-redux',
+ '../actions',
+], function(ClassicLoginForm, { connect }, actions) {
+ const mapStateToProps = ({ requests }) => ({
+ classicMirrorsRequest: requests.FETCH_CLASSIC_MIRRORS,
+ loginClassicRequest: requests.LOGIN_CLASSIC,
+ loginClassicCheckRequest: requests.LOGIN_CLASSIC_CHECK,
+ });
+
+ const {
+ goTo,
+ fetchClassicMirrors,
+ loginClassic,
+ loginClassicCheck,
+ } = actions;
+
+ const actionCreators = {
+ goTo,
+ fetchClassicMirrors,
+ loginClassic,
+ loginClassicCheck,
+ };
+
+ return connect(mapStateToProps, actionCreators)(ClassicLoginForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/Dashboard.js b/src/js/react/MyAdsDashboard/containers/Dashboard.js
new file mode 100644
index 000000000..7d7fa5274
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/Dashboard.js
@@ -0,0 +1,39 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/Dashboard.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(Dashboard, { connect }, actions, { page }) {
+ const mapStateToProps = ({ notifications, requests }) => ({
+ notifications,
+ getNotificationsRequest: requests.GET_NOTIFICATIONS,
+ updateNotificationRequest: requests.UPDATE_NOTIFICATION,
+ removeNotificationRequest: requests.REMOVE_NOTIFICATION,
+ getNotificationRequest: requests.GET_NOTIFICATION,
+ });
+
+ const {
+ updateNotification,
+ getNotifications,
+ getNotification,
+ removeNotification,
+ goTo,
+ toggleActive,
+ importNotifications,
+ runQuery,
+ } = actions;
+
+ const actionCreators = {
+ updateNotification,
+ getNotifications,
+ getNotification,
+ removeNotification,
+ toggleActive,
+ importNotifications,
+ runQuery,
+ editNotification: (id) => getNotification(id),
+ createNewNotification: () => goTo(page.SELECT_TEMPLATE),
+ };
+
+ return connect(mapStateToProps, actionCreators)(Dashboard);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/GeneralForm.js b/src/js/react/MyAdsDashboard/containers/GeneralForm.js
new file mode 100644
index 000000000..e8c0bdcdf
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/GeneralForm.js
@@ -0,0 +1,30 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/GeneralForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(GeneralForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({
+ requests,
+ notifications,
+ editingNotification,
+ }) => ({
+ requests: {
+ updateNotification: requests.UPDATE_NOTIFICATION,
+ getQuery: requests.GET_QUERY,
+ },
+ editingNotification,
+ notifications,
+ });
+
+ const { goTo, updateNotification, getQuery } = actions;
+
+ const actionCreators = {
+ updateNotification,
+ getQuery,
+ onSuccess: () => goTo(page.DASHBOARD),
+ onCancel: () => goTo(page.DASHBOARD),
+ };
+
+ return connect(mapStateToProps, actionCreators)(GeneralForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/ImportNotificationsForm.js b/src/js/react/MyAdsDashboard/containers/ImportNotificationsForm.js
new file mode 100644
index 000000000..b32ddb801
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/ImportNotificationsForm.js
@@ -0,0 +1,19 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(ImportNotificationsForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({ requests }) => ({
+ importClassicRequest: requests.IMPORT_CLASSIC,
+ });
+
+ const { goTo, importClassic } = actions;
+
+ const actionCreators = {
+ onSuccess: () => goTo(page.DASHBOARD),
+ importClassic,
+ };
+
+ return connect(mapStateToProps, actionCreators)(ImportNotificationsForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/KeywordForm.js b/src/js/react/MyAdsDashboard/containers/KeywordForm.js
new file mode 100644
index 000000000..2de894643
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/KeywordForm.js
@@ -0,0 +1,32 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/KeywordForm.jsx',
+ 'react-redux',
+ '../actions',
+ '../constants',
+], function(KeywordForm, { connect }, actions, { page }) {
+ const mapStateToProps = ({
+ requests,
+ notifications,
+ editingNotification,
+ }) => ({
+ addNotificationRequest: requests.ADD_NOTIFICATION,
+ updateNotificationRequest: requests.UPDATE_NOTIFICATION,
+ editingNotification,
+ notifications,
+ });
+
+ const { addNotification, updateNotification, goTo } = actions;
+
+ const actionCreators = {
+ addNotification: (notification) =>
+ addNotification({
+ ...notification,
+ template: 'keyword',
+ type: 'template',
+ }),
+ updateNotification,
+ onSuccess: () => goTo(page.DASHBOARD),
+ onCancel: () => goTo(page.DASHBOARD),
+ };
+ return connect(mapStateToProps, actionCreators)(KeywordForm);
+});
diff --git a/src/js/react/MyAdsDashboard/containers/SelectTemplate.js b/src/js/react/MyAdsDashboard/containers/SelectTemplate.js
new file mode 100644
index 000000000..29bf491a8
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/containers/SelectTemplate.js
@@ -0,0 +1,22 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/SelectTemplate.jsx',
+ 'react-redux',
+ '../actions'
+], function(
+ SelectTemplate,
+ {connect},
+ actions
+) {
+
+ const mapStateToProps = ({}) => ({});
+
+ const {
+ goTo
+ } = actions;
+
+ const actionCreators = {
+ goTo
+ };
+
+ return connect(mapStateToProps, actionCreators)(SelectTemplate);
+});
diff --git a/src/js/react/MyAdsDashboard/index.js b/src/js/react/MyAdsDashboard/index.js
new file mode 100644
index 000000000..134b34bc4
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/index.js
@@ -0,0 +1,45 @@
+define([
+ 'es6!js/react/MyAdsDashboard/components/App.jsx',
+ 'js/react/WithBackboneView',
+ 'js/react/configureStore',
+ 'react-redux',
+ './actions',
+ './middleware',
+ './reducer',
+ 'js/react/shared/helpers',
+ 'js/react/shared/middleware/api',
+], function(
+ App,
+ WithBackboneView,
+ configureStore,
+ { connect },
+ actions,
+ middleware,
+ reducer,
+ { withContext },
+ sharedMiddleware
+) {
+ const mapStateToProps = ({ page, editingNotification }) => ({
+ page,
+ editingNotification,
+ });
+
+ const { goTo } = actions;
+
+ const actionCreators = {
+ goTo,
+ };
+
+ return WithBackboneView(
+ connect(
+ mapStateToProps,
+ actionCreators
+ )(App),
+ (context) =>
+ configureStore(
+ context,
+ reducer,
+ withContext(middleware, sharedMiddleware)
+ )
+ );
+});
diff --git a/src/js/react/MyAdsDashboard/middleware.js b/src/js/react/MyAdsDashboard/middleware.js
new file mode 100644
index 000000000..1d3e3fd57
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/middleware.js
@@ -0,0 +1,154 @@
+define(['underscore', './actions', './constants'], function(
+ _,
+ actions,
+ { page }
+) {
+ const {
+ SET_NOTIFICATIONS,
+ EDIT_NOTIFICATION,
+ SET_EDITING_NOTIFICATION,
+ RESET_EDITING_NOTIFICATION,
+ TOGGLE_ACTIVE,
+ GET_QUERY,
+ getQuery,
+ runQuery,
+ goTo,
+ getNotifications,
+ getNotification,
+ updateNotification,
+ } = actions;
+
+ const delay = (cb) => {
+ if (cb.toKey) {
+ window.clearTimeout(cb.toKey);
+ }
+ cb.toKey = setTimeout(cb, 3000);
+ };
+ const apiSuccess = _.memoize((str) => `${str}_API_REQUEST_SUCCESS`);
+
+ const parseScope = (requestType) => {
+ const [scope, status] = requestType.split('_API_REQUEST_');
+ return { scope, status };
+ };
+
+ const resetAfterRequest = ({ trigger }, { dispatch }) => (next) => (
+ action
+ ) => {
+ next(action);
+
+ if (/_API_REQUEST_(SUCCESS|FAILURE)$/.test(action.type)) {
+ const { scope } = parseScope(action.type);
+
+ // don't bother if we are getting the full list
+ if (scope === 'GET_NOTIFICATIONS') {
+ return;
+ }
+
+ delay(() => {
+ dispatch({ type: `${scope}_RESET` });
+ });
+ }
+ };
+
+ const updateNotifications = ({ trigger }, { dispatch, getState }) => (
+ next
+ ) => (action) => {
+ next(action);
+
+ /**
+ * Set the current notifications after a successful GET
+ */
+ if (action.type === apiSuccess('GET_NOTIFICATIONS')) {
+ dispatch({ type: SET_NOTIFICATIONS, result: action.result });
+ }
+
+ if (action.type === apiSuccess('GET_NOTIFICATION')) {
+ const result = action.result[0];
+
+ if (getState().runQuery) {
+ dispatch(runQuery(false));
+ dispatch(getQuery(result.qid));
+ return;
+ }
+
+ // After requesting a single notification, set it as the active editing one
+ dispatch({ type: SET_EDITING_NOTIFICATION, result });
+ const { notifications } = getState();
+ const { template, type } = notifications[result.id];
+ let form;
+ if (type === 'query') {
+ form = page['GENERAL_FORM'];
+ } else {
+ form = page[`${template.toUpperCase()}_FORM`];
+ }
+ dispatch(goTo(form));
+ }
+
+ if (action.type === apiSuccess('GET_QUERY')) {
+ if (action.result && action.result.query) {
+ try {
+ trigger('doSearch', JSON.parse(action.result.query).query);
+ } catch (event) {
+ console.error(event);
+ }
+ } else {
+ console.error('no query found');
+ }
+ setTimeout(() => {
+ dispatch(goTo(page.DASHBOARD));
+ }, 1000);
+ }
+
+ if (action.type === TOGGLE_ACTIVE) {
+ const { notifications } = getState();
+ const item = notifications[action.id];
+ if (item) {
+ dispatch(
+ updateNotification({
+ ...notifications[action.id],
+ active: !notifications[action.id].active,
+ })
+ );
+ }
+ }
+
+ if (
+ action.type === apiSuccess('UPDATE_NOTIFICATION') ||
+ action.type === apiSuccess('REMOVE_NOTIFICATION') ||
+ action.type === apiSuccess('ADD_NOTIFICATION')
+ ) {
+ dispatch(getNotifications());
+ }
+ };
+
+ /**
+ * When going to dashboard, reset the current editing notification
+ */
+ const resetEditingNotificationAfterGoTo = (
+ { trigger },
+ { dispatch, getState }
+ ) => (next) => (action) => {
+ next(action);
+
+ if (action.type === 'GOTO' && action.payload === page.DASHBOARD) {
+ dispatch({ type: RESET_EDITING_NOTIFICATION });
+ }
+ };
+
+ const importNotifications = ({ trigger }, { dispatch, getState }) => (
+ next
+ ) => (action) => {
+ next(action);
+
+ if (action.type === 'IMPORT_NOTIFICATIONS') {
+ dispatch(goTo(page.IMPORT_NOTIFICATIONS));
+ }
+ };
+
+ return {
+ resetAfterRequest,
+ updateNotifications,
+ resetEditingNotificationAfterGoTo,
+ importNotifications,
+ };
+});
diff --git a/src/js/react/MyAdsDashboard/models/arxivClasses.js b/src/js/react/MyAdsDashboard/models/arxivClasses.js
new file mode 100644
index 000000000..47bbf126b
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/models/arxivClasses.js
@@ -0,0 +1,887 @@
+define([], function() {
+ const ARXIV_CLASSES = {
+ "astro-ph": {
+ "key": "astro-ph",
+ "label": "Astrophysics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "astro-ph.CO": {
+ "key": "astro-ph.CO",
+ "label": "Cosmology and Nongalactic Astrophysics",
+ "selected": false
+ },
+ "astro-ph.EP": {
+ "key": "astro-ph.EP",
+ "label": "Earth and Planetary Astrophysics",
+ "selected": false
+ },
+ "astro-ph.GA": {
+ "key": "astro-ph.GA",
+ "label": "Astrophysics of Galaxies",
+ "selected": false
+ },
+ "astro-ph.HE": {
+ "key": "astro-ph.HE",
+ "label": "High Energy Astrophysical Phenomena",
+ "selected": false
+ },
+ "astro-ph.IM": {
+ "key": "astro-ph.IM",
+ "label": "Instrumentation and Methods for Astrophysics",
+ "selected": false
+ },
+ "astro-ph.SR": {
+ "key": "astro-ph.SR",
+ "label": "Solar and Stellar Astrophysics",
+ "selected": false
+ }
+ }
+ },
+ "cond-mat": {
+ "key": "cond-mat",
+ "label": "Condensed Matter",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "cond-mat.dis-nn": {
+ "key": "cond-mat.dis-nn",
+ "label": "Disordered Systems and Neural Networks",
+ "selected": false
+ },
+ "cond-mat.mtrl-sci": {
+ "key": "cond-mat.mtrl-sci",
+ "label": "Materials Science",
+ "selected": false
+ },
+ "cond-mat.mes-hall": {
+ "key": "cond-mat.mes-hall",
+ "label": "Mesoscopic Systems and Quantum Hall Effect",
+ "selected": false
+ },
+ "cond-mat.other": {
+ "key": "cond-mat.other",
+ "label": "Other",
+ "selected": false
+ },
+ "cond-mat.quant-gas": {
+ "key": "cond-mat.quant-gas",
+ "label": "Quantum Gases",
+ "selected": false
+ },
+ "cond-mat.soft": {
+ "key": "cond-mat.soft",
+ "label": "Soft Condensed Matter",
+ "selected": false
+ },
+ "cond-mat.stat-mech": {
+ "key": "cond-mat.stat-mech",
+ "label": "Statistical Mechanics",
+ "selected": false
+ },
+ "cond-mat.str-el": {
+ "key": "cond-mat.str-el",
+ "label": "Strongly Correlated Electrons",
+ "selected": false
+ },
+ "cond-mat.supr-con": {
+ "key": "cond-mat.supr-con",
+ "label": "Superconductivity",
+ "selected": false
+ }
+ }
+ },
+ "gr-qc": {
+ "key": "gr-qc",
+ "label": "General Relativity and Quantum Cosmology",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "hep-ex": {
+ "key": "hep-ex",
+ "label": "High Energy Physics (Experiment)",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "hep-lat": {
+ "key": "hep-lat",
+ "label": "High Energy Physics (Lattice)",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "hep-ph": {
+ "key": "hep-ph",
+ "label": "High Energy Physics (Phenomenology)",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "hep-th": {
+ "key": "hep-th",
+ "label": "High Energy Physics (Theory)",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "math-ph": {
+ "key": "math-ph",
+ "label": "Mathematical Physics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "nucl-ex": {
+ "key": "nucl-ex",
+ "label": "Nuclear Experiment",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "nucl-th": {
+ "key": "nucl-th",
+ "label": "Nuclear Theory",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "econ": {
+ "key": "econ",
+ "label": "Economics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "econ.EM": {
+ "key": "econ.EM",
+ "label": "Econometrics",
+ "selected": false
+ },
+ "econ.GN": {
+ "key": "econ.GN",
+ "label": "General Economics",
+ "selected": false
+ },
+ "econ.TH": {
+ "key": "econ.TH",
+ "label": "Theoretical Economics",
+ "selected": false
+ }
+ }
+ },
+ "eess": {
+ "key": "eess",
+ "label": "Electrical Engineering and Systems Science",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "eess.AS": {
+ "key": "eess.AS",
+ "label": "Audio and Speech Processing",
+ "selected": false
+ },
+ "eess.IV": {
+ "key": "eess.IV",
+ "label": "Image and Video Processing",
+ "selected": false
+ },
+ "eess.SP": {
+ "key": "eess.SP",
+ "label": "Signal Processing",
+ "selected": false
+ },
+ "eess.SY": {
+ "key": "eess.SY",
+ "label": "Systems and Control",
+ "selected": false
+ }
+ }
+ },
+ "physics": {
+ "key": "physics",
+ "label": "Physics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "physics.acc-ph": {
+ "key": "physics.acc-ph",
+ "label": "Accelerator Physics",
+ "selected": false
+ },
+ "physics.app-ph": {
+ "key": "physics.app-ph",
+ "label": "Applied Physics",
+ "selected": false
+ },
+ "physics.ao-ph": {
+ "key": "physics.ao-ph",
+ "label": "Atmospheric and Oceanic Physics",
+ "selected": false
+ },
+ "physics.atom-ph": {
+ "key": "physics.atom-ph",
+ "label": "Atomic Physics",
+ "selected": false
+ },
+ "physics.atm-clus": {
+ "key": "physics.atm-clus",
+ "label": "Atomic and Molecular Clusters",
+ "selected": false
+ },
+ "physics.bio-ph": {
+ "key": "physics.bio-ph",
+ "label": "Biological Physics",
+ "selected": false
+ },
+ "physics.chem-ph": {
+ "key": "physics.chem-ph",
+ "label": "Chemical Physics",
+ "selected": false
+ },
+ "physics.class-ph": {
+ "key": "physics.class-ph",
+ "label": "Classical Physics",
+ "selected": false
+ },
+ "physics.comp-ph": {
+ "key": "physics.comp-ph",
+ "label": "Computational Physics",
+ "selected": false
+ },
+ "physics.data-an": {
+ "key": "physics.data-an",
+ "label": "Data Analysis, Statistics and Probability",
+ "selected": false
+ },
+ "physics.flu-dyn": {
+ "key": "physics.flu-dyn",
+ "label": "Fluid Dynamics",
+ "selected": false
+ },
+ "physics.gen-ph": {
+ "key": "physics.gen-ph",
+ "label": "General Physics",
+ "selected": false
+ },
+ "physics.geo-ph": {
+ "key": "physics.geo-ph",
+ "label": "Geophysics",
+ "selected": false
+ },
+ "physics.hist-ph": {
+ "key": "physics.hist-ph",
+ "label": "History and Philosophy of Physics",
+ "selected": false
+ },
+ "physics.ins-det": {
+ "key": "physics.ins-det",
+ "label": "Instrumentation and Detectors",
+ "selected": false
+ },
+ "physics.med-ph": {
+ "key": "physics.med-ph",
+ "label": "Medical Physics",
+ "selected": false
+ },
+ "physics.optics": {
+ "key": "physics.optics",
+ "label": "Optics",
+ "selected": false
+ },
+ "physics.ed-ph": {
+ "key": "physics.ed-ph",
+ "label": "Physics Education",
+ "selected": false
+ },
+ "physics.soc-ph": {
+ "key": "physics.soc-ph",
+ "label": "Physics and Society",
+ "selected": false
+ },
+ "physics.plasm-ph": {
+ "key": "physics.plasm-ph",
+ "label": "Plasma Physics",
+ "selected": false
+ },
+ "physics.pop-ph": {
+ "key": "physics.pop-ph",
+ "label": "Popular Physics",
+ "selected": false
+ },
+ "physics.space-ph": {
+ "key": "physics.space-ph",
+ "label": "Space Physics",
+ "selected": false
+ }
+ }
+ },
+ "quant-ph": {
+ "key": "quant-ph",
+ "label": "Quantum Physics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {}
+ },
+ "math": {
+ "key": "math",
+ "label": "Mathematics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "math.AG": {
+ "key": "math.AG",
+ "label": "Algebraic Geometry",
+ "selected": false
+ },
+ "math.AT": {
+ "key": "math.AT",
+ "label": "Algebraic Topology",
+ "selected": false
+ },
+ "math.AP": {
+ "key": "math.AP",
+ "label": "Analysis of PDEs",
+ "selected": false
+ },
+ "math.CT": {
+ "key": "math.CT",
+ "label": "Category Theory",
+ "selected": false
+ },
+ "math.CA": {
+ "key": "math.CA",
+ "label": "Classical Analysis and ODEs",
+ "selected": false
+ },
+ "math.CO": {
+ "key": "math.CO",
+ "label": "Combinatorics",
+ "selected": false
+ },
+ "math.AC": {
+ "key": "math.AC",
+ "label": "Commutative Algebra",
+ "selected": false
+ },
+ "math.CV": {
+ "key": "math.CV",
+ "label": "Complex Variables",
+ "selected": false
+ },
+ "math.DG": {
+ "key": "math.DG",
+ "label": "Differential Geometry",
+ "selected": false
+ },
+ "math.DS": {
+ "key": "math.DS",
+ "label": "Dynamical Systems",
+ "selected": false
+ },
+ "math.FA": {
+ "key": "math.FA",
+ "label": "Functional Analysis",
+ "selected": false
+ },
+ "math.GM": {
+ "key": "math.GM",
+ "label": "General Mathematics",
+ "selected": false
+ },
+ "math.GN": {
+ "key": "math.GN",
+ "label": "General Topology",
+ "selected": false
+ },
+ "math.GT": {
+ "key": "math.GT",
+ "label": "Geometric Topology",
+ "selected": false
+ },
+ "math.GR": {
+ "key": "math.GR",
+ "label": "Group Theory",
+ "selected": false
+ },
+ "math.HO": {
+ "key": "math.HO",
+ "label": "History and Overview",
+ "selected": false
+ },
+ "math.IT": {
+ "key": "math.IT",
+ "label": "Information Theory",
+ "selected": false
+ },
+ "math.KT": {
+ "key": "math.KT",
+ "label": "K-Theory and Homology",
+ "selected": false
+ },
+ "math.LO": {
+ "key": "math.LO",
+ "label": "Logic",
+ "selected": false
+ },
+ "math.MP": {
+ "key": "math.MP",
+ "label": "Mathematical Physics",
+ "selected": false
+ },
+ "math.MG": {
+ "key": "math.MG",
+ "label": "Metric Geometry",
+ "selected": false
+ },
+ "math.NT": {
+ "key": "math.NT",
+ "label": "Number Theory",
+ "selected": false
+ },
+ "math.NA": {
+ "key": "math.NA",
+ "label": "Numerical Analysis",
+ "selected": false
+ },
+ "math.OA": {
+ "key": "math.OA",
+ "label": "Operator Algebras",
+ "selected": false
+ },
+ "math.OC": {
+ "key": "math.OC",
+ "label": "Optimization and Control",
+ "selected": false
+ },
+ "math.PR": {
+ "key": "math.PR",
+ "label": "Probability",
+ "selected": false
+ },
+ "math.QA": {
+ "key": "math.QA",
+ "label": "Quantum Algebra",
+ "selected": false
+ },
+ "math.RT": {
+ "key": "math.RT",
+ "label": "Representation Theory",
+ "selected": false
+ },
+ "math.RA": {
+ "key": "math.RA",
+ "label": "Rings and Algebras",
+ "selected": false
+ },
+ "math.SP": {
+ "key": "math.SP",
+ "label": "Spectral Theory",
+ "selected": false
+ },
+ "math.ST": {
+ "key": "math.ST",
+ "label": "Statistics Theory",
+ "selected": false
+ },
+ "math.SG": {
+ "key": "math.SG",
+ "label": "Symplectic Geometry",
+ "selected": false
+ }
+ }
+ },
+ "nlin": {
+ "key": "nlin",
+ "label": "Nonlinear Sciences",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "nlin.AO": {
+ "key": "nlin.AO",
+ "label": "Adaptation and Self-Organizing Systems",
+ "selected": false
+ },
+ "nlin.CG": {
+ "key": "nlin.CG",
+ "label": "Cellular Automata and Lattice Gases",
+ "selected": false
+ },
+ "nlin.CD": {
+ "key": "nlin.CD",
+ "label": "Chaotic Dynamics",
+ "selected": false
+ },
+ "nlin.SI": {
+ "key": "nlin.SI",
+ "label": "Exactly Solvable and Integrable Systems",
+ "selected": false
+ },
+ "nlin.PS": {
+ "key": "nlin.PS",
+ "label": "Pattern Formation and Solitons",
+ "selected": false
+ }
+ }
+ },
+ "cs": {
+ "key": "cs",
+ "label": "Computer Science",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "cs.AR": {
+ "key": "cs.AR",
+ "label": "Hardware Architecture",
+ "selected": false
+ },
+ "cs.AI": {
+ "key": "cs.AI",
+ "label": "Artificial Intelligence",
+ "selected": false
+ },
+ "cs.CL": {
+ "key": "cs.CL",
+ "label": "Computation and Language",
+ "selected": false
+ },
+ "cs.CC": {
+ "key": "cs.CC",
+ "label": "Computational Complexity",
+ "selected": false
+ },
+ "cs.CE": {
+ "key": "cs.CE",
+ "label": "Computational Engineering",
+ "selected": false
+ },
+ "cs.CG": {
+ "key": "cs.CG",
+ "label": "Computational Geometry",
+ "selected": false
+ },
+ "cs.GT": {
+ "key": "cs.GT",
+ "label": "Computer Science and Game Theory",
+ "selected": false
+ },
+ "cs.CV": {
+ "key": "cs.CV",
+ "label": "Computer Vision and Pattern Recognition",
+ "selected": false
+ },
+ "cs.CY": {
+ "key": "cs.CY",
+ "label": "Computers and Society",
+ "selected": false
+ },
+ "cs.CR": {
+ "key": "cs.CR",
+ "label": "Cryptography and Security",
+ "selected": false
+ },
+ "cs.DS": {
+ "key": "cs.DS",
+ "label": "Data Structures and Algorithms",
+ "selected": false
+ },
+ "cs.DB": {
+ "key": "cs.DB",
+ "label": "Databases",
+ "selected": false
+ },
+ "cs.DL": {
+ "key": "cs.DL",
+ "label": "Digital Libraries",
+ "selected": false
+ },
+ "cs.DM": {
+ "key": "cs.DM",
+ "label": "Discrete Mathematics",
+ "selected": false
+ },
+ "cs.DC": {
+ "key": "cs.DC",
+ "label": "Distributed",
+ "selected": false
+ },
+ "cs.ET": {
+ "key": "cs.ET",
+ "label": "Emerging Technologies",
+ "selected": false
+ },
+ "cs.FL": {
+ "key": "cs.FL",
+ "label": "Formal Languages and Automata Theory",
+ "selected": false
+ },
+ "cs.GL": {
+ "key": "cs.GL",
+ "label": "General Literature",
+ "selected": false
+ },
+ "cs.GR": {
+ "key": "cs.GR",
+ "label": "Graphics",
+ "selected": false
+ },
+ "cs.HC": {
+ "key": "cs.HC",
+ "label": "Human-Computer Interaction",
+ "selected": false
+ },
+ "cs.IR": {
+ "key": "cs.IR",
+ "label": "Information Retrieval",
+ "selected": false
+ },
+ "cs.IT": {
+ "key": "cs.IT",
+ "label": "Information Theory",
+ "selected": false
+ },
+ "cs.LG": {
+ "key": "cs.LG",
+ "label": "Machine Learning",
+ "selected": false
+ },
+ "cs.LO": {
+ "key": "cs.LO",
+ "label": "Logic in Computer Science",
+ "selected": false
+ },
+ "cs.MS": {
+ "key": "cs.MS",
+ "label": "Mathematical Software",
+ "selected": false
+ },
+ "cs.MA": {
+ "key": "cs.MA",
+ "label": "Multiagent Systems",
+ "selected": false
+ },
+ "cs.MM": {
+ "key": "cs.MM",
+ "label": "Multimedia",
+ "selected": false
+ },
+ "cs.NI": {
+ "key": "cs.NI",
+ "label": "Networking and Internet Architecture",
+ "selected": false
+ },
+ "cs.NE": {
+ "key": "cs.NE",
+ "label": "Neural and Evolutionary Computing",
+ "selected": false
+ },
+ "cs.NA": {
+ "key": "cs.NA",
+ "label": "Numerical Analysis",
+ "selected": false
+ },
+ "cs.OS": {
+ "key": "cs.OS",
+ "label": "Operating Systems",
+ "selected": false
+ },
+ "cs.OH": {
+ "key": "cs.OH",
+ "label": "Other",
+ "selected": false
+ },
+ "cs.PF": {
+ "key": "cs.PF",
+ "label": "Performance",
+ "selected": false
+ },
+ "cs.PL": {
+ "key": "cs.PL",
+ "label": "Programming Languages",
+ "selected": false
+ },
+ "cs.RO": {
+ "key": "cs.RO",
+ "label": "Robotics",
+ "selected": false
+ },
+ "cs.SE": {
+ "key": "cs.SE",
+ "label": "Software Engineering",
+ "selected": false
+ },
+ "cs.SD": {
+ "key": "cs.SD",
+ "label": "Sound",
+ "selected": false
+ },
+ "cs.SC": {
+ "key": "cs.SC",
+ "label": "Symbolic Computation",
+ "selected": false
+ },
+ "cs.SI": {
+ "key": "cs.SI",
+ "label": "Social and Information Networks",
+ "selected": false
+ },
+ "cs.SY": {
+ "key": "cs.SY",
+ "label": "Systems and Control",
+ "selected": false
+ }
+ }
+ },
+ "q-bio": {
+ "key": "q-bio",
+ "label": "Quantitative Biology",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "q-bio.BM": {
+ "key": "q-bio.BM",
+ "label": "Biomolecules",
+ "selected": false
+ },
+ "q-bio.CB": {
+ "key": "q-bio.CB",
+ "label": "Cell Behavior",
+ "selected": false
+ },
+ "q-bio.GN": {
+ "key": "q-bio.GN",
+ "label": "Genomics",
+ "selected": false
+ },
+ "q-bio.MN": {
+ "key": "q-bio.MN",
+ "label": "Molecular Networks",
+ "selected": false
+ },
+ "q-bio.NC": {
+ "key": "q-bio.NC",
+ "label": "Neurons and Cognition",
+ "selected": false
+ },
+ "q-bio.OT": {
+ "key": "q-bio.OT",
+ "label": "Other Quantitative Biology",
+ "selected": false
+ },
+ "q-bio.PE": {
+ "key": "q-bio.PE",
+ "label": "Populations and Evolution",
+ "selected": false
+ },
+ "q-bio.QM": {
+ "key": "q-bio.QM",
+ "label": "Quantitative Methods",
+ "selected": false
+ },
+ "q-bio.SC": {
+ "key": "q-bio.SC",
+ "label": "Subcellular Processes",
+ "selected": false
+ },
+ "q-bio.TO": {
+ "key": "q-bio.TO",
+ "label": "Tissues and Organs",
+ "selected": false
+ }
+ }
+ },
+ "q-fin": {
+ "key": "q-fin",
+ "label": "Quantitative Finance",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "q-fin.CP": {
+ "key": "q-fin.CP",
+ "label": "Computational Finance",
+ "selected": false
+ },
+ "q-fin.EC": {
+ "key": "q-fin.EC",
+ "label": "Economics",
+ "selected": false
+ },
+ "q-fin.GN": {
+ "key": "q-fin.GN",
+ "label": "General Finance",
+ "selected": false
+ },
+ "q-fin.MF": {
+ "key": "q-fin.MF",
+ "label": "Mathematical Finance",
+ "selected": false
+ },
+ "q-fin.PM": {
+ "key": "q-fin.PM",
+ "label": "Portfolio Management",
+ "selected": false
+ },
+ "q-fin.PR": {
+ "key": "q-fin.PR",
+ "label": "Pricing of Securities",
+ "selected": false
+ },
+ "q-fin.RM": {
+ "key": "q-fin.RM",
+ "label": "Risk Management",
+ "selected": false
+ },
+ "q-fin.ST": {
+ "key": "q-fin.ST",
+ "label": "Statistical Finance",
+ "selected": false
+ },
+ "q-fin.TR": {
+ "key": "q-fin.TR",
+ "label": "Trading and Market Microstructure",
+ "selected": false
+ }
+ }
+ },
+ "stat": {
+ "key": "stat",
+ "label": "Statistics",
+ "selected": false,
+ "indeterminate": false,
+ "children": {
+ "stat.AP": {
+ "key": "stat.AP",
+ "label": "Applications",
+ "selected": false
+ },
+ "stat.CO": {
+ "key": "stat.CO",
+ "label": "Computation",
+ "selected": false
+ },
+ "stat.ML": {
+ "key": "stat.ML",
+ "label": "Machine Learning",
+ "selected": false
+ },
+ "stat.ME": {
+ "key": "stat.ME",
+ "label": "Methodology",
+ "selected": false
+ },
+ "stat.TH": {
+ "key": "stat.TH",
+ "label": "Theory",
+ "selected": false
+ },
+ "stat.OT": {
+ "key": "stat.OT",
+ "label": "Other Statistics",
+ "selected": false
+ }
+ }
+ }
+ };
+
+ return ARXIV_CLASSES;
+});
diff --git a/src/js/react/MyAdsDashboard/reducer.js b/src/js/react/MyAdsDashboard/reducer.js
new file mode 100644
index 000000000..dc5fa07f8
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/reducer.js
@@ -0,0 +1,106 @@
+define(['./actions', 'redux', './constants'], function(
+ actions,
+ { combineReducers },
+ { page: PAGE }
+) {
+ const {
+ SET_NOTIFICATIONS,
+ SET_EDITING_NOTIFICATION,
+ RESET_EDITING_NOTIFICATION,
+ GOTO,
+ RUN_QUERY,
+ } = actions;
+
+ /**
+ * @typedef {Object.} NotificationState
+ */
+
+ /** @type {NotificationState} */
+ const notificationsState = {};
+ const notifications = (state = notificationsState, action) => {
+ if (action.type === SET_NOTIFICATIONS && action.result) {
+ return action.result.reduce(
+ (acc, entry) => ({ ...acc, [entry.id]: entry }),
+ {}
+ );
+ }
+ return state;
+ };
+
+ const editingNotificationState = null;
+ const editingNotification = (state = editingNotificationState, action) => {
+ if (action.type === SET_EDITING_NOTIFICATION && action.result) {
+ return action.result;
+ }
+
+ if (action.type === RESET_EDITING_NOTIFICATION) {
+ return editingNotificationState;
+ }
+
+ return state;
+ };
+
+ /** @type {string} */
+ const pageState = PAGE.DASHBOARD;
+ const page = (state = pageState, action) => {
+ if (action.type === GOTO && action.payload) {
+ return action.payload;
+ }
+ return state;
+ };
+
+ const runQueryState = false;
+ const runQuery = (state = runQueryState, action) => {
+ if (action.type === RUN_QUERY && action.result) {
+ return action.result;
+ }
+ return state;
+ };
+
+ /**
+ * @typedef {Object.} RequestState
+ */
+
+ /** @type {RequestState} */
+ const requestState = {
+ ADD_NOTIFICATION: { status: null, result: null, error: null },
+ GET_NOTIFICATIONS: { status: null, result: null, error: null },
+ GET_NOTIFICATION: { status: null, result: null, error: null },
+ UPDATE_NOTIFICATION: { status: null, result: null, error: null },
+ REMOVE_NOTIFICATION: { status: null, result: null, error: null },
+ FETCH_CLASSIC_MIRRORS: { status: null, result: null, error: null },
+ LOGIN_CLASSIC: { status: null, result: null, error: null },
+ LOGIN_CLASSIC_CHECK: { status: null, result: null, error: null },
+ IMPORT_CLASSIC: { status: null, result: null, error: null },
+ GET_QUERY: { status: null, result: null, error: null },
+ };
+ const requests = (state = requestState, action) => {
+ if (/_API_REQUEST_/.test(action.type)) {
+ const [scope, status] = action.type.split('_API_REQUEST_');
+ const { result = null, error = null } = action;
+ return {
+ ...state,
+ [scope]: {
+ status: status.toLowerCase(),
+ result,
+ error,
+ },
+ };
+ } else if (/_RESET$/.test(action.type)) {
+ const scope = action.type.replace('_RESET', '');
+ return {
+ ...state,
+ [scope]: requestState[scope],
+ };
+ }
+ return state;
+ };
+
+ return combineReducers({
+ notifications,
+ page,
+ runQuery,
+ requests,
+ editingNotification,
+ });
+});
diff --git a/src/js/react/MyAdsDashboard/typedefs.js b/src/js/react/MyAdsDashboard/typedefs.js
new file mode 100644
index 000000000..e55a8e6ae
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/typedefs.js
@@ -0,0 +1,27 @@
+/**
+ * @enum {string}
+ */
+const TemplateTypes = {
+ ARXIV: 'arxiv',
+ AUTHORS: 'authors',
+ CITATIONS: 'citations',
+ KEYWORD: 'keyword',
+ GENERAL: 'general'
+};
+
+/**
+ * @typedef Notification
+ * @property {string} id
+ * @property {string} name
+ * @property {TemplateTypes} type
+ * @property {string} frequency
+ * @property {string} created
+ * @property {boolean} active
+ *
+ * @typedef Request
+ * @property {string} status
+ * @property {any} result
+ * @property {string} error
+ */
+
+export { TemplateTypes };
diff --git a/src/js/react/MyAdsFreeform/actions.js b/src/js/react/MyAdsFreeform/actions.js
new file mode 100644
index 000000000..323330a54
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/actions.js
@@ -0,0 +1,56 @@
+define([], function() {
+ const actions = {
+ ADD_NOTIFICATION: 'ADD_NOTIFICATION',
+ ERROR_RESET: 'ERROR_RESET',
+ ERROR: 'ERROR',
+ GET_QID: 'GET_QID',
+ RESET: 'RESET',
+ SAVE_NEW_NOTIFICATION: 'SAVE_NEW_NOTIFICATION',
+ SET_UPDATE_DATA: 'SET_UPDATE_DATA',
+ SET_LOGIN_STATUS: 'SET_LOGIN_STATUS',
+ CHECK_LOGIN_STATUS: 'CHECK_LOGIN_STATUS',
+ };
+ const actionCreators = {
+ addNotification: (notification) => ({
+ type: 'API_REQUEST',
+ scope: actions.ADD_NOTIFICATION,
+ options: {
+ type: 'POST',
+ target: 'vault/notifications',
+ data: { ...notification, type: 'query' },
+ },
+ }),
+ getQID: (queryParams) => ({
+ type: 'API_REQUEST',
+ scope: actions.GET_QID,
+ options: {
+ type: 'POST',
+ target: 'vault/query',
+ data: queryParams,
+ },
+ }),
+ saveNewNotification: (notification) => ({
+ type: actions.SAVE_NEW_NOTIFICATION,
+ result: notification,
+ }),
+ makeError: (error) => ({
+ type: actions.ERROR,
+ result: error,
+ }),
+ reset: () => ({
+ type: actions.RESET,
+ }),
+ checkLoginStatus: () => ({
+ type: actions.CHECK_LOGIN_STATUS,
+ }),
+ setLoginStatus: (result) => ({
+ type: actions.SET_LOGIN_STATUS,
+ result,
+ }),
+ };
+
+ return {
+ ...actions,
+ ...actionCreators,
+ };
+});
diff --git a/src/js/react/MyAdsFreeform/components/App.jsx.js b/src/js/react/MyAdsFreeform/components/App.jsx.js
new file mode 100644
index 000000000..f322459f9
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/components/App.jsx.js
@@ -0,0 +1,123 @@
+define([
+ 'react',
+ 'react-prop-types',
+ 'react-bootstrap',
+ 'es6!./CollapsePanel.jsx',
+ '../containers/SaveQueryForm',
+], function(React, PropTypes, { Alert }, CollapsePanel, SaveQueryForm) {
+ const Message = ({ children, show, type, ...otherProps }) => {
+ return show ? (
+
+ {children}
+
+ ) : null;
+ };
+
+ class MyADSFreeform extends React.Component {
+ constructor(props) {
+ super(props);
+ this.loginStatusCheckTimer = null;
+ }
+
+ componentDidMount() {
+ const check = () => {
+ requestAnimationFrame(() => this.props.checkLoginStatus());
+ this.loginStatusCheckTimer = setTimeout(check, 1000);
+ };
+ check();
+ }
+
+ componentWillUnmount() {
+ window.clearTimeout(this.loginStatusCheckTimer);
+ }
+
+ render() {
+ const {
+ requests,
+ saveNewNotification,
+ generalError,
+ loggedIn,
+ } = this.props;
+
+ if (!loggedIn) {
+ return null;
+ }
+
+ const addNotificationStatus = requests.addNotification.status;
+ const getQIDStatus = requests.getQID.status;
+ const isPending =
+ addNotificationStatus === 'pending' || getQIDStatus === 'pending';
+ return (
+
+ (
+
+
+
+ {' '}
+ Creating...
+
+
+ Success! Notification created.
+
+
+
+ {' '}
+ Error!
+ {' '}
+ {requests.addNotification.error}
+
+
+
+ {' '}
+ Error!
+ {' '}
+ {requests.getQID.error}
+
+
+
+ {' '}
+ Error!
+ {' '}
+ {generalError}
+
+
+ )}
+ />
+
+ );
+ }
+ }
+
+ MyADSFreeform.defaultProps = {
+ saveNewNotification: () => {},
+ requests: {},
+ generalError: null,
+ };
+ MyADSFreeform.propTypes = {
+ saveNewNotification: PropTypes.func,
+ requests: PropTypes.object,
+ generalError: PropTypes.object,
+ };
+
+ return MyADSFreeform;
+});
diff --git a/src/js/react/MyAdsFreeform/components/CollapsePanel.jsx.js b/src/js/react/MyAdsFreeform/components/CollapsePanel.jsx.js
new file mode 100644
index 000000000..b1a23c779
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/components/CollapsePanel.jsx.js
@@ -0,0 +1,96 @@
+define(['react', 'react-prop-types', 'react-bootstrap'], function(
+ React,
+ PropTypes,
+ { Panel }
+) {
+ const initialState = {
+ open: false,
+ hovered: false,
+ focused: false,
+ };
+ class CollapsePanel extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state = initialState;
+ this.toggle = this.toggle.bind(this);
+ this.toggleHover = this.toggleHover.bind(this);
+ this.onChangeFocus = this.onChangeFocus.bind(this);
+ }
+
+ toggle() {
+ this.setState({ open: !this.state.open });
+ }
+
+ toggleHover() {
+ this.setState({ hovered: !this.state.hovered });
+ }
+
+ onChangeFocus(val) {
+ return () => {
+ this.setState({ focused: val });
+ };
+ }
+
+ render() {
+ const caretDir = this.state.open ? 'down' : 'right';
+ return (
+
+
+
+
+
+
+
+
+ {this.props.render({ collapse: () => this.toggle() })}
+
+
+
+ );
+ }
+ }
+
+ CollapsePanel.defaultProps = {
+ render: () => {},
+ };
+ CollapsePanel.propTypes = {
+ render: PropTypes.func,
+ };
+
+ return CollapsePanel;
+});
diff --git a/src/js/react/MyAdsFreeform/components/SaveQueryForm.jsx.js b/src/js/react/MyAdsFreeform/components/SaveQueryForm.jsx.js
new file mode 100644
index 000000000..b1e00423e
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/components/SaveQueryForm.jsx.js
@@ -0,0 +1,162 @@
+define([
+ 'underscore',
+ 'react',
+ 'react-prop-types',
+ 'react-bootstrap',
+ '../constants',
+ 'js/react/shared/helpers',
+], function(
+ _,
+ React,
+ PropTypes,
+ {
+ FormGroup,
+ FormControl,
+ ControlLabel,
+ Checkbox,
+ Radio,
+ Button,
+ ButtonToolbar,
+ },
+ { Frequency },
+ { isEmpty }
+) {
+ const initialState = {
+ name: '',
+ frequency: Frequency.DAILY,
+ };
+ class SaveQueryForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = initialState;
+ this.onSubmit = this.onSubmit.bind(this);
+ this.onFormChange = this.onFormChange.bind(this);
+ this.onCancel = this.onCancel.bind(this);
+ this._onSubmit = _.debounce(this._onSubmit, 1000, {
+ leading: true,
+ trailing: false,
+ });
+ }
+
+ reset() {
+ this.setState(initialState);
+ }
+
+ _onSubmit() {
+ const { name, frequency } = this.state;
+ if (!isEmpty(name)) {
+ this.props.onSubmit({ name, frequency });
+ }
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ this._onSubmit();
+ }
+
+ onCancel() {
+ this.props.onCancel();
+ }
+
+ onFormChange(prop) {
+ return (e) => {
+ const value =
+ e.target.type === 'checkbox' ? e.target.checked : e.target.value;
+ this.setState({
+ [prop]: value,
+ updated: true,
+ });
+ };
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.requests.addNotification.status === 'success') {
+ this.reset();
+ }
+ }
+
+ render() {
+ const isDisabled = this.props.disabled;
+ return (
+
+ );
+ }
+ }
+ SaveQueryForm.defaultProps = {
+ onSubmit: () => {},
+ onCancel: () => {},
+ disabled: false,
+ requests: {},
+ };
+ SaveQueryForm.propTypes = {
+ onSubmit: PropTypes.func,
+ onCancel: PropTypes.func,
+ disabled: PropTypes.bool,
+ requests: PropTypes.object,
+ };
+
+ return SaveQueryForm;
+});
diff --git a/src/js/react/MyAdsFreeform/constants.js b/src/js/react/MyAdsFreeform/constants.js
new file mode 100644
index 000000000..cd1e051a2
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/constants.js
@@ -0,0 +1,10 @@
+define([], function() {
+ const Frequency = Object.freeze({
+ WEEKLY: 'weekly',
+ DAILY: 'daily',
+ });
+
+ return {
+ Frequency,
+ };
+});
diff --git a/src/js/react/MyAdsFreeform/containers/SaveQueryForm.js b/src/js/react/MyAdsFreeform/containers/SaveQueryForm.js
new file mode 100644
index 000000000..fc3934b67
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/containers/SaveQueryForm.js
@@ -0,0 +1,17 @@
+define([
+ 'es6!../components/SaveQueryForm.jsx',
+ 'react-redux',
+ '../actions',
+], function(SaveQueryForm, { connect }, actions) {
+ const mapStateToProps = ({ requests }) => ({
+ requests: {
+ addNotification: requests.ADD_NOTIFICATION,
+ },
+ });
+
+ const {} = actions;
+
+ const actionCreators = {};
+
+ return connect(mapStateToProps, actionCreators)(SaveQueryForm);
+});
diff --git a/src/js/react/MyAdsFreeform/index.js b/src/js/react/MyAdsFreeform/index.js
new file mode 100644
index 000000000..3b40b6efe
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/index.js
@@ -0,0 +1,45 @@
+define([
+ 'es6!./components/App.jsx',
+ 'js/react/WithBackboneView',
+ 'js/react/configureStore',
+ 'react-redux',
+ './actions',
+ './middleware',
+ './reducer',
+ 'js/react/shared/helpers',
+ 'js/react/shared/middleware/api',
+], function(
+ App,
+ WithBackboneView,
+ configureStore,
+ { connect },
+ actions,
+ middleware,
+ reducer,
+ { withContext },
+ sharedMiddleware
+) {
+ const mapStateToProps = ({ requests, generalError, loggedIn }) => ({
+ requests: {
+ addNotification: requests.ADD_NOTIFICATION,
+ getQID: requests.GET_QID,
+ },
+ loggedIn,
+ generalError,
+ });
+ const { saveNewNotification, checkLoginStatus } = actions;
+ const actionCreators = {
+ saveNewNotification,
+ checkLoginStatus,
+ };
+
+ return WithBackboneView(
+ connect(mapStateToProps, actionCreators)(App),
+ (context) =>
+ configureStore(
+ context,
+ reducer,
+ withContext(middleware, sharedMiddleware)
+ )
+ );
+});
diff --git a/src/js/react/MyAdsFreeform/middleware.js b/src/js/react/MyAdsFreeform/middleware.js
new file mode 100644
index 000000000..2655a06a5
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/middleware.js
@@ -0,0 +1,112 @@
+define(['underscore', './actions', 'js/react/shared/helpers'], function(
+ _,
+ {
+ SET_UPDATE_DATA,
+ GET_QID,
+ SAVE_NEW_NOTIFICATION,
+ ERROR_RESET,
+ ERROR,
+ CHECK_LOGIN_STATUS,
+ setLoginStatus,
+ addNotification,
+ makeError,
+ getQID,
+ },
+ { middleware }
+) {
+ const apiSuccess = _.memoize((str) => `${str}_API_REQUEST_SUCCESS`);
+
+ const filterQueryParams = (queryParams) => {
+ return Object.keys(queryParams).reduce((acc, k) => {
+ if (!k.startsWith('filter_') && !k.startsWith('p_')) {
+ acc[k] = queryParams[k];
+ }
+ return acc;
+ }, {});
+ };
+
+ const saveNotification = middleware(
+ ({ trigger, next, dispatch, action, getState }) => {
+ next(action);
+
+ if (action.type === SAVE_NEW_NOTIFICATION) {
+ trigger('getCurrentQuery', (currentQuery) => {
+ if (currentQuery && currentQuery.toJSON) {
+ const queryParams = currentQuery.toJSON();
+
+ // if sort has 'score' then stateful is false
+ let stateful = true;
+ if (queryParams.sort && queryParams.sort[0].startsWith('score')) {
+ stateful = false;
+ }
+ dispatch({ type: SET_UPDATE_DATA, result: { stateful } });
+ dispatch(getQID(filterQueryParams(queryParams)));
+ } else {
+ dispatch(makeError('Current query not found'));
+ }
+ });
+ dispatch({ type: SET_UPDATE_DATA, result: action.result });
+ }
+
+ if (action.type === apiSuccess(GET_QID)) {
+ if (action.result && action.result.qid) {
+ const qid = action.result.qid;
+ const { updateData } = getState();
+
+ dispatch(addNotification({ ...updateData, qid }));
+ } else {
+ dispatch(makeError('No QID returned from the server'));
+ }
+ }
+ }
+ );
+
+ const parseScope = (requestType) => {
+ const [scope, status] = requestType.split('_API_REQUEST_');
+ return { scope, status };
+ };
+
+ const delay = (cb) => {
+ if (cb.toKey) {
+ window.clearTimeout(cb.toKey);
+ }
+ cb.toKey = setTimeout(cb, 3000);
+ };
+
+ const requestReset = middleware(({ dispatch, next, action }) => {
+ next(action);
+ if (/_API_REQUEST_(SUCCESS|FAILURE)$/.test(action.type)) {
+ const { scope } = parseScope(action.type);
+
+ delay(() => {
+ dispatch({ type: `${scope}_RESET` });
+ });
+ }
+ });
+
+ const errorReset = middleware(({ dispatch, next, action }) => {
+ next(action);
+ if (action.type === ERROR) {
+ delay(() => {
+ dispatch({ type: ERROR_RESET });
+ });
+ }
+ });
+
+ const loggedInStatus = middleware(({ trigger, dispatch, next, action }) => {
+ next(action);
+
+ if (action.type === CHECK_LOGIN_STATUS) {
+ trigger('isLoggedIn', (isLoggedIn) => {
+ dispatch(setLoginStatus(isLoggedIn));
+ });
+ }
+ });
+
+ return {
+ saveNotification,
+ requestReset,
+ errorReset,
+ loggedInStatus,
+ };
+});
diff --git a/src/js/react/MyAdsFreeform/reducer.js b/src/js/react/MyAdsFreeform/reducer.js
new file mode 100644
index 000000000..3cb8fa98f
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/reducer.js
@@ -0,0 +1,67 @@
+define(['redux', './actions'], function(
+ { combineReducers },
+ { SET_UPDATE_DATA, RESET, ERROR, ERROR_RESET, SET_LOGIN_STATUS }
+) {
+ const requestState = {
+ ADD_NOTIFICATION: { status: null, result: null, error: null },
+ GET_QID: { status: null, result: null, error: null },
+ };
+ const requests = (state = requestState, action) => {
+ if (/_API_REQUEST_/.test(action.type)) {
+ const [scope, status] = action.type.split('_API_REQUEST_');
+ const { result = null, error = null } = action;
+ return {
+ ...state,
+ [scope]: {
+ status: status.toLowerCase(),
+ result,
+ error,
+ },
+ };
+ } else if (/_RESET$/.test(action.type)) {
+ const scope = action.type.replace('_RESET', '');
+ return {
+ ...state,
+ [scope]: requestState[scope],
+ };
+ }
+ return state;
+ };
+
+ const updateDataState = null;
+ const updateData = (state = updateDataState, action) => {
+ if (action.type === SET_UPDATE_DATA) {
+ return { ...state, ...action.result };
+ }
+ if (action.type === RESET) {
+ return updateDataState;
+ }
+ return state;
+ };
+
+ const generalErrorState = null;
+ const generalError = (state = generalErrorState, action) => {
+ if (action.type === ERROR && action.result) {
+ return action.result;
+ }
+ if (action.type === RESET || action.type === ERROR_RESET) {
+ return generalErrorState;
+ }
+ return state;
+ };
+
+ const loggedInState = false;
+ const loggedIn = (state = loggedInState, action) => {
+ if (action.type === SET_LOGIN_STATUS) {
+ return action.result;
+ }
+ return state;
+ };
+
+ return combineReducers({
+ requests,
+ updateData,
+ generalError,
+ loggedIn,
+ });
+});
diff --git a/src/js/react/shared/components/ApiMessage.jsx.js b/src/js/react/shared/components/ApiMessage.jsx.js
new file mode 100644
index 000000000..c9943d6ca
--- /dev/null
+++ b/src/js/react/shared/components/ApiMessage.jsx.js
@@ -0,0 +1,41 @@
+define(['react', 'react-bootstrap', 'react-prop-types'], function(
+ React,
+ { Alert },
+ PropTypes
+) {
+ const TYPES = {
+ pending: 'warning',
+ error: 'danger',
+ success: 'success',
+ };
+
+ const ApiMessage = ({ request }) => {
+ const { status, error } = request;
+
+ if (!error) {
+ return null;
+ }
+
+ return (
+
+ {error && (
+
+
+ {' '}
+ Error:
+ {' '}
+ {error}
+
+ )}
+
+ );
+ };
+ ApiMessage.defaultProps = {
+ request: { status: null, result: null, error: null },
+ };
+ ApiMessage.propTypes = {
+ request: PropTypes.object,
+ };
+
+ return ApiMessage;
+});
diff --git a/src/js/react/shared/middleware/api.js b/src/js/react/shared/middleware/api.js
index df52f2678..ce687b3c9 100644
--- a/src/js/react/shared/middleware/api.js
+++ b/src/js/react/shared/middleware/api.js
@@ -16,9 +16,13 @@ define([], function() {
};
const fail = (error = defaultFail) => {
- let response = error.responseJSON;
- if (!response) {
- response = defaultFail.responseJSON;
+ const { responseJSON, statusText, status } = error;
+ let errorMsg = defaultFail.responseJSON.error;
+ if (responseJSON) {
+ errorMsg =
+ responseJSON.error || responseJSON.message || responseJSON.msg;
+ } else if (statusText) {
+ errorMsg = statusText;
}
const { error: err, ...result } = response;
dispatch({
@@ -28,7 +32,13 @@ define([], function() {
});
};
- const { target, query = {}, type = 'GET', data } = action.options;
+ const {
+ target,
+ query = {},
+ type = 'GET',
+ data,
+ headers,
+ } = action.options;
if (!target) {
return;
@@ -42,7 +52,11 @@ define([], function() {
done,
fail,
data: JSON.stringify(data),
- contentType: 'application/json',
+ headers: {
+ Accept: 'application/json; charset=utf-8',
+ 'Content-Type': 'application/json; charset=utf-8',
+ ...headers,
+ },
},
});
}
diff --git a/src/js/widgets/query_info/query_info_template.html b/src/js/widgets/query_info/query_info_template.html
index 3a5359730..91a17a3db 100644
--- a/src/js/widgets/query_info/query_info_template.html
+++ b/src/js/widgets/query_info/query_info_template.html
@@ -11,10 +11,26 @@
{{#if loggedIn}}
-
-
Add papers to library
+
+
+
+
+
-
+
+
+
{{#compare selected 0 operator=">"}}
add
@@ -38,7 +54,7 @@
{{/each}}
-
+
OR
@@ -54,7 +70,7 @@
value="{{newLibraryName}}"
style="max-width:166px;margin-bottom:10px;"
aria-label="enter new library name"/>
-
+
@@ -99,13 +115,10 @@
{{/if}}
{{/if}}
-
-
-
+
-
-
+
{{/if}}
diff --git a/src/js/widgets/query_info/query_info_widget.js b/src/js/widgets/query_info/query_info_widget.js
index d6adb3b6c..61e1bd796 100644
--- a/src/js/widgets/query_info/query_info_widget.js
+++ b/src/js/widgets/query_info/query_info_widget.js
@@ -38,6 +38,10 @@ define([
var QueryDisplayView = Marionette.ItemView.extend({
className: 'query-info-widget s-query-info-widget',
template: queryInfoTemplate,
+ initialize() {
+ this.onOpen = this.onOpen.bind(this);
+ this.onClose = this.onClose.bind(this);
+ },
serializeData: function() {
var data = this.model.toJSON();
@@ -50,6 +54,7 @@ define([
'change:loggedIn': 'render',
'change:libraries': 'render',
'change:feedback': 'render',
+ 'change:libraryDrawerOpen': 'render',
},
triggers: {
@@ -125,12 +130,28 @@ define([
);
},
+ onOpen() {
+ this.model.set('libraryDrawerOpen', true);
+ this.model.trigger('change:libraryDrawerOpen');
+ },
+
+ onClose() {
+ this.model.set('libraryDrawerOpen', false);
+ this.model.trigger('change:libraryDrawerOpen');
+ },
+
onRender: function() {
this.$('.icon-help').popover({
trigger: 'hover',
placement: 'right',
html: true,
});
+
+ this.$('#library-console')
+ .off('show.bs.collapse', this.onOpen)
+ .on('show.bs.collapse', this.onOpen)
+ .off('hide.bs.collapse', this.onClose)
+ .on('hide.bs.collapse', this.onClose);
},
});
@@ -269,8 +290,8 @@ define([
},
libraryCreateSubmit: function(data) {
- var options = {};
- var that = this;
+ var options = {},
+ that = this;
// are we adding the current query or just the selected bibcodes?
// if it's an abstract page widget, will have this._bibcode val
if (this.abstractPage) {
@@ -308,9 +329,9 @@ define([
},
clearFeedbackWithDelay: function() {
- var that = this;
- // ten seconds
- var timeout = 30000;
+ var that = this,
+ // ten seconds
+ timeout = 30000;
setTimeout(function() {
that.model.unset('feedback');
diff --git a/src/js/wraps/abstract_page_library_add/template.html b/src/js/wraps/abstract_page_library_add/template.html
index 2a9e7e594..d6fa8a7dc 100644
--- a/src/js/wraps/abstract_page_library_add/template.html
+++ b/src/js/wraps/abstract_page_library_add/template.html
@@ -1,70 +1,89 @@
{{#if loggedIn}}
-
-
Add paper to a library
+
+
+
+
+
+
-
+
+
+
- {{#if libraries}}
-
-
-
-
-
-
- OR
-
- {{/if}}
-
-
-
-
-
-
-
-
-
-
+ {{#if libraries}}
+
+
+
+
+
+
+ OR
+
+ {{/if}}
-
+
+
+
- {{#if feedback}}
+
- {{#if feedback.success}}
+
-
-
- {{#if feedback.create}}
-
Library {{feedback.name}} was successfully created and {{feedback.numRecords}} new record was added.
- {{else}}
- {{feedback.numRecords}} new record was added to
library {{feedback.name}}.
- {{#if feedback.numAlreadyInLib}}
- ({{feedback.numAlreadyInLib}} was already in the library).
- {{/if}}
- {{/if}}
-
+
+ {{#if feedback}}
+
+ {{#if feedback.success}}
+
+
+
+ {{#if feedback.create}}
+
Library {{feedback.name}} was successfully created and {{feedback.numRecords}} new record was added.
+ {{else}}
+ {{feedback.numRecords}} new record was added to
library {{feedback.name}}.
+ {{#if feedback.numAlreadyInLib}}
+ ({{feedback.numAlreadyInLib}} was already in the library).
+ {{/if}}
+ {{/if}}
+
+
+ {{else}}
+
+
+
+ {{#if feedback.create}}
+ {{feedback.name}} was not created, error occured.
+ {{feedback.error}}
+ {{else}}
+ Record was not added to {{feedback.name}}, error occurred.
+ {{feedback.error}}
+ {{/if}}
+
+
+ {{/if}}
- {{else}}
-
-
-
- {{#if feedback.create}}
- {{feedback.name}} was not created, error occured.
- {{feedback.error}}
- {{else}}
- Record was not added to {{feedback.name}}, error occurred.
- {{feedback.error}}
- {{/if}}
+ {{/if}}
+
+
- {{/if}}
- {{/if}}
diff --git a/src/js/wraps/user_settings_page_manager/user-settings-layout.html b/src/js/wraps/user_settings_page_manager/user-settings-layout.html
index 1fe336929..4832b6eac 100644
--- a/src/js/wraps/user_settings_page_manager/user-settings-layout.html
+++ b/src/js/wraps/user_settings_page_manager/user-settings-layout.html
@@ -12,11 +12,14 @@
diff --git a/src/js/wraps/user_settings_page_manager/user_nav.html b/src/js/wraps/user_settings_page_manager/user_nav.html
index 2020ab19d..a8f680467 100644
--- a/src/js/wraps/user_settings_page_manager/user_nav.html
+++ b/src/js/wraps/user_settings_page_manager/user_nav.html
@@ -1,56 +1,53 @@
+
diff --git a/src/js/wraps/user_settings_page_manager/user_page_manager.js b/src/js/wraps/user_settings_page_manager/user_page_manager.js
index 70dbed6ab..ea4e9c661 100644
--- a/src/js/wraps/user_settings_page_manager/user_page_manager.js
+++ b/src/js/wraps/user_settings_page_manager/user_page_manager.js
@@ -69,6 +69,11 @@ define([
path: 'user/settings/delete',
category: 'settings',
},
+ MyAdsDashboard: {
+ title: 'myADS',
+ path: 'user/settings/myads',
+ category: 'notifications',
+ },
},
});
return PageManager;
diff --git a/src/styles/img/how_to_add_general_query.gif b/src/styles/img/how_to_add_general_query.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2e11928551911b4d1166edae3d7506e21fc4fcf6
GIT binary patch
literal 4077163
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