diff --git a/.eslintrc.json b/.eslintrc.json
index a1de8acf0..8ed908804 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -3,7 +3,7 @@
"env": {
"browser": true,
"amd": true,
- "es6": false,
+ "es6": true,
"commonjs": false,
"jquery": true,
"mocha": true
diff --git a/.gitignore b/.gitignore
index 0c080ba78..c0c43c7a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ grunt/requirejs.js
package-lock.json
!.nvmrc
!.prettierrc
+!.gitmodules
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..6e7a9ce3f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src/shared"]
+ path = src/shared
+ url = git@github.com:adsabs/ui-component-library.git
diff --git a/.travis.yml b/.travis.yml
index 487533240..95002b426 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,8 @@
language: node_js
-node_js: "11.14.0"
+node_js: '11.14.0'
cache: npm
+git:
+ submodules: false
before_install: npm install -g grunt-cli
install: npm install
deployg: grunt release
@@ -10,5 +12,5 @@ script:
#- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then echo "grunt sauce"; grunt sauce; fi
env:
global:
- - secure: "QiHFURctWLbr3uGEwqgABdKBzzXHHD819h4XTQsJ90Lcv04DsSzXZV0GAHa1bXiNIrNwxZD7T7fsakOBViEjIsginD1ZFAUMkR5KHYII3Gzlpu1ikuIHBrrPRLzTFhrsnJxFDRau9gwLeIoTgIP5AC3bw8ne/+6Oe6mW2fpphgU="
- - secure: "SRFYya2d43gzs0WWmYhIqekI1gUoxqeox/hlc7vn6dEWxVNFsHA0oWkMSivSY8y9fqvUyh4p40e4NDlN86EnGF9aq/r4iNBMlIR0UVuxjVBi8HaMR5w3/JvlD7gNI7Y6du0cpkJaVAekJbi21HP1bmcV0sYXcXUj6uL4HUVq4sw="
+ - secure: 'QiHFURctWLbr3uGEwqgABdKBzzXHHD819h4XTQsJ90Lcv04DsSzXZV0GAHa1bXiNIrNwxZD7T7fsakOBViEjIsginD1ZFAUMkR5KHYII3Gzlpu1ikuIHBrrPRLzTFhrsnJxFDRau9gwLeIoTgIP5AC3bw8ne/+6Oe6mW2fpphgU='
+ - secure: 'SRFYya2d43gzs0WWmYhIqekI1gUoxqeox/hlc7vn6dEWxVNFsHA0oWkMSivSY8y9fqvUyh4p40e4NDlN86EnGF9aq/r4iNBMlIR0UVuxjVBi8HaMR5w3/JvlD7gNI7Y6du0cpkJaVAekJbi21HP1bmcV0sYXcXUj6uL4HUVq4sw='
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/scripts/templates/react-widget/index.js b/scripts/templates/react-widget/index.js
index c4185d9e1..75a42667e 100644
--- a/scripts/templates/react-widget/index.js
+++ b/scripts/templates/react-widget/index.js
@@ -7,7 +7,7 @@ define([
'./middleware',
'./reducer',
'js/react/shared/helpers',
- 'js/react/shared/middleware/api',
+ 'js/react/shared/middleware/index',
], function(
App,
WithBackboneView,
@@ -22,14 +22,10 @@ define([
const mapStateToProps = ({}) => ({});
const {} = actions;
const actionCreators = {};
+ const middlewares = [middleware, ...sharedMiddleware];
return WithBackboneView(
connect(mapStateToProps, actionCreators)(App),
- (context) =>
- configureStore(
- context,
- reducer,
- withContext(middleware, sharedMiddleware)
- )
+ (context) => configureStore(context, reducer, withContext(...middlewares))
);
});
diff --git a/src/config/discovery.config.js b/src/config/discovery.config.js
index e153bff67..4e3a14d5b 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
@@ -129,7 +130,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',
@@ -142,6 +143,12 @@ require.config({
LibraryActionsWidget: 'es6!js/widgets/library_actions/widget.jsx',
AllLibrariesWidget: 'js/widgets/libraries_all/widget',
LibraryListWidget: 'js/widgets/library_list/widget',
+
+ // react widgets
+
+ MyAdsFreeform: 'reactify!js/react/BumblebeeWidget?MyAdsFreeform',
+ MyAdsDashboard: 'reactify!js/react/BumblebeeWidget?MyAdsDashboard',
+ RecommenderWidget: 'reactify!js/react/BumblebeeWidget?Recommender',
},
plugins: {},
},
@@ -174,7 +181,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/main.js b/src/js/apps/discovery/main.js
index 3c4be6224..8edcbb6f9 100644
--- a/src/js/apps/discovery/main.js
+++ b/src/js/apps/discovery/main.js
@@ -131,6 +131,7 @@ define(['config/discovery.config', 'module'], function(config, module) {
// accessibility: skip to main content
$('body').on('click', '#skip-to-main-content', function() {
+ $('#main-content').focus();
return false;
});
diff --git a/src/js/apps/discovery/navigator.js b/src/js/apps/discovery/navigator.js
index 4e016e8d5..fe9731ac3 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',
@@ -123,7 +124,7 @@ define([
var defer = $.Deferred();
app
.getObject('MasterPageManager')
- .show('LandingPage', ['SearchWidget'])
+ .show('LandingPage', ['SearchWidget', 'RecommenderWidget'])
.then(function() {
return app.getWidget('LandingPage').then(function(widget) {
if (data && data.origin === 'SearchWidget') {
@@ -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..75a1bbfef 100644
--- a/src/js/components/api_targets.js
+++ b/src/js/components/api_targets.js
@@ -5,7 +5,7 @@
* and any related limits
*/
-define(['underscore', 'backbone'], function(_, Backbone) {
+define([], function() {
var config = {
BOOTSTRAP: '/accounts/bootstrap',
SEARCH: 'search/query',
@@ -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/mixins/discovery_bootstrap.js b/src/js/mixins/discovery_bootstrap.js
index 07f744b59..02f17bb61 100644
--- a/src/js/mixins/discovery_bootstrap.js
+++ b/src/js/mixins/discovery_bootstrap.js
@@ -325,24 +325,6 @@ define([
// apply a global link handler for push state (after router is started)
startGlobalHandler();
-
- $(document).on('scroll', function() {
- if ($('#landing-page-layout').length > 0) {
- return;
- }
- // navbar is currently 40 px height
- if ($(window).scrollTop() > 50) {
- $('.s-quick-add').addClass('hidden');
- $('.s-search-bar-full-width-container').addClass(
- 's-search-bar-motion'
- );
- } else {
- $('.s-search-bar-full-width-container').removeClass(
- 's-search-bar-motion'
- );
- $('.s-quick-add').removeClass('hidden');
- }
- });
defer.resolve();
});
diff --git a/src/js/page_managers/master.js b/src/js/page_managers/master.js
index 0de13266f..b48379f7b 100644
--- a/src/js/page_managers/master.js
+++ b/src/js/page_managers/master.js
@@ -93,8 +93,7 @@ define([
model.attributes.numAttach += 1;
// scroll to top
- document.body.scrollTop = document.documentElement.scrollTop = 0;
- $('#app-container').scrollTop(0);
+ $(document.documentElement).scrollTop(0);
// and fix the search bar back in its default spot
$('.s-search-bar-full-width-container').removeClass(
's-search-bar-motion'
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 b5e866342..8c92bcecf 100644
--- a/src/js/react/BumblebeeWidget.js
+++ b/src/js/react/BumblebeeWidget.js
@@ -29,7 +29,21 @@ define([
};
const BumblebeeWidget = BaseWidget.extend({
- initialize({ componentId, initialData }) {
+ /**
+ * @override
+ */
+ getBeeHive() {
+ return getBeeHive();
+ },
+
+ /**
+ * @override
+ */
+ hasBeeHive() {
+ return true;
+ },
+
+ initialize({ componentId }) {
this.view.on({
sendRequest: _.bind(this.onSendRequest, this),
subscribeToPubSub: _.bind(this.subscribeToPubSub, this),
@@ -37,7 +51,6 @@ define([
doSearch: _.bind(this.doSearch, this),
getCurrentQuery: _.bind(this.onGetCurrentQuery, this),
isLoggedIn: _.bind(this.isLoggedIn, this),
- getInitialData: _.bind(this.getInitialData, this),
analyticsEvent: _.bind(this.analyticsEvent, this),
});
@@ -46,29 +59,41 @@ define([
this.view.destroy().render();
}
});
- this.initialData = initialData;
this.activate();
this.onSendRequest = _.debounce(this.onSendRequest, 1000, {
leading: true,
trailing: false,
});
+
+ if (this.view._store) {
+ this._store = this.view._store;
+ }
},
- getInitialData(cb) {
- if (typeof cb === 'function') {
- cb(this.initialData);
+ dispatch({ type, ...args }) {
+ if (this._store && typeof this._store.dispatch === 'function') {
+ this._store.dispatch({ type, ...args });
}
},
+ getState() {
+ return this._store.getState();
+ },
activate() {
const ps = getPubSub();
subscribe(ps.USER_ANNOUNCEMENT, this.handleUserAnnouncement.bind(this));
- },
- handleUserAnnouncement(event, data) {
- const user = getBeeHive().getObject('User');
- if (event == user.USER_SIGNED_IN) {
- } else if (event == user.USER_SIGNED_OUT) {
+
+ const user = this.getBeeHive().getObject('User');
+ if (user && typeof user.getUserName === 'function') {
+ this.dispatch({
+ type: 'USER_ANNOUNCEMENT/user_signed_in',
+ payload: user.getUserName(),
+ });
}
},
+ handleUserAnnouncement(event, payload) {
+ const type = `USER_ANNOUNCEMENT/${event}`;
+ this.dispatch({ type, payload });
+ },
isLoggedIn(cb) {
const user = this.getBeeHive().getObject('User');
if (typeof cb === 'function') {
@@ -88,9 +113,11 @@ define([
},
doSearch(queryParams) {
const query = new ApiQuery();
- _.isString(queryParams)
- ? query.load(queryParams)
- : query.set({ ...queryParams });
+ if (_.isString(queryParams)) {
+ query.load(queryParams);
+ } else {
+ query.set({ ...queryParams });
+ }
this.publishToPubSub('NAVIGATE', 'search-page', {
q: query,
});
@@ -100,8 +127,17 @@ define([
const request = new ApiRequest({
target,
query: new ApiQuery(query),
- options,
});
+ request.set('options', {
+ ...options,
+ contentType:
+ target === 'search/query'
+ ? 'application/x-www-form-urlencoded'
+ : options.contentType,
+ data:
+ target === 'search/query' ? request.get('query').url() : options.data,
+ });
+
publish(ps.EXECUTE_REQUEST, request);
},
analyticsEvent(...args) {
diff --git a/src/js/react/MyAdsDashboard/actions.js b/src/js/react/MyAdsDashboard/actions.js
new file mode 100644
index 000000000..ddfa0fcea
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/actions.js
@@ -0,0 +1,144 @@
+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',
+ SET_NOTIFICATION_QUERY_KEY: 'SET_NOTIFICATION_QUERY_KEY',
+
+ // 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',
+ SET_NOTIFICATION_QUERY: 'SET_NOTIFICATION_QUERY',
+ GET_NOTIFICATION_QUERIES: 'GET_NOTIFICATION_QUERIES',
+
+ // paging
+ GOTO: 'GOTO',
+
+ // imports
+ IMPORT_NOTIFICATIONS: 'IMPORT_NOTIFICATIONS',
+
+ // searching
+ GET_QUERY_FROM_QID: 'GET_QUERY_FROM_QID',
+ 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 }),
+ getNotificationQueries: (id) => ({
+ type: 'API_REQUEST',
+ scope: actions.GET_NOTIFICATION_QUERIES,
+ options: {
+ type: 'GET',
+ target: `vault/notification_query/${id}`,
+ },
+ }),
+ getQueryFromQID: (qid) => ({
+ type: 'API_REQUEST',
+ scope: 'GET_QUERY_FROM_QID',
+ options: {
+ type: 'GET',
+ target: `vault/query/${qid}`,
+ },
+ }),
+ runQuery: (id, queryKey) => ({
+ type: 'RUN_QUERY',
+ payload: {
+ id,
+ queryKey,
+ },
+ }),
+ };
+
+ return {
+ ...actions,
+ ...actionCreators,
+ };
+});
diff --git a/src/js/react/MyAdsDashboard/components/ActionsDropdown.jsx.js b/src/js/react/MyAdsDashboard/components/ActionsDropdown.jsx.js
new file mode 100644
index 000000000..908aa8199
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ActionsDropdown.jsx.js
@@ -0,0 +1,111 @@
+/* eslint-disable no-script-url */
+define(['react', 'react-bootstrap', 'react-prop-types'], function(
+ React,
+ { Dropdown, MenuItem, Label },
+ PropTypes
+) {
+ const renderRunButtons = (item, onSelect) => {
+ let labels = [];
+ if (item.type === 'template') {
+ if (item.template === 'arxiv') {
+ if (item.data === null) {
+ labels = ['Search'];
+ } else {
+ labels = [
+ 'Keyword Matches - Recent Papers',
+ 'Other Recent Papers in Selected Categories',
+ ];
+ }
+ } else if (item.template === 'keyword' && item.data !== null) {
+ labels = ['Recent Papers', 'Most Popular', 'Most Cited'];
+ } else {
+ labels = ['Search'];
+ }
+ } else {
+ labels = ['Search'];
+ }
+ return labels.map((l, i) => (
+
+ {l}
+
+ ));
+ };
+
+ const ActionsDropdown = ({
+ onToggleActive,
+ onRunQuery,
+ onEdit,
+ onDelete,
+ item,
+ disable,
+ dropup,
+ }) => {
+ return (
+
+
+ Actions
+
+
+ View in search results page
+ {renderRunButtons(item, (queryKey) => onRunQuery(item, queryKey))}
+
+ Actions
+ onToggleActive(item)}
+ >
+
+
+ ENABLED
+
+
+ DISABLED
+
+
+
+ onEdit(item)}>
+ Edit
+
+ onDelete(item)}>
+ Delete
+
+
+
+ );
+ };
+
+ ActionsDropdown.defaultProps = {
+ disable: false,
+ dropup: false,
+ onDelete: () => {},
+ onEdit: () => {},
+ onRunQuery: () => {},
+ onToggleActive: () => {},
+ };
+
+ ActionsDropdown.propTypes = {
+ disable: PropTypes.bool,
+ item: PropTypes.shape({
+ active: PropTypes.bool,
+ id: PropTypes.number,
+ type: PropTypes.string,
+ }).isRequired,
+ onDelete: PropTypes.func,
+ onEdit: PropTypes.func,
+ onRunQuery: PropTypes.func,
+ onToggleActive: PropTypes.func,
+ dropup: PropTypes.bool,
+ };
+
+ return ActionsDropdown;
+});
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 (
+
+ Go Back
+
+ );
+ };
+
+ 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..b93e5e274
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ArxivClassList.jsx.js
@@ -0,0 +1,347 @@
+/* eslint-disable max-classes-per-file */
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ '../models/arxivClasses',
+], function(
+ _,
+ React,
+ { Checkbox, ListGroup, ListGroupItem },
+ 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) => (
+
+ ))}
+
+
+ );
+ }
+ }
+
+ 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..ee3630465
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ArxivForm.jsx.js
@@ -0,0 +1,274 @@
+define([
+ 'react',
+ 'react-bootstrap',
+ 'es6!./ArxivClassList.jsx',
+ 'react-prop-types',
+], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock },
+ ArxivClassList,
+ PropTypes
+) {
+ const getStatusMessage = ({ status, error, editing }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error}) ;
+ case 'success':
+ return (
+
+ Notification {editing ? 'saved' : 'created'}!
+
+ );
+ default:
+ return null;
+ }
+ };
+ getStatusMessage.defaultProps = {
+ status: '',
+ error: '',
+ editing: false,
+ };
+
+ getStatusMessage.propTypes = {
+ status: PropTypes.string,
+ error: PropTypes.string,
+ editing: PropTypes.bool,
+ };
+
+ class ArxivForm extends React.Component {
+ constructor(props) {
+ super(props);
+
+ let updatedState = {};
+ const { editingNotification } = this.props;
+ if (editingNotification) {
+ updatedState = {
+ groups: editingNotification.classes,
+ keywords: editingNotification.data,
+ name: editingNotification.name,
+ editing: true,
+ };
+ }
+
+ this.state = {
+ groups: [],
+ keywords: '',
+ name: '',
+ message: null,
+ editing: false,
+ pending: false,
+ ...updatedState,
+ };
+ this.onClassSelection = this.onClassSelection.bind(this);
+ }
+
+ // eslint-disable-next-line camelcase
+ UNSAFE_componentWillReceiveProps(next) {
+ const addStatus = next.addNotificationRequest.status;
+ const updateStatus = next.updateNotificationRequest.status;
+ const { onSuccess } = this.props;
+
+ // fires success handler if our request was successful
+ if (addStatus === 'success' || updateStatus === 'success') {
+ setTimeout(() => 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 });
+ }
+ }
+
+ onClassSelection(groups) {
+ this.setState({
+ groups,
+ });
+ }
+
+ onSubmit(e) {
+ e.preventDefault();
+ const { keywords, groups, name, pending, editing } = this.state;
+ const {
+ updateNotification,
+ editingNotification,
+ addNotification,
+ } = this.props;
+
+ if (pending) {
+ return;
+ }
+
+ if (groups.length <= 0) {
+ this.showMessage('must select at least one group');
+ } else {
+ const payload = {
+ data: keywords,
+ classes: groups,
+ name: name,
+ };
+
+ if (editing) {
+ updateNotification({
+ ...editingNotification,
+ ...payload,
+ });
+ } else {
+ addNotification(payload);
+ }
+ }
+ }
+
+ showMessage(message) {
+ this.setState({ message }, () => {
+ setTimeout(() => this.setState({ message: null }), 3000);
+ });
+ }
+
+ render() {
+ const { editing, keywords, name, groups, message } = this.state;
+ const {
+ onCancel,
+ updateNotificationRequest,
+ addNotificationRequest,
+ } = this.props;
+
+ return (
+
+ );
+ }
+ }
+
+ ArxivForm.defaultProps = {
+ addNotification: () => {},
+ addNotificationRequest: PropTypes.shape({
+ status: null,
+ result: null,
+ error: null,
+ }),
+ editingNotification: () => {},
+ onCancel: () => {},
+ onSuccess: () => {},
+ updateNotification: () => {},
+ updateNotificationRequest: PropTypes.shape({
+ status: null,
+ result: null,
+ error: null,
+ }),
+ };
+
+ ArxivForm.propTypes = {
+ addNotification: PropTypes.func,
+ addNotificationRequest: PropTypes.shape({
+ status: PropTypes.string,
+ result: PropTypes.string,
+ error: PropTypes.string,
+ }),
+ editingNotification: PropTypes.func,
+ onCancel: PropTypes.func,
+ onSuccess: PropTypes.func,
+ updateNotification: PropTypes.func,
+ updateNotificationRequest: PropTypes.shape({
+ status: PropTypes.string,
+ result: PropTypes.string,
+ error: PropTypes.string,
+ }),
+ };
+
+ 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..993b8d853
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/AuthorsForm.jsx.js
@@ -0,0 +1,229 @@
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ 'es6!./CitationsEntry.jsx',
+ 'js/react/shared/helpers',
+ 'react-prop-types',
+], function(
+ _,
+ React,
+ { Form, Alert, FormGroup, FormControl, ControlLabel, HelpBlock },
+ CitationsEntry,
+ { escape, unescape },
+ PropTypes
+) {
+ const getStatusMessage = ({ status, error, editing }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error}) ;
+ case 'success':
+ return (
+
+ Notification {editing ? 'saved' : 'created'}!
+
+ );
+ default:
+ return null;
+ }
+ };
+ getStatusMessage.defaultProps = {
+ status: '',
+ error: '',
+ editing: false,
+ };
+
+ getStatusMessage.propTypes = {
+ status: PropTypes.string,
+ error: PropTypes.string,
+ editing: PropTypes.bool,
+ };
+
+ 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, type, text] = /^(author|orcid):"(.*)"$/.exec(str);
+ return {
+ type: type === 'author' ? 'Name' : 'ORCiD',
+ text,
+ };
+ });
+ }
+ return { entries };
+ } catch (e) {
+ return { editing: false };
+ }
+ }
+
+ entriesUpdated(entries) {
+ this.setState({ entries });
+ }
+
+ createQueryString() {
+ const { entries } = this.state;
+ return entries
+ .map(({ type, text }) => {
+ return `${type === 'Name' ? 'author' : 'orcid'}:"${text}"`;
+ })
+ .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..6e733d501
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/CitationsEntry.jsx.js
@@ -0,0 +1,266 @@
+define(['underscore', 'react', 'react-bootstrap', 'react-prop-types'], function(
+ { uniqueId },
+ React,
+ {
+ FormControl,
+ Button,
+ FormGroup,
+ Tooltip,
+ OverlayTrigger,
+ Alert,
+ InputGroup,
+ },
+ PropTypes
+) {
+ const initialState = {
+ text: '',
+ type: '',
+ valid: true,
+ error: '',
+ entries: [],
+ };
+ class CitationsEntry extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ ...initialState,
+ ...props.initialState,
+ };
+ this.inputRef = null;
+ }
+
+ removeEntry(id) {
+ const { entries } = this.state;
+ const idx = entries.findIndex((entry) => entry.id === id);
+ if (idx >= 0) {
+ this.setState(
+ {
+ entries: [...entries.slice(0, idx), ...entries.slice(idx + 1)],
+ },
+ () => {
+ const { entries } = this.state;
+ const { entriesUpdated } = this.props;
+ entriesUpdated(entries);
+ }
+ );
+ }
+ }
+
+ addEntry() {
+ const { type, text, entries } = this.state;
+
+ // check if entry is valid first
+ if (this.isValid()) {
+ const formattedText =
+ type === 'ORCiD'
+ ? text
+ .replace(/-/g, '')
+ .match(/\d{4}/g)
+ .join('-')
+ : text;
+
+ this.setState(
+ {
+ entries: [
+ ...entries,
+ {
+ id: uniqueId(),
+ text: formattedText,
+ type: type,
+ },
+ ],
+ text: '',
+ type: '',
+ },
+ () => {
+ // refocus on input, call updated callback
+ const { entries } = this.state;
+ const { entriesUpdated } = this.props;
+ this.inputRef.focus();
+ entriesUpdated(entries);
+ }
+ );
+ }
+ }
+
+ /**
+ * Detects the type of entry from the text the user typed in
+ */
+ detectType() {
+ const { text } = this.state;
+ let type = '';
+ if (text.match(/^\d/)) {
+ type = 'ORCiD';
+ } else if (text.length > 0) {
+ type = 'Name';
+ }
+ this.setState({
+ type,
+ });
+ }
+
+ isValid() {
+ const { text, type, entries } = this.state;
+
+ let valid = true;
+ let error = '';
+
+ if (type === 'ORCiD' && !text.match(/^\d{4}-?\d{4}-?\d{4}-?\d{4}$/)) {
+ // orcid formatting is off
+ valid = false;
+ error = 'ORCiD must in the format: 9999-9999-9999-9999';
+ } else if (entries.some((e) => e.text === text)) {
+ // there are duplicate(s)
+ valid = false;
+ error = 'Already in the list!';
+ } else if (text.length === 0) {
+ valid = false;
+ }
+
+ this.setState({ valid, error });
+ return valid;
+ }
+
+ render() {
+ const { entries, valid, type, text, error } = this.state;
+
+ return (
+
+
+
+
+ Author (name or ORCiD)
+ Type
+ Action
+
+
+
+ {entries.length <= 0 && (
+
+
+
+ No entries! Add a new entry below
+
+
+
+ )}
+ {entries.map(({ text, type, id }) => (
+ this.removeEntry(id)}
+ />
+ ))}
+
+
+
+
+ );
+ }
+ }
+
+ CitationsEntry.propTypes = {
+ entriesUpdated: () => {},
+ initialState: PropTypes.shape({
+ entries: PropTypes.array,
+ }),
+ };
+
+ CitationsEntry.defaultProps = {
+ initialState: {},
+ entriesUpdated: [],
+ };
+
+ const getWarning = (text, type) => {
+ if (type === 'Name' && !text.match(/^[^,]*,[^,]*$/)) {
+ return "Author names should be formatted as 'Last, First M'";
+ }
+ return null;
+ };
+
+ const Entry = ({ text, type, onRemove }) => {
+ const warning = getWarning(text, type);
+
+ return (
+
+
+ {text}
+ {warning && (
+ {warning}}
+ >
+
+
+
+
+ )}
+
+ {type}
+
+
+ Remove
+
+
+
+ );
+ };
+
+ Entry.propTypes = {
+ onRemove: PropTypes.func,
+ text: PropTypes.string,
+ type: PropTypes.string,
+ };
+
+ Entry.defaultProps = {
+ text: '',
+ type: '',
+ onRemove: () => {},
+ };
+
+ 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..4527a9935
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/CitationsForm.jsx.js
@@ -0,0 +1,227 @@
+define([
+ 'react',
+ 'react-bootstrap',
+ 'react-prop-types',
+ 'es6!./CitationsEntry.jsx',
+ 'js/react/shared/helpers',
+], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock },
+ PropTypes,
+ CitationsEntry,
+ { escape, unescape }
+) {
+ const getStatusMessage = ({ status, error, editing }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error}) ;
+ case 'success':
+ return (
+
+ Notification {editing ? 'saved' : 'created'}!
+
+ );
+ default:
+ return null;
+ }
+ };
+ getStatusMessage.defaultProps = {
+ status: '',
+ error: '',
+ editing: false,
+ };
+
+ getStatusMessage.propTypes = {
+ status: PropTypes.string,
+ error: PropTypes.string,
+ editing: PropTypes.bool,
+ };
+
+ 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, type, text] = /^(author|orcid):"(.*)"$/.exec(str);
+ return {
+ type: type === 'author' ? 'Name' : 'ORCiD',
+ text,
+ };
+ });
+ }
+ return { entries };
+ } catch (e) {
+ return { editing: false };
+ }
+ }
+
+ entriesUpdated(entries) {
+ this.setState({ entries });
+ }
+
+ createQueryString() {
+ const { entries } = this.state;
+ return entries
+ .map(({ type, text }) => {
+ return `${type === 'Name' ? 'author' : 'orcid'}:"${text}"`;
+ })
+ .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..8a4bece7a
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/Dashboard.jsx.js
@@ -0,0 +1,460 @@
+define([
+ 'underscore',
+ 'react',
+ 'es6!./TemplatePill.jsx',
+ 'moment',
+ 'es6!./ActionsDropdown.jsx',
+ 'react-prop-types',
+], function(_, React, TemplatePill, moment, ActionsDropdown, PropTypes) {
+ const getFriendlyDateString = (dateStr) => {
+ return moment(dateStr).format('lll');
+ };
+
+ const SortableHeader = ({ children, onClick, direction, active }) => {
+ if (!active) {
+ return (
+ onClick('asc')}>
+ {children}
+
+ );
+ }
+ const caret = direction === 'desc' ? 'down' : 'up';
+ return (
+ onClick(direction === 'desc' ? 'asc' : 'desc')}
+ >
+ {children}
+
+ );
+ };
+
+ SortableHeader.defaultProps = {
+ active: false,
+ children: null,
+ direction: PropTypes.string,
+ onClick: PropTypes.func,
+ };
+
+ SortableHeader.propTypes = {
+ active: PropTypes.bool,
+ children: PropTypes.node,
+ direction: PropTypes.string,
+ onClick: PropTypes.func,
+ };
+
+ /**
+ * @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,
+ loadingQuery: false,
+ };
+ this.onFilter = _.debounce(this.onFilter, 100);
+ this.onRunQuery = this.onRunQuery.bind(this);
+ this.onToggleActive = this.onToggleActive.bind(this);
+ this.onDelete = this.onDelete.bind(this);
+ this.onEdit = this.onEdit.bind(this);
+ }
+
+ componentDidMount() {
+ const { notifications, getNotifications } = this.props;
+ if (Object.keys(notifications).length === 0) {
+ getNotifications();
+ }
+ }
+
+ onLeaveItem() {
+ requestAnimationFrame(() => this.setState({ activeItem: null }));
+ }
+
+ /**
+ * @param {string} id
+ */
+ onEnterItem(id) {
+ requestAnimationFrame(() => this.setState({ activeItem: id }));
+ }
+
+ /**
+ * @param {Notification} item
+ */
+ onEdit(item) {
+ const { editNotification } = this.props;
+ editNotification(item.id);
+ }
+
+ /**
+ * @param {Notification} item
+ */
+ onDelete(item) {
+ const { removeNotification } = this.props;
+ // eslint-disable-next-line no-alert
+ if (window.confirm('Are you sure?')) {
+ removeNotification(item.id);
+ }
+ }
+
+ onCreateNewNotification() {
+ const { createNewNotification } = this.props;
+ createNewNotification();
+ }
+
+ onImportNotifications() {
+ const { importNotifications } = this.props;
+ importNotifications();
+ }
+
+ /**
+ *
+ * @param {Notification} item
+ */
+ onToggleActive(item) {
+ const { toggleActive } = this.props;
+ toggleActive(item.id);
+ }
+
+ 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 }, queryKey) {
+ const { runQuery } = this.props;
+ runQuery(id, queryKey);
+ this.setState({ loadingQuery: true });
+ }
+
+ render() {
+ const {
+ notifications,
+ getNotificationsRequest: getRequest,
+ updateNotificationRequest: updateRequest,
+ removeNotificationRequest: removeRequest,
+ } = this.props;
+
+ const {
+ filterText,
+ sortCol,
+ sortDir,
+ searchValue,
+ activeItem,
+ loadingQuery,
+ } = this.state;
+
+ let ids = Object.keys(notifications);
+ if (filterText && filterText.length > 0) {
+ const regx = new RegExp('.*' + filterText + '.*', 'ig');
+ ids = Object.keys(notifications).filter((k) => {
+ if (!filterText) {
+ return true;
+ }
+
+ return _.values(notifications[k])
+ .join(' ')
+ .match(regx);
+ });
+ }
+
+ if (sortCol && sortDir) {
+ ids = ids.sort((a, b) => {
+ const { [a]: left, [b]: right } = notifications;
+ const prop = sortCol === '#' ? 'id' : sortCol;
+ const dir = 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 disable =
+ removeRequest.status === 'pending' ||
+ updateRequest.status === 'pending' ||
+ getRequest.status === 'pending';
+
+ if (
+ (ids.length === 0 && getRequest.status === 'pending') ||
+ loadingQuery
+ ) {
+ return getRequest.status === 'pending' || loadingQuery ? (
+
+
+ {' '}
+ Loading...
+
+
+ ) : (
+
+
Error: {getRequest.error}
+
+ );
+ }
+
+ return (
+
+
+
+
+ Email Notifications
+
+
+
+ this.onSearch(e.target.value)}
+ />
+
+
+
+
+ this.onCreateNewNotification()}
+ title="create new notification"
+ disabled={disable}
+ >
+ Create
+
+ this.onImportNotifications()}
+ title="import notification"
+ disabled={disable}
+ >
+ Import
+
+
+
+
+
+
+ this.onCreateNewNotification()}
+ title="create new notification"
+ disabled={disable}
+ >
+ Create
+
+
+
+ this.onImportNotifications()}
+ title="import notification"
+ disabled={disable}
+ >
+ Import
+
+
+
+
+
+
+
+
+
+
+ #
+
+
+ Name
+
+
+ Type
+
+
+ Frequency
+
+
+
+ Updated
+
+ Actions
+
+
+
+ {ids.map((id, i) => {
+ /** @type {Notification} */
+ const item = notifications[id];
+
+ return (
+
+
+ {i + 1}
+
+
+
+ {item.name}
+
+
+
+
+
+
+ {item.frequency}
+
+
+ {getFriendlyDateString(item.updated)}
+
+
+ 2}
+ />
+
+
+ );
+ })}
+
+
+
+ {ids.length === 0 && searchValue && (
+
Your search is not matching any notifications.
+ )}
+ {ids.length === 0 && !searchValue && (
+
You don't have any notifications yet!
+ )}
+
+ );
+ }
+ }
+
+ MyAdsDashboard.defaultProps = {
+ createNewNotification: () => {},
+ editNotification: () => {},
+ getNotifications: () => {},
+ getRequest: () => {},
+ importNotifications: () => {},
+ notifications: [],
+ removeNotification: () => {},
+ removeRequest: () => {},
+ runQuery: () => {},
+ toggleActive: () => {},
+ updateRequest: () => {},
+ getNotificationsRequest: () => {},
+ updateNotificationRequest: () => {},
+ removeNotificationRequest: () => {},
+ };
+
+ MyAdsDashboard.propTypes = {
+ createNewNotification: PropTypes.func,
+ editNotification: PropTypes.func,
+ getNotifications: PropTypes.func,
+ getRequest: PropTypes.func,
+ importNotifications: PropTypes.func,
+ notifications: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ type: PropTypes.string,
+ frequency: PropTypes.string,
+ updated: PropTypes.string,
+ active: PropTypes.bool,
+ template: PropTypes.string,
+ })
+ ),
+ removeNotification: PropTypes.func,
+ removeRequest: PropTypes.func,
+ runQuery: PropTypes.func,
+ toggleActive: PropTypes.func,
+ updateRequest: PropTypes.func,
+ getNotificationsRequest: PropTypes.func,
+ updateNotificationRequest: PropTypes.func,
+ removeNotificationRequest: PropTypes.func,
+ };
+
+ 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..5f9da8948
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/GeneralForm.jsx.js
@@ -0,0 +1,274 @@
+define([
+ 'react',
+ 'react-bootstrap',
+ 'js/react/shared/helpers',
+ 'react-prop-types',
+], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, Checkbox, Radio, Button },
+ { isEmpty },
+ PropTypes
+) {
+ const getStatusMessage = ({ status, error, editing, noSuccess }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error}) ;
+ case 'success':
+ return (
+
+ {noSuccess ? '' : `Notification ${editing ? 'saved' : 'created'}!`}
+
+ );
+ default:
+ return null;
+ }
+ };
+ getStatusMessage.defaultProps = {
+ status: '',
+ error: '',
+ editing: false,
+ noSuccess: false,
+ };
+
+ getStatusMessage.propTypes = {
+ status: PropTypes.string,
+ error: PropTypes.string,
+ editing: PropTypes.bool,
+ noSuccess: PropTypes.bool,
+ };
+
+ 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() {
+ const { editing, name, stateful, frequency, message } = this.state;
+
+ return (
+
+ {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..4ceca66d4
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/ImportNotificationsForm.jsx.js
@@ -0,0 +1,157 @@
+// @ts-nocheck
+define([
+ 'underscore',
+ 'react',
+ 'react-bootstrap',
+ '../containers/ClassicLoginForm',
+ 'react-prop-types',
+], function(
+ { debounce },
+ React,
+ {
+ Form,
+ FormGroup,
+ FormControl,
+ ControlLabel,
+ HelpBlock,
+ Button,
+ Alert,
+ Modal,
+ },
+ ClassicLoginForm,
+ PropTypes
+) {
+ 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' : ''}
+
+
+
+
+
+ Close
+
+
+
+ )}
+
+ );
+ }
+ }
+
+ 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..b908f96ad
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/components/KeywordForm.jsx.js
@@ -0,0 +1,194 @@
+define(['react', 'react-bootstrap', 'react-prop-types'], function(
+ React,
+ { Form, FormGroup, ControlLabel, FormControl, HelpBlock },
+ PropTypes
+) {
+ const getStatusMessage = ({ status, error, editing }) => {
+ switch (status) {
+ case 'pending':
+ return (
+
+ Sending
+ request...
+
+ );
+ case 'failure':
+ return Request failed. ({error}) ;
+ case 'success':
+ return (
+
+ Notification {editing ? 'saved' : 'created'}!
+
+ );
+ default:
+ return null;
+ }
+ };
+ getStatusMessage.defaultProps = {
+ status: '',
+ error: '',
+ editing: false,
+ };
+
+ getStatusMessage.propTypes = {
+ status: PropTypes.string,
+ error: PropTypes.string,
+ editing: PropTypes.bool,
+ };
+
+ 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..a02df53bc
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/middleware.js
@@ -0,0 +1,200 @@
+define([
+ 'underscore',
+ './actions',
+ './constants',
+ '../shared/helpers',
+], function(_, actions, { page }, { middleware, apiSuccess }) {
+ const {
+ SET_NOTIFICATIONS,
+ SET_EDITING_NOTIFICATION,
+ RESET_EDITING_NOTIFICATION,
+ TOGGLE_ACTIVE,
+ SET_NOTIFICATION_QUERY_KEY,
+ RUN_QUERY,
+ goTo,
+ getQueryFromQID,
+ getNotification,
+ getNotifications,
+ updateNotification,
+ getNotificationQueries,
+ } = actions;
+
+ const delay = (cb) => {
+ if (cb.toKey) {
+ window.clearTimeout(cb.toKey);
+ }
+ cb.toKey = setTimeout(cb, 3000);
+ };
+
+ const parseScope = (requestType) => {
+ const [scope, status] = requestType.split('_API_REQUEST_');
+ return { scope, status };
+ };
+
+ const resetAfterRequest = (_, { 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 runQueries = middleware(
+ ({ action, next, dispatch, trigger, getState }) => {
+ next(action);
+
+ if (action.type === RUN_QUERY) {
+ const { id, queryKey } = action.payload;
+
+ const item = getState().notifications[id];
+ if (item && item.type === 'query') {
+ // if general query, then we must get the qid first
+ dispatch(getNotification(id));
+ } else {
+ dispatch({
+ type: SET_NOTIFICATION_QUERY_KEY,
+ payload: queryKey,
+ });
+ dispatch(getNotificationQueries(id));
+ }
+ }
+
+ if (action.type === apiSuccess('GET_NOTIFICATION_QUERIES')) {
+ const queryKey = getState().queryKey;
+ if (queryKey !== null && action.result && action.result.length > 0) {
+ try {
+ trigger('doSearch', action.result[queryKey]);
+ } catch (e) {
+ dispatch(goTo(page.DASHBOARD));
+ }
+ }
+
+ // reset queryKey
+ dispatch({
+ type: SET_NOTIFICATION_QUERY_KEY,
+ payload: null,
+ });
+ }
+
+ if (action.type === apiSuccess('GET_QUERY_FROM_QID')) {
+ if (action.result && action.result.query) {
+ try {
+ trigger('doSearch', JSON.parse(action.result.query).query);
+ } catch (e) {
+ dispatch(goTo(page.DASHBOARD));
+ }
+ } else {
+ dispatch(goTo(page.DASHBOARD));
+ }
+ setTimeout(() => {
+ dispatch(goTo(page.DASHBOARD));
+ }, 1000);
+ }
+ }
+ );
+
+ const updateNotifications = (__, { 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];
+
+ // iterrupt here in case it is a general query so we can run it seperately
+ if (result && result.qid) {
+ dispatch(getQueryFromQID(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 === 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 = (_, { dispatch }) => (next) => (
+ action
+ ) => {
+ next(action);
+
+ if (action.type === 'GOTO' && action.payload === page.DASHBOARD) {
+ dispatch({ type: RESET_EDITING_NOTIFICATION });
+ }
+ };
+
+ const importNotifications = (_, { dispatch }) => (next) => (action) => {
+ next(action);
+
+ if (action.type === 'IMPORT_NOTIFICATIONS') {
+ dispatch(goTo(page.IMPORT_NOTIFICATIONS));
+ }
+ };
+
+ const reloadNotificationsAfterGoTo = (_, { dispatch }) => (next) => (
+ action
+ ) => {
+ next(action);
+
+ if (action.type === 'GOTO' && action.payload === page.DASHBOARD) {
+ dispatch(getNotifications());
+ }
+ };
+
+ return {
+ resetAfterRequest,
+ updateNotifications,
+ resetEditingNotificationAfterGoTo,
+ importNotifications,
+ reloadNotificationsAfterGoTo,
+ runQueries,
+ };
+});
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..e614a0c4b
--- /dev/null
+++ b/src/js/react/MyAdsDashboard/reducer.js
@@ -0,0 +1,108 @@
+define(['./actions', 'redux', './constants'], function(
+ actions,
+ { combineReducers },
+ { page: PAGE }
+) {
+ const {
+ SET_NOTIFICATIONS,
+ SET_EDITING_NOTIFICATION,
+ RESET_EDITING_NOTIFICATION,
+ GOTO,
+ SET_NOTIFICATION_QUERY_KEY,
+ } = 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;
+ };
+
+ /**
+ * @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,
+ },
+ };
+ }
+
+ if (/_RESET$/.test(action.type)) {
+ const scope = action.type.replace('_RESET', '');
+ return {
+ ...state,
+ [scope]: requestState[scope],
+ };
+ }
+ return state;
+ };
+
+ const queryKeyState = null;
+ const queryKey = (state = queryKeyState, action) => {
+ if (action.type === SET_NOTIFICATION_QUERY_KEY) {
+ return action.payload;
+ }
+ return state;
+ };
+
+ return combineReducers({
+ notifications,
+ page,
+ requests,
+ editingNotification,
+ queryKey,
+ });
+});
diff --git a/src/js/react/MyAdsDashboard/typedefs.js b/src/js/react/MyAdsDashboard/typedefs.js
new file mode 100644
index 000000000..3eaa37b21
--- /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} updated
+ * @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..5b3dfede4
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/actions.js
@@ -0,0 +1,53 @@
+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,
+ }),
+ 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..bcf5dc9b1
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/components/App.jsx.js
@@ -0,0 +1,124 @@
+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 { checkLoginStatus } = this.props;
+ checkLoginStatus();
+ }
+
+ 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,
+ checkLoginStatus: () => {},
+ loggedIn: false,
+ };
+ MyADSFreeform.propTypes = {
+ saveNewNotification: PropTypes.func,
+ checkLoginStatus: PropTypes.func,
+ requests: PropTypes.object,
+ generalError: PropTypes.object,
+ loggedIn: PropTypes.bool,
+ };
+
+ 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 (
+
+
+
+
+
+
+ Create email
+ notification
+
+
+
+
+
+
+
+
+
+
+ {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..a6c8c56fd
--- /dev/null
+++ b/src/js/react/MyAdsFreeform/middleware.js
@@ -0,0 +1,114 @@
+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 ||
+ action.type === 'USER_ANNOUNCEMENT/user_signed_in' ||
+ action.type === 'USER_ANNOUNCEMENT/user_signed_out'
+ ) {
+ trigger('isLoggedIn', (status) => dispatch(setLoginStatus(status)));
+ }
+ });
+
+ 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/Recommender/actions.js b/src/js/react/Recommender/actions.js
new file mode 100644
index 000000000..62f3ad9ed
--- /dev/null
+++ b/src/js/react/Recommender/actions.js
@@ -0,0 +1,62 @@
+define([], function() {
+ const actions = {
+ GET_RECOMMENDATIONS: 'GET_RECOMMENDATIONS',
+ GET_DOCS: 'GET_DOCS',
+ SET_DOCS: 'SET_DOCS',
+ SET_QUERY: 'SET_QUERY',
+ UPDATE_SEARCH_BAR: 'UPDATE_SEARCH_BAR',
+ GET_FULL_LIST: 'GET_FULL_LIST',
+ EMIT_ANALYTICS: 'EMIT_ANALYTICS',
+ SET_TAB: 'SET_TAB',
+ SET_ORACLE_TARGET: 'SET_ORACLE_TARGET',
+ SET_QUERY_PARAMS: 'SET_QUERY_PARAMS',
+ };
+
+ const actionCreators = {
+ getRecommendations: () => ({
+ type: actions.GET_RECOMMENDATIONS,
+ }),
+ getDocs: (query) => ({
+ type: 'API_REQUEST',
+ scope: actions.GET_DOCS,
+ options: {
+ type: 'GET',
+ target: 'search/query',
+ query,
+ },
+ }),
+ setDocs: (docs) => ({
+ type: actions.SET_DOCS,
+ payload: docs,
+ }),
+ setQuery: (query) => ({
+ type: actions.SET_QUERY,
+ payload: query,
+ }),
+ setQueryParams: (payload) => ({
+ type: actions.SET_QUERY_PARAMS,
+ payload,
+ }),
+ updateSearchBar: (text) => ({
+ type: actions.UPDATE_SEARCH_BAR,
+ payload: text,
+ }),
+ getFullList: () => ({
+ type: actions.GET_FULL_LIST,
+ }),
+ emitAnalytics: (payload) => ({
+ type: actions.EMIT_ANALYTICS,
+ payload,
+ }),
+ setTab: (tab) => ({
+ type: actions.SET_TAB,
+ payload: tab,
+ }),
+ setOracleTarget: (target) => ({
+ type: actions.SET_ORACLE_TARGET,
+ payload: target,
+ }),
+ };
+
+ return { ...actions, ...actionCreators };
+});
diff --git a/src/js/react/Recommender/components/App.jsx.js b/src/js/react/Recommender/components/App.jsx.js
new file mode 100644
index 000000000..bda4e8f7a
--- /dev/null
+++ b/src/js/react/Recommender/components/App.jsx.js
@@ -0,0 +1,60 @@
+define([
+ 'react',
+ 'react-bootstrap',
+ 'react-prop-types',
+ 'react-redux',
+ '../actions',
+ 'es6!./RecommendedList.jsx',
+ 'es6!./SearchExamples.jsx',
+], function(
+ React,
+ { Nav, NavItem },
+ PropTypes,
+ { useDispatch, useSelector },
+ { setTab, emitAnalytics },
+ RecommendedList,
+ SearchExamples
+) {
+ const selector = (state) => ({
+ tab: state.tab,
+ });
+
+ const App = () => {
+ const dispatch = useDispatch();
+ const { tab } = useSelector(selector);
+ const onSelected = (key) => {
+ dispatch(setTab(key));
+ dispatch(
+ emitAnalytics([
+ 'send',
+ 'event',
+ 'interaction.main-page',
+ key === 1 ? 'recommender' : 'help',
+ ])
+ );
+ };
+
+ return (
+
+
onSelected(key)}
+ >
+
+ Recommended for you
+
+
+ Search examples
+
+
+
+ {tab === 1 ? : }
+
+
+ );
+ };
+
+ return App;
+});
diff --git a/src/js/react/Recommender/components/RecommendedList.jsx.js b/src/js/react/Recommender/components/RecommendedList.jsx.js
new file mode 100644
index 000000000..c6d9c866e
--- /dev/null
+++ b/src/js/react/Recommender/components/RecommendedList.jsx.js
@@ -0,0 +1,165 @@
+define([
+ 'react',
+ 'react-prop-types',
+ 'react-redux',
+ 'react-bootstrap',
+ '../actions',
+], function(
+ React,
+ PropTypes,
+ { useSelector, useDispatch },
+ { Button },
+ { getRecommendations, getFullList, emitAnalytics }
+) {
+ const Paper = ({ title, bibcode, author, totalAuthors, onClick }) => {
+ const el = React.useRef(null);
+ React.useEffect(() => {
+ if (el.current) {
+ el.current.addEventListener('click', onClick);
+ }
+ return () => {
+ if (el.current) {
+ el.current.removeEventListener('click', onClick);
+ }
+ };
+ }, []);
+
+ return (
+
+
+ {title}
+
+
+ {author.map((entry, i) => (
+ {`${entry}${i < 2 ? ';' : ''}`}
+ ))}
+ {totalAuthors > 3 && ... }
+
+
+ );
+ };
+ Paper.defaultProps = {
+ title: '',
+ bibcode: '',
+ author: [],
+ totalAuthors: 0,
+ onClick: () => {},
+ };
+
+ Paper.propTypes = {
+ title: PropTypes.string,
+ bibcode: PropTypes.string,
+ author: PropTypes.arrayOf(PropTypes.string),
+ totalAuthors: PropTypes.number,
+ onClick: PropTypes.func,
+ };
+
+ const Message = ({ children }) => (
+
+ {children}
+
+ );
+ Message.propTypes = {
+ children: PropTypes.element.isRequired,
+ };
+
+ const selector = (state) => {
+ return {
+ getRecommendationsRequest: state.requests.GET_RECOMMENDATIONS,
+ getDocsRequest: state.requests.GET_DOCS,
+ docs: state.docs,
+ };
+ };
+
+ const RecommendedList = () => {
+ const dispatch = useDispatch();
+ const onGetMore = () => {
+ dispatch(getFullList());
+ };
+ const { getRecommendationsRequest, getDocsRequest, docs } = useSelector(
+ selector
+ );
+ React.useEffect(() => {
+ if (docs.length === 0) {
+ dispatch(getRecommendations());
+ }
+ }, [docs]);
+
+ const onPaperSelect = ({ bibcode }, index) => {
+ dispatch(
+ emitAnalytics([
+ 'send',
+ 'event',
+ 'interaction.select-paper',
+ index,
+ { bibcode },
+ ])
+ );
+ };
+
+ if (
+ getRecommendationsRequest.status === 'pending' ||
+ getDocsRequest.status === 'pending'
+ ) {
+ return (
+
+
+ {' '}
+ Loading...
+
+
+ );
+ }
+
+ if (
+ getRecommendationsRequest.status === 'failure' ||
+ getDocsRequest.status === 'failure'
+ ) {
+ return (
+
+
+ {' '}
+ {getRecommendationsRequest.error || getDocsRequest.error}
+
+
+ );
+ }
+
+ if (docs.length === 0) {
+ return No recommendations right now, check back later ;
+ }
+
+ return (
+
+
+ {docs.map(({ title, bibcode, author, totalAuthors }, index) => (
+ onPaperSelect(docs[index], index)}
+ />
+ ))}
+
+
+
+ See full list
+
+
+
+ );
+ };
+
+ return RecommendedList;
+});
diff --git a/src/js/react/Recommender/components/SearchExamples.jsx.js b/src/js/react/Recommender/components/SearchExamples.jsx.js
new file mode 100644
index 000000000..129befde0
--- /dev/null
+++ b/src/js/react/Recommender/components/SearchExamples.jsx.js
@@ -0,0 +1,94 @@
+define([
+ 'react',
+ 'react-prop-types',
+ 'react-redux',
+ '../models/index',
+ '../actions',
+], function(
+ React,
+ PropTypes,
+ { useDispatch },
+ { searchExamples },
+ { updateSearchBar }
+) {
+ const Dl = ({ children }) => {
+ return {children} ;
+ };
+
+ Dl.propTypes = {
+ children: PropTypes.element.isRequired,
+ };
+
+ const Entry = ({ label, text, onClick, tooltip }) => {
+ return (
+ // eslint-disable-next-line react/jsx-fragments
+
+ {label}
+
+
+ {text}
+
+ {tooltip && (
+
+ )}
+
+
+ );
+ };
+ Entry.defaultProps = {
+ label: '',
+ text: '',
+ tooltip: '',
+ onClick: () => {},
+ };
+
+ Entry.propTypes = {
+ label: PropTypes.string,
+ onClick: PropTypes.func,
+ text: PropTypes.string,
+ tooltip: PropTypes.string,
+ };
+
+ const SearchExamples = () => {
+ const dispatch = useDispatch();
+ const onClick = (text) => {
+ dispatch(updateSearchBar(text));
+ };
+
+ return (
+
+
+
+ {searchExamples.slice(0, 7).map((entry) => (
+ onClick(entry.example)}
+ />
+ ))}
+
+
+
+
+ {searchExamples.slice(7).map((entry) => (
+ onClick(entry.example)}
+ />
+ ))}
+
+
+
+ );
+ };
+
+ return SearchExamples;
+});
diff --git a/src/js/react/Recommender/containers/main.js b/src/js/react/Recommender/containers/main.js
new file mode 100644
index 000000000..710984108
--- /dev/null
+++ b/src/js/react/Recommender/containers/main.js
@@ -0,0 +1,3 @@
+define([], function() {
+ return null;
+});
diff --git a/src/js/react/Recommender/index.js b/src/js/react/Recommender/index.js
new file mode 100644
index 000000000..2e69aa21a
--- /dev/null
+++ b/src/js/react/Recommender/index.js
@@ -0,0 +1,23 @@
+define([
+ 'es6!./components/App.jsx',
+ 'js/react/WithBackboneView',
+ 'js/react/configureStore',
+ './middleware',
+ './reducer',
+ 'js/react/shared/helpers',
+ 'js/react/shared/middleware/index',
+], function(
+ App,
+ WithBackboneView,
+ configureStore,
+ middleware,
+ reducer,
+ { withContext },
+ sharedMiddleware
+) {
+ const middlewares = [middleware, ...sharedMiddleware];
+
+ return WithBackboneView(App, (context) =>
+ configureStore(context, reducer, withContext(...middlewares))
+ );
+});
diff --git a/src/js/react/Recommender/middleware.js b/src/js/react/Recommender/middleware.js
new file mode 100644
index 000000000..19e2299b9
--- /dev/null
+++ b/src/js/react/Recommender/middleware.js
@@ -0,0 +1,111 @@
+define(['../shared/helpers', './actions'], function(
+ { middleware, apiSuccess, apiFailure, parseScope },
+ {
+ GET_RECOMMENDATIONS,
+ getDocs,
+ GET_DOCS,
+ setDocs,
+ setQuery,
+ UPDATE_SEARCH_BAR,
+ GET_FULL_LIST,
+ EMIT_ANALYTICS,
+ }
+) {
+ const updateTarget = middleware(({ next, action, getState }) => {
+ if (action.type === 'API_REQUEST' && action.scope === GET_RECOMMENDATIONS) {
+ const { oracleTarget } = getState();
+ action = {
+ ...action,
+ options: { ...action.options, target: oracleTarget },
+ };
+ }
+ next(action);
+ });
+
+ const getRecommendations = middleware(
+ ({ next, action, dispatch, getState }) => {
+ next(action);
+
+ 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 === 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 === 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);
+
+ if (action.type === UPDATE_SEARCH_BAR) {
+ trigger(
+ 'publishToPubSub',
+ 'CUSTOM_EVENT',
+ 'recommender/update-search-text',
+ {
+ text: action.payload,
+ }
+ );
+ }
+ });
+
+ const getFullList = middleware(({ next, action, trigger, getState }) => {
+ next(action);
+
+ if (action.type === GET_FULL_LIST) {
+ const { query } = getState();
+ trigger('doSearch', { q: query });
+ }
+ });
+
+ const analytics = middleware(({ next, action, trigger }) => {
+ next(action);
+
+ if (action.type === EMIT_ANALYTICS) {
+ trigger('analyticsEvent', ...action.payload);
+ }
+ });
+
+ return {
+ getRecommendations,
+ updateSearchBar,
+ getFullList,
+ analytics,
+ updateTarget,
+ };
+});
diff --git a/src/js/react/Recommender/models/index.js b/src/js/react/Recommender/models/index.js
new file mode 100644
index 000000000..9864bbbae
--- /dev/null
+++ b/src/js/react/Recommender/models/index.js
@@ -0,0 +1,5 @@
+define(['./searchExamples'], function(searchExamples) {
+ return {
+ searchExamples,
+ };
+});
diff --git a/src/js/react/Recommender/models/searchExamples.js b/src/js/react/Recommender/models/searchExamples.js
new file mode 100644
index 000000000..8f625f0df
--- /dev/null
+++ b/src/js/react/Recommender/models/searchExamples.js
@@ -0,0 +1,68 @@
+define([], function() {
+ const searchExamples = [
+ {
+ label: 'author',
+ example: 'author:"huchra, john"',
+ },
+ {
+ label: 'first author',
+ example: 'author:"^huchra, john"',
+ },
+ {
+ label: 'abstract + title',
+ example: 'abs:"dark energy"',
+ },
+ {
+ label: 'year',
+ example: 'year:2000',
+ },
+ {
+ label: 'year range',
+ example: 'year:2000-2005',
+ },
+ {
+ label: 'full text',
+ example: 'full:"gravity waves"',
+ },
+ {
+ label: 'publication',
+ example: 'bibstem:ApJ',
+ tooltip:
+ "this field requires the bibstem, or journal abbreviation--try going to the 'Paper' tab above for an easy-to-use form version",
+ },
+ {
+ label: 'citations',
+ example: 'citations(author:"huchra, j")',
+ tooltip: 'finds all papers that cite a given set of papers',
+ },
+ {
+ label: 'references',
+ example: 'references(author:"huchra, j")',
+ tooltip: 'finds all papers referenced by a given set of papers',
+ },
+ {
+ label: 'reviews',
+ example: 'reviews("gamma-ray bursts")',
+ tooltip:
+ 'finds articles citing the most relevant papers on the topic being researched',
+ },
+ {
+ label: 'refereed',
+ example: 'property:refereed',
+ tooltip: 'limit to non-refereed papers by searching property:notrefereed',
+ },
+ {
+ label: 'astronomy',
+ example: 'database:astronomy',
+ tooltip: 'limit to physics papers by searching collection:physics',
+ },
+ {
+ label: 'OR',
+ example: 'abs:(planet OR star)',
+ tooltip:
+ 'default logic is AND, e.g. abs:(planet star) would be interpreted as abs:(planet AND star)',
+ },
+ ];
+
+ return searchExamples;
+});
diff --git a/src/js/react/Recommender/reducer.js b/src/js/react/Recommender/reducer.js
new file mode 100644
index 000000000..796482f76
--- /dev/null
+++ b/src/js/react/Recommender/reducer.js
@@ -0,0 +1,94 @@
+define(['redux', './actions'], function(
+ { combineReducers },
+ { SET_DOCS, SET_QUERY, SET_TAB, SET_ORACLE_TARGET, SET_QUERY_PARAMS }
+) {
+ const requestState = {
+ GET_RECOMMENDATIONS: { status: null, result: null, error: null },
+ GET_DOCS: { 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,
+ },
+ };
+ }
+
+ if (/_RESET$/.test(action.type)) {
+ const scope = action.type.replace('_RESET', '');
+ return {
+ ...state,
+ [scope]: requestState[scope],
+ };
+ }
+ return state;
+ };
+
+ const docsState = [];
+ const docs = (state = docsState, action) => {
+ if (action.type === SET_DOCS && action.payload) {
+ return action.payload.map((doc) => ({
+ ...doc,
+ title: doc.title[0],
+ totalAuthors: doc.author_count,
+ }));
+ }
+ return state;
+ };
+
+ const queryState = null;
+ const query = (state = queryState, action) => {
+ if (action.type === SET_QUERY && action.payload) {
+ return action.payload;
+ }
+ return state;
+ };
+
+ const tabState = 2;
+ const tab = (state = tabState, action) => {
+ if (action.type === SET_TAB && action.payload) {
+ return action.payload;
+ }
+ return state;
+ };
+
+ const oracleTargetState = '_oracle/readhist';
+ const oracleTarget = (state = oracleTargetState, action) => {
+ if (action.type === SET_ORACLE_TARGET && action.payload) {
+ return action.payload;
+ }
+ 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,
+ });
+});
diff --git a/src/js/react/WithBackboneView.js b/src/js/react/WithBackboneView.js
index 3c2e5a536..079f85f97 100644
--- a/src/js/react/WithBackboneView.js
+++ b/src/js/react/WithBackboneView.js
@@ -11,6 +11,9 @@ define(['underscore', 'react', 'react-dom', 'react-redux'], function(
this.props = {
trigger: (...args) => this.trigger(...args),
};
+ if (typeof getStore === 'function') {
+ this._store = getStore(this.props);
+ }
},
render(el) {
@@ -20,12 +23,12 @@ define(['underscore', 'react', 'react-dom', 'react-redux'], function(
this.setElement(document.createElement('div'));
}
- if (getStore) {
+ if (this._store) {
ReactDOM.render(
React.createElement(
Provider,
{
- store: getStore(this.props),
+ store: this._store,
},
React.createElement(component, this.props)
),
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 1ebffeee0..ce687b3c9 100644
--- a/src/js/react/shared/middleware/api.js
+++ b/src/js/react/shared/middleware/api.js
@@ -16,17 +16,29 @@ 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({
type: `${action.scope}_API_REQUEST_FAILURE`,
- error: response.error,
+ error: err,
+ result,
});
};
- const { target, query = {}, type = 'GET', data } = action.options;
+ const {
+ target,
+ query = {},
+ type = 'GET',
+ data,
+ headers,
+ } = action.options;
if (!target) {
return;
@@ -40,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/react/shared/middleware/main.js b/src/js/react/shared/middleware/main.js
index f2480edef..81e2ed6cd 100644
--- a/src/js/react/shared/middleware/main.js
+++ b/src/js/react/shared/middleware/main.js
@@ -1,5 +1,5 @@
-define([], function() {
- const getInitialData = ({ trigger }, { dispatch }) => (next) => (action) => {
+define(['../helpers'], function({ middleware }) {
+ const getInitialData = middleware(({ trigger, next, action, dispatch }) => {
next(action);
if (action.type === 'GET_INITIAL_DATA') {
@@ -7,7 +7,7 @@ define([], function() {
dispatch({ type: 'SET_INITIAL_DATA', result });
});
}
- };
+ });
return {
getInitialData,
diff --git a/src/js/widgets/alerts/modal_view.js b/src/js/widgets/alerts/modal_view.js
index c97a09f68..35211e899 100644
--- a/src/js/widgets/alerts/modal_view.js
+++ b/src/js/widgets/alerts/modal_view.js
@@ -26,7 +26,7 @@ define([
$('body').append(() => {
let out = '';
if ($('#alert-modal-label').length === 0) {
- out += 'Alert
';
+ out += 'Alert
';
}
out +=
diff --git a/src/js/widgets/list_of_things/paginated_view.js b/src/js/widgets/list_of_things/paginated_view.js
index eebd08241..dad12226d 100644
--- a/src/js/widgets/list_of_things/paginated_view.js
+++ b/src/js/widgets/list_of_things/paginated_view.js
@@ -210,7 +210,7 @@ define([
},
goToBottom: function() {
- $('#app-container').animate(
+ $(document.documentElement).animate(
{
scrollTop: this.$el.outerHeight(),
},
@@ -219,7 +219,7 @@ define([
},
goToTop: function() {
- $('#app-container').animate(
+ $(document.documentElement).animate(
{
scrollTop: 0,
},
diff --git a/src/js/widgets/list_of_things/widget.js b/src/js/widgets/list_of_things/widget.js
index ec2570ec5..933666340 100644
--- a/src/js/widgets/list_of_things/widget.js
+++ b/src/js/widgets/list_of_things/widget.js
@@ -533,8 +533,7 @@ define([
this.collection.reset(this.hiddenCollection.getVisibleModels());
// finally, scroll back to the top
- document.body.scrollTop = document.documentElement.scrollTop = 0;
- $('#app-container').scrollTop(0);
+ $(document.documentElement).scrollTop(0);
},
onAllInternalEvents: function(ev, arg1, arg2) {
diff --git a/src/js/widgets/navbar/template/navbar.html b/src/js/widgets/navbar/template/navbar.html
index 56a5c01fc..b751c3b6f 100644
--- a/src/js/widgets/navbar/template/navbar.html
+++ b/src/js/widgets/navbar/template/navbar.html
@@ -172,7 +172,7 @@ ads
You are signed in as {{currentUser}}
My Home Page
ADS Libraries
- Customize Settings
+ Settings
Log Out
diff --git a/src/js/widgets/preferences/templates/application.html b/src/js/widgets/preferences/templates/application.html
index c132f45f4..4e22d65e8 100644
--- a/src/js/widgets/preferences/templates/application.html
+++ b/src/js/widgets/preferences/templates/application.html
@@ -209,7 +209,7 @@
title="edit custom format">
-
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
+
+
+
+
+
+ Add papers to library
+
+ {{#if libraryDrawerOpen}}
+
+ {{else}}
+
+ {{/if}}
+
+
+
+
-
+
+
+
{{#compare selected 0 operator=">"}}
add
@@ -38,7 +54,7 @@
{{this.name}}
{{/each}}
- submit
+ submit
OR
@@ -54,7 +70,7 @@
value="{{newLibraryName}}"
style="max-width:166px;margin-bottom:10px;"
aria-label="enter new library name"/>
- submit
+ submit
@@ -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/widgets/results/widget.js b/src/js/widgets/results/widget.js
index 344418d3f..fdd264e63 100644
--- a/src/js/widgets/results/widget.js
+++ b/src/js/widgets/results/widget.js
@@ -157,7 +157,7 @@ define([
if ($link) {
// found it, clear the interval and scroll
clearInterval(focusInterval);
- $('#app-container').animate(
+ $(document.documentElement).animate(
{ scrollTop: $link.offset().top },
'fast'
);
diff --git a/src/js/widgets/search_bar/search_bar_widget.js b/src/js/widgets/search_bar/search_bar_widget.js
index 0a80bd86c..d48a23d6b 100644
--- a/src/js/widgets/search_bar/search_bar_widget.js
+++ b/src/js/widgets/search_bar/search_bar_widget.js
@@ -344,15 +344,6 @@ define([
});
this.$('[data-toggle="tooltip"]').tooltip();
-
- // this is for search examples, sets form value on click
- this.$('.search-quick-links button').on('click', (e) => {
- const value = e.currentTarget.innerText.trim();
-
- if (value) {
- this.setFormVal(value);
- }
- });
},
events: {
@@ -769,8 +760,10 @@ define([
if (!$input.is(':focus')) {
arg.preventDefault();
$input.select();
- $('#app-container').scrollTop(0);
+ $(document.documentElement).scrollTop(0);
}
+ } else if (event === 'recommender/update-search-text') {
+ this.view.setFormVal(arg.text);
}
},
diff --git a/src/js/widgets/search_bar/templates/search_bar_template.html b/src/js/widgets/search_bar/templates/search_bar_template.html
index 24d7578fc..c3c3cda45 100644
--- a/src/js/widgets/search_bar/templates/search_bar_template.html
+++ b/src/js/widgets/search_bar/templates/search_bar_template.html
@@ -54,79 +54,6 @@ Search Bar to Enter New Query
-
-
-
-
- author
-
- author:"huchra, john"
-
- first author
-
- author:"^huchra, john"
-
- abstract + title
-
- abs:"dark energy"
-
- year
-
- year:2000
-
- year range
-
- year:2000-2005
-
- full text
-
- full:"gravity waves"
-
- publication
-
- bibstem:ApJ
-
-
-
-
-
-
- citations
-
- citations(author:"huchra, j")
-
-
- references
-
- references(author:"huchra, j")
-
-
- reviews
-
- reviews("gamma-ray bursts")
-
-
- refereed
-
- property:refereed
-
-
- astronomy
-
- collection:astronomy
-
-
- OR
-
- abs:(planet OR star)
-
-
-
-
-
-
-
-
Your search returned
{{numFound}}
diff --git a/src/js/widgets/user_navbar/nav_template.html b/src/js/widgets/user_navbar/nav_template.html
index 57a075904..e08f5e0dd 100644
--- a/src/js/widgets/user_navbar/nav_template.html
+++ b/src/js/widgets/user_navbar/nav_template.html
@@ -11,8 +11,8 @@
-
- Customize Settings
+
+ Settings
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
+
+
+
+
+
+
+ Add paper to library
+
+ {{#if libraryDrawerOpen}}
+
+ {{else}}
+
+ {{/if}}
+
+
+
+
-
+
+
+
- {{#if libraries}}
-
- add to an existing library:
-
- {{#each libraries}}
- {{this.name}}
- {{/each}}
-
- submit
-
-
- OR
-
- {{/if}}
-
-
create a new library
-
-
submit
-
-
-
-
-
-
+ {{#if libraries}}
+
+ add to an existing library:
+
+ {{#each libraries}}
+ {{this.name}}
+ {{/each}}
+
+ submit
+
+
+ OR
+
+ {{/if}}
-
+
create a new library
+
+
submit
- {{#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/landing_page_manager/landing-page-layout.html b/src/js/wraps/landing_page_manager/landing-page-layout.html
index 59088a5b6..6e9fce27a 100644
--- a/src/js/wraps/landing_page_manager/landing-page-layout.html
+++ b/src/js/wraps/landing_page_manager/landing-page-layout.html
@@ -13,22 +13,15 @@
-
-
+
diff --git a/src/js/wraps/landing_page_manager/landing_page_manager.js b/src/js/wraps/landing_page_manager/landing_page_manager.js
index 51bdf88e3..d9fee23e8 100644
--- a/src/js/wraps/landing_page_manager/landing_page_manager.js
+++ b/src/js/wraps/landing_page_manager/landing_page_manager.js
@@ -63,6 +63,10 @@ define([
}
},
+ onWidgetSelected(child, ev, attrs) {
+ PageManagerController.prototype.onWidgetSelected.apply(this, arguments);
+ },
+
/**
* Override the setActive method to inject the default navItem, if we have one
* @param {string} navItem the navitem name
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..09f05d124 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,12 @@
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 9aa824d8d..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 @@
+
+
- Customize Settings
+ Settings
-
-
- {{#each preferences}}
-
-
-
- {{this.title}}
-
-
-
- {{/each}}
-
-
-
- Coming soon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{#each settings}}
-
-
-
-
- {{this.title}} {{#if this.showCount}}
-
- {{#if this.numFound}}({{this.numFound}}){{/if}}
- {{/if}}
-
-
-
-
- {{/each}}
+
+
+ {{#each preferences}}
+
+
+
+ {{this.title}}
+
+
+
+ {{/each}}
+
+
+
+ {{#each notifications}}
+
+
+
+
+ {{this.title}}
+ {{#if this.showCount}}
+ {{#if this.numFound}}({{this.numFound}}){{/if}}
+ {{/if}}
+
+
+
+
+ {{/each}}
+
+
+
+ {{#each settings}}
+
+
+
+
+ {{this.title}}
+ {{#if this.showCount}}
+ {{#if this.numFound}}({{this.numFound}}){{/if}}
+ {{/if}}
+
+
+
+
+ {{/each}}
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/shared b/src/shared
new file mode 160000
index 000000000..444ee8c90
--- /dev/null
+++ b/src/shared
@@ -0,0 +1 @@
+Subproject commit 444ee8c90168499802f87840a9782e053c666286
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 000000000..2e1192855
Binary files /dev/null and b/src/styles/img/how_to_add_general_query.gif differ
diff --git a/src/styles/img/myADS.gif b/src/styles/img/myADS.gif
new file mode 100644
index 000000000..ffeeae804
Binary files /dev/null and b/src/styles/img/myADS.gif differ
diff --git a/src/styles/sass/ads-sass/general-components.scss b/src/styles/sass/ads-sass/general-components.scss
index 49c48150e..9b7d3451d 100644
--- a/src/styles/sass/ads-sass/general-components.scss
+++ b/src/styles/sass/ads-sass/general-components.scss
@@ -1,25 +1,24 @@
-
-#app-container{
- height: 100%;
- position: relative;
+#app-container {
will-change: transform;
}
-#body-template-container{
- height: 100%
+#body-template-container {
+ height: 100%;
}
-.s-master-page-manager{
+.s-master-page-manager {
height: 100%;
}
-
.s-title-format {
margin-bottom: 10px;
padding: 1px 2%;
background-color: lighten($gray-lighter, 4%);
- h1, h2, h3, h4 {
+ h1,
+ h2,
+ h3,
+ h4 {
margin: 6px 0;
font-size: 28px;
font-weight: 300;
@@ -27,7 +26,7 @@
}
.s-edit-indicator {
- border-bottom: 1px dashed $brand-primary-faded;
+ border-bottom: 1px dashed $brand-primary-faded;
&:before {
@extend .fa;
@@ -44,21 +43,21 @@
.btn-primary.btn-inverse {
color: $brand-primary;
- box-shadow:none;
+ box-shadow: none;
&:hover {
- box-shadow:none;
+ box-shadow: none;
background-color: mix($brand-primary, #fff, 15%);
}
}
.btn-primary-faded.btn-inverse {
color: $brand-primary-faded;
background-color: #fff;
- box-shadow:none;
+ box-shadow: none;
&:hover {
- box-shadow:none;
+ box-shadow: none;
background-color: mix($brand-primary-faded, #fff, 15%);
}
- box-shadow : none;
+ box-shadow: none;
}
.btn-info.btn-inverse {
color: $brand-info;
@@ -68,42 +67,42 @@
box-shadow: none;
background-color: mix($brand-info, #fff, 15%);
}
- box-shadow : none;
+ box-shadow: none;
}
.btn-success.btn-inverse {
color: $brand-success;
background-color: #fff;
- box-shadow : none;
+ box-shadow: none;
&:hover {
- box-shadow : none;
+ box-shadow: none;
background-color: mix($brand-success, #fff, 15%);
- }
- box-shadow : none;
+ }
+ box-shadow: none;
}
.btn-warning.btn-inverse {
color: $brand-warning;
background-color: #fff;
- box-shadow:none;
+ box-shadow: none;
&:hover {
- box-shadow:none;
+ box-shadow: none;
background-color: mix($brand-warning, #fff, 15%);
}
- box-shadow : none;
+ box-shadow: none;
}
.btn-danger.btn-inverse {
color: $brand-danger;
background-color: #fff;
- box-shadow:none;
+ box-shadow: none;
&:hover {
- box-shadow:none;
+ box-shadow: none;
background-color: mix($brand-danger, #fff, 15%);
}
- box-shadow : none;
+ box-shadow: none;
}
.s-tab {
- display:inline-block;
+ display: inline-block;
text-align: center;
min-width: 150px;
font-size: 18px;
@@ -119,15 +118,15 @@
a {
width: 180px;
display: inline-block;
- color : $text-color;
+ color: $text-color;
&:hover {
text-decoration: none;
}
- &:focus{
+ &:focus {
text-decoration: none;
}
}
- transition : all, .5s;
+ transition: all, 0.5s;
.dropdown-menu {
li {
@@ -141,28 +140,27 @@
}
}
-.dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
//no idea why this is necessary
background-color: transparent;
}
-
.s-tab .dropdown-menu li {
color: $text-color;
}
-
-.s-tab.active{
+.s-tab.active {
border-bottom: 4px solid $brand-primary-faded;
- color: $brand-primary-faded;
+ color: $brand-primary-faded;
a {
- color: $brand-primary-faded;
+ color: $brand-primary-faded;
}
}
.s-tab.s-disabled {
color: $gray-light;
- &:hover{
+ &:hover {
border-bottom: 4px solid $body-bg;
}
cursor: default;
diff --git a/src/styles/sass/ads-sass/landing-page.scss b/src/styles/sass/ads-sass/landing-page.scss
index 48aa7eec6..67bafc760 100644
--- a/src/styles/sass/ads-sass/landing-page.scss
+++ b/src/styles/sass/ads-sass/landing-page.scss
@@ -1,8 +1,6 @@
$landing-page-hero-background: desaturate(lighten($brand-info, 20%), 15%);
.s-landing-page-layout {
- margin-left: -15px;
- margin-right: -15px;
padding-left: 15px;
padding-right: 15px;
}
@@ -38,7 +36,6 @@ $landing-page-hero-background: desaturate(lighten($brand-info, 20%), 15%);
justify-content: center;
background-attachment: fixed;
- @extend .row;
padding-top: 20px;
text-align: center;
}
diff --git a/src/styles/sass/ads-sass/miscellaneous.scss b/src/styles/sass/ads-sass/miscellaneous.scss
index 599a97cca..bd66cf4e6 100644
--- a/src/styles/sass/ads-sass/miscellaneous.scss
+++ b/src/styles/sass/ads-sass/miscellaneous.scss
@@ -1,16 +1,13 @@
-
//tests
#mocha-stats > li.progress {
height: 40px;
}
-
// the hidden live region
-#aria-announcement-container{
-
- color : fade(#fff, 0%);
+#aria-announcement-container {
+ color: fade(#fff, 0%);
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
@@ -19,3 +16,20 @@
.grecaptcha-badge {
display: none;
}
+
+// make label group like
+.label-group {
+ .label:first-child {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .label:last-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ .label:not(:first-child):not(:last-child) {
+ border-radius: 0;
+ }
+}
diff --git a/src/styles/sass/ads-sass/page-managers.scss b/src/styles/sass/ads-sass/page-managers.scss
index ea551c9fa..31ceb2f86 100644
--- a/src/styles/sass/ads-sass/page-managers.scss
+++ b/src/styles/sass/ads-sass/page-managers.scss
@@ -8,7 +8,7 @@ s-search-bar-motion is applied */
@mixin container-fixed($gutter: $grid-gutter-width) {
margin-right: auto;
margin-left: auto;
- padding-left: floor(($gutter / 2));
+ padding-left: floor(($gutter / 2));
padding-right: ceil(($gutter / 2));
@include clearfix;
}
@@ -22,7 +22,7 @@ s-search-bar-motion is applied */
color: $gray-light;
font-weight: 300;
width: 100%;
- @media only screen and (min-width : 768px) {
+ @media only screen and (min-width: 768px) {
font-size: 2rem;
min-height: 50vh;
width: auto;
@@ -39,7 +39,7 @@ s-search-bar-motion is applied */
.logo__data-system {
color: lighten($gray-light, 12%);
}
- @media only screen and (min-width : 768px) {
+ @media only screen and (min-width: 768px) {
font-size: 5rem;
}
}
@@ -52,7 +52,7 @@ s-search-bar-motion is applied */
font-size: 1rem;
margin-top: 20px;
text-align: center;
- @media only screen and (min-width : 768px) {
+ @media only screen and (min-width: 768px) {
font-size: 1.5rem;
}
}
@@ -61,34 +61,32 @@ s-search-bar-motion is applied */
.loading-screen__loading {
position: relative;
top: -40px;
- font-size: .5em;
+ font-size: 0.5em;
}
-.hidden-col{
- width: 0 ;
- padding: 0 ;
- margin: 0 ;
+.hidden-col {
+ width: 0;
+ padding: 0;
+ margin: 0;
opacity: 0;
}
.btn-expand {
-
- display:none;
+ display: none;
.descriptor {
display: none;
}
//show in desktops
- @media (min-width : $screen-sm-min){
+ @media (min-width: $screen-sm-min) {
display: inline-block;
- transition : all .5s ease-out;
+ transition: all 0.5s ease-out;
&:hover {
- box-shadow : 1px 1px 5px darken($gray-light, 4%);
+ box-shadow: 1px 1px 5px darken($gray-light, 4%);
}
}
-
}
.left-expand {
@@ -102,7 +100,6 @@ s-search-bar-motion is applied */
&:before {
content: $fa-var-angle-double-left;
}
-
}
.right-expand {
@@ -124,32 +121,28 @@ s-search-bar-motion is applied */
/* Styles for results and abstract page */
-
.s-main-content-container {
-
@extend .clearfix;
@extend .s-ads-card;
min-height: 600px;
padding-bottom: 20px;
position: relative;
margin: 0 auto 40px auto;
-
}
//this class is useful if you want to apply a background to
//div[data-widget] when the widget doesnt always need the background
.s-inner-div-background {
- margin: 30px 0;
- >div {
+ margin: 30px 0;
+ > div {
@extend .s-main-content-container;
margin: 0;
}
-
}
.s-dynamic-page-body {
- background-color: $darker-background;
+ background-color: $darker-background;
min-height: 500px;
}
@@ -169,9 +162,7 @@ s-search-bar-motion is applied */
/* Styles just for results page */
-
.s-results-page-layout {
-
.s-left-col-container {
@extend .s-ads-card;
padding: 5px;
@@ -180,14 +171,15 @@ s-search-bar-motion is applied */
margin-bottom: 25px;
@extend .unselectable;
}
+ @extend .container-fluid;
}
.s-right-column {
- padding-right: 0;
+ padding-right: 0;
}
.btn-upside-down i {
- transform : rotate(-180deg);
+ transform: rotate(-180deg);
}
#body-template-container {
@@ -199,34 +191,29 @@ s-search-bar-motion is applied */
top: 1.25%;
right: 2%;
background-color: white;
-
}
.right-col-close {
-
@extend .fa;
@extend .fa-lg;
- &:before{
- content : $fa-var-chevron-circle-left;
+ &:before {
+ content: $fa-var-chevron-circle-left;
}
-
}
.right-col-open {
@extend .fa;
@extend .fa-lg;
- &:before{
- content : $fa-var-chevron-circle-right;
+ &:before {
+ content: $fa-var-chevron-circle-right;
}
}
-
.navbar h1 {
- color : $gray-light;
+ color: $gray-light;
margin: 0;
padding: 0;
font-size: 25px;
-
}
/*
@@ -249,7 +236,6 @@ Styles for Abstract Page
li {
margin: 0 0 1% 6%;
}
-
}
.s-right-col-widget-title {
margin-top: 2px;
@@ -260,26 +246,22 @@ Styles for Abstract Page
color: $gray-lighter;
}
-#app-container {
- overflow-x: hidden;
- overflow-y: scroll;
- -webkit-overflow-scrolling: touch;
+#main-content:focus {
+ outline: none;
}
-#main-content:focus { outline: none; }
.s-results-middle-column {
- @media (min-width : $screen-sm-min){
+ @media (min-width: $screen-sm-min) {
float: right;
}
- @media (min-width : $screen-md-min){
+ @media (min-width: $screen-md-min) {
float: left;
}
}
.s-right-column {
-
- @media (min-width : $screen-md-min){
+ @media (min-width: $screen-md-min) {
padding-left: 0;
padding-right: 15px;
}
diff --git a/src/styles/sass/ads-sass/print.scss b/src/styles/sass/ads-sass/print.scss
index 76de0b4e4..bb7854043 100644
--- a/src/styles/sass/ads-sass/print.scss
+++ b/src/styles/sass/ads-sass/print.scss
@@ -1,9 +1,10 @@
.print-visible {
- display : none;
+ display: none;
}
@media print {
-
- *, *:before, *:after {
+ *,
+ *:before,
+ *:after {
overflow: visible !important;
}
@@ -13,7 +14,7 @@
}
.print-visible {
- display:block;
+ display: block;
}
.s-results-links {
@@ -31,21 +32,27 @@
}
//general, any page adjustments
- .s-results-control-row-container, #navbar-container, .s-stable-search-bar-height, .s-metrics-metadata, .footer {
- display:none !important;
+ .s-results-control-row-container,
+ #navbar-container,
+ .s-stable-search-bar-height,
+ .s-metrics-metadata,
+ .footer {
+ display: none !important;
}
- .nav-container, .toggle-menu {
+ .nav-container,
+ .toggle-menu {
display: none !important;
}
- .s-right-col-widget-container, .query-info-widget {
- display : none;
+ .s-right-col-widget-container,
+ .query-info-widget {
+ display: none;
}
-.main-content-container {
- border: none;
-}
+ .main-content-container {
+ border: none;
+ }
//abstract-page
li.author a {
@@ -55,11 +62,11 @@
//metrics
.icon-help {
- display:none;
+ display: none;
}
.close-widget {
- display:none;
+ display: none;
}
.s-metrics-title {
@@ -74,11 +81,12 @@
}
.nv-controlsWrap {
- display : none;
+ display: none;
}
- .s-metrics-table, .s-metrics-graph {
- display:block !important;
+ .s-metrics-table,
+ .s-metrics-graph {
+ display: block !important;
margin: 0 auto;
width: 100%;
}
@@ -87,6 +95,4 @@
width: 60%;
margin-bottom: 50px;
}
-
-
}
diff --git a/src/styles/sass/ads-sass/results-page-widgets.scss b/src/styles/sass/ads-sass/results-page-widgets.scss
index 49fd9c941..50eebc38d 100644
--- a/src/styles/sass/ads-sass/results-page-widgets.scss
+++ b/src/styles/sass/ads-sass/results-page-widgets.scss
@@ -80,35 +80,28 @@
font-size: 18px;
}
.s-library-area {
- margin-top: 15px;
- margin-bottom: 20px;
border: 0 solid transparent;
background-color: white;
@extend .s-ads-card;
}
- #library-console,
- .s-library-add-title {
+ #library-console {
padding: 10px;
}
- .s-library-add-title {
- background: $brand-info;
- transition: background 0.4s;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
- color: white;
- font-weight: 300;
- font-size: 16px;
- cursor: pointer;
- i.fa-caret-down {
- position: relative;
- top: 4px;
- left: -8px;
- float: right;
- font-size: 1.5em;
- }
- &:hover {
- background: darken($brand-info, 5%);
+
+ .library-add-title {
+ &:hover,
+ &:focus {
+ background: darken(white, 5%) !important;
}
+ text-decoration: none;
+ }
+
+ .s-library-add-title__title {
+ display: flex;
+ justify-content: space-between;
+ color: #5d5d5d;
+ font-size: 1.25em;
}
.s-filter-div {
font-weight: 300;
@@ -141,3 +134,9 @@
.s-empty-view-msg {
padding: 0 10% 0 10%;
}
+
+.results-right-column {
+ div[data-widget] {
+ margin-bottom: 1em;
+ }
+}
diff --git a/src/styles/sass/manifest.scss b/src/styles/sass/manifest.scss
index 47ce769d1..a954ded9d 100644
--- a/src/styles/sass/manifest.scss
+++ b/src/styles/sass/manifest.scss
@@ -1,5 +1,4 @@
-
-$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.6.3/fonts"; // for referencing Bootstrap CDN font files directly
+$fa-font-path: '//netdna.bootstrapcdn.com/font-awesome/4.6.3/fonts'; // for referencing Bootstrap CDN font files directly
@import '../../libs/fontawesome/scss/font-awesome';
@import '../../styles/smoothness/jquery-ui.min';