diff --git a/.gitignore b/.gitignore index 148e0230..0cba50a1 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,7 @@ mup.json packages_update.py -versions +# versions get_file_list.sh packages_update.py diff --git a/.meteor/packages b/.meteor/packages index ce5ffd62..ffde40e8 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -1,23 +1,28 @@ +# base vulcan package for comments, posting, all forum functions: +example-forum +fourseven:scss@4.10.0 # see http://docs.vulcanjs.org/packages -# Internal dependencies +vulcan:core +# vulcan:admin +# subscribe and notifications manually imported +vulcan:notifications +vulcan:subscribe dynamic-import -accounts-password@1.4.0 -fourseven:scss@4.5.0 - -vulcan:admin@1.7.0 -vulcan:core@1.7.0 -vulcan:posts@1.7.0 -vulcan:comments@1.7.0 -vulcan:voting@1.7.0 -vulcan:accounts@1.7.0 -vulcan:categories@1.7.0 -vulcan:forms@1.7.0 -vulcan:i18n-en-us@1.7.0 -vulcan:notifications@1.7.0 -vulcan:email-templates@1.7.0 -vulcan:subscribe@1.7.0 - -networkcanvas-styles -artwells-accounts-guest + +############ Language Packages ############ + +vulcan:i18n-en-us + +############ Accounts Packages ############ + +accounts-password@1.5.1 +# accounts-twitter +# accounts-facebook + +############ Your Packages ############ + +artwells-accounts-guest-custom +accounts-ui +#networkcanvas-styles diff --git a/.meteor/release b/.meteor/release index 1e7fc5b5..91e05fc1 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.5.1 +METEOR@1.8.0.2 diff --git a/.meteor/versions b/.meteor/versions index acc72ddb..5df67614 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,105 +1,116 @@ -accounts-base@1.3.1 -accounts-password@1.4.0 -allow-deny@1.0.6 -artwells-accounts-guest@0.1.13_1 -autoupdate@1.3.12 -babel-compiler@6.19.4 -babel-runtime@1.0.1 -base64@1.0.10 -binary-heap@1.0.10 -blaze@2.3.2 +accounts-base@1.4.3 +accounts-password@1.5.1 +accounts-ui@1.3.1 +accounts-ui-unstyled@1.4.2 +allow-deny@1.1.0 +artwells-accounts-guest-custom@0.1.13_1 +autoupdate@1.5.1 +babel-compiler@7.2.4 +babel-runtime@1.3.0 +base64@1.0.11 +binary-heap@1.0.11 +blaze@2.3.3 blaze-tools@1.0.10 -boilerplate-generator@1.1.1 +boilerplate-generator@1.6.0 brettle:accounts-login-state@0.0.4 brettle:accounts-patch-ui@0.1.3 buffer@0.0.0 -caching-compiler@1.1.9 -caching-html-compiler@1.0.7 -callback-hook@1.0.10 -check@1.2.5 -ddp@1.3.0 -ddp-client@2.0.0 -ddp-common@1.2.9 +caching-compiler@1.2.1 +caching-html-compiler@1.1.3 +callback-hook@1.1.0 +check@1.3.1 +ddp@1.4.0 +ddp-client@2.3.3 +ddp-common@1.4.0 ddp-rate-limiter@1.0.7 -ddp-server@2.0.0 +ddp-server@2.2.0 deps@1.0.12 -diff-sequence@1.0.7 -dynamic-import@0.1.1 -ecmascript@0.8.2 -ecmascript-runtime@0.4.1 -ecmascript-runtime-client@0.4.3 -ecmascript-runtime-server@0.4.1 -ejson@1.0.13 +diff-sequence@1.1.1 +dynamic-import@0.5.1 +ecmascript@0.12.4 +ecmascript-runtime@0.7.0 +ecmascript-runtime-client@0.8.0 +ecmascript-runtime-server@0.7.1 +ejson@1.1.0 email@1.2.3 -fourseven:scss@4.5.4 +es5-shim@4.8.0 +example-forum@1.12.12 +fetch@0.1.0 +fourseven:scss@4.10.0 geojson-utils@1.0.10 hot-code-push@1.0.4 html-tools@1.0.11 htmljs@1.0.11 -http@1.2.12 -id-map@1.0.9 -jquery@1.11.10 -livedata@1.0.18 -localstorage@1.1.1 -logging@1.1.17 -meteor@1.7.1 -meteor-base@1.1.0 +http@1.4.2 +id-map@1.1.0 +inter-process-messaging@0.1.0 +jquery@1.11.11 +less@2.8.0 +localstorage@1.2.0 +logging@1.1.20 +meteor@1.9.2 meteorhacks:inject-initial@1.0.4 meteorhacks:picker@1.0.3 -minifier-css@1.2.16 -minifier-js@2.1.1 -minimongo@1.2.1 -modules@0.9.2 -modules-runtime@0.8.0 -mongo@1.1.22 -mongo-id@1.0.6 +minifier-css@1.4.1 +minifier-js@2.4.0 +minimongo@1.4.5 +modern-browsers@0.1.3 +modules@0.13.0 +modules-runtime@0.10.3 +mongo@1.6.0 +mongo-decimal@0.1.0 +mongo-dev-server@1.1.0 +mongo-id@1.0.7 networkcanvas-styles@1.7.0 npm-bcrypt@0.9.3 -npm-mongo@2.2.30 +npm-mongo@3.1.1 observe-sequence@1.0.16 -ordered-dict@1.0.9 +ordered-dict@1.1.0 percolatestudio:synced-cron@1.1.0 -promise@0.8.9 -random@1.0.10 -rate-limit@1.0.8 -reactive-dict@1.1.9 +promise@0.11.2 +random@1.1.0 +rate-limit@1.0.9 +reactive-dict@1.2.1 reactive-var@1.0.11 -reload@1.1.11 -retry@1.0.9 -routepolicy@1.0.12 +reload@1.2.0 +retry@1.1.0 +routepolicy@1.1.0 +server-render@0.3.1 service-configuration@1.0.11 -session@1.1.7 +session@1.2.0 sha@1.0.9 -shell-server@0.2.4 +shell-server@0.4.0 +socket-stream-client@0.2.2 spacebars@1.0.15 -spacebars-compiler@1.1.2 -srp@1.0.10 -standard-minifier-css@1.3.4 -standard-minifier-js@2.1.1 -standard-minifiers@1.1.0 -templating@1.1.14 +spacebars-compiler@1.1.3 +srp@1.0.12 +standard-minifier-css@1.5.2 +standard-minifier-js@2.4.0 +static-html@1.2.2 +templating@1.2.15 +templating-compiler@1.2.15 +templating-runtime@1.2.15 templating-tools@1.1.2 -tracker@1.1.3 -ui@1.0.13 +tracker@1.2.0 underscore@1.0.10 -url@1.1.0 -vulcan:accounts@1.7.0 -vulcan:admin@1.7.0 -vulcan:categories@1.7.0 -vulcan:comments@1.7.0 -vulcan:core@1.7.0 -vulcan:email@1.7.0 -vulcan:email-templates@1.7.0 -vulcan:events@1.7.0 -vulcan:forms@1.7.0 -vulcan:i18n@1.7.0 -vulcan:i18n-en-us@1.7.0 -vulcan:lib@1.7.0 +url@1.2.0 +vulcan:accounts@1.12.16 +vulcan:admin@1.12.16 +vulcan:core@1.12.16 +vulcan:debug@1.12.16 +vulcan:email@1.12.16 +vulcan:embed@1.12.16 +vulcan:events@1.12.16 +vulcan:forms@1.12.16 +vulcan:i18n@1.12.16 +vulcan:i18n-en-us@1.12.16 +vulcan:lib@1.12.16 +vulcan:newsletter@1.12.16 vulcan:notifications@1.7.0 -vulcan:posts@1.7.0 -vulcan:routing@1.7.0 +vulcan:routing@1.12.16 vulcan:subscribe@1.7.0 -vulcan:users@1.7.0 -vulcan:voting@1.7.0 -webapp@1.3.17 +vulcan:ui-bootstrap@1.12.16 +vulcan:users@1.12.16 +vulcan:voting@1.12.16 +webapp@1.7.2 webapp-hashing@1.0.9 diff --git a/.vulcan/prestart_vulcan.js b/.vulcan/prestart_vulcan.js new file mode 100644 index 00000000..0712e36d --- /dev/null +++ b/.vulcan/prestart_vulcan.js @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +//Functions +var fs = require('fs'); +function existsSync(filePath){ + try{ + fs.statSync(filePath); + }catch(err){ + if(err.code == 'ENOENT') return false; + } + return true; +} + +function copySync(origin,target){ + try{ + fs.writeFileSync(target, fs.readFileSync(origin)); + }catch(err){ + if(err.code == 'ENOENT') return false; + } + return true; +} + +//Add Definition Colors +const chalk = require('chalk'); + +//Vulcan letters +console.log(chalk.gray(' ___ ___ ')); +console.log(chalk.gray(' '+String.fromCharCode(92))+chalk.redBright(String.fromCharCode(92))+chalk.dim.yellow(String.fromCharCode(92))+chalk.gray(String.fromCharCode(92)+' /')+chalk.dim.yellow('/')+chalk.yellowBright('/')+chalk.gray('/')); +console.log(chalk.gray(' '+String.fromCharCode(92))+chalk.redBright(String.fromCharCode(92))+chalk.dim.yellow(String.fromCharCode(92))+chalk.gray(String.fromCharCode(92))+chalk.gray('/')+chalk.dim.yellow('/')+chalk.yellowBright('/')+chalk.gray('/ Vulcan.js')); +console.log(chalk.gray(' '+String.fromCharCode(92))+chalk.redBright(String.fromCharCode(92))+chalk.dim.yellow(String.fromCharCode(92))+chalk.dim.yellow('/')+chalk.yellowBright('/')+chalk.gray('/ The full-stack React+GraphQL framework')); +console.log(chalk.gray(' ──── ')); + + +var os = require('os'); +var exec = require('child_process').execSync; +var options = { + encoding: 'utf8' +}; +//Check Meteor and install if not installed +var checker = exec("meteor --version", options); +if (!checker.includes("Meteor ")) { +console.log("Vulcan requires Meteor but it's not installed. Trying to Install..."); + //Check platform + if (os.platform()=='darwin') { + //Mac OS platform + console.log("🌋 "+chalk.bold.yellow("Good news you have a Mac and we will install it now! }")); + console.log(exec("curl https://install.meteor.com/ | bash", options)); + } else if (os.platform()=='linux') { + //GNU/Linux platform + console.log("🌋 "+chalk.bold.yellow("Good news you are on GNU/Linux platform and we will install Meteor now!")); + console.log(exec("curl https://install.meteor.com/ | bash", options)); + } else if (os.platform()=='win32') { + //Windows NT platform + console.log("> "+chalk.bold.yellow("Oh no! you are on a Windows platform and you will need to install Meteor Manually!")); + console.log("> "+chalk.dim.yellow("Meteor for Windows is available at: ")+chalk.redBright("https://install.meteor.com/windows")); + process.exit(-1) + } +} else { +//Check exist file settings and create if not exist +if (!existsSync("settings.json")) { + console.log("> "+chalk.bold.yellow("Creating your own settings.json file...\n")); + if (!copySync("sample_settings.json","settings.json")) { + console.log("> "+chalk.bold.red("Error Creating your own settings.json file...check files and permissions\n")); + process.exit(-1); + } +} + + console.log("> "+chalk.bold.yellow("Happy hacking with Vulcan!")); + console.log("> "+chalk.dim.yellow("The docs are available at: ")+chalk.redBright("http://docs.vulcanjs.org")); +} diff --git a/imports/components/account/GuestLoginLink.jsx b/imports/components/accounts/GuestLoginLink.jsx similarity index 100% rename from imports/components/account/GuestLoginLink.jsx rename to imports/components/accounts/GuestLoginLink.jsx diff --git a/imports/components/account/SimpleLoginForm.jsx b/imports/components/accounts/SimpleLoginForm.jsx similarity index 100% rename from imports/components/account/SimpleLoginForm.jsx rename to imports/components/accounts/SimpleLoginForm.jsx diff --git a/imports/components/account/SimpleSignupForm.jsx b/imports/components/accounts/SimpleSignupForm.jsx similarity index 100% rename from imports/components/account/SimpleSignupForm.jsx rename to imports/components/accounts/SimpleSignupForm.jsx diff --git a/imports/components/account/SwitchableLoginForm.jsx b/imports/components/accounts/SwitchableLoginForm.jsx similarity index 92% rename from imports/components/account/SwitchableLoginForm.jsx rename to imports/components/accounts/SwitchableLoginForm.jsx index 062179ad..dadd772d 100644 --- a/imports/components/account/SwitchableLoginForm.jsx +++ b/imports/components/accounts/SwitchableLoginForm.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { Link } from 'react-router'; import PropTypes from 'prop-types'; -import { Components, registerComponent } from 'meteor/vulcan:core'; +import { Components, replaceComponent, withCurrentUser } from 'meteor/vulcan:core'; const LOGIN_MODE = "login"; const SIGNUP_MODE = "signup"; @@ -87,4 +87,4 @@ SwitchableLoginForm.propTypes = { onComplete: PropTypes.func, } -registerComponent('SwitchableLoginForm', SwitchableLoginForm); +replaceComponent('AccountsLoginForm', SwitchableLoginForm); diff --git a/imports/components/categories/CategoriesEditForm.jsx b/imports/components/categories/CategoriesEditForm.jsx index d31e3566..3396fc6d 100644 --- a/imports/components/categories/CategoriesEditForm.jsx +++ b/imports/components/categories/CategoriesEditForm.jsx @@ -1,8 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { intlShape } from 'meteor/vulcan:i18n'; -import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; -import Categories from "meteor/vulcan:categories"; +import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; +import { getFragment } from 'meteor/vulcan:lib'; +import { Categories } from "meteor/example-forum"; const CategoriesEditForm = (props, context) => { diff --git a/imports/components/categories/CategoriesList.jsx b/imports/components/categories/CategoriesList.jsx index ddf7c93a..b2c4ae04 100644 --- a/imports/components/categories/CategoriesList.jsx +++ b/imports/components/categories/CategoriesList.jsx @@ -2,12 +2,9 @@ import { ModalTrigger, Components, registerComponent, withList, Utils } from "me import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'meteor/vulcan:i18n'; -import Button from 'react-bootstrap/lib/Button'; -import DropdownButton from 'react-bootstrap/lib/DropdownButton'; -import MenuItem from 'react-bootstrap/lib/MenuItem'; import { LinkContainer } from 'react-router-bootstrap'; -import { withRouter } from 'react-router' -import Categories from 'meteor/vulcan:categories'; +import { withRouter } from 'react-router'; +import { Categories } from 'meteor/example-forum'; import { withApollo } from 'react-apollo'; class CategoriesList extends PureComponent { @@ -47,7 +44,6 @@ class CategoriesList extends PureComponent { renderEdit() { - return ( }> @@ -107,38 +103,24 @@ class CategoriesList extends PureComponent { const allCategoriesQuery = _.clone(this.props.router.location.query); delete allCategoriesQuery.cat; - return ( -
- } - id="categories-dropdown" - > - - - - - - {this.renderCategories()} - - - - - - } component={}> - - - - - - -
- ) - + return ( + }} + id="categories-list" + className="views" + labelId={'posts.view'} + menuItems={[ + ...views.map(view => ({ + to: { pathname: Utils.getRoutePath('posts.list'), query: { ...query, view: view } }, + labelId: `posts.${view}`, + })), + { + to: `/daily`, + labelId: `posts.daily`, + }, + ]} + /> + ); } } diff --git a/imports/components/categories/CategoriesNewForm.jsx b/imports/components/categories/CategoriesNewForm.jsx index 2756c2dd..fafb03dd 100644 --- a/imports/components/categories/CategoriesNewForm.jsx +++ b/imports/components/categories/CategoriesNewForm.jsx @@ -1,8 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { intlShape } from 'meteor/vulcan:i18n'; -import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; -import Categories from "meteor/vulcan:categories"; +import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; +import { getFragment } from 'meteor/vulcan:lib'; +import { Categories } from "meteor/example-forum"; const CategoriesNewForm = (props, context) => { diff --git a/imports/components/categories/Category.jsx b/imports/components/categories/Category.jsx index 5914f1e3..3fcd9996 100644 --- a/imports/components/categories/Category.jsx +++ b/imports/components/categories/Category.jsx @@ -2,9 +2,9 @@ import { ModalTrigger, Components, registerComponent } from 'meteor/vulcan:core' import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { LinkContainer } from 'react-router-bootstrap'; -import MenuItem from 'react-bootstrap/lib/MenuItem' -import { withRouter } from 'react-router' -import Categories from 'meteor/vulcan:categories'; +// import MenuItem from 'react-bootstrap/lib/MenuItem' +import { withRouter } from 'react-router'; +import { Categories } from 'meteor/example-forum'; class Category extends PureComponent { diff --git a/imports/components/comments/CustomCommentsItem.jsx b/imports/components/comments/CustomCommentsItem.jsx new file mode 100644 index 00000000..e68a3465 --- /dev/null +++ b/imports/components/comments/CustomCommentsItem.jsx @@ -0,0 +1,84 @@ +import { Components, getRawComponent, replaceComponent }from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage /*, intlShape */ } from 'meteor/vulcan:i18n'; +import moment from 'moment'; +import { Comments } from 'meteor/example-forum'; + + +class CustomCommentsItem extends getRawComponent('CommentsItem') { + constructor() { + super(); + ['showEdit', 'editCancelCallback', 'editSuccessCallback', 'removeSuccessCallback'].forEach(methodName => { + this[methodName] = this[methodName].bind(this) + }); + this.state = { + showEdit: false + }; + } + showEdit(event) { + event.preventDefault(); + this.setState({showEdit: true}); + } + + editCancelCallback(event) { + event.preventDefault(); + this.setState({showEdit: false}); + } + + editSuccessCallback() { + this.setState({showEdit: false}); + } + + removeSuccessCallback({documentId}) { + const deleteDocumentSuccess = this.context.intl.formatMessage({id: 'comments.delete_success'}); + this.props.flash(deleteDocumentSuccess, "success"); + // todo: handle events in async callback + // this.context.events.track("comment deleted", {_id: documentId}); + } + + renderComment() { + const htmlBody = {__html: this.props.comment.htmlBody}; + + // const showReplyButton = !this.props.comment.isDeleted && !!this.props.currentUser; + + return ( +
+
+
+ ) + } + + renderEdit() { + + return ( + + ) + } + render() { + const comment = this.props.comment; + + return ( +
+
+
+ +
{moment(new Date(comment.postedAt)).fromNow()}
+ {Comments.options.mutations.edit.check(this.props.currentUser, this.props.comment) && +
+ +
+ } +
+ {this.state.showEdit ? this.renderEdit() : this.renderComment()} +
+
+ ) + } +} + +replaceComponent('CommentsItem', CustomCommentsItem); diff --git a/imports/components/common/CustomFooter.jsx b/imports/components/common/CustomFooter.jsx new file mode 100644 index 00000000..3d956baa --- /dev/null +++ b/imports/components/common/CustomFooter.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { replaceComponent } from 'meteor/vulcan:core'; + +const CustomFooter = () => { + return ( + + ) +} + +replaceComponent('Footer', CustomFooter); \ No newline at end of file diff --git a/imports/components/common/CustomHeader.jsx b/imports/components/common/CustomHeader.jsx new file mode 100644 index 00000000..f56506d7 --- /dev/null +++ b/imports/components/common/CustomHeader.jsx @@ -0,0 +1,41 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withCurrentUser, getSetting, Components, replaceComponent } from 'meteor/vulcan:core'; +import { Posts } from 'meteor/example-forum'; + +const CustomHeader = (props) => { + + const logoUrl = getSetting('logoUrl'); + const siteTitle = getSetting('title', 'My App'); + const tagline = getSetting('tagline'); + + return ( +
+ +
+ +
+ + {tagline ?

{tagline}

: "" } +
+ +
+ +
+ {!!props.currentUser ? : } +
+ +
+ +
+
+ ) +} + +CustomHeader.displayName = "Header"; + +CustomHeader.propTypes = { + currentUser: PropTypes.object, +}; + +replaceComponent('Header', CustomHeader, withCurrentUser); diff --git a/imports/components/common/CustomLayout.jsx b/imports/components/common/CustomLayout.jsx new file mode 100644 index 00000000..96ed4396 --- /dev/null +++ b/imports/components/common/CustomLayout.jsx @@ -0,0 +1,31 @@ +import { Components, replaceComponent, withCurrentUser } from 'meteor/vulcan:core'; +import React, { PropTypes, Component } from 'react'; +import classNames from 'classnames'; +import Helmet from 'react-helmet'; + +const CustomLayout = ({currentUser, children, currentRoute}) => + +
+ + + + + + + + {currentUser ? : null} + + + +
+ + + + {children} + + +
+ +
+ +replaceComponent('Layout', CustomLayout); diff --git a/imports/components/common/CustomLogo.jsx b/imports/components/common/CustomLogo.jsx new file mode 100644 index 00000000..7b93a081 --- /dev/null +++ b/imports/components/common/CustomLogo.jsx @@ -0,0 +1,21 @@ +/* +The original Logo components is defined using React's +functional stateless component syntax, so we redefine +it the same way. +*/ + +import React from 'react'; +import { IndexLink } from 'react-router'; +import Users from 'meteor/vulcan:users'; +import { replaceComponent } from 'meteor/vulcan:core'; + +const CustomLogo = ({logoUrl, siteTitle, currentUser}) => { + return ( +
+

⭐{siteTitle}⭐

+ { currentUser ? Welcome {Users.getDisplayName(currentUser)} 👋 : null} +
+ ) +} + +replaceComponent('Logo', CustomLogo); \ No newline at end of file diff --git a/imports/components/common/Flash.jsx b/imports/components/common/Flash.jsx deleted file mode 100644 index bd12486a..00000000 --- a/imports/components/common/Flash.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { registerComponent } from 'meteor/vulcan:core'; -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import Alert from 'react-bootstrap/lib/Alert' - -class Flash extends PureComponent { - - constructor() { - super(); - this.dismissFlash = this.dismissFlash.bind(this); - } - - componentDidMount() { - this.props.markAsSeen(this.props.message._id); - } - - dismissFlash(e) { - e.preventDefault(); - this.props.clear(this.props.message._id); - } - - render() { - - let flashType = this.props.message.flashType; - flashType = flashType === "error" ? "danger" : flashType; // if flashType is "error", use "danger" instead - - return ( - - {this.props.message.content} - - ) - } -} - -Flash.propTypes = { - message: PropTypes.object.isRequired -} - -registerComponent('Flash', Flash); \ No newline at end of file diff --git a/imports/components/common/FlashMessages.jsx b/imports/components/common/FlashMessages.jsx deleted file mode 100644 index 15bfaa02..00000000 --- a/imports/components/common/FlashMessages.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; -import React from 'react'; - -const FlashMessages = ({messages, clear, markAsSeen}) => { - return ( -
- {messages - .filter(message => message.show) - .map(message => )} -
- ); -} - -FlashMessages.displayName = "FlashMessages"; - -registerComponent('FlashMessages', FlashMessages, withMessages); diff --git a/imports/components/common/Header.jsx b/imports/components/common/Header.jsx deleted file mode 100644 index 86401147..00000000 --- a/imports/components/common/Header.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withCurrentUser, getSetting, Components, registerComponent } from 'meteor/vulcan:core'; - -const Header = (props, context) => { - - const logoUrl = getSetting("logoUrl"); - const siteTitle = getSetting("title", "My App"); - - return ( - - ) -} - -Header.displayName = "Header"; - -Header.propTypes = { - currentUser: PropTypes.object, -}; - -registerComponent('Header', Header, withCurrentUser); diff --git a/imports/components/common/Layout.jsx b/imports/components/common/Layout.jsx deleted file mode 100644 index 95603898..00000000 --- a/imports/components/common/Layout.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import React, { PropTypes, Component } from 'react'; -import classNames from 'classnames'; -import Helmet from 'react-helmet'; - -const Layout = ({currentUser, children, currentRoute}) => - -
- - - - - - - - - {currentUser ? : null} - - - -
- - - - {children} - - -
- -
- -registerComponent('Layout', Layout, withCurrentUser); diff --git a/imports/components/common/SearchForm.jsx b/imports/components/common/SearchForm.jsx deleted file mode 100644 index 5c6a65c8..00000000 --- a/imports/components/common/SearchForm.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import { registerComponent, Components } from 'meteor/vulcan:core'; -import React, { PropTypes, Component } from 'react'; -import { intlShape } from 'meteor/vulcan:i18n'; -import Formsy from 'formsy-react'; -import FRC from 'formsy-react-components'; -import { withRouter, Link } from 'react-router' - -const Input = FRC.Input; - -// see: http://stackoverflow.com/questions/1909441/jquery-keyup-delay -const delay = (function(){ - var timer = 0; - return function(callback, ms){ - clearTimeout (timer); - timer = setTimeout(callback, ms); - }; -})(); - -class SearchForm extends Component{ - - constructor(props) { - super(props); - this.search = this.search.bind(this); - this.state = { - search: props.router.location.query.query || '' - } - } - - // note: why do we need this? - componentWillReceiveProps(nextProps) { - this.setState({ - search: this.props.router.location.query.query || '' - }); - } - - search(data) { - - const router = this.props.router; - const routerQuery = _.clone(router.location.query); - delete routerQuery.query; - - const query = data.searchQuery === '' ? routerQuery : {...routerQuery, query: data.searchQuery}; - - delay(() => { - router.push({pathname: "/", query: query}); - }, 700 ); - - } - - render() { - - const resetQuery = _.clone(this.props.location.query); - delete resetQuery.query; - - return ( -
- - - {this.state.search !== '' ? : null} - -
- ) - } -} - -SearchForm.contextTypes = { - intl: intlShape -}; - -registerComponent('SearchForm', SearchForm, withRouter); \ No newline at end of file diff --git a/imports/components/common/Vote.jsx b/imports/components/common/Vote.jsx deleted file mode 100644 index 803e4495..00000000 --- a/imports/components/common/Vote.jsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { withVote, hasUpvoted, hasDownvoted } from 'meteor/vulcan:voting'; -import { /*FormattedMessage,*/ intlShape } from 'meteor/vulcan:i18n'; -import Users from 'meteor/vulcan:users'; - -class Vote extends PureComponent { - - constructor() { - super(); - this.upvote = this.upvote.bind(this); - this.getActionClass = this.getActionClass.bind(this); - // this.startLoading = this.startLoading.bind(this); - // this.stopLoading = this.stopLoading.bind(this); - this.state = { - loading: false - } - } - - /* - - note: with optimisitc UI, loading functions are not needed - also, setState triggers issues when the component is unmounted - before the vote mutation returns. - - */ - - // startLoading() { - // this.setState({ loading: true }); - // } - - // stopLoading() { - // this.setState({ loading: false }); - // } - - upvote(e) { - e.preventDefault(); - - // this.startLoading(); - - const document = this.props.document; - const collection = this.props.collection; - const user = this.props.currentUser; - - if (Users.isGuest(user)) { - this.props.flash(this.context.intl.formatMessage({id: 'users.please_log_in'})); - // this.stopLoading(); - } else { - const voteType = hasUpvoted(user, document) ? "cancelUpvote" : "upvote"; - this.props.vote({document, voteType, collection, currentUser: this.props.currentUser}).then(result => { - // this.stopLoading(); - }); - } - } - - getActionClass() { - const document = this.props.document; - const user = this.props.currentUser; - - const isUpvoted = hasUpvoted(user, document); - const isDownvoted = hasDownvoted(user, document); - const actionsClass = classNames( - 'vote', - {voted: isUpvoted || isDownvoted}, - {upvoted: isUpvoted}, - {downvoted: isDownvoted} - ); - - return actionsClass; - } - - render() { - return ( - - ) - } - -} - -Vote.propTypes = { - document: PropTypes.object.isRequired, // the document to upvote - collection: PropTypes.object.isRequired, // the collection containing the document - vote: PropTypes.func.isRequired, // mutate function with callback inside - currentUser: PropTypes.object, // user might not be logged in, so don't make it required -}; - -Vote.contextTypes = { - intl: intlShape -}; - -registerComponent('Vote', Vote, withMessages, withVote); diff --git a/imports/components/index.js b/imports/components/index.js index db5b0ea4..5d796083 100644 --- a/imports/components/index.js +++ b/imports/components/index.js @@ -1,49 +1,36 @@ -// common +// account/login -import './common/Layout.jsx'; -import './common/Header.jsx'; -import './common/Logo.jsx'; -import './common/Flash.jsx'; -import './common/FlashMessages.jsx'; -import './common/SearchForm.jsx'; -import './common/Vote.jsx'; +import './accounts/SwitchableLoginForm.jsx'; +import './accounts/SimpleLoginForm.jsx'; +import './accounts/SimpleSignupForm.jsx'; +import './accounts/GuestLoginLink.jsx'; -// account/login +// common + +import './common/CustomLayout.jsx'; +import './common/CustomFooter.jsx'; +import './common/CustomHeader.jsx'; +// import './common/CustomVote.jsx'; -import './account/SimpleLoginForm.jsx'; -import './account/SimpleSignupForm.jsx'; -import './account/SwitchableLoginForm.jsx'; -import './account/GuestLoginLink.jsx'; // posts -import './posts/SuggestedPosts.jsx'; import './posts/CreatePostFlow.jsx'; -import './posts/PostsNewButton.jsx'; -import './posts/PostsLoadMore.jsx'; -import './posts/PostsNoMore.jsx'; -import './posts/PostsNoResults.jsx'; -import './posts/PostsItem.jsx'; -import './posts/PostsLoading.jsx'; -import './posts/PostsViews.jsx'; -import './posts/PostsList.jsx'; -import './posts/PostsListHeader.jsx'; -import './posts/PostsCategories.jsx'; -import './posts/PostsCommenters.jsx'; -import './posts/PostsPage.jsx'; -import './posts/PostsStats.jsx'; -import './posts/PostsEditForm.jsx'; -import './posts/PostsNewForm.jsx'; -import './posts/PostsCommentsThread.jsx'; + +import './posts/SuggestionList.jsx'; +import './posts/SuggestedPosts.jsx'; +import './posts/CustomPostsCommentsThread.jsx'; +import './posts/CustomPostCommenters.jsx'; +import './posts/CustomPostsItem.jsx'; +import './posts/CustomPostsPage.jsx'; +import './posts/CustomPostsListHeader.jsx'; +import './posts/CustomPostsNewForm.jsx'; +import './posts/CustomPostsNewButton.jsx'; // comments -import './comments/CommentsItem.jsx'; -import './comments/CommentsList.jsx'; -import './comments/CommentsNode.jsx'; -import './comments/CommentsNewForm.jsx'; -import './comments/CommentsEditForm.jsx'; -import './comments/CommentsLoadMore.jsx'; +import './comments/CustomCommentsItem.jsx'; + // categories @@ -55,11 +42,8 @@ import './categories/CategoriesNewForm.jsx'; // users -import './users/UsersAccountStatus.jsx'; -import './users/UsersEditForm.jsx'; -import './users/UsersProfile.jsx'; -import './users/UsersProfileCheck.jsx'; -import './users/UsersAvatar.jsx'; -import './users/UsersName.jsx'; -import './users/UsersMenu.jsx'; -import './users/UsersAccountMenu.jsx'; +import './users/CustomUsersEditForm.jsx'; +import './users/CustomUsersProfile.jsx'; +// import './users/CustomUsersName.jsx'; + + diff --git a/imports/components/posts/CreatePostFlow.jsx b/imports/components/posts/CreatePostFlow.jsx index a343d91f..9df0b362 100644 --- a/imports/components/posts/CreatePostFlow.jsx +++ b/imports/components/posts/CreatePostFlow.jsx @@ -1,8 +1,7 @@ import React from 'react'; import { Components, registerComponent, getComponent, withCurrentUser } from 'meteor/vulcan:core'; -import { withRouter } from 'react-router' +import { withRouter } from 'react-router'; import Users from 'meteor/vulcan:users'; -import {Button} from 'react-bootstrap'; import PropTypes from 'prop-types'; const LOGIN_STEP = 'login'; diff --git a/imports/components/posts/CustomPostCommenters.jsx b/imports/components/posts/CustomPostCommenters.jsx new file mode 100644 index 00000000..33374b0a --- /dev/null +++ b/imports/components/posts/CustomPostCommenters.jsx @@ -0,0 +1,22 @@ +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import { Link } from 'react-router'; +import { Posts } from 'meteor/example-forum'; + +const CustomPostsCommenters = ({post}) => { + return ( +
+
+ + + {post.commentCount} + Comments + +
+
+ ); +}; + +CustomPostsCommenters.displayName = "PostsCommenters"; + +replaceComponent('PostsCommenters', CustomPostsCommenters); \ No newline at end of file diff --git a/imports/components/posts/CustomPostsCommentsThread.jsx b/imports/components/posts/CustomPostsCommentsThread.jsx new file mode 100644 index 00000000..4077dec7 --- /dev/null +++ b/imports/components/posts/CustomPostsCommentsThread.jsx @@ -0,0 +1,112 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { Components, replaceComponent, Utils } from 'meteor/vulcan:core'; +import { Comments } from 'meteor/example-forum'; + +class CustomPostsCommentThread extends React.Component { + + constructor(props) { + super(props); + + this.state = { + replyComment: {}, + }; + + this.handleNewComment = this.handleNewComment.bind(this); + } + + prefilledReply() { + const quoted = this.state.replyComment.body; + if (quoted) { + return quoted.split('\n').map(line => `> ${line}`).join('/n') + '\n\n'; + } + + return ""; + } + + componentDidMount() { + if (this.props.currentUser && + this.props.post.userId == this.props.currentUser._id) { + // Ugly hack- refetch own posts after initial load because auto-subscribing to self + // posts happens async. Must refetch to get correct status for subscribe button after initially + // creating a post. + setTimeout(() => { + this.props.refetchUser(); + }, 500); + } + } + + handleNewComment(replyComment = {}) { + this.setState({ replyComment }); + window.scrollBy(0, 9999); // Just scroll to the bottom of the page + // TODO: focus input + } + + renderSubscribeButton() { + const user = this.props.currentUser; + + if (user && !user.isGuest) { + return ( + + ); + } + return null; + } + + render() { + const {loading, terms: { postId }, results, totalCount, currentUser} = this.props; + + if (loading) { + + return
+ + } else { + + const resultsClone = _.map(results, _.clone); // we don't want to modify the objects we got from props + const nestedComments = Utils.unflatten(resultsClone, {idProperty: '_id', parentIdProperty: 'parentCommentId'}); + + return ( +
+

+ + {this.renderSubscribeButton()} +

+ +
+

+ +
+
+ ); + } + } + +} +// +// PostsCommentsThread.displayName = 'PostsCommentsThread'; +// +CustomPostsCommentThread.propTypes = { + currentUser: PropTypes.object, + post: PropTypes.object, +}; +// +// const options = { +// collection: Comments, +// queryName: 'commentsListQuery', +// fragmentName: 'CommentsList', +// limit: 0, +// }; + +// registerComponent('PostsCommentsThread', PostsCommentsThread, [withList, options], withCurrentUser); +replaceComponent('PostsCommentsThread', CustomPostsCommentThread) \ No newline at end of file diff --git a/imports/components/posts/CustomPostsItem.jsx b/imports/components/posts/CustomPostsItem.jsx new file mode 100644 index 00000000..97dda2b8 --- /dev/null +++ b/imports/components/posts/CustomPostsItem.jsx @@ -0,0 +1,63 @@ +import { Components, getRawComponent, replaceComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { Link } from 'react-router'; +import { Posts } from 'meteor/example-forum'; +import moment from 'moment'; + +class CustomPostsItem extends getRawComponent('PostsItem') { + + render() { + + const post = this.props.post; + + let postClass = "posts-item"; + if (post.sticky) postClass += " posts-sticky"; + + // ⭐ custom code starts here ⭐ + if (post.color) { + postClass += " post-"+post.color; + } + // ⭐ custom code ends here ⭐ + + return ( +
+ +
+ +
+ +
+ +

+ + {post.title} + + {this.renderCategories()} +

+ +
+ {post.user?
: null} +
{post.postedAt ? moment(new Date(post.postedAt)).fromNow() : }
+
+ + {!post.commentCount || post.commentCount === 0 ? : + post.commentCount === 1 ? : + + } + +
+ {this.props.currentUser && this.props.currentUser.isAdmin ? : null} + {Posts.options.mutations.edit.check(this.props.currentUser, post) ? this.renderActions() : null} +
+ +
+ + {this.renderCommenters()} + +
+ ) + } +} + +replaceComponent('PostsItem', CustomPostsItem); diff --git a/imports/components/posts/CustomPostsListHeader.jsx b/imports/components/posts/CustomPostsListHeader.jsx new file mode 100644 index 00000000..ec211834 --- /dev/null +++ b/imports/components/posts/CustomPostsListHeader.jsx @@ -0,0 +1,22 @@ +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import React from 'react'; + +const CustomPostsListHeader = () => { + + return ( +
+
+
+ +
+ + + +
+
+ ) +} + +CustomPostsListHeader.displayName = "CustomPostsListHeader"; + +replaceComponent('PostsListHeader', CustomPostsListHeader); diff --git a/imports/components/posts/CustomPostsNewButton.jsx b/imports/components/posts/CustomPostsNewButton.jsx new file mode 100644 index 00000000..e1417f65 --- /dev/null +++ b/imports/components/posts/CustomPostsNewButton.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import {Components, replaceComponent} from 'meteor/vulcan:core'; +import {FormattedMessage, intlShape} from "meteor/vulcan:i18n"; +import PropTypes from "prop-types"; + +const CustomPostsNewButton = (props, context) => { + const size = props.currentUser ? 'lg' : 'sm'; + const button = ; + return ( + + + + ) +}; + +CustomPostsNewButton.contextTypes = { + closeCallback: PropTypes.func, + intl: intlShape +}; + +replaceComponent('PostsNewButton', CustomPostsNewButton); diff --git a/imports/components/posts/CustomPostsNewForm.jsx b/imports/components/posts/CustomPostsNewForm.jsx new file mode 100644 index 00000000..90429cc0 --- /dev/null +++ b/imports/components/posts/CustomPostsNewForm.jsx @@ -0,0 +1,72 @@ +import { Components, replaceComponent, withMessages } from 'meteor/vulcan:core'; +import { getFragment } from 'meteor/vulcan:lib'; +import { Posts } from "meteor/example-forum"; +import React from 'react'; +import PropTypes from 'prop-types'; +import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; +import { withRouter } from 'react-router'; +import SimpleSchema from 'simpl-schema'; + +const formSchema = new SimpleSchema({ + title: { + type: String, + optional: false, + max: 500, + viewableBy: ['guests'], + insertableBy: ['members'], + editableBy: ['members'], + control: "text", + order: 20 + }, + body: { + type: String, + optional: true, + max: 3000, + viewableBy: ['guests'], + insertableBy: ['members'], + editableBy: ['members'], + control: "textarea", + order: 30 + }, +})._schema; + +const CustomPostsNewForm = (props, context) => +

} + > +
+ { + props.closeModal(); + props.router.push({pathname: props.redirect || Posts.getPageUrl(post)}); + // props.flash(context.intl.formatMessage({id: "posts.created_message"}), "success"); + props.flash({ id: 'posts.created_message', type: 'success'}); + + }} + /> +
+
+ +CustomPostsNewForm.propTypes = { + closeModal: PropTypes.func, + router: PropTypes.object, + flash: PropTypes.func, + redirect: PropTypes.string, + title: PropTypes.string, + body: PropTypes.string, +} + +CustomPostsNewForm.contextTypes = { + closeCallback: PropTypes.func, + intl: intlShape +}; + +CustomPostsNewForm.displayName = "PostsNewForm"; + +replaceComponent('PostsNewForm', CustomPostsNewForm, withRouter, withMessages); diff --git a/imports/components/posts/CustomPostsPage.jsx b/imports/components/posts/CustomPostsPage.jsx new file mode 100644 index 00000000..945a832f --- /dev/null +++ b/imports/components/posts/CustomPostsPage.jsx @@ -0,0 +1,45 @@ +import { Components, replaceComponent, withDocument, withCurrentUser, getActions, withMutation } from 'meteor/vulcan:core'; +import { Posts } from 'meteor/example-forum'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; + +class CustomPostsPage extends Component { + + render() { + if (this.props.loading && !this.props.document) { + + return
+ + } else if (!this.props.document) { + + return
+ + } else { + const post = this.props.document; + + const htmlBody = {__html: post.htmlBody}; + + return ( +
+ + + + + + {post.htmlBody ?
: null} + + + +
+ ); + + } + } +} + +replaceComponent('PostsPage', CustomPostsPage); + diff --git a/imports/components/posts/PostsCommentsThread.jsx b/imports/components/posts/PostsCommentsThread.jsx deleted file mode 100644 index a7f3250c..00000000 --- a/imports/components/posts/PostsCommentsThread.jsx +++ /dev/null @@ -1,111 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; -import { withList, withCurrentUser, Components, registerComponent, Utils } from 'meteor/vulcan:core'; -import Comments from 'meteor/vulcan:comments'; - -class PostsCommentsThread extends React.Component { - - constructor(props) { - super(props); - - this.state = { - replyComment: {}, - }; - - this.handleNewComment = this.handleNewComment.bind(this); - } - - prefilledReply() { - const quoted = this.state.replyComment.body; - if (quoted) { - return quoted.split('\n').map(line => `> ${line}`).join('/n') + '\n\n'; - } - - return ""; - } - - componentDidMount() { - if (this.props.currentUser && - this.props.post.userId == this.props.currentUser._id) { - // Ugly hack- refetch own posts after initial load because auto-subscribing to self - // posts happens async. Must refetch to get correct status for subscribe button after initially - // creating a post. - setTimeout(() => { - this.props.refetchUser(); - }, 500); - } - } - - handleNewComment(replyComment = {}) { - this.setState({ replyComment }); - window.scrollBy(0, 9999); // Just scroll to the bottom of the page - // TODO: focus input - } - - renderSubscribeButton() { - const user = this.props.currentUser; - - if (user && !user.isGuest) { - return ( - - ); - } - return null; - } - - render() { - const {loading, terms: { postId }, results, totalCount, currentUser} = this.props; - - if (loading) { - - return
- - } else { - - const resultsClone = _.map(results, _.clone); // we don't want to modify the objects we got from props - const nestedComments = Utils.unflatten(resultsClone, {idProperty: '_id', parentIdProperty: 'parentCommentId'}); - - return ( -
-

- - {this.renderSubscribeButton()} -

- -
-

- -
-
- ); - } - } - -} - -PostsCommentsThread.displayName = 'PostsCommentsThread'; - -PostsCommentsThread.propTypes = { - currentUser: PropTypes.object, - post: PropTypes.object, -}; - -const options = { - collection: Comments, - queryName: 'commentsListQuery', - fragmentName: 'CommentsList', - limit: 0, -}; - -registerComponent('PostsCommentsThread', PostsCommentsThread, [withList, options], withCurrentUser); diff --git a/imports/components/posts/PostsLoadMore.jsx b/imports/components/posts/PostsLoadMore.jsx deleted file mode 100644 index 1a68d3a5..00000000 --- a/imports/components/posts/PostsLoadMore.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Components, registerComponent } from 'meteor/vulcan:core'; -import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; - -const PostsLoadMore = ({loading, loadMore, count, totalCount}) => { - if (loading) { - return ( - - ); - } - - return ( - - ); -} - -PostsLoadMore.displayName = "PostsLoadMore"; - -registerComponent('PostsLoadMore', PostsLoadMore); diff --git a/imports/components/posts/PostsNewForm.jsx b/imports/components/posts/PostsNewForm.jsx deleted file mode 100644 index 751fd1b2..00000000 --- a/imports/components/posts/PostsNewForm.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; -import Posts from "meteor/vulcan:posts"; -import React from 'react'; -import PropTypes from 'prop-types'; -import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; -import { withRouter } from 'react-router' -import SimpleSchema from 'simpl-schema'; - -const formSchema = new SimpleSchema({ - title: { - type: String, - optional: false, - max: 500, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - control: "text", - order: 20 - }, - body: { - type: String, - optional: true, - max: 3000, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - control: "textarea", - order: 30 - }, -})._schema; - -const PostsNewForm = (props, context) => -

} - > -
- { - props.closeModal(); - props.router.push({pathname: props.redirect || Posts.getPageUrl(post)}); - props.flash(context.intl.formatMessage({id: "posts.created_message"}), "success"); - }} - /> -
-
- -PostsNewForm.propTypes = { - closeModal: PropTypes.func, - router: PropTypes.object, - flash: PropTypes.func, - redirect: PropTypes.string, - title: PropTypes.string, - body: PropTypes.string, -} - -PostsNewForm.contextTypes = { - closeCallback: PropTypes.func, - intl: intlShape -}; - -PostsNewForm.displayName = "PostsNewForm"; - -registerComponent('PostsNewForm', PostsNewForm, withRouter, withMessages); diff --git a/imports/components/posts/PostsViews.jsx b/imports/components/posts/PostsViews.jsx deleted file mode 100644 index 70db66d6..00000000 --- a/imports/components/posts/PostsViews.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import { registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import DropdownButton from 'react-bootstrap/lib/DropdownButton'; -import MenuItem from 'react-bootstrap/lib/MenuItem'; -import { LinkContainer } from 'react-router-bootstrap'; -import { withRouter } from 'react-router' -import Users from 'meteor/vulcan:users'; - -const PostsViews = (props, context) => { - - let views = ["top", "new", "best"]; - const adminViews = ["pending", "rejected", "scheduled"]; - - if (Users.canDo(props.currentUser, "posts.edit.all")) { - views = views.concat(adminViews); - } - - const query = _.clone(props.router.location.query); - - return ( -
- - {views.map(view => - - - - - - )} - -
- ) -} - -PostsViews.propTypes = { - currentUser: PropTypes.object, - defaultView: PropTypes.string -}; - -PostsViews.defaultProps = { - defaultView: "top" -}; - -PostsViews.contextTypes = { - currentRoute: PropTypes.object, - intl: intlShape -}; - -PostsViews.displayName = "PostsViews"; - -registerComponent('PostsViews', PostsViews, withCurrentUser, withRouter); diff --git a/imports/components/posts/SuggestedPosts.jsx b/imports/components/posts/SuggestedPosts.jsx index 503fd322..6829801c 100644 --- a/imports/components/posts/SuggestedPosts.jsx +++ b/imports/components/posts/SuggestedPosts.jsx @@ -1,132 +1,83 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Accounts } from 'meteor/accounts-base'; -import { Components, registerComponent, withCurrentUser, withList } from 'meteor/vulcan:core'; -import { withApollo } from 'react-apollo'; -import { intlShape } from 'meteor/vulcan:i18n'; -import Posts from "meteor/vulcan:posts"; -import { Link } from 'react-router'; +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import { Posts } from "meteor/example-forum"; const delay = (function(){ - var timer = 0; - return function(callback, ms){ - clearTimeout (timer); - timer = setTimeout(callback, ms); - }; + var timer = 0; + return function(callback, ms){ + clearTimeout (timer); + timer = setTimeout(callback, ms); + }; })(); -class SuggestionList extends React.Component { +class SuggestedPosts extends React.Component { + constructor(props) { + super(props); - renderPostRow(post) { - return ( - -

{post.title}

- {post.body ? -
{post.body.slice(0, 200)}
: null - } - - ); - } + this.handleQueryUpdate = this.handleQueryUpdate.bind(this); + this.handleCreate = this.handleCreate.bind(this); - render() { - if (!this.props.terms.query) { - // Don't show suggestions for an empty query - return null; + this.state = { + query: "", + debouncedQuery: "", + }; } - const results = this.props.results; - if (!results || !results.length) { - return null; + searchTerms() { + return { + query: this.state.debouncedQuery + }; } - const posts = results.slice(0, 5).map(post => this.renderPostRow(post)); - - return ( -
-

Comment on an existing suggestion:

-
- {posts} -
-
- ) - } -} - -const options = { - collection: Posts, - queryName: 'postsListQuery', - fragmentName: 'PostsList', -}; - -const WrappedSuggestionList = withList(options)(SuggestionList); - -class SuggestedPosts extends React.Component { - constructor(props) { - super(props); - - this.handleQueryUpdate = this.handleQueryUpdate.bind(this); - this.handleCreate = this.handleCreate.bind(this); + handleQueryUpdate(evt) { + const query = evt.target.value; + this.setState({ query }); - this.state = { - query: "", - debouncedQuery: "", - }; - } - - searchTerms() { - return { - query: this.state.debouncedQuery - }; - } - - handleQueryUpdate(evt) { - const query = evt.target.value; - this.setState({ query }); - - delay(() => { - this.setState({ debouncedQuery: query }); - }, 500 ); - } - - handleCreate(event) { - event.preventDefault(); - this.props.onComplete({ title: this.state.query }); - } - - render() { - return ( -
-
-
- - -
+ delay(() => { + this.setState({ debouncedQuery: query }); + }, 500 ); + } - + handleCreate(event) { + event.preventDefault(); + this.props.onComplete({ title: this.state.query }); + } - - -
- ); - } + render() { + return ( +
+
+
+ + +
+ + + + + +
+ ); + } } SuggestedPosts.displayName = "SuggestedPosts"; SuggestedPosts.propTypes = { - onComplete: PropTypes.func.isRequired, - results: PropTypes.array, - terms: PropTypes.object, - hasMore: PropTypes.bool, - loading: PropTypes.bool, - count: PropTypes.number, - totalCount: PropTypes.number, - loadMore: PropTypes.func, - showHeader: PropTypes.bool, + onComplete: PropTypes.func.isRequired, + results: PropTypes.array, + terms: PropTypes.object, + hasMore: PropTypes.bool, + loading: PropTypes.bool, + count: PropTypes.number, + totalCount: PropTypes.number, + loadMore: PropTypes.func, + showHeader: PropTypes.bool, }; -registerComponent('SuggestedPosts', SuggestedPosts, withCurrentUser, ); +registerComponent('SuggestedPosts', SuggestedPosts, withCurrentUser ); \ No newline at end of file diff --git a/imports/components/posts/SuggestionList.jsx b/imports/components/posts/SuggestionList.jsx new file mode 100644 index 00000000..1c38092d --- /dev/null +++ b/imports/components/posts/SuggestionList.jsx @@ -0,0 +1,50 @@ +import React from "react"; +import {Link} from "react-router"; +import {registerComponent} from "meteor/vulcan:lib"; +import {withList} from "meteor/vulcan:core"; +import { Posts } from "meteor/example-forum"; + +class SuggestionList extends React.Component { + + renderPostRow(post) { + return ( + +

{post.title}

+ {post.body ? +
{post.body.slice(0, 200)}
: null + } + + ); + } + + render() { + if (!this.props.terms.query) { + // Don't show suggestions for an empty query + return null; + } + + const results = this.props.results; + if (!results || !results.length) { + return null; + } + + const posts = results.slice(0, 5).map(post => this.renderPostRow(post)); + + return ( +
+

Comment on an existing suggestion:

+
+ {posts} +
+
+ ) + } +} + +const options = { + collection: Posts, + queryName: 'postsListQuery', + fragmentName: 'PostsList', +}; + +registerComponent('SuggestionList', SuggestionList, [withList, options]); \ No newline at end of file diff --git a/imports/components/users/CustomUsersEditForm.jsx b/imports/components/users/CustomUsersEditForm.jsx new file mode 100644 index 00000000..d8caac23 --- /dev/null +++ b/imports/components/users/CustomUsersEditForm.jsx @@ -0,0 +1,76 @@ +import { Components, replaceComponent} from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import Users from 'meteor/vulcan:users'; +import { STATES } from 'meteor/vulcan:accounts'; + +class CustomUsersEditForm extends React.Component { + constructor(props) { + super(props); + + this.state = { + username: props.terms && props.terms.slug + } + } + + componentDidMount() { + if (!this.props.terms.slug) { + this.setState({ + username: Users.getUserNameById(this.props.terms.documentId) + }); + } + } + + render() { + return ( + } + > +
+

+ +
+ }> + + +
+ +
+ +
+ {this.state.username} +
+
+ + { + this.props.flash({ id: 'users.edit_success', properties: {name: Users.getDisplayName(user)}, type: 'success'}) + + // this.props.flash({ id: 'users.edit_success', name: Users.getDisplayName(user), type: 'success'}); + }} + showRemove={true} + /> +
+
+ ); + } + +}; + + +CustomUsersEditForm.propTypes = { + terms: PropTypes.object, // a user is defined by its unique _id or its unique slug +}; + +CustomUsersEditForm.contextTypes = { + intl: intlShape +}; + +CustomUsersEditForm.displayName = 'UsersEditForm'; + +replaceComponent('UsersEditForm', CustomUsersEditForm); diff --git a/imports/components/users/CustomUsersProfile.jsx b/imports/components/users/CustomUsersProfile.jsx new file mode 100644 index 00000000..193a7136 --- /dev/null +++ b/imports/components/users/CustomUsersProfile.jsx @@ -0,0 +1,80 @@ +import { Components, replaceComponent, registerComponent, withCurrentUser, withDocument} from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import Users from 'meteor/vulcan:users'; +import { Link } from 'react-router'; + +class CustomUsersProfile extends React.Component { + get user() { + return this.props.document; + } + + renderBio() { + if (!this.user || !this.user.htmlBio) { return null; } + + return ( +
+ +
+
+ ); + } + + renderContactInfo() { + //console.log(this.user); + if (!this.user || !this.user.contactInfo) { return null; } + + return ( +
+ +
{this.user.contactInfo}
+
+ ); + } + + render() { + if (this.props.loading) { + return
+ } else if (!this.user) { + console.log(`// missing user (_id/slug: ${this.props.documentId || this.props.slug})`); + return
+ } else { + const user = this.user; + + const terms = { view: "userPosts", userId: user._id }; + + return ( +
+ +

{Users.getDisplayName(user)}

+ + + + +
+ + {this.renderBio()} + {this.renderContactInfo()} + +

+ +
+ ) + } + } +} + +CustomUsersProfile.propTypes = { + // document: PropTypes.object.isRequired, +} + +CustomUsersProfile.displayName = "CustomUsersProfile"; + +const options = { + collection: Users, + queryName: 'usersSingleQuery', + fragmentName: 'UsersProfile', +}; + +// registerComponent('CustomUsersProfile', CustomUsersProfile, withCurrentUser, [withDocument, options]); +replaceComponent('UsersProfile', CustomUsersProfile) \ No newline at end of file diff --git a/imports/components/users/UsersAccountMenu.jsx b/imports/components/users/UsersAccountMenu.jsx deleted file mode 100644 index c39c77b2..00000000 --- a/imports/components/users/UsersAccountMenu.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Components, registerComponent } from 'meteor/vulcan:core'; -import React, { PropTypes, Component } from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; -import { NavDropdown, Dropdown } from 'react-bootstrap'; - -const DropdownItem = (props) => { - return ( -
- {props.children} -
- ); -} - -const UsersAccountMenu = () => { - - return ( - - - - - - ) -}; - -UsersAccountMenu.displayName = "UsersAccountMenu"; - -registerComponent('UsersAccountMenu', UsersAccountMenu); diff --git a/imports/components/users/UsersAccountStatus.jsx b/imports/components/users/UsersAccountStatus.jsx deleted file mode 100644 index bbc0c6aa..00000000 --- a/imports/components/users/UsersAccountStatus.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Users from 'meteor/vulcan:users'; -import { withCurrentUser, Components, registerComponent } from 'meteor/vulcan:core'; - -const UsersAccountStatus = (props, context) => { - return ( - !!props.currentUser ? - : - - ) -} - -UsersAccountStatus.displayName = 'UsersAccountStatus'; - -UsersAccountStatus.propTypes = { - currentUser: PropTypes.object, -} - -registerComponent('UsersAccountStatus', UsersAccountStatus, withCurrentUser); diff --git a/imports/components/users/UsersAvatar.jsx b/imports/components/users/UsersAvatar.jsx deleted file mode 100644 index f0393e38..00000000 --- a/imports/components/users/UsersAvatar.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import { registerComponent } from 'meteor/vulcan:core'; -import React from 'react'; -import PropTypes from 'prop-types'; -import Users from 'meteor/vulcan:users'; -import { Link } from 'react-router'; -import classNames from 'classnames'; - -const UsersAvatar = ({className, user, link}) => { - const gravatarUrl = user.avatarUrl || Users.avatar.getGravatarUrl(user); - - let avatar; - if (Users.isGuest(user)) { - avatar = ; - } else if (gravatarUrl) { - avatar = {Users.getDisplayName(user)}; - } else { - avatar = {Users.avatar.getInitials(user) || '?'}; - } - - const url = Users.getProfileUrl(user); - - return ( -
- {link && url ? - - {avatar} - - : {avatar} - } -
- ); - -} - -UsersAvatar.propTypes = { - user: PropTypes.object.isRequired, - size: PropTypes.string, - link: PropTypes.bool -} - -UsersAvatar.defaultProps = { - size: 'medium', - link: true -} - -UsersAvatar.displayName = 'UsersAvatar'; - -registerComponent('UsersAvatar', UsersAvatar); diff --git a/imports/components/users/UsersEditForm.jsx b/imports/components/users/UsersEditForm.jsx deleted file mode 100644 index a68d8aea..00000000 --- a/imports/components/users/UsersEditForm.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import { Components, registerComponent, withCurrentUser, withMessages } from 'meteor/vulcan:core'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import Users from 'meteor/vulcan:users'; -import { STATES } from 'meteor/vulcan:accounts'; - -class UsersEditForm extends React.Component { - constructor(props) { - super(props); - - this.state = { - username: props.terms && props.terms.slug - } - } - - componentDidMount() { - if (!this.props.terms.slug) { - this.setState({ - username: Users.getUserNameById(this.props.terms.documentId) - }); - } - } - - render() { - return ( - } - > -
-

- -
- }> - - -
- -
- -
- {this.state.username} -
-
- - { - this.props.flash(this.context.intl.formatMessage({ id: 'users.edit_success' }, { name: Users.getDisplayName(user) }), 'success') - }} - showRemove={true} - /> -
-
- ); - } - -}; - - -UsersEditForm.propTypes = { - terms: PropTypes.object, // a user is defined by its unique _id or its unique slug -}; - -UsersEditForm.contextTypes = { - intl: intlShape -}; - -UsersEditForm.displayName = 'UsersEditForm'; - -registerComponent('UsersEditForm', UsersEditForm, withMessages, withCurrentUser); diff --git a/imports/components/users/UsersMenu.jsx b/imports/components/users/UsersMenu.jsx deleted file mode 100644 index fb475a7c..00000000 --- a/imports/components/users/UsersMenu.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; -import { Meteor } from 'meteor/meteor'; -import NavDropdown from 'react-bootstrap/lib/NavDropdown'; -import MenuItem from 'react-bootstrap/lib/MenuItem'; -import { LinkContainer } from 'react-router-bootstrap'; -import Users from 'meteor/vulcan:users'; -import { withApollo } from 'react-apollo'; - -const UsersMenu = ({currentUser, client}) => - - {currentUser.isGuest ? - null : - - - - } - - {currentUser.isGuest ? - null : - - - - } - - { - currentUser.isAdmin ? - - Admin - - : null - } - Meteor.logout(() => client.resetStore())}> - - - -UsersMenu.propsTypes = { - currentUser: PropTypes.object, - client: PropTypes.object, -}; - -registerComponent('UsersMenu', UsersMenu, withCurrentUser, withApollo); diff --git a/imports/components/users/UsersName.jsx b/imports/components/users/UsersName.jsx deleted file mode 100644 index 45c17e22..00000000 --- a/imports/components/users/UsersName.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { registerComponent } from 'meteor/vulcan:core'; -import React from 'react'; -import PropTypes from 'prop-types'; -import Users from 'meteor/vulcan:users'; -import { Link } from 'react-router'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; - -const UsersName = ({ user }) => { - if (Users.isGuest(user)) { - return ( -
Guest
- ); - } - - const flair = Users.isAdmin(user) ? - : null; - - return ( - - - {Users.getDisplayName(user)} - - {flair} - - ); -} - -UsersName.propTypes = { - user: PropTypes.object.isRequired, -} - -UsersName.displayName = 'UsersName'; - -registerComponent('UsersName', UsersName); diff --git a/imports/components/users/UsersProfile.jsx b/imports/components/users/UsersProfile.jsx deleted file mode 100644 index d8187216..00000000 --- a/imports/components/users/UsersProfile.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Components, registerComponent, withDocument, withCurrentUser } from 'meteor/vulcan:core'; -import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; -import Users from 'meteor/vulcan:users'; -import { Link } from 'react-router'; - -class UsersProfile extends React.Component { - get user() { - return this.props.document; - } - - renderBio() { - if (!this.user || !this.user.htmlBio) { return null; } - - return ( -
- -
-
- ); - } - - renderContactInfo() { - if (!this.user || !this.user.contactInfo) { return null; } - - return ( -
- -
{this.user.contactInfo}
-
- ); - } - - render() { - if (this.props.loading) { - return
- } else if (!this.user) { - console.log(`// missing user (_id/slug: ${this.props.documentId || this.props.slug})`); - return
- } else { - const user = this.user; - - const terms = { view: "userPosts", userId: user._id }; - - return ( -
- -

{Users.getDisplayName(user)}

- - - - -
- - {this.renderBio()} - {this.renderContactInfo()} - -

- -
- ) - } - } -} - -UsersProfile.propTypes = { - // document: PropTypes.object.isRequired, -} - -UsersProfile.displayName = "UsersProfile"; - -const options = { - collection: Users, - queryName: 'usersSingleQuery', - fragmentName: 'UsersProfile', -}; - -registerComponent('UsersProfile', UsersProfile, withCurrentUser, [withDocument, options]); diff --git a/imports/components/users/UsersProfileCheck.jsx b/imports/components/users/UsersProfileCheck.jsx deleted file mode 100644 index 2caa2ea0..00000000 --- a/imports/components/users/UsersProfileCheck.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Modal from 'react-bootstrap/lib/Modal' -import Users from 'meteor/vulcan:users'; -import { withDocument, Components, registerComponent, withMessages } from 'meteor/vulcan:core'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import { gql } from 'react-apollo'; - -const UsersProfileCheck = ({currentUser, document, loading, flash}, context) => { - - // we're loading all fields marked as "mustComplete" using withDocument - const userMustCompleteFields = document; - - // if user is not logged in, or userMustCompleteFields is still loading, don't return anything - if (!currentUser || loading) { - - return null; - - } else { - - // return fields that are required by the schema but haven't been filled out yet - const fieldsToComplete = _.filter(Users.getRequiredFields(), fieldName => { - return !userMustCompleteFields[fieldName]; - }); - - if (fieldsToComplete.length > 0) { - - return ( - - - - - - { - const newUser = {...currentUser, ...user}; - if (Users.hasCompletedProfile(newUser)) { - flash(context.intl.formatMessage({id: "users.profile_completed"}), 'success'); - } - }} - /> - - - Meteor.logout(() => window.location.reload() /* something is broken here when giving the apollo client as a prop*/) }> - - - ) - } else { - - return null; - - } - } - -}; - - -UsersProfileCheck.propTypes = { - currentUser: PropTypes.object -}; - -UsersProfileCheck.contextTypes = { - intl: intlShape -}; - -UsersProfileCheck.displayName = 'UsersProfileCheck'; - -const mustCompleteFragment = gql` - fragment UsersMustCompleteFragment on User { - _id - ${Users.getRequiredFields().join('\n')} - } -` - -const options = { - collection: Users, - queryName: 'usersMustCompleteQuery', - fragment: mustCompleteFragment, -}; - -registerComponent('UsersProfileCheck', UsersProfileCheck, withMessages, [withDocument, options]); diff --git a/imports/config.js b/imports/config.js index 439f1e2a..88572db0 100644 --- a/imports/config.js +++ b/imports/config.js @@ -5,5 +5,5 @@ Users.avatar.setOptions({ "defaultImageUrl": "https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&f=y" }); -import { AccountsGuest } from 'meteor/artwells-accounts-guest'; +import { AccountsGuest } from 'meteor/artwells-accounts-guest-custom'; AccountsGuest.forced = false; diff --git a/imports/fragments.js b/imports/fragments.js index 764c9177..bcc76e5f 100644 --- a/imports/fragments.js +++ b/imports/fragments.js @@ -1,4 +1,4 @@ -import { registerFragment, getFragment } from 'meteor/vulcan:core'; +import { registerFragment, getFragment, extendFragment } from 'meteor/vulcan:core'; // ------------------------------ Vote ------------------------------ // @@ -23,7 +23,6 @@ registerFragment(` username displayName emailHash - isAdmin } `); @@ -43,18 +42,18 @@ registerFragment(` # vulcan:comments commentCount # vulcan:voting - downvotedComments { - ...VotedItem - } - downvotedPosts { - ...VotedItem - } - upvotedComments { - ...VotedItem - } - upvotedPosts { - ...VotedItem - } + #downvotedComments { + # ...VotedItem + #} + #downvotedPosts { + # ...VotedItem + #} + #upvotedComments { + # ...VotedItem + #} + #upvotedPosts { + # ...VotedItem + #} } `); @@ -86,11 +85,12 @@ registerFragment(` // ------------------------------ Posts ------------------------------ // -registerFragment(` +extendFragment(` fragment PostsList on Post { # vulcan:posts _id title + url slug postedAt createdAt @@ -116,14 +116,14 @@ registerFragment(` ...UsersMinimumInfo } # vulcan:voting - upvoters { - _id - } - downvoters { - _id - } - upvotes - downvotes + #upvoters { + # _id + #} + #downvoters { + # _id + # } + #upvotes + #downvotes baseScore score } @@ -162,14 +162,17 @@ registerFragment(` } } # vulcan:voting - upvoters { - _id - } - downvoters { - _id - } - upvotes - downvotes + #upvoters { + # _id + #} + #downvoters { + # _id + # } + # upvotes + # downvotes + #voters { + # _id + #} baseScore score } diff --git a/packages/vulcan-i18n-en-us/lib/en_US.js b/imports/i18n.js similarity index 100% rename from packages/vulcan-i18n-en-us/lib/en_US.js rename to imports/i18n.js diff --git a/imports/main-modules.js b/imports/main-modules.js index 151d1574..5fc221f6 100644 --- a/imports/main-modules.js +++ b/imports/main-modules.js @@ -1,6 +1,7 @@ -import './schema.js'; import './fragments.js'; +import './routes.js'; +import './schema.js'; import './components/index.js'; import './callbacks.js'; import './config.js'; -import './routes.js'; +import './i18n.js'; diff --git a/imports/pages/ForgotPassword.jsx b/imports/pages/ForgotPassword.jsx index 83c70026..900e882c 100644 --- a/imports/pages/ForgotPassword.jsx +++ b/imports/pages/ForgotPassword.jsx @@ -108,3 +108,4 @@ ForgotPassword.contextTypes = { ForgotPassword.displayName = 'ForgotPassword'; export default ForgotPassword; +// registerComponent('ForgotPassword', ForgotPassword); \ No newline at end of file diff --git a/imports/pages/PostsHome.jsx b/imports/pages/PostsHome.jsx index 583458d0..4b583b47 100644 --- a/imports/pages/PostsHome.jsx +++ b/imports/pages/PostsHome.jsx @@ -8,6 +8,6 @@ const PostsHome = (props, context) => { PostsHome.displayName = "PostsHome"; -registerComponent('PostsHome', PostsHome); +// registerComponent('PostsHome', PostsHome); export default PostsHome; diff --git a/imports/pages/UsersAccount.jsx b/imports/pages/UsersAccount.jsx index 03876b96..f4b9736b 100644 --- a/imports/pages/UsersAccount.jsx +++ b/imports/pages/UsersAccount.jsx @@ -14,6 +14,6 @@ UsersAccount.propTypes = { UsersAccount.displayName = "UsersAccount"; -//registerComponent('UsersAccount', UsersAccount, withCurrentUser); +// registerComponent('UsersAccount', UsersAccount, withCurrentUser); export default withCurrentUser(UsersAccount); diff --git a/imports/pages/UsersSingle.jsx b/imports/pages/UsersSingle.jsx index f3ac97b1..5ff7a79b 100644 --- a/imports/pages/UsersSingle.jsx +++ b/imports/pages/UsersSingle.jsx @@ -7,6 +7,6 @@ const UsersSingle = (props, context) => { UsersSingle.displayName = "UsersSingle"; -registerComponent('UsersSingle', UsersSingle); +// registerComponent('UsersSingle', UsersSingle); export default UsersSingle; diff --git a/imports/routes.js b/imports/routes.js index ff91d8b8..c3a660ae 100644 --- a/imports/routes.js +++ b/imports/routes.js @@ -10,8 +10,10 @@ addRoute([ {name:'posts.list', path: '/', component: PostsHome}, {name:'posts.single', path:'posts/:_id(/:slug)', component: PostsSingle}, {name:'users.account', path:'account', component: UsersAccount}, - {name:'resetPassword', path:'reset-password/:token', componentName: 'AccountsResetPassword'}, + // {name:'resetPassword', path:'reset-password/:token', componentName: 'AccountsResetPassword'}, {name:'forgotPassword', path:'reset-password', component: ForgotPassword}, + // TODO: what should the /admin page look like (admin/users and admin/categories are built-in)? + // {name:'users.admin', path:'admin', component: UsersAccount}, {name:'users.single', path:'users/:slug', component: UsersSingle}, {name:'users.edit', path:'users/:slug/edit', component: UsersAccount}, ]); diff --git a/imports/schema.js b/imports/schema.js index 060b7f3f..8fdbc904 100644 --- a/imports/schema.js +++ b/imports/schema.js @@ -1,7 +1,38 @@ /* Modifying builtin types */ -import Posts from 'meteor/vulcan:posts'; -import Categories from 'meteor/vulcan:categories'; +import { Posts }from 'meteor/example-forum'; +import { Categories } from 'meteor/example-forum'; +import Users from 'meteor/vulcan:users'; +// import Votes from 'meteor/vulcan:voting'; //Categories.removeField('image'); // removeField doesn't do what I think? -//Categories.removeField('parentId'); \ No newline at end of file +//Categories.removeField('parentId'); + + +/** + Contact info for the user + */ +Users.addField({ + fieldName: "contactInfo", + fieldSchema: { + type: String, + optional: true, + control: "text", + insertableBy: ['members'], + editableBy: ['members'], + viewableBy: ['guests'], + order: 50, + } +}); + +Users.addField({ + fieldName: "karma", + fieldSchema: { + type: Number, + optional: true, + viewableBy: ['guests'], + } +}); + +Users.removeField( "twitterUsername"); +Users.removeField( "website"); diff --git a/package.json b/package.json index 4083de05..c28410c9 100644 --- a/package.json +++ b/package.json @@ -1,83 +1,93 @@ { - "name": "network-canvas-feedback", - "version": "0.0.1", + "name": "Vulcan", + "version": "1.12.15", "engines": { "npm": "^3.0" }, "scripts": { - "prestart": "sh prestart_vulcan.sh", + "prestart": "node .vulcan/prestart_vulcan.js", "start": "meteor --settings settings.json", "lint": "eslint --cache --ext .jsx,js packages" }, "dependencies": { + "@babel/runtime": "^7.1.2", "analytics-node": "^2.1.1", "apollo-client": "^1.2.2", - "apollo-errors": "^1.4.0", - "babel-runtime": "^6.18.0", - "bcrypt": "^0.8.7", - "body-parser": "^1.15.2", - "bootstrap-sass": "^3.3.7", + "apollo-engine": "^0.5.4", + "apollo-errors": "^1.9.0", + "apollo-server-express": "^1.3.6", + "babel-runtime": "^6.26.0", + "bcrypt": "^3.0.0", + "body-parser": "^1.18.2", + "bootstrap-sass": "^3.4.0", + "chalk": "2.2.0", "classnames": "^2.2.3", + "combined-stream2": "^1.1.2", + "compression": "^1.7.2", "cookie-parser": "^1.4.3", + "cross-fetch": "^0.0.8", "crypto-js": "^3.1.9-1", - "dataloader": "^1.3.0", + "dataloader": "^1.4.0", "deepmerge": "^1.2.0", + "dot-object": "^1.7.0", "escape-string-regexp": "^1.0.5", - "express": "^4.14.0", - "formsy-react": "^0.18.1", - "formsy-react-components": "^0.8.1", - "graphql": "^0.9.6", + "express": "^4.16.3", + "flat": "^4.0.0", + "formsy-react": "^1.1.5", + "formsy-react-components": "^1.0.0", + "graphql": "^0.10.5", "graphql-anywhere": "^3.0.1", "graphql-date": "^1.0.2", - "graphql-server-express": "^0.6.0", - "graphql-tag": "^2.0.0", - "graphql-tools": "^0.10.1", + "graphql-tag": "^2.9.2", + "graphql-tools": "^3.0.1", "graphql-type-json": "^0.1.4", "handlebars": "^4.0.5", "he": "^1.1.1", "history": "^3.0.0", "html-to-text": "^2.1.0", - "immutability-helper": "^2.0.0", + "immutability-helper": "^2.7.0", "import": "0.0.6", "intl": "^1.2.4", "intl-locales-supported": "^1.0.0", - "isomorphic-fetch": "^2.2.1", - "juice": "^1.11.0", + "juice": "^5.1.0", + "lodash": "^4.17.10", "mailchimp": "^1.1.6", - "marked": "^0.3.5", - "metascraper": "^1.0.6", - "meteor-node-stubs": "^0.2.3", + "marked": "^0.5.2", + "meteor-node-stubs": "^0.4.1", "mingo": "^0.8.1", "moment": "^2.13.0", - "optics-agent": "^1.0.5", - "prop-types": "^15.5.10", - "react": "^15.4.1", + "prop-types": "^15.6.0", + "react": "^16.4.1", "react-addons-pure-render-mixin": "^15.4.1", - "react-apollo": "^1.1.1", - "react-bootstrap": "^0.30.7", - "react-bootstrap-datetimepicker": "0.0.22", - "react-cookie": "^0.4.6", - "react-datetime": "^2.3.2", - "react-dom": "^15.4.1", - "react-dropzone": "^3.12.2", + "react-apollo": "^1.4.15", + "react-bootstrap": "^1.0.0-beta.5", + "react-cookie": "^2.1.6", + "react-datetime": "^2.11.1", + "react-dom": "^16.4.1", + "react-dropzone": "^3.13.4", "react-helmet": "^5.1.3", "react-intl": "^2.1.3", "react-loadable": "^4.0.3", + "react-markdown": "^3.1.5", "react-places-autocomplete": "^5.0.0", - "react-redux": "^5.0.1", - "react-router": "^3.0.0", + "react-redux": "^5.0.6", + "react-router": "^3.2.0", "react-router-bootstrap": "^0.23.1", - "react-router-scroll": "^0.4.1", - "recompose": "^0.21.2", + "react-router-scroll": "^0.4.4", + "react-select": "^1.2.1", + "react-stripe-checkout": "^2.4.0", + "react-syntax-highlighter": "^6.1.2", + "recompose": "^0.26.0", "redux": "^3.6.0", "rss": "^1.2.1", - "sanitize-html": "^1.11.4", - "sendy-api": "^0.1.0", - "simpl-schema": "^0.2.3", + "sanitize-html": "^1.16.3", + "simpl-schema": "^1.4.2", "speakingurl": "^9.0.0", + "stripe": "^4.23.1", "styled-components": "^2.1.1", "tracker-component": "^1.3.14", "underscore": "^1.8.3", + "universal-cookie-express": "^2.1.5", "url": "^0.11.0" }, "private": true, @@ -89,9 +99,9 @@ "eslint-config-meteor": "0.0.9", "eslint-import-resolver-meteor": "^0.3.3", "eslint-plugin-babel": "^3.3.0", - "eslint-plugin-import": "^2.2.0", + "eslint-plugin-import": "^2.11.0", "eslint-plugin-jsx-a11y": "^2.2.3", - "eslint-plugin-meteor": "^4.0.1", + "eslint-plugin-meteor": "^4.2.1", "eslint-plugin-react": "^6.7.1" }, "postcss": { diff --git a/packages/artwells-accounts-guest/LICENSE b/packages/artwells-accounts-guest-custom/LICENSE similarity index 100% rename from packages/artwells-accounts-guest/LICENSE rename to packages/artwells-accounts-guest-custom/LICENSE diff --git a/packages/artwells-accounts-guest/README.md b/packages/artwells-accounts-guest-custom/README.md similarity index 100% rename from packages/artwells-accounts-guest/README.md rename to packages/artwells-accounts-guest-custom/README.md diff --git a/packages/artwells-accounts-guest/accounts-guest-client-tests.js b/packages/artwells-accounts-guest-custom/accounts-guest-client-tests.js similarity index 100% rename from packages/artwells-accounts-guest/accounts-guest-client-tests.js rename to packages/artwells-accounts-guest-custom/accounts-guest-client-tests.js diff --git a/packages/artwells-accounts-guest/accounts-guest-server-tests.js b/packages/artwells-accounts-guest-custom/accounts-guest-server-tests.js similarity index 100% rename from packages/artwells-accounts-guest/accounts-guest-server-tests.js rename to packages/artwells-accounts-guest-custom/accounts-guest-server-tests.js diff --git a/packages/artwells-accounts-guest/client.js b/packages/artwells-accounts-guest-custom/client.js similarity index 100% rename from packages/artwells-accounts-guest/client.js rename to packages/artwells-accounts-guest-custom/client.js diff --git a/packages/artwells-accounts-guest/common.js b/packages/artwells-accounts-guest-custom/common.js similarity index 100% rename from packages/artwells-accounts-guest/common.js rename to packages/artwells-accounts-guest-custom/common.js diff --git a/packages/artwells-accounts-guest/package.js b/packages/artwells-accounts-guest-custom/package.js similarity index 96% rename from packages/artwells-accounts-guest/package.js rename to packages/artwells-accounts-guest-custom/package.js index eccb23ce..2b637144 100644 --- a/packages/artwells-accounts-guest/package.js +++ b/packages/artwells-accounts-guest-custom/package.js @@ -1,7 +1,7 @@ Package.describe({ summary: "Automatically add visitor as anonymous guest with userId", version: "0.1.13_1", - name: "artwells-accounts-guest", + name: "artwells-accounts-guest-custom", git: "https://github.com/artwells/meteor-accounts-guest.git" }); diff --git a/packages/artwells-accounts-guest/server.js b/packages/artwells-accounts-guest-custom/server.js similarity index 99% rename from packages/artwells-accounts-guest/server.js rename to packages/artwells-accounts-guest-custom/server.js index f064ffc7..5eb638ac 100644 --- a/packages/artwells-accounts-guest/server.js +++ b/packages/artwells-accounts-guest-custom/server.js @@ -121,7 +121,7 @@ function createGuestOptions(email) { guest: true, // setupGuestUser callback checks this name: 'Guest' }, - password: Meteor.uuid(), + password: Random.secret(), }; return guest; } diff --git a/packages/example-forum/README.md b/packages/example-forum/README.md new file mode 100644 index 00000000..9031e953 --- /dev/null +++ b/packages/example-forum/README.md @@ -0,0 +1 @@ +Vulcan forum example package. \ No newline at end of file diff --git a/packages/example-forum/lib/assets/content/customizing.md b/packages/example-forum/lib/assets/content/customizing.md new file mode 100644 index 00000000..7581d303 --- /dev/null +++ b/packages/example-forum/lib/assets/content/customizing.md @@ -0,0 +1,9 @@ +If you want to learn how to customize Vulcan, we suggest checking out the [docs](http://docs.vulcanjs.org). + +The first things you'll want to do are probably create a `settings.json` file to hold all your settings, and then taking a look at the sample custom package by uncommenting `network-canvas` in `.meteor/packages`. + +Here are two tutorials to get further: +* [Getting Started](http://docs.vulcanjs.org/#Getting-Started) +* [Customization Example](http://docs.vulcanjs.org/example-customization.html) + +Happy hacking! diff --git a/packages/example-forum/lib/assets/content/deploying.md b/packages/example-forum/lib/assets/content/deploying.md new file mode 100644 index 00000000..8018c9ec --- /dev/null +++ b/packages/example-forum/lib/assets/content/deploying.md @@ -0,0 +1,5 @@ +Once you've played around with Vulcan, you might want to deploy your app for the whole world to see. + +We recommend using [Meteor Up](http://meteor-up.com/) to deploy to a [Digital Ocean](http://digitalocean.com) server, along with [Compose](http://compose.io) to host your database. Another good solution is [Galaxy](http://galaxy.meteor.com/). + +Learn more in the [Vulcan docs](http://docs.vulcanjs.org/deployment.html). diff --git a/packages/example-forum/lib/assets/content/getting_help.md b/packages/example-forum/lib/assets/content/getting_help.md new file mode 100644 index 00000000..0b3bca7f --- /dev/null +++ b/packages/example-forum/lib/assets/content/getting_help.md @@ -0,0 +1,9 @@ +### Slack Chatroom + +If you have a question, the best place to ask is the [Slack chatroom](http://slack.vulcanjs.org) to get help. Or you can also drop to just say hello! + +### GitHub Issues + +If you've found a bug in this codebase, then please [leave an issue on GitHub](https://github.com/VulcanJS/Vulcan-Starter/issues/). + +If on the other hand the issue is with Vulcan in general, [leave an issue on the Vulcan core repo](https://github.com/VulcanJS/Vulcan/issues/). If you're not sure which to use, feel free to drop by the Slack chatroom to ask! \ No newline at end of file diff --git a/packages/example-forum/lib/assets/content/read_this_first.md b/packages/example-forum/lib/assets/content/read_this_first.md new file mode 100644 index 00000000..7d36418b --- /dev/null +++ b/packages/example-forum/lib/assets/content/read_this_first.md @@ -0,0 +1,15 @@ +### Welcome to Vulcan.js! + +If you're reading this, it means you've successfully got Vulcan's Forum example project to run. + +To make your first run a bit easier, we've taken the liberty of preloading your brand new app with a few posts that will walk you through your first steps with the app. + +### Creating An Account + +The first thing you'll need to do is create your account. Since this will be the first ever account created in this project, it will automatically be assigned admin rights, and you'll then be able to access Vulcan's settings panel. + +Click the “Log In” link in the top menu and come back here once you're done! + +### Start Posting! + +You're now all set to start using Vulcan. Check out the other posts for more information, or just start posting! diff --git a/packages/example-forum/lib/assets/content/removing_getting_started_posts.md b/packages/example-forum/lib/assets/content/removing_getting_started_posts.md new file mode 100644 index 00000000..e91eb933 --- /dev/null +++ b/packages/example-forum/lib/assets/content/removing_getting_started_posts.md @@ -0,0 +1,11 @@ +### Removing Getting Started Posts + +If you're ready to insert your own content and want to delete these getting started posts, comments, and users, you can do so with a single command. + +Open a new [Meteor shell](https://docs.meteor.com/commandline.html#meteorshell) and type: + +```js +Vulcan.removeGettingStartedContent() +``` + +See you on the other side! \ No newline at end of file diff --git a/packages/example-forum/lib/assets/images/stackoverflow.png b/packages/example-forum/lib/assets/images/stackoverflow.png new file mode 100644 index 00000000..e3f7eac9 Binary files /dev/null and b/packages/example-forum/lib/assets/images/stackoverflow.png differ diff --git a/packages/example-forum/lib/assets/images/telescope.png b/packages/example-forum/lib/assets/images/telescope.png new file mode 100644 index 00000000..f8a71159 Binary files /dev/null and b/packages/example-forum/lib/assets/images/telescope.png differ diff --git a/packages/vulcan-admin/lib/client/main.js b/packages/example-forum/lib/client/main.js similarity index 100% rename from packages/vulcan-admin/lib/client/main.js rename to packages/example-forum/lib/client/main.js diff --git a/packages/example-forum/lib/components/admin/AdminUsersPosts.js b/packages/example-forum/lib/components/admin/AdminUsersPosts.js new file mode 100644 index 00000000..7dbb87f3 --- /dev/null +++ b/packages/example-forum/lib/components/admin/AdminUsersPosts.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { Posts } from '../../modules/posts/index.js'; +import { Link } from 'react-router'; + +const AdminUsersPosts = ({ document: user }) => +
    + {user.posts && user.posts.map(post => +
  • {post.title}
  • + )} +
+ +export default AdminUsersPosts; \ No newline at end of file diff --git a/packages/example-forum/lib/components/categories/CategoriesDashboard.jsx b/packages/example-forum/lib/components/categories/CategoriesDashboard.jsx new file mode 100644 index 00000000..dc833030 --- /dev/null +++ b/packages/example-forum/lib/components/categories/CategoriesDashboard.jsx @@ -0,0 +1,30 @@ +/* + +Show a dashboard of all categories + +http://docs.vulcanjs.org/core-components.html#Datatable + +*/ + +import React from 'react'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; + +import { Categories } from '../../modules/categories'; + +const CategoriesDashboard = () => + +
+ +

+ + + +
+ +registerComponent({ name: 'CategoriesDashboard', component: CategoriesDashboard }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/categories/CategoriesEditForm.jsx b/packages/example-forum/lib/components/categories/CategoriesEditForm.jsx new file mode 100644 index 00000000..98f7daf3 --- /dev/null +++ b/packages/example-forum/lib/components/categories/CategoriesEditForm.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; +import { Categories } from '../../modules/categories/index.js'; + +const CategoriesEditForm = (props, context) => { + + return ( +
+
+
ID: {props.category._id}
+
+ { + props.closeModal(); + props.flash({ id: 'categories.edit_success', properties: { name: category.name }, type: 'success'}); + }} + removeSuccessCallback={({ documentId, documentTitle }) => { + props.closeModal(); + props.flash({ id: 'categories.delete_success', properties: {name: documentTitle }, type: 'success'}); + // context.events.track("category deleted", {_id: documentId}); + }} + showRemove={true} + /> +
+ ); +}; + +CategoriesEditForm.propTypes = { + category: PropTypes.object.isRequired, + closeModal: PropTypes.func, + flash: PropTypes.func, +}; + +CategoriesEditForm.contextTypes = { + intl: intlShape, +}; + +registerComponent({ name: 'CategoriesEditForm', component: CategoriesEditForm, hocs: [withMessages] }); diff --git a/packages/example-forum/lib/components/categories/CategoriesMenu.jsx b/packages/example-forum/lib/components/categories/CategoriesMenu.jsx new file mode 100644 index 00000000..8689d60d --- /dev/null +++ b/packages/example-forum/lib/components/categories/CategoriesMenu.jsx @@ -0,0 +1,136 @@ +import { Components, registerComponent, withList, Utils, withCurrentUser } from 'meteor/vulcan:core'; +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { withRouter } from 'react-router'; +import { Categories } from '../../modules/categories/index.js'; +import { withApollo } from 'react-apollo'; + +/* + +Category menu item + +*/ +const CategoryMenuItem = ({ category, active, expanded }) => ( + {category.name} +); + +class CategoriesMenu extends PureComponent { + /* + + Menu item for the "All Categories" link + + */ + getResetCategoriesItem = () => { + const resetCategoriesQuery = _.clone(this.props.router.location.query); + delete resetCategoriesQuery.cat; + + const menuItem = { + to: { pathname: Utils.getRoutePath('posts.list'), query: resetCategoriesQuery }, + itemProps: { + eventKey: 0, + className: 'category-menu-item category-menu-item-all dropdown-item', + }, + component: , + }; + + return menuItem; + }; + + /* + + Menu items for categoeries + + */ + getCategoriesItems = () => { + const categories = this.props.results || []; + + // check if a category is currently active in the route + const currentCategorySlug = this.props.router.location.query && this.props.router.location.query.cat; + const currentCategory = Categories.findOneInStore(this.props.client.store, { slug: currentCategorySlug }); + const parentCategories = Categories.getParents(currentCategory, this.props.client.store); + + // decorate categories with active and expanded properties + const categoriesClone = categories.map((category, index) => { + const query = _.clone(this.props.router.location.query); + query.cat = category.slug; + + const active = currentCategory && category.slug === currentCategory.slug; + const expanded = parentCategories && _.contains(_.pluck(parentCategories, 'slug'), category.slug); + + return { + to: { pathname: Utils.getRoutePath('posts.list'), query }, + component: , + itemProps: { + active, + className: 'dropdown-item', + }, + componentProps: { + // will be passed to component defined above + _id: category._id, + parentId: category.parentId, + category, + index, + currentUser: this.props.currentUser, + active, + expanded, + }, + }; + }); + + // add `childrenItems` on each item in categoriesClone + const nestedCategories = Utils.unflatten(categoriesClone, { + idProperty: 'componentProps._id', + parentIdProperty: 'componentProps.parentId', + childrenProperty: 'childrenItems', + }); + + return nestedCategories; + }; + + /* + + Get all menu items + + */ + getMenuItems = () => { + const menuItems = [this.getResetCategoriesItem(), ...this.getCategoriesItems()]; + return menuItems; + }; + + render() { + return ( +
+ {this.props.loading ? ( + + ) : ( + + )} +
+ ); + } +} + +CategoriesMenu.propTypes = { + results: PropTypes.array, +}; + +const options = { + collection: Categories, + queryName: 'categoriesListQuery', + fragmentName: 'CategoriesList', + limit: 0, + pollInterval: 0, +}; + +registerComponent({ + name: 'CategoriesMenu', + component: CategoriesMenu, + hocs: [withRouter, withApollo, [withList, options], withCurrentUser], +}); diff --git a/packages/example-forum/lib/components/categories/CategoriesNewForm.jsx b/packages/example-forum/lib/components/categories/CategoriesNewForm.jsx new file mode 100644 index 00000000..9556d519 --- /dev/null +++ b/packages/example-forum/lib/components/categories/CategoriesNewForm.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; +import { Categories } from '../../modules/categories/index.js'; + +const CategoriesNewForm = (props, context) => { + + return ( +
+ { + props.closeModal(); + props.flash({id: 'categories.new_success', properties: {name: category.name}, type: "success"}); + }} + /> +
+ ) +} + +CategoriesNewForm.displayName = "CategoriesNewForm"; + +CategoriesNewForm.propTypes = { + closeCallback: PropTypes.func, + flash: PropTypes.func, +}; + +CategoriesNewForm.contextTypes = { + intl: intlShape, +}; + +registerComponent({ name: 'CategoriesNewForm', component: CategoriesNewForm, hocs: [withMessages] }); diff --git a/imports/components/comments/CommentsEditForm.jsx b/packages/example-forum/lib/components/comments/CommentsEditForm.jsx similarity index 83% rename from imports/components/comments/CommentsEditForm.jsx rename to packages/example-forum/lib/components/comments/CommentsEditForm.jsx index 8e213b54..840194fc 100644 --- a/imports/components/comments/CommentsEditForm.jsx +++ b/packages/example-forum/lib/components/comments/CommentsEditForm.jsx @@ -1,7 +1,7 @@ import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; import React from 'react'; import PropTypes from 'prop-types'; -import Comments from "meteor/vulcan:comments"; +import { Comments } from '../../modules/comments/index.js'; const CommentsEditForm = (props, context) => { return ( @@ -26,4 +26,4 @@ CommentsEditForm.propTypes = { cancelCallback: PropTypes.func }; -registerComponent('CommentsEditForm', CommentsEditForm, withMessages); +registerComponent({ name: 'CommentsEditForm', component: CommentsEditForm, hocs: [withMessages] }); diff --git a/imports/components/comments/CommentsItem.jsx b/packages/example-forum/lib/components/comments/CommentsItem.jsx similarity index 72% rename from imports/components/comments/CommentsItem.jsx rename to packages/example-forum/lib/components/comments/CommentsItem.jsx index ec39fdf9..d0bd80f9 100644 --- a/imports/components/comments/CommentsItem.jsx +++ b/packages/example-forum/lib/components/comments/CommentsItem.jsx @@ -1,33 +1,27 @@ import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; -import Comments from 'meteor/vulcan:comments'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { Comments } from '../../modules/comments/index.js'; import moment from 'moment'; class CommentsItem extends PureComponent { constructor() { super(); - [ - 'handleReply', 'replyCancelCallback', 'replySuccessCallback', 'showEdit', - 'editCancelCallback', 'editSuccessCallback', 'removeSuccessCallback' - ].forEach(methodName => { this[methodName] = this[methodName].bind(this) }); - + ['showReply', 'replyCancelCallback', 'replySuccessCallback', 'showEdit', 'editCancelCallback', 'editSuccessCallback', 'removeSuccessCallback'].forEach(methodName => {this[methodName] = this[methodName].bind(this)}); this.state = { showReply: false, showEdit: false }; } - handleReply(event) { + showReply(event) { event.preventDefault(); - - this.props.onNewComment(this.props.comment); + this.setState({showReply: true}); } replyCancelCallback(event) { - event.preventDefault(); this.setState({showReply: false}); } @@ -41,7 +35,6 @@ class CommentsItem extends PureComponent { } editCancelCallback(event) { - event.preventDefault(); this.setState({showEdit: false}); } @@ -50,10 +43,7 @@ class CommentsItem extends PureComponent { } removeSuccessCallback({documentId}) { - const deleteDocumentSuccess = this.context.intl.formatMessage({id: 'comments.delete_success'}); - this.props.flash(deleteDocumentSuccess, "success"); - // todo: handle events in async callback - // this.context.events.track("comment deleted", {_id: documentId}); + this.props.flash({id: 'comments.delete_success', type: 'success'}); } renderComment() { @@ -65,7 +55,7 @@ class CommentsItem extends PureComponent {
{ showReplyButton ? - + : null}
@@ -106,14 +96,17 @@ class CommentsItem extends PureComponent {
+
+ +
{moment(new Date(comment.postedAt)).fromNow()}
- + {Comments.options.mutations.edit.check(this.props.currentUser, this.props.comment) &&
-
+ }
{this.state.showEdit ? this.renderEdit() : this.renderComment()}
@@ -126,13 +119,8 @@ class CommentsItem extends PureComponent { CommentsItem.propTypes = { comment: PropTypes.object.isRequired, // the current comment - onNewComment: PropTypes.func.isRequired, currentUser: PropTypes.object, + flash: PropTypes.func, }; -CommentsItem.contextTypes = { - events: PropTypes.object, - intl: intlShape -}; - -registerComponent('CommentsItem', CommentsItem); +registerComponent({ name: 'CommentsItem', component: CommentsItem, hocs: [withMessages] }); diff --git a/imports/components/comments/CommentsList.jsx b/packages/example-forum/lib/components/comments/CommentsList.jsx similarity index 67% rename from imports/components/comments/CommentsList.jsx rename to packages/example-forum/lib/components/comments/CommentsList.jsx index 84e7577b..b84a1a87 100644 --- a/imports/components/comments/CommentsList.jsx +++ b/packages/example-forum/lib/components/comments/CommentsList.jsx @@ -2,14 +2,12 @@ import { Components, registerComponent } from 'meteor/vulcan:core'; import React from 'react'; import { FormattedMessage } from 'meteor/vulcan:i18n'; -const CommentsList = ({comments, commentCount, currentUser, onNewComment}) => { +const CommentsList = ({comments, commentCount, currentUser}) => { if (commentCount > 0) { return (
- {comments.map(comment => - ) - } + {comments.map(comment => )} {/*hasMore ? (ready ? : ) : null*/}
) @@ -27,4 +25,4 @@ const CommentsList = ({comments, commentCount, currentUser, onNewComment}) => { CommentsList.displayName = "CommentsList"; -registerComponent('CommentsList', CommentsList); +registerComponent({ name: 'CommentsList', component: CommentsList }); diff --git a/imports/components/comments/CommentsLoadMore.jsx b/packages/example-forum/lib/components/comments/CommentsLoadMore.jsx similarity index 83% rename from imports/components/comments/CommentsLoadMore.jsx rename to packages/example-forum/lib/components/comments/CommentsLoadMore.jsx index 9739eed8..0f3b7835 100644 --- a/imports/components/comments/CommentsLoadMore.jsx +++ b/packages/example-forum/lib/components/comments/CommentsLoadMore.jsx @@ -8,4 +8,4 @@ const CommentsLoadMore = ({loadMore, count, totalCount}) => { CommentsLoadMore.displayName = "CommentsLoadMore"; -registerComponent('CommentsLoadMore', CommentsLoadMore); \ No newline at end of file +registerComponent({ name: 'CommentsLoadMore', component: CommentsLoadMore }); \ No newline at end of file diff --git a/imports/components/comments/CommentsNewForm.jsx b/packages/example-forum/lib/components/comments/CommentsNewForm.jsx similarity index 71% rename from imports/components/comments/CommentsNewForm.jsx rename to packages/example-forum/lib/components/comments/CommentsNewForm.jsx index 84836262..5d8efec9 100644 --- a/imports/components/comments/CommentsNewForm.jsx +++ b/packages/example-forum/lib/components/comments/CommentsNewForm.jsx @@ -1,27 +1,12 @@ import { Components, registerComponent, getFragment, withMessages } from 'meteor/vulcan:core'; import React from 'react'; import PropTypes from 'prop-types'; -import Comments from "meteor/vulcan:comments"; +import { Comments } from '../../modules/comments/index.js'; import { FormattedMessage } from 'meteor/vulcan:i18n'; -const LoggedOut = (props) => { - return ( -
- Please log in or - - continue as guest - - in order to comment. -
- ) -} - const CommentsNewForm = (props, context) => { - let prefilledProps = { - postId: props.postId, - body: props.initialBody, - }; + let prefilledProps = {postId: props.postId}; if (props.parentComment) { prefilledProps = Object.assign(prefilledProps, { @@ -34,13 +19,14 @@ const CommentsNewForm = (props, context) => { return ( } + failureComponent={} >
@@ -56,12 +42,10 @@ CommentsNewForm.propTypes = { parentComment: PropTypes.object, // if reply, the comment being replied to parentCommentId: PropTypes.string, // if reply topLevelCommentId: PropTypes.string, // if reply - successCallback: PropTypes.func, // a callback to execute when the submission has been successful - + cancelCallback: PropTypes.func, router: PropTypes.object, flash: PropTypes.func, - initialBody: PropTypes.string, }; -registerComponent('CommentsNewForm', CommentsNewForm, withMessages); +registerComponent({ name: 'CommentsNewForm', component: CommentsNewForm, hocs: [withMessages] }); diff --git a/imports/components/comments/CommentsNode.jsx b/packages/example-forum/lib/components/comments/CommentsNode.jsx similarity index 53% rename from imports/components/comments/CommentsNode.jsx rename to packages/example-forum/lib/components/comments/CommentsNode.jsx index 0cf71838..604969b5 100644 --- a/imports/components/comments/CommentsNode.jsx +++ b/packages/example-forum/lib/components/comments/CommentsNode.jsx @@ -2,14 +2,12 @@ import { Components, registerComponent } from 'meteor/vulcan:core'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -const CommentsNode = ({ comment, currentUser, onNewComment }) => +const CommentsNode = ({ comment, currentUser }) =>
- - {comment.childrenResults ? + + {comment.childrenResults ?
- {comment.childrenResults.map(comment => - ) - } + {comment.childrenResults.map(comment => )}
: null } @@ -19,4 +17,4 @@ CommentsNode.propTypes = { comment: PropTypes.object.isRequired, // the current comment }; -registerComponent('CommentsNode', CommentsNode); +registerComponent({ name: 'CommentsNode', component: CommentsNode }); diff --git a/packages/example-forum/lib/components/common/Footer.jsx b/packages/example-forum/lib/components/common/Footer.jsx new file mode 100644 index 00000000..126c42a7 --- /dev/null +++ b/packages/example-forum/lib/components/common/Footer.jsx @@ -0,0 +1,13 @@ +import { registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; + +const Footer = props => { + return ( +
+ ) +} + +Footer.displayName = "Footer"; + +registerComponent({ name: 'Footer', component: Footer }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/common/Header.jsx b/packages/example-forum/lib/components/common/Header.jsx new file mode 100644 index 00000000..93cd8a66 --- /dev/null +++ b/packages/example-forum/lib/components/common/Header.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withCurrentUser, getSetting, Components, registerComponent } from 'meteor/vulcan:core'; +import { Posts } from '../../modules/posts/index.js'; + +const Header = (props) => { + + const logoUrl = getSetting('logoUrl'); + const siteTitle = getSetting('title', 'My App'); + const tagline = getSetting('tagline'); + + return ( +
+ +
+ +
+ + {tagline ?

{tagline}

: "" } +
+ +
+ +
+ {!!props.currentUser ? : } +
+ +
+ + + +
+ +
+ +
+
+ ) +} + +Header.displayName = "Header"; + +Header.propTypes = { + currentUser: PropTypes.object, +}; + +registerComponent({ name: 'Header', component: Header, hocs: [withCurrentUser] }); diff --git a/packages/example-forum/lib/components/common/Layout.jsx b/packages/example-forum/lib/components/common/Layout.jsx new file mode 100644 index 00000000..141add0a --- /dev/null +++ b/packages/example-forum/lib/components/common/Layout.jsx @@ -0,0 +1,36 @@ +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import Helmet from 'react-helmet'; + +const Layout = ({currentUser, children, currentRoute}) => + +
+ + + + + + + + + {currentUser ? : null} + + + +
+ + + + + + {children} + +
+ + + +
+ +registerComponent({ name: 'Layout', component: Layout, hocs: [withCurrentUser] }); \ No newline at end of file diff --git a/imports/components/common/Logo.jsx b/packages/example-forum/lib/components/common/Logo.jsx similarity index 82% rename from imports/components/common/Logo.jsx rename to packages/example-forum/lib/components/common/Logo.jsx index c63999b1..f0a85561 100644 --- a/imports/components/common/Logo.jsx +++ b/packages/example-forum/lib/components/common/Logo.jsx @@ -5,11 +5,11 @@ import { IndexLink } from 'react-router'; const Logo = ({logoUrl, siteTitle}) => { if (logoUrl) { return ( - +

{siteTitle} - +

) } else { return ( @@ -22,4 +22,4 @@ const Logo = ({logoUrl, siteTitle}) => { Logo.displayName = "Logo"; -registerComponent('Logo', Logo); +registerComponent({ name: 'Logo', component: Logo }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/common/Newsletter.jsx b/packages/example-forum/lib/components/common/Newsletter.jsx new file mode 100644 index 00000000..6bbe4a22 --- /dev/null +++ b/packages/example-forum/lib/components/common/Newsletter.jsx @@ -0,0 +1,137 @@ +import { Components, registerComponent, withCurrentUser, withMutation, withMessages } from 'meteor/vulcan:core'; +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import Users from 'meteor/vulcan:users'; +import Cookies from 'universal-cookie'; + +const cookies = new Cookies(); + +class Newsletter extends PureComponent { + constructor(props, context) { + super(props); + this.subscribeEmail = this.subscribeEmail.bind(this); + this.successCallbackSubscription = this.successCallbackSubscription.bind(this); + this.dismissBanner = this.dismissBanner.bind(this); + + this.state = { + showBanner: false, + email: '', + }; + } + + componentDidMount() { + this.setState({ + showBanner: showBanner(this.props.currentUser), + }); + } + + componentWillReceiveProps(nextProps, nextContext) { + if (nextProps.currentUser) { + this.setState({ showBanner: showBanner(nextProps.currentUser) }); + } + } + + async subscribeEmail(e) { + e.preventDefault(); + try { + const result = await this.props.addEmailNewsletter({ email: this.state.email }); + this.successCallbackSubscription(result); + } catch (error) { + const graphQLError = error.graphQLErrors[0]; + console.error(graphQLError); // eslint-disable-line no-console + this.props.flash({ + id: `newsletter.error_${this.state.error.name}`, + message: this.state.error.message, + type: 'error', + }); + } + } + + successCallbackSubscription(/* result*/) { + this.props.flash({ id: 'newsletter.success_message', type: 'success' }); + this.dismissBanner(); + } + + dismissBanner(e) { + if (e && e.preventDefault) e.preventDefault(); + + this.setState({ showBanner: false }); + + // set cookie to keep the banner dismissed persistently + cookies.set('showBanner', 'no'); + } + + renderButton() { + return ( + this.successCallbackSubscription()} + user={this.props.currentUser} + /> + ); + } + + renderForm() { + return ( + + { + const value = e.target.value; + this.setState({ email: value }); + } + }} + /> + + + + + ); + } + + render() { + return this.state.showBanner ? ( +
+

+ +

+ {this.props.currentUser ? this.renderButton() : this.renderForm()} + + + +
+ ) : null; + } +} + +Newsletter.contextTypes = { + actions: PropTypes.object, + intl: intlShape, +}; + +const mutationOptions = { + name: 'addEmailNewsletter', + args: { email: 'String' }, +}; + +function showBanner(user) { + return ( + // showBanner cookie either doesn't exist or is not set to "no" + cookies.get('showBanner') !== 'no' && + // and user is not subscribed to the newsletter already (setting either DNE or is not set to false) + !Users.getSetting(user, 'newsletter_subscribeToNewsletter', false) + ); +} + +registerComponent({ + name: 'Newsletter', + component: Newsletter, + hocs: [withMutation(mutationOptions), withCurrentUser, withMessages], +}); diff --git a/packages/example-forum/lib/components/common/NewsletterButton.jsx b/packages/example-forum/lib/components/common/NewsletterButton.jsx new file mode 100644 index 00000000..9eeebcc2 --- /dev/null +++ b/packages/example-forum/lib/components/common/NewsletterButton.jsx @@ -0,0 +1,63 @@ +import { Components, registerComponent, withMutation, withCurrentUser, withMessages, getErrors } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; + +class NewsletterButton extends Component { + constructor(props) { + super(props); + this.subscriptionAction = this.subscriptionAction.bind(this); + } + + // use async/await + try/catch <=> promise.then(res => ..).catch(e => ...) + async subscriptionAction() { + + const { + flash, + mutationName, + successCallback, + user, + [mutationName]: mutationToTrigger, // dynamic 'mutationToTrigger' variable based on the mutationName (addUserNewsletter or removeUserNewsletter) + } = this.props; + + try { + const mutationResult = await mutationToTrigger({userId: user._id}); + + successCallback(mutationResult); + } catch(error) { + console.error(getErrors(error)); // eslint-disable-line no-console + flash(getErrors(error)[0]); + } + } + + render() { + + return ( + + + + ) + } +} + +NewsletterButton.propTypes = { + mutationName: PropTypes.string.isRequired, // mutation to fire + label: PropTypes.string.isRequired, // label of the button + user: PropTypes.object.isRequired, // user to operate on + successCallback: PropTypes.func.isRequired, // what do to after the mutationName + addUserNewsletter: PropTypes.func.isRequired, // prop given by withMutation HOC + removeUserNewsletter: PropTypes.func.isRequired, // prop given by withMutation HOC +}; + +NewsletterButton.contextTypes = { + intl: intlShape, +}; + +const addOptions = {name: 'addUserNewsletter', args: {userId: 'String'}}; +const removeOptions = {name: 'removeUserNewsletter', args: {userId: 'String'}}; + +registerComponent({ name: 'NewsletterButton', component: NewsletterButton, hocs: [withCurrentUser, withMutation(addOptions), withMutation(removeOptions), withMessages] }); diff --git a/packages/example-forum/lib/components/common/SearchForm.jsx b/packages/example-forum/lib/components/common/SearchForm.jsx new file mode 100644 index 00000000..1f022cfe --- /dev/null +++ b/packages/example-forum/lib/components/common/SearchForm.jsx @@ -0,0 +1,81 @@ +import { registerComponent, Components, Utils } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { withRouter, Link } from 'react-router'; + +// see: http://stackoverflow.com/questions/1909441/jquery-keyup-delay +const delay = (function() { + var timer = 0; + return function(callback, ms) { + clearTimeout(timer); + timer = setTimeout(callback, ms); + }; +})(); + +class SearchForm extends Component { + constructor(props) { + super(props); + this.search = this.search.bind(this); + this.state = { + pathname: props.router.location.pathname, + search: props.router.location.query.query || '', + }; + } + + // note: why do we need this? + componentWillReceiveProps(nextProps) { + this.setState({ + search: this.props.router.location.query.query || '', + }); + } + + search(e) { + const value = e.target.value; + const router = this.props.router; + const routerQuery = _.clone(router.location.query); + delete routerQuery.query; + + const query = value === '' ? routerQuery : { ...routerQuery, query: value }; + this.setState({ search: value }); + + delay(() => { + // only update the route if the path hasn't changed in the meantime + if (this.state.pathname === router.location.pathname) { + router.push({ pathname: Utils.getRoutePath('posts.list'), query: query }); + } + }, 700); + } + + render() { + const resetQuery = _.clone(this.props.location.query); + delete resetQuery.query; + + return ( +
+ + + {this.state.search !== '' ? ( + + + + ) : null} + +
+ ); + } +} + +SearchForm.contextTypes = { + intl: intlShape, +}; + +registerComponent({ name: 'SearchForm', component: SearchForm, hocs: [withRouter] }); diff --git a/packages/example-forum/lib/components/common/Vote.jsx b/packages/example-forum/lib/components/common/Vote.jsx new file mode 100644 index 00000000..976b8c6e --- /dev/null +++ b/packages/example-forum/lib/components/common/Vote.jsx @@ -0,0 +1,71 @@ +import { Components, registerComponent, withMessages } from 'meteor/vulcan:core'; +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { withVote, hasVotedClient } from 'meteor/vulcan:voting'; +import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; + +class Vote extends PureComponent { + + constructor() { + super(); + this.vote = this.vote.bind(this); + this.getActionClass = this.getActionClass.bind(this); + this.hasVoted = this.hasVoted.bind(this); + } + + vote(e) { + + e.preventDefault(); + + const document = this.props.document; + const collection = this.props.collection; + const user = this.props.currentUser; + + if(!user){ + this.props.flash({id: 'users.please_log_in'}); + } else { + this.props.vote({document, voteType: 'upvote', collection, currentUser: this.props.currentUser}); + } + } + + hasVoted() { + return hasVotedClient({document: this.props.document, voteType: 'upvote'}) + } + + getActionClass() { + + const actionsClass = classNames( + 'vote-button', + {upvoted: this.hasVoted()}, + ); + + return actionsClass; + } + + render() { + return ( + + ) + } + +} + +Vote.propTypes = { + document: PropTypes.object.isRequired, // the document to upvote + collection: PropTypes.object.isRequired, // the collection containing the document + vote: PropTypes.func.isRequired, // mutate function with callback inside + currentUser: PropTypes.object, // user might not be logged in, so don't make it required +}; + +Vote.contextTypes = { + intl: intlShape +}; + +registerComponent({ name: 'Vote', component: Vote, hocs: [withMessages, withVote] }); diff --git a/imports/components/posts/PostsCategories.jsx b/packages/example-forum/lib/components/posts/PostsCategories.jsx similarity index 85% rename from imports/components/posts/PostsCategories.jsx rename to packages/example-forum/lib/components/posts/PostsCategories.jsx index d68ee16d..5c868c80 100644 --- a/imports/components/posts/PostsCategories.jsx +++ b/packages/example-forum/lib/components/posts/PostsCategories.jsx @@ -14,4 +14,4 @@ const PostsCategories = ({post}) => { PostsCategories.displayName = "PostsCategories"; -registerComponent('PostsCategories', PostsCategories); \ No newline at end of file +registerComponent({ name: 'PostsCategories', component: PostsCategories }); \ No newline at end of file diff --git a/imports/components/posts/PostsCommenters.jsx b/packages/example-forum/lib/components/posts/PostsCommenters.jsx similarity index 66% rename from imports/components/posts/PostsCommenters.jsx rename to packages/example-forum/lib/components/posts/PostsCommenters.jsx index b60f9694..24aae779 100644 --- a/imports/components/posts/PostsCommenters.jsx +++ b/packages/example-forum/lib/components/posts/PostsCommenters.jsx @@ -1,11 +1,14 @@ import { Components, registerComponent } from 'meteor/vulcan:core'; import React from 'react'; import { Link } from 'react-router'; -import Posts from "meteor/vulcan:posts"; +import { Posts } from '../../modules/posts/index.js'; const PostsCommenters = ({post}) => { return (
+
+ {_.take(post.commenters, 4).map(user => user && )} +
@@ -19,4 +22,4 @@ const PostsCommenters = ({post}) => { PostsCommenters.displayName = "PostsCommenters"; -registerComponent('PostsCommenters', PostsCommenters); +registerComponent({ name: 'PostsCommenters', component: PostsCommenters }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/posts/PostsCommentsThread.jsx b/packages/example-forum/lib/components/posts/PostsCommentsThread.jsx new file mode 100644 index 00000000..cd6f4351 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsCommentsThread.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { withList, withCurrentUser, Components, registerComponent, Utils } from 'meteor/vulcan:core'; + +const PostsCommentsThread = (props, /* context*/) => { + + const {loading, terms: { postId }, results, totalCount, currentUser} = props; + + if (loading) { + + return
+ + } else { + + const resultsClone = _.map(results, _.clone); // we don't want to modify the objects we got from props + const nestedComments = Utils.unflatten(resultsClone, {idProperty: '_id', parentIdProperty: 'parentCommentId'}); + + return ( +
+

+ + {!!currentUser ? +
+

+ +
: +
+ }> + + +
+ } +
+ ); + } +}; + +PostsCommentsThread.displayName = 'PostsCommentsThread'; + +PostsCommentsThread.propTypes = { + currentUser: PropTypes.object +}; + +const options = { + collectionName: 'Comments', + queryName: 'commentsListQuery', + fragmentName: 'CommentsList', + limit: 0, +}; + +registerComponent({ name: 'PostsCommentsThread', component: PostsCommentsThread, hocs: [[withList, options], withCurrentUser] }); diff --git a/packages/example-forum/lib/components/posts/PostsDaily.jsx b/packages/example-forum/lib/components/posts/PostsDaily.jsx new file mode 100644 index 00000000..18eb55f6 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsDaily.jsx @@ -0,0 +1,22 @@ +import { Components, registerComponent, getSetting, registerSetting } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import moment from 'moment'; + +registerSetting('forum.numberOfDays', 5, 'Number of days to display in Daily view'); + +const PostsDaily = props => { + // const terms = props.location && props.location.query; + const numberOfDays = getSetting('forum.numberOfDays', 5); + const terms = { + view: 'top', + after: moment().subtract(numberOfDays - 1, 'days').format('YYYY-MM-DD'), + before: moment().format('YYYY-MM-DD'), + }; + + return +}; + +PostsDaily.displayName = 'PostsDaily'; + +registerComponent({ name: 'PostsDaily', component: PostsDaily }); diff --git a/packages/example-forum/lib/components/posts/PostsDailyList.jsx b/packages/example-forum/lib/components/posts/PostsDailyList.jsx new file mode 100644 index 00000000..3c8f0157 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsDailyList.jsx @@ -0,0 +1,119 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import moment from 'moment'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { Posts } from '../../modules/posts/index.js'; +import { withCurrentUser, withList, getSetting, Components, getRawComponent, registerComponent } from 'meteor/vulcan:core'; + +class PostsDailyList extends PureComponent { + + constructor(props) { + super(props); + this.loadMoreDays = this.loadMoreDays.bind(this); + this.state = { + days: props.days, + after: props.terms.after, + daysLoaded: props.days, + afterLoaded: props.terms.after, + before: props.terms.before, + loading: true, + }; + } + + // intercept prop change and only show more days once data is done loading + componentWillReceiveProps(nextProps) { + if (nextProps.networkStatus === 2) { + this.setState({loading: true}); + } else { + this.setState((prevState, props) => ({ + loading: false, + daysLoaded: prevState.days, + afterLoaded: prevState.after, + })); + } + } + + // return date objects for all the dates in a range + getDateRange(after, before) { + const mAfter = moment(after, 'YYYY-MM-DD'); + const mBefore = moment(before, 'YYYY-MM-DD'); + const daysCount = mBefore.diff(mAfter, 'days') + 1; + const range = _.range(daysCount).map( + i => moment(before, 'YYYY-MM-DD').subtract(i, 'days').startOf('day') + ); + return range; + } + + getDatePosts(posts, date) { + return _.filter(posts, post => moment(new Date(post.postedAt)).startOf('day').isSame(date, 'day')); + } + + // variant 1: reload everything each time (works with polling) + loadMoreDays(e) { + e.preventDefault(); + const numberOfDays = getSetting('forum.numberOfDays', 5); + const loadMoreAfter = moment(this.state.after, 'YYYY-MM-DD').subtract(numberOfDays, 'days').format('YYYY-MM-DD'); + + this.props.loadMore({ + ...this.props.terms, + after: loadMoreAfter, + }); + + this.setState({ + days: this.state.days + this.props.increment, + after: loadMoreAfter, + }); + } + + // variant 2: only load new data (need to disable polling) + loadMoreDaysInc(e) { + e.preventDefault(); + const numberOfDays = getSetting('forum.numberOfDays', 5); + const loadMoreAfter = moment(this.state.after, 'YYYY-MM-DD').subtract(numberOfDays, 'days').format('YYYY-MM-DD'); + const loadMoreBefore = moment(this.state.after, 'YYYY-MM-DD').subtract(1, 'days').format('YYYY-MM-DD'); + + this.props.loadMoreInc({ + ...this.props.terms, + before: loadMoreBefore, + after: loadMoreAfter, + }); + + this.setState({ + days: this.state.days + this.props.increment, + after: loadMoreAfter, + }); + } + + render() { + const posts = this.props.results; + const dates = this.getDateRange(this.state.afterLoaded, this.state.before); + + return ( +
+ + {dates.map((date, index) => )} + {this.state.loading? : } +
+ ) + } +} + +PostsDailyList.propTypes = { + currentUser: PropTypes.object, + days: PropTypes.number, + increment: PropTypes.number +}; + +PostsDailyList.defaultProps = { + days: getSetting('forum.numberOfDays', 5), + increment: getSetting('forum.numberOfDays', 5) +}; + +const options = { + collection: Posts, + queryName: 'postsDailyListQuery', + fragmentName: 'PostsList', + limit: 0, +}; + +registerComponent({ name: 'PostsDailyList', component: PostsDailyList, hocs: [withCurrentUser, [withList, options]] }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/posts/PostsDay.jsx b/packages/example-forum/lib/components/posts/PostsDay.jsx new file mode 100644 index 00000000..f028a549 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsDay.jsx @@ -0,0 +1,32 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:core'; + +class PostsDay extends PureComponent { + + render() { + const {date, posts} = this.props; + const noPosts = posts.length === 0; + + return ( +
+

{date.format('dddd, MMMM Do YYYY')}

+ { noPosts ? : +
+
+ {posts.map((post, index) => )} +
+
+ } +
+ ); + } +} + +PostsDay.propTypes = { + currentUser: PropTypes.object, + date: PropTypes.object, + number: PropTypes.number +}; + +registerComponent({ name: 'PostsDay', component: PostsDay }); diff --git a/imports/components/posts/PostsEditForm.jsx b/packages/example-forum/lib/components/posts/PostsEditForm.jsx similarity index 73% rename from imports/components/posts/PostsEditForm.jsx rename to packages/example-forum/lib/components/posts/PostsEditForm.jsx index 03140422..b5e4ba01 100644 --- a/imports/components/posts/PostsEditForm.jsx +++ b/packages/example-forum/lib/components/posts/PostsEditForm.jsx @@ -1,8 +1,8 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Components, registerComponent, getFragment, withMessages, withCurrentUser } from 'meteor/vulcan:core'; +import { Components, registerComponent, withMessages, withCurrentUser } from 'meteor/vulcan:core'; import { intlShape } from 'meteor/vulcan:i18n'; -import Posts from "meteor/vulcan:posts"; +import { Posts } from '../../modules/posts/index.js'; import Users from "meteor/vulcan:users"; import { withRouter } from 'react-router' @@ -27,11 +27,11 @@ class PostsEditForm extends PureComponent { { this.props.closeModal(); - this.props.flash(this.context.intl.formatMessage({ id: 'posts.edit_success' }, { title: post.title }), 'success'); + this.props.flash({ id: 'posts.edit_success', properties: { title: post.title }, type: 'success'}); }} + mutationFragmentName="PostsPage" removeSuccessCallback={({ documentId, documentTitle }) => { // post edit form is being included from a single post, redirect to index // note: this.props.params is in the worst case an empty obj (from react-router) @@ -39,8 +39,7 @@ class PostsEditForm extends PureComponent { this.props.router.push('/'); } - const deleteDocumentSuccess = this.context.intl.formatMessage({ id: 'posts.delete_success' }, { title: documentTitle }); - this.props.flash(deleteDocumentSuccess, 'success'); + this.props.flash({ id: 'posts.delete_success' , properties: { title: documentTitle }, type: 'success'}); // todo: handle events in collection callbacks // this.context.events.track("post deleted", {_id: documentId}); }} @@ -62,4 +61,4 @@ PostsEditForm.contextTypes = { intl: intlShape } -registerComponent('PostsEditForm', PostsEditForm, withMessages, withRouter, withCurrentUser); +registerComponent({ name: 'PostsEditForm', component: PostsEditForm, hocs: [withMessages, withRouter, withCurrentUser] }); diff --git a/packages/example-forum/lib/components/posts/PostsHome.jsx b/packages/example-forum/lib/components/posts/PostsHome.jsx new file mode 100644 index 00000000..e5e593d7 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsHome.jsx @@ -0,0 +1,12 @@ +import { Components, registerComponent } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +const PostsHome = (props, context) => { + const terms = _.isEmpty(props.location && props.location.query) ? {view: 'top'}: props.location.query; + return +}; + +PostsHome.displayName = "PostsHome"; + +registerComponent({ name: 'PostsHome', component: PostsHome }); diff --git a/imports/components/posts/PostsItem.jsx b/packages/example-forum/lib/components/posts/PostsItem.jsx similarity index 72% rename from imports/components/posts/PostsItem.jsx rename to packages/example-forum/lib/components/posts/PostsItem.jsx index a36e1b69..07c26ccd 100644 --- a/imports/components/posts/PostsItem.jsx +++ b/packages/example-forum/lib/components/posts/PostsItem.jsx @@ -1,9 +1,9 @@ -import { Components, registerComponent, ModalTrigger } from 'meteor/vulcan:core'; +import { Components, registerComponent } from 'meteor/vulcan:core'; import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'meteor/vulcan:i18n'; import { Link } from 'react-router'; -import Posts from "meteor/vulcan:posts"; +import { Posts } from '../../modules/posts/index.js'; import moment from 'moment'; class PostsItem extends PureComponent { @@ -19,13 +19,13 @@ class PostsItem extends PureComponent { renderActions() { return (
- }> + }> - +
) } - + render() { const {post} = this.props; @@ -37,35 +37,33 @@ class PostsItem extends PureComponent {
- +
+ {post.thumbnailUrl ? : null} +

- + {post.title} {this.renderCategories()}

-
- - -
- + {post.user?
: null}
{post.postedAt ? moment(new Date(post.postedAt)).fromNow() : }
- {!post.commentCount || post.commentCount === 0 ? : + {!post.commentCount || post.commentCount === 0 ? : post.commentCount === 1 ? : }
{this.props.currentUser && this.props.currentUser.isAdmin ? : null} - {Posts.options.mutations.edit.check(this.props.currentUser, post) ? this.renderActions() : null} + {Posts.options.mutations.edit.check(this.props.currentUser, post) && this.renderActions()}
@@ -83,4 +81,4 @@ PostsItem.propTypes = { terms: PropTypes.object, }; -registerComponent('PostsItem', PostsItem); +registerComponent({ name: 'PostsItem', component: PostsItem }); diff --git a/imports/components/posts/PostsList.jsx b/packages/example-forum/lib/components/posts/PostsList.jsx similarity index 78% rename from imports/components/posts/PostsList.jsx rename to packages/example-forum/lib/components/posts/PostsList.jsx index 1e41ff20..180ad535 100644 --- a/imports/components/posts/PostsList.jsx +++ b/packages/example-forum/lib/components/posts/PostsList.jsx @@ -1,12 +1,11 @@ import { Components, registerComponent, withList, withCurrentUser, Utils } from 'meteor/vulcan:core'; import React from 'react'; import PropTypes from 'prop-types'; -import Posts from 'meteor/vulcan:posts'; -import Alert from 'react-bootstrap/lib/Alert' +import { Posts } from '../../modules/posts/index.js'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; import classNames from 'classnames'; -const Error = ({error}) => {error.message} +const Error = ({error}) => {error.message} const PostsList = ({className, results, loading, count, totalCount, loadMore, showHeader = true, showLoadMore = true, networkStatus, currentUser, error, terms}) => { @@ -17,16 +16,16 @@ const PostsList = ({className, results, loading, count, totalCount, loadMore, sh const hasMore = totalCount > results.length; return ( -
+
{showHeader ? : null} {error ? : null }
{results.map(post => )}
- {showLoadMore ? - hasMore ? - : - : + {showLoadMore ? + hasMore ? + : + : null }
@@ -50,9 +49,9 @@ const PostsList = ({className, results, loading, count, totalCount, loadMore, sh
- ) + ) } - + }; PostsList.displayName = "PostsList"; @@ -78,4 +77,4 @@ const options = { fragmentName: 'PostsList', }; -registerComponent('PostsList', PostsList, withCurrentUser, [withList, options]); +registerComponent({ name: 'PostsList', component: PostsList, hocs: [withCurrentUser, [withList, options]] }); diff --git a/imports/components/posts/PostsListHeader.jsx b/packages/example-forum/lib/components/posts/PostsListHeader.jsx similarity index 75% rename from imports/components/posts/PostsListHeader.jsx rename to packages/example-forum/lib/components/posts/PostsListHeader.jsx index 7544294f..dcf73f68 100644 --- a/imports/components/posts/PostsListHeader.jsx +++ b/packages/example-forum/lib/components/posts/PostsListHeader.jsx @@ -7,11 +7,10 @@ const PostsListHeader = () => {
- +
-
) @@ -19,4 +18,4 @@ const PostsListHeader = () => { PostsListHeader.displayName = "PostsListHeader"; -registerComponent('PostsListHeader', PostsListHeader); +registerComponent({ name: 'PostsListHeader', component: PostsListHeader }); diff --git a/packages/example-forum/lib/components/posts/PostsLoadMore.jsx b/packages/example-forum/lib/components/posts/PostsLoadMore.jsx new file mode 100644 index 00000000..4874a3d6 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsLoadMore.jsx @@ -0,0 +1,21 @@ +import { Components, registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import classNames from 'classnames'; + +const PostsLoadMore = ({loading, loadMore, count, totalCount}) => { + return ( + + ) +} + +PostsLoadMore.displayName = "PostsLoadMore"; + +registerComponent({ name: 'PostsLoadMore', component: PostsLoadMore }); diff --git a/imports/components/posts/PostsLoading.jsx b/packages/example-forum/lib/components/posts/PostsLoading.jsx similarity index 78% rename from imports/components/posts/PostsLoading.jsx rename to packages/example-forum/lib/components/posts/PostsLoading.jsx index fa97c958..3b0f3184 100644 --- a/imports/components/posts/PostsLoading.jsx +++ b/packages/example-forum/lib/components/posts/PostsLoading.jsx @@ -7,4 +7,4 @@ const PostsLoading = props => { PostsLoading.displayName = "PostsLoading"; -registerComponent('PostsLoading', PostsLoading); \ No newline at end of file +registerComponent({ name: 'PostsLoading', component: PostsLoading }); \ No newline at end of file diff --git a/imports/components/posts/PostsNewButton.jsx b/packages/example-forum/lib/components/posts/PostsNewButton.jsx similarity index 63% rename from imports/components/posts/PostsNewButton.jsx rename to packages/example-forum/lib/components/posts/PostsNewButton.jsx index 48f75016..c41212ea 100644 --- a/imports/components/posts/PostsNewButton.jsx +++ b/packages/example-forum/lib/components/posts/PostsNewButton.jsx @@ -2,14 +2,14 @@ import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:co import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import Button from 'react-bootstrap/lib/Button'; const PostsNewButton = (props, context) => { - const size = 'large'; - const button = ; + + const size = props.currentUser ? 'lg' : 'sm'; + const button = ; return ( - + ) } @@ -25,4 +25,4 @@ PostsNewButton.contextTypes = { intl: intlShape }; -registerComponent('PostsNewButton', PostsNewButton, withCurrentUser); +registerComponent({ name: 'PostsNewButton', component: PostsNewButton, hocs: [withCurrentUser] }); diff --git a/packages/example-forum/lib/components/posts/PostsNewForm.jsx b/packages/example-forum/lib/components/posts/PostsNewForm.jsx new file mode 100644 index 00000000..3b263851 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsNewForm.jsx @@ -0,0 +1,67 @@ +import { + Components, + registerComponent, + getRawComponent, + getFragment, + withMessages, + withList, +} from 'meteor/vulcan:core'; +import { Posts } from '../../modules/posts/index.js'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { withRouter } from 'react-router' + +const PostsNewForm = (props, context) => { + if (props.loading) { + return
; + } + return ( + +

+ +

+ +
+ } + > +
+ { + props.closeModal(); + props.router.push({pathname: props.redirect || Posts.getPageUrl(post)}); + props.flash({id: "posts.created_message", type: "success"}); + }} + /> +
+ + ); +}; + +PostsNewForm.propTypes = { + closeModal: PropTypes.func, + router: PropTypes.object, + flash: PropTypes.func, + redirect: PropTypes.string, +} + +PostsNewForm.contextTypes = { + closeCallback: PropTypes.func, +}; + +PostsNewForm.displayName = "PostsNewForm"; + +const options = { + collectionName: 'Categories', + queryName: 'categoriesListQuery', + fragmentName: 'CategoriesList', + limit: 0, + pollInterval: 0, +}; + +registerComponent({ name: 'PostsNewForm', component: PostsNewForm, hocs: [withRouter, withMessages, [withList, options]] }); diff --git a/imports/components/posts/PostsNoMore.jsx b/packages/example-forum/lib/components/posts/PostsNoMore.jsx similarity index 80% rename from imports/components/posts/PostsNoMore.jsx rename to packages/example-forum/lib/components/posts/PostsNoMore.jsx index 7df37bed..85b9f7dd 100644 --- a/imports/components/posts/PostsNoMore.jsx +++ b/packages/example-forum/lib/components/posts/PostsNoMore.jsx @@ -6,4 +6,4 @@ const PostsNoMore = props =>

- + } else if (!this.props.document) { - - return
+ + // console.log(`// missing post (_id: ${this.props.documentId})`); + return
} else { const post = this.props.document; @@ -25,44 +27,44 @@ class PostsPage extends Component {
- + {post.htmlBody ?
: null} - + -
+
); - + } } - + // triggered after the component did mount on the client async componentDidMount() { try { - + // destructure the relevant props - const { + const { // from the parent component, used in withDocument, GraphQL HOC documentId, - // from connect, Redux HOC - setViewed, - postsViewed, + // from connect, Redux HOC + setViewed, + postsViewed, // from withMutation, GraphQL HOC increasePostViewCount, } = this.props; - + // a post id has been found & it's has not been seen yet on this client session if (documentId && !postsViewed.includes(documentId)) { - + // trigger the asynchronous mutation with postId as an argument await increasePostViewCount({postId: documentId}); - + // once the mutation is done, update the redux store setViewed(documentId); } - + } catch(error) { console.log(error); // eslint-disable-line } @@ -95,15 +97,15 @@ const mapDispatchToProps = dispatch => bindActionCreators(getActions().postsView registerComponent( // component name used by Vulcan - 'PostsPage', - // React component + 'PostsPage', + // React component PostsPage, // HOC to give access to the current user - withCurrentUser, + withCurrentUser, // HOC to load the data of the document, based on queryOptions & a documentId props - [withDocument, queryOptions], + [withDocument, queryOptions], // HOC to provide a single mutation, based on mutationOptions - withMutation(mutationOptions), + withMutation(mutationOptions), // HOC to give access to the redux store & related actions connect(mapStateToProps, mapDispatchToProps) ); diff --git a/packages/example-forum/lib/components/posts/PostsSingle.jsx b/packages/example-forum/lib/components/posts/PostsSingle.jsx new file mode 100644 index 00000000..8d1dfa26 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsSingle.jsx @@ -0,0 +1,10 @@ +import { Components, registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; + +const PostsSingle = (props, context) => { + return +}; + +PostsSingle.displayName = "PostsSingle"; + +registerComponent({ name: 'PostsSingle', component: PostsSingle }); diff --git a/imports/components/posts/PostsStats.jsx b/packages/example-forum/lib/components/posts/PostsStats.jsx similarity index 50% rename from imports/components/posts/PostsStats.jsx rename to packages/example-forum/lib/components/posts/PostsStats.jsx index 56c5098a..dab46a9e 100644 --- a/imports/components/posts/PostsStats.jsx +++ b/packages/example-forum/lib/components/posts/PostsStats.jsx @@ -5,14 +5,14 @@ const PostsStats = ({post}) => { return (
- {post.score ? {Math.floor(post.score*10000)/10000} Score : ""} - {post.upvotes} Upvotes - {post.clickCount} Clicks - {post.viewCount} Views + {post.score ? {Math.floor((post.score || 0)*10000)/10000} Score : ""} + {post.baseScore || 0} Upvotes + {post.clickCount || 0} Clicks + {post.viewCount || 0} Views
) } PostsStats.displayName = "PostsStats"; -registerComponent('PostsStats', PostsStats); \ No newline at end of file +registerComponent({ name: 'PostsStats', component: PostsStats }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/posts/PostsThumbnail.jsx b/packages/example-forum/lib/components/posts/PostsThumbnail.jsx new file mode 100644 index 00000000..c72ac966 --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsThumbnail.jsx @@ -0,0 +1,12 @@ +import { registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import { Posts } from '../../modules/posts/index.js'; + +const PostsThumbnail = ({post}) => + + + + +PostsThumbnail.displayName = "PostsThumbnail"; + +registerComponent({ name: 'PostsThumbnail', component: PostsThumbnail }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/posts/PostsViews.jsx b/packages/example-forum/lib/components/posts/PostsViews.jsx new file mode 100644 index 00000000..3b23422b --- /dev/null +++ b/packages/example-forum/lib/components/posts/PostsViews.jsx @@ -0,0 +1,54 @@ +import { Components, registerComponent, withCurrentUser, Utils } from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { withRouter } from 'react-router'; +import Users from 'meteor/vulcan:users'; + +const PostsViews = (props, context) => { + let views = ['top', 'new', 'best']; + const adminViews = ['pending', 'rejected', 'scheduled']; + + if (Users.canDo(props.currentUser, 'posts.edit.all')) { + views = views.concat(adminViews); + } + + const query = _.clone(props.router.location.query); + + return ( +
+ ({ + to: { pathname: Utils.getRoutePath('posts.list'), query: { ...query, view: view } }, + labelId: `posts.${view}`, + })), + { + to: `/daily`, + labelId: `posts.daily`, + }, + ]} + /> +
+ ); +}; + +PostsViews.propTypes = { + currentUser: PropTypes.object, + defaultView: PropTypes.string, +}; + +PostsViews.defaultProps = { + defaultView: 'top', +}; + +PostsViews.contextTypes = { + currentRoute: PropTypes.object, +}; + +PostsViews.displayName = 'PostsViews'; + +registerComponent({ name: 'PostsViews', component: PostsViews, hocs: [withCurrentUser, withRouter] }); diff --git a/packages/example-forum/lib/components/users/UsersAccount.jsx b/packages/example-forum/lib/components/users/UsersAccount.jsx new file mode 100644 index 00000000..2c97de30 --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersAccount.jsx @@ -0,0 +1,17 @@ +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; + +const UsersAccount = (props, /* context*/) => { + // note: terms is as the same as a document-shape the SmartForm edit-mode expects to receive + const terms = props.params.slug ? { slug: props.params.slug } : props.currentUser ? { documentId: props.currentUser._id } : {}; + return +}; + +UsersAccount.propTypes = { + currentUser: PropTypes.object +}; + +UsersAccount.displayName = 'UsersAccount'; + +registerComponent({ name: 'UsersAccount', component: UsersAccount, hocs: [withCurrentUser] }); diff --git a/packages/example-forum/lib/components/users/UsersAccountMenu.jsx b/packages/example-forum/lib/components/users/UsersAccountMenu.jsx new file mode 100644 index 00000000..82136086 --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersAccountMenu.jsx @@ -0,0 +1,24 @@ +import { Components, registerComponent } from 'meteor/vulcan:core'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { STATES } from 'meteor/vulcan:accounts'; + +const UsersAccountMenu = ({ state }) => ( + + + +
+ } + menuContents={} + /> +); + +UsersAccountMenu.displayName = 'UsersAccountMenu'; + +registerComponent({ name: 'UsersAccountMenu', component: UsersAccountMenu }); diff --git a/packages/vulcan-core/lib/modules/components/Avatar.jsx b/packages/example-forum/lib/components/users/UsersAvatar.jsx similarity index 76% rename from packages/vulcan-core/lib/modules/components/Avatar.jsx rename to packages/example-forum/lib/components/users/UsersAvatar.jsx index 4f7fd151..53187177 100644 --- a/packages/vulcan-core/lib/modules/components/Avatar.jsx +++ b/packages/example-forum/lib/components/users/UsersAvatar.jsx @@ -1,11 +1,11 @@ -import { registerComponent } from 'meteor/vulcan:lib'; +import { registerComponent } from 'meteor/vulcan:core'; import React from 'react'; import PropTypes from 'prop-types'; import Users from 'meteor/vulcan:users'; import { Link } from 'react-router'; import classNames from 'classnames'; -const Avatar = ({className, user, link}) => { +const UsersAvatar = ({className, user, link}) => { const avatarUrl = user.avatarUrl || Users.avatar.getUrl(user); @@ -27,17 +27,17 @@ const Avatar = ({className, user, link}) => { } -Avatar.propTypes = { +UsersAvatar.propTypes = { user: PropTypes.object.isRequired, size: PropTypes.string, link: PropTypes.bool } -Avatar.defaultProps = { +UsersAvatar.defaultProps = { size: 'medium', link: true } -Avatar.displayName = 'Avatar'; +UsersAvatar.displayName = 'UsersAvatar'; -registerComponent('Avatar', Avatar); +registerComponent({ name: 'UsersAvatar', component: UsersAvatar }); diff --git a/packages/example-forum/lib/components/users/UsersEditForm.jsx b/packages/example-forum/lib/components/users/UsersEditForm.jsx new file mode 100644 index 00000000..ed61265c --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersEditForm.jsx @@ -0,0 +1,48 @@ +import { Components, registerComponent, withCurrentUser, withMessages } from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; +import Users from 'meteor/vulcan:users'; +import { STATES } from 'meteor/vulcan:accounts'; + +const UsersEditForm = (props, context) => { + return ( + } + > +
+

+ +
+ }> + + +
+ + { + props.flash({ id: 'users.edit_success', properties: {name: Users.getDisplayName(user)}, type: 'success'}) + }} + showRemove={true} + /> +
+
+ ); +}; + + +UsersEditForm.propTypes = { + terms: PropTypes.object, // a user is defined by its unique _id or its unique slug +}; + +UsersEditForm.contextTypes = { + intl: intlShape +}; + +UsersEditForm.displayName = 'UsersEditForm'; + +registerComponent({ name: 'UsersEditForm', component: UsersEditForm, hocs: [withMessages, withCurrentUser] }); diff --git a/packages/example-forum/lib/components/users/UsersMenu.jsx b/packages/example-forum/lib/components/users/UsersMenu.jsx new file mode 100644 index 00000000..459a75d0 --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersMenu.jsx @@ -0,0 +1,60 @@ +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; +import { Meteor } from 'meteor/meteor'; +import Users from 'meteor/vulcan:users'; +import { withApollo } from 'react-apollo'; + +const UsersMenu = ({ currentUser, client }) => { + const menuItems = [ + { + to: `/users/${currentUser.slug}`, + labelId: 'users.profile', + }, + { + to: `/account`, + labelId: 'users.edit_account', + }, + ]; + + if (Users.isAdmin(currentUser)) { + menuItems.push({ + to: `/admin/users`, + labelId: 'admin.users', + }); + menuItems.push({ + to: `/admin/categories`, + labelId: 'admin.categories', + }); + } + + menuItems.push({ + labelId: 'users.log_out', + itemProps: { + onClick: () => Meteor.logout(() => client.resetStore()), + }, + }); + + return ( +
+ + +
{Users.getDisplayName(currentUser)}
+
+ } + menuItems={menuItems} + /> +
+ ); +}; + +UsersMenu.propsTypes = { + currentUser: PropTypes.object, + client: PropTypes.object, +}; + +registerComponent({ name: 'UsersMenu', component: UsersMenu, hocs: [withCurrentUser, withApollo] }); diff --git a/packages/example-forum/lib/components/users/UsersName.jsx b/packages/example-forum/lib/components/users/UsersName.jsx new file mode 100644 index 00000000..e9a362f2 --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersName.jsx @@ -0,0 +1,15 @@ +import { registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; +import PropTypes from 'prop-types'; +import Users from 'meteor/vulcan:users'; +import { Link } from 'react-router'; + +const UsersName = ({user}) => {Users.getDisplayName(user)} + +UsersName.propTypes = { + user: PropTypes.object.isRequired, +} + +UsersName.displayName = 'UsersName'; + +registerComponent({ name: 'UsersName', component: UsersName }); \ No newline at end of file diff --git a/packages/example-forum/lib/components/users/UsersProfile.jsx b/packages/example-forum/lib/components/users/UsersProfile.jsx new file mode 100644 index 00000000..912c2a73 --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersProfile.jsx @@ -0,0 +1,54 @@ +import { Components, registerComponent, withDocument, withCurrentUser } from 'meteor/vulcan:core'; +import React from 'react'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import Users from 'meteor/vulcan:users'; +import { Link } from 'react-router'; + +const UsersProfile = (props) => { + if (props.loading) { + + return
+ + } else if (!props.document) { + + // console.log(`// missing user (_id/slug: ${props.documentId || props.slug})`); + return
+ + } else { + + const user = props.document; + + const terms = {view: "userPosts", userId: user._id}; + + return ( +
+ +

{Users.getDisplayName(user)}

+ {user.htmlBio ?
: null } + +

+ +
+ ) + } +} + +UsersProfile.propTypes = { + // document: PropTypes.object.isRequired, +} + +UsersProfile.displayName = "UsersProfile"; + +const options = { + collection: Users, + queryName: 'usersSingleQuery', + fragmentName: 'UsersProfile', +}; + +registerComponent({ name: 'UsersProfile', component: UsersProfile, hocs: [withCurrentUser, [withDocument, options]] }); diff --git a/packages/example-forum/lib/components/users/UsersProfileCheck.jsx b/packages/example-forum/lib/components/users/UsersProfileCheck.jsx new file mode 100644 index 00000000..84639afd --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersProfileCheck.jsx @@ -0,0 +1,82 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Users from 'meteor/vulcan:users'; +import { withDocument, Components, registerComponent, withMessages } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { gql } from 'react-apollo'; + +const UsersProfileCheck = ({currentUser, document, loading, flash}, context) => { + + // we're loading all fields marked as "mustComplete" using withDocument + const userMustCompleteFields = document; + + // if user is not logged in, or userMustCompleteFields is still loading, don't return anything + if (!currentUser || loading) { + + return null; + + } else { + + // return fields that are required by the schema but haven't been filled out yet + const fieldsToComplete = _.filter(Users.getRequiredFields(), fieldName => { + return !userMustCompleteFields[fieldName]; + }); + + if (fieldsToComplete.length > 0) { + const footer = ( + Meteor.logout(() => window.location.reload() /* something is broken here when giving the apollo client as a prop*/) }> + + + ); + return ( + } + footerContent={ footer } + > + { + const newUser = {...currentUser, ...user}; + if (Users.hasCompletedProfile(newUser)) { + flash({id: "users.profile_completed", type: 'success'}); + } + }} + /> + + ); + } else { + + return null; + + } + } + +}; + + +UsersProfileCheck.propTypes = { + currentUser: PropTypes.object +}; + +UsersProfileCheck.displayName = 'UsersProfileCheck'; + +const mustCompleteFragment = gql` + fragment UsersMustCompleteFragment on User { + _id + ${Users.getRequiredFields().join('\n')} + } +` + +const options = { + collection: Users, + queryName: 'usersMustCompleteQuery', + fragment: mustCompleteFragment, +}; + +registerComponent({ name: 'UsersProfileCheck', component: UsersProfileCheck, hocs: [withMessages, [withDocument, options]] }); diff --git a/packages/example-forum/lib/components/users/UsersSingle.jsx b/packages/example-forum/lib/components/users/UsersSingle.jsx new file mode 100644 index 00000000..58d311af --- /dev/null +++ b/packages/example-forum/lib/components/users/UsersSingle.jsx @@ -0,0 +1,10 @@ +import { Components, registerComponent } from 'meteor/vulcan:core'; +import React from 'react'; + +const UsersSingle = (props, context) => { + return +}; + +UsersSingle.displayName = "UsersSingle"; + +registerComponent({ name: 'UsersSingle', component: UsersSingle }); diff --git a/packages/example-forum/lib/modules/categories/collection.js b/packages/example-forum/lib/modules/categories/collection.js new file mode 100644 index 00000000..7e6e3126 --- /dev/null +++ b/packages/example-forum/lib/modules/categories/collection.js @@ -0,0 +1,26 @@ +/* + +The Categories collection + +*/ + +import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core'; +import schema from './schema.js'; + +/** + * @summary The global namespace for Categories. + * @namespace Categories + */ + export const Categories = createCollection({ + + collectionName: 'Categories', + + typeName: 'Category', + + schema, + + resolvers: getDefaultResolvers('Categories'), + + mutations: getDefaultMutations('Categories'), + +}); \ No newline at end of file diff --git a/packages/example-forum/lib/modules/categories/custom_fields.js b/packages/example-forum/lib/modules/categories/custom_fields.js new file mode 100644 index 00000000..8e20317e --- /dev/null +++ b/packages/example-forum/lib/modules/categories/custom_fields.js @@ -0,0 +1,52 @@ +/* + +Custom fields on Posts collection + +*/ + +import { Posts } from '../../modules/posts/index.js'; +import { getCategoriesAsOptions } from './schema.js'; + +Posts.addField([ + { + fieldName: 'categoriesIds', + fieldSchema: { + type: Array, + input: 'checkboxgroup', + optional: true, + canCreate: ['members'], + canUpdate: ['members'], + canRead: ['guests'], + options: props => { + return getCategoriesAsOptions(props.data.categories.results); + }, + query: ` + categories{ + results{ + _id + name + slug + order + } + } + `, + resolveAs: { + fieldName: 'categories', + type: '[Category]', + resolver: async (post, args, {currentUser, Users, Categories}) => { + if (!post.categoriesIds) return []; + const categories = _.compact(await Categories.loader.loadMany(post.categoriesIds)); + return Users.restrictViewableFields(currentUser, Categories, categories); + }, + addOriginalField: true, + } + } + }, + { + fieldName: 'categoriesIds.$', + fieldSchema: { + type: String, + optional: true + } + } +]); diff --git a/packages/example-forum/lib/modules/categories/fragments.js b/packages/example-forum/lib/modules/categories/fragments.js new file mode 100644 index 00000000..609822c0 --- /dev/null +++ b/packages/example-forum/lib/modules/categories/fragments.js @@ -0,0 +1,25 @@ +import { registerFragment } from 'meteor/vulcan:core'; + +// note: fragment used by default on CategoriesList & PostsList fragments +registerFragment(` + fragment CategoriesMinimumInfo on Category { + # vulcan:categories + _id + name + slug + } +`); + +registerFragment(` + fragment CategoriesList on Category { + # vulcan:categories + ...CategoriesMinimumInfo + description + order + image + parentId + parent { + ...CategoriesMinimumInfo + } + } +`); diff --git a/packages/vulcan-categories/lib/helpers.js b/packages/example-forum/lib/modules/categories/helpers.js similarity index 81% rename from packages/vulcan-categories/lib/helpers.js rename to packages/example-forum/lib/modules/categories/helpers.js index 1b05aba9..fcf29c1a 100644 --- a/packages/vulcan-categories/lib/helpers.js +++ b/packages/example-forum/lib/modules/categories/helpers.js @@ -1,5 +1,5 @@ -import Posts from "meteor/vulcan:posts"; -import Categories from "./collection.js"; +import { Posts } from '../posts/index.js'; +import { Categories } from './collection.js'; import { Utils } from 'meteor/vulcan:core'; /** @@ -30,7 +30,7 @@ Categories.getChildren = function (category) { var categoriesArray = []; var getChildren = function recurse (categories) { - var children = Categories.find({parentId: {$in: _.pluck(categories, "_id")}}).fetch() + var children = Categories.find({parentId: {$in: _.pluck(categories, '_id')}}).fetch() if (children.length > 0) { categoriesArray = categoriesArray.concat(children); recurse(children); @@ -52,9 +52,9 @@ Posts.getCategories = function (post) { * @param {Object} category */ Categories.getUrl = function (category, isAbsolute) { - isAbsolute = typeof isAbsolute === "undefined" ? false : isAbsolute; // default to false - const prefix = isAbsolute ? Utils.getSiteUrl().slice(0,-1) : ""; - // return prefix + FlowRouter.path("postsCategory", category); + isAbsolute = typeof isAbsolute === 'undefined' ? false : isAbsolute; // default to false + const prefix = isAbsolute ? Utils.getSiteUrl().slice(0,-1) : ''; + // return prefix + FlowRouter.path('postsCategory', category); return `${prefix}/?cat=${category.slug}`; }; /** @@ -62,6 +62,6 @@ Categories.getUrl = function (category, isAbsolute) { * @param {Object} category */ Categories.getCounterName = function (category) { - return category._id + "-postsCount"; + return category._id + '-postsCount'; } diff --git a/packages/example-forum/lib/modules/categories/index.js b/packages/example-forum/lib/modules/categories/index.js new file mode 100644 index 00000000..9ed57224 --- /dev/null +++ b/packages/example-forum/lib/modules/categories/index.js @@ -0,0 +1,8 @@ +export * from './collection.js'; + +import './fragments.js'; +import './views.js'; +import './custom_fields.js'; +import './helpers.js'; +import './permissions.js'; +import './parameters.js'; \ No newline at end of file diff --git a/packages/example-forum/lib/modules/categories/parameters.js b/packages/example-forum/lib/modules/categories/parameters.js new file mode 100644 index 00000000..e4b67c83 --- /dev/null +++ b/packages/example-forum/lib/modules/categories/parameters.js @@ -0,0 +1,61 @@ +/* + +Categories parameter + +*/ + +import { addCallback, getSetting, registerSetting } from 'meteor/vulcan:core'; +import gql from 'graphql-tag'; +import { Categories } from './collection.js'; + +registerSetting('forum.categoriesFilter', 'union', 'Display posts belonging to all (“intersection”) or at least one of (“union”) the selected categories'); + +// Category Posts Parameters +// Add a 'categories' property to terms which can be used to filter *all* existing Posts views. +function PostsCategoryParameter(parameters, terms, apolloClient) { + + // get category slugs + const cat = terms.cat || terms['cat[]']; + const categoriesSlugs = Array.isArray(cat) ? cat : [cat]; + let allCategories = []; + + if (cat && cat.length) { + + // get all categories + // note: specify all arguments, see https://github.com/apollographql/apollo-client/issues/2051 + const query = ` + query GetCategories($terms: JSON) { + CategoriesList(terms: $terms, enableCache: $enableCache) { + _id + slug + } + } + ` + + if (Meteor.isClient) { + // get categories from Redux store + allCategories = apolloClient.readQuery({ + query: gql`${query}`, + variables: {terms: {limit: 0, itemsPerPage: 0}, enableCache: false} + }).CategoriesList; + } else { + // TODO: figure out how to make this async without messing up withList on the client + // get categories through GraphQL API using runQuery + // const results = await runQuery(query); + // allCategories = results.data.CategoriesList; + allCategories = Categories.find().fetch(); + } + + // get corresponding category ids + const categoriesIds = _.pluck(_.filter(allCategories, category => _.contains(categoriesSlugs, category.slug)), '_id'); + + const operator = getSetting('forum.categoriesFilter', 'union') === 'union' ? '$in' : '$all'; + + // parameters.selector = Meteor.isClient ? {...parameters.selector, 'categories._id': {$in: categoriesIds}} : {...parameters.selector, categories: {[operator]: categoriesIds}}; + parameters.selector = {...parameters.selector, categoriesIds: {[operator]: categoriesIds}}; + } + + return parameters; +} + +addCallback('posts.parameters', PostsCategoryParameter); diff --git a/packages/vulcan-categories/lib/permissions.js b/packages/example-forum/lib/modules/categories/permissions.js similarity index 60% rename from packages/vulcan-categories/lib/permissions.js rename to packages/example-forum/lib/modules/categories/permissions.js index 22d68d47..2181bd17 100644 --- a/packages/vulcan-categories/lib/permissions.js +++ b/packages/example-forum/lib/modules/categories/permissions.js @@ -1,19 +1,25 @@ +/* + +Categories permissions + +*/ + import Users from 'meteor/vulcan:users'; const guestsActions = [ - "categories.view" + 'categories.view' ]; Users.groups.guests.can(guestsActions); const membersActions = [ - "categories.view" + 'categories.view' ]; Users.groups.members.can(membersActions); const adminActions = [ - "categories.view", - "categories.new", - "categories.edit.all", - "categories.remove.all" + 'categories.view', + 'categories.new', + 'categories.edit.all', + 'categories.remove.all' ]; Users.groups.admins.can(adminActions); diff --git a/packages/example-forum/lib/modules/categories/schema.js b/packages/example-forum/lib/modules/categories/schema.js new file mode 100644 index 00000000..ad26169b --- /dev/null +++ b/packages/example-forum/lib/modules/categories/schema.js @@ -0,0 +1,122 @@ +/* + +Categories schema + +*/ + +import { Utils } from 'meteor/vulcan:core'; + +export function getCategoriesAsOptions (categories) { + // give the form component (here: checkboxgroup) exploitable data + return categories.map(category => ({ + value: category._id, + label: category.name, + // slug: category.slug, // note: it may be used to look up from prefilled props + })); +} + +export function getCategoriesAsNestedOptions (categories) { + // give the form component (here: checkboxgroup) exploitable data + const formattedCategories = categories.map(function (category) { + return { + value: category._id, + label: category.name, + parentId: category.parentId, + _id: category._id + // slug: category.slug, // note: it may be used to look up from prefilled props + }; + }); + const nestedCategories = Utils.unflatten(formattedCategories, {idProperty: '_id', parentIdProperty: 'parentId', childrenProperty: 'options'}); + return nestedCategories; +} + +// category schema +const schema = { + _id: { + type: String, + canRead: ['guests'], + optional: true, + }, + name: { + type: String, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + }, + description: { + type: String, + optional: true, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + inputProperties: { + rows: 3 + } + }, + order: { + type: Number, + optional: true, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + }, + slug: { + type: String, + optional: true, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + onCreate: ({document: category}) => { + // if no slug has been provided, generate one + const slug = category.slug || Utils.slugify(category.name); + return Utils.getUnusedSlugByCollectionName('Categories', slug); + }, + onUpdate: ({data, document: category}) => { + // if slug is changing + if (data.slug && data.slug !== category.slug) { + const slug = data.slug; + return Utils.getUnusedSlugByCollectionName('Categories', slug); + } + } + }, + image: { + type: String, + optional: true, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + }, + parentId: { + type: String, + optional: true, + input: "select", + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + resolveAs: { + fieldName: 'parent', + type: 'Category', + resolver: async (category, args, {currentUser, Users, Categories}) => { + if (!category.parentId) return null; + const parent = await Categories.loader.load(category.parentId); + return Users.restrictViewableFields(currentUser, Categories, parent); + }, + addOriginalField: true + }, + options: props => { + return getCategoriesAsOptions(props.data.categories.results); + }, + query: ` + categories{ + results{ + _id + name + slug + order + } + } + `, + } +}; + +export default schema; diff --git a/packages/example-forum/lib/modules/categories/views.js b/packages/example-forum/lib/modules/categories/views.js new file mode 100644 index 00000000..68ce2aac --- /dev/null +++ b/packages/example-forum/lib/modules/categories/views.js @@ -0,0 +1,15 @@ +/* + +Default sort + +*/ + +import { Categories } from './collection.js'; + +Categories.addDefaultView(terms => ({ + options: { + sort: { + order: 1 + } + } +})); \ No newline at end of file diff --git a/packages/example-forum/lib/modules/comments/collection.js b/packages/example-forum/lib/modules/comments/collection.js new file mode 100644 index 00000000..368aad76 --- /dev/null +++ b/packages/example-forum/lib/modules/comments/collection.js @@ -0,0 +1,37 @@ +/* + +Comments collection + +*/ + +import schema from './schema.js'; +import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core'; +import Users from 'meteor/vulcan:users'; + +/** + * @summary The global namespace for Comments. + * @namespace Comments + */ + export const Comments = createCollection({ + + collectionName: 'Comments', + + typeName: 'Comment', + + schema, + + resolvers: getDefaultResolvers('Comments'), + + mutations: getDefaultMutations('Comments'), + +}); + +Comments.checkAccess = (currentUser, comment) => { + if (Users.isAdmin(currentUser) || Users.owns(currentUser, comment)) { // admins can always see everything, users can always see their own posts + return true; + } else if (comment.isDeleted) { + return false; + } else { + return true; + } +} \ No newline at end of file diff --git a/packages/vulcan-comments/lib/custom_fields.js b/packages/example-forum/lib/modules/comments/custom_fields.js similarity index 75% rename from packages/vulcan-comments/lib/custom_fields.js rename to packages/example-forum/lib/modules/comments/custom_fields.js index 4954cc4b..957ae317 100644 --- a/packages/vulcan-comments/lib/custom_fields.js +++ b/packages/example-forum/lib/modules/comments/custom_fields.js @@ -1,17 +1,17 @@ -import Posts from "meteor/vulcan:posts"; -import Users from "meteor/vulcan:users"; +import { Posts } from '../posts/index.js'; +import Users from 'meteor/vulcan:users'; Users.addField([ /** Count of the user's comments */ { - fieldName: "commentCount", + fieldName: 'commentCount', fieldSchema: { type: Number, optional: true, defaultValue: 0, - viewableBy: ['guests'], + canRead: ['guests'], } } ]); @@ -21,19 +21,19 @@ Posts.addField([ Count of the post's comments */ { - fieldName: "commentCount", + fieldName: 'commentCount', fieldSchema: { type: Number, optional: true, defaultValue: 0, - viewableBy: ['guests'], + canRead: ['guests'], } }, /** An array containing the `_id`s of commenters */ { - fieldName: "commenters", + fieldName: 'commenters', fieldSchema: { type: Array, optional: true, @@ -46,11 +46,11 @@ Posts.addField([ return Users.restrictViewableFields(currentUser, Users, commenters); }, }, - viewableBy: ['guests'], + canRead: ['guests'], } }, { - fieldName: "commenters.$", + fieldName: 'commenters.$', fieldSchema: { type: String, optional: true diff --git a/packages/example-forum/lib/modules/comments/fragments.js b/packages/example-forum/lib/modules/comments/fragments.js new file mode 100644 index 00000000..db359508 --- /dev/null +++ b/packages/example-forum/lib/modules/comments/fragments.js @@ -0,0 +1,36 @@ +import { registerFragment } from 'meteor/vulcan:core'; + +// ----------------------------- Comments ------------------------------ // + +registerFragment(/* GraphQL */` + fragment CommentsList on Comment { + # vulcan:comments + _id + postId + parentCommentId + topLevelCommentId + body + htmlBody + postedAt + # vulcan:users + userId + user { + ...UsersMinimumInfo + } + # vulcan:posts + post { + _id + commentCount + commenters { + ...UsersMinimumInfo + } + } + # voting + currentUserVotes{ + ...VoteFragment + } + baseScore + score + } +`); + diff --git a/packages/vulcan-comments/lib/helpers.js b/packages/example-forum/lib/modules/comments/helpers.js similarity index 85% rename from packages/vulcan-comments/lib/helpers.js rename to packages/example-forum/lib/modules/comments/helpers.js index 3823fb38..546b9fc6 100644 --- a/packages/vulcan-comments/lib/helpers.js +++ b/packages/example-forum/lib/modules/comments/helpers.js @@ -1,5 +1,11 @@ -import Comments from './collection.js'; -import Posts from 'meteor/vulcan:posts'; +/* + +Comments helpers + +*/ + +import { Comments } from './index.js'; +import { Posts } from '../posts/index.js'; import Users from 'meteor/vulcan:users'; ////////////////// diff --git a/packages/example-forum/lib/modules/comments/index.js b/packages/example-forum/lib/modules/comments/index.js new file mode 100644 index 00000000..7e7b91dd --- /dev/null +++ b/packages/example-forum/lib/modules/comments/index.js @@ -0,0 +1,7 @@ +export * from './collection.js'; + +import './fragments.js'; +import './custom_fields.js'; +import './helpers.js'; +import './permissions.js'; +import './views.js'; \ No newline at end of file diff --git a/packages/example-forum/lib/modules/comments/permissions.js b/packages/example-forum/lib/modules/comments/permissions.js new file mode 100644 index 00000000..5c6a27fe --- /dev/null +++ b/packages/example-forum/lib/modules/comments/permissions.js @@ -0,0 +1,30 @@ +/* + +Comments permissions + +*/ + +import Users from 'meteor/vulcan:users'; + +const guestsActions = [ + 'comments.view' +]; +Users.groups.guests.can(guestsActions); + +const membersActions = [ + 'comments.view', + 'comments.new', + 'comments.edit.own', + 'comments.remove.own', + 'comments.upvote', + 'comments.cancelUpvote', + 'comments.downvote', + 'comments.cancelDownvote' +]; +Users.groups.members.can(membersActions); + +const adminActions = [ + 'comments.edit.all', + 'comments.remove.all' +]; +Users.groups.admins.can(adminActions); \ No newline at end of file diff --git a/packages/vulcan-comments/lib/schema.js b/packages/example-forum/lib/modules/comments/schema.js similarity index 72% rename from packages/vulcan-comments/lib/schema.js rename to packages/example-forum/lib/modules/comments/schema.js index adf40ee4..7de19dae 100644 --- a/packages/vulcan-comments/lib/schema.js +++ b/packages/example-forum/lib/modules/comments/schema.js @@ -1,4 +1,12 @@ +/* + +Comments schema + +*/ + import Users from 'meteor/vulcan:users'; +import marked from 'marked'; +import { Utils } from 'meteor/vulcan:core'; /** * @summary Comments schema @@ -11,7 +19,7 @@ const schema = { _id: { type: String, optional: true, - viewableBy: ['guests'], + canRead: ['guests'], }, /** The `_id` of the parent comment, if there is one @@ -20,8 +28,8 @@ const schema = { type: String, // regEx: SimpleSchema.RegEx.Id, max: 500, - viewableBy: ['guests'], - insertableBy: ['members'], + canRead: ['guests'], + canCreate: ['members'], optional: true, resolveAs: { fieldName: 'parentComment', @@ -42,8 +50,8 @@ const schema = { type: String, // regEx: SimpleSchema.RegEx.Id, max: 500, - viewableBy: ['guests'], - insertableBy: ['members'], + canRead: ['guests'], + canCreate: ['members'], optional: true, resolveAs: { fieldName: 'topLevelComment', @@ -63,8 +71,8 @@ const schema = { createdAt: { type: Date, optional: true, - viewableBy: ['admins'], - onInsert: (document, currentUser) => { + canRead: ['admins'], + onCreate: () => { return new Date(); } }, @@ -74,8 +82,8 @@ const schema = { postedAt: { type: Date, optional: true, - viewableBy: ['guests'], - onInsert: (document, currentUser) => { + canRead: ['guests'], + onCreate: () => { return new Date(); } }, @@ -85,10 +93,10 @@ const schema = { body: { type: String, max: 3000, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - control: "textarea" + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + input: "textarea" }, /** The HTML version of the comment body @@ -96,7 +104,17 @@ const schema = { htmlBody: { type: String, optional: true, - viewableBy: ['guests'], + canRead: ['guests'], + onCreate: ({document: comment}) => { + if (comment.body) { + return Utils.sanitize(marked(comment.body)); + } + }, + onUpdate: ({data}) => { + if (data.body) { + return Utils.sanitize(marked(data.body)); + } + } }, /** The comment author's name @@ -104,30 +122,22 @@ const schema = { author: { type: String, optional: true, - viewableBy: ['guests'], - onEdit: (modifier, document, currentUser) => { + canRead: ['guests'], + onUpdate: ({data}) => { // if userId is changing, change the author name too - if (modifier.$set && modifier.$set.userId) { - return Users.getDisplayNameById(modifier.$set.userId) + if (data.userId) { + return Users.getDisplayNameById(data.userId) } } }, - /** - Whether the comment is inactive. Inactive comments' scores gets recalculated less often - */ - inactive: { - type: Boolean, - optional: true, - viewableBy: ['guests'], - }, /** The post's `_id` */ postId: { type: String, optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], + canRead: ['guests'], + canCreate: ['members'], // regEx: SimpleSchema.RegEx.Id, max: 500, resolveAs: { @@ -148,8 +158,8 @@ const schema = { userId: { type: String, optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], + canRead: ['guests'], + canCreate: ['members'], hidden: true, resolveAs: { fieldName: 'user', @@ -168,23 +178,38 @@ const schema = { isDeleted: { type: Boolean, optional: true, - viewableBy: ['guests'], + canRead: ['guests'], }, userIP: { type: String, optional: true, - viewableBy: ['admins'], + canRead: ['admins'], }, userAgent: { type: String, optional: true, - viewableBy: ['admins'], + canRead: ['admins'], }, referrer: { type: String, optional: true, - viewableBy: ['admins'], - } + canRead: ['admins'], + }, + + // GraphQL only fields + + pageUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + fieldName: 'pageUrl', + type: 'String', + resolver: (comment, args, context) => { + return context.Comments.getPageUrl(comment, true); + }, + } + }, }; export default schema; diff --git a/packages/example-forum/lib/modules/comments/views.js b/packages/example-forum/lib/modules/comments/views.js new file mode 100644 index 00000000..6f596711 --- /dev/null +++ b/packages/example-forum/lib/modules/comments/views.js @@ -0,0 +1,21 @@ +/* + +Comments views + +*/ + +import { Comments } from './index.js'; + +Comments.addView('postComments', function (terms) { + return { + selector: {postId: terms.postId}, + options: {sort: {postedAt: -1}} + }; +}); + +Comments.addView('userComments', function (terms) { + return { + selector: {userId: terms.userId}, + options: {sort: {postedAt: -1}} + }; +}); \ No newline at end of file diff --git a/packages/example-forum/lib/modules/components.js b/packages/example-forum/lib/modules/components.js new file mode 100644 index 00000000..68b35233 --- /dev/null +++ b/packages/example-forum/lib/modules/components.js @@ -0,0 +1,64 @@ + +// common + +import '../components/common/Footer.jsx'; +import '../components/common/Header.jsx'; +import '../components/common/Layout.jsx'; +import '../components/common/Logo.jsx'; +import '../components/common/Newsletter.jsx'; +import '../components/common/NewsletterButton.jsx'; +import '../components/common/SearchForm.jsx'; +import '../components/common/Vote.jsx'; + +// posts + +import '../components/posts/PostsHome.jsx'; +import '../components/posts/PostsSingle.jsx'; +import '../components/posts/PostsNewButton.jsx'; +import '../components/posts/PostsLoadMore.jsx'; +import '../components/posts/PostsNoMore.jsx'; +import '../components/posts/PostsNoResults.jsx'; +import '../components/posts/PostsItem.jsx'; +import '../components/posts/PostsLoading.jsx'; +import '../components/posts/PostsViews.jsx'; +import '../components/posts/PostsList.jsx'; +import '../components/posts/PostsListHeader.jsx'; +import '../components/posts/PostsCategories.jsx'; +import '../components/posts/PostsCommenters.jsx'; +import '../components/posts/PostsPage.jsx'; +import '../components/posts/PostsStats.jsx'; +import '../components/posts/PostsDaily.jsx'; +import '../components/posts/PostsDailyList.jsx'; +import '../components/posts/PostsDay.jsx'; +import '../components/posts/PostsThumbnail.jsx'; +import '../components/posts/PostsEditForm.jsx'; +import '../components/posts/PostsNewForm.jsx'; +import '../components/posts/PostsCommentsThread.jsx'; + +// comments + +import '../components/comments/CommentsItem.jsx'; +import '../components/comments/CommentsList.jsx'; +import '../components/comments/CommentsNode.jsx'; +import '../components/comments/CommentsNewForm.jsx'; +import '../components/comments/CommentsEditForm.jsx'; +import '../components/comments/CommentsLoadMore.jsx'; + +// categories + +import '../components/categories/CategoriesMenu.jsx'; +import '../components/categories/CategoriesEditForm.jsx'; +import '../components/categories/CategoriesNewForm.jsx'; +import '../components/categories/CategoriesDashboard.jsx'; + +// users + +import '../components/users/UsersSingle.jsx'; +import '../components/users/UsersAccount.jsx'; +import '../components/users/UsersEditForm.jsx'; +import '../components/users/UsersProfile.jsx'; +import '../components/users/UsersProfileCheck.jsx'; +import '../components/users/UsersAvatar.jsx'; +import '../components/users/UsersName.jsx'; +import '../components/users/UsersMenu.jsx'; +import '../components/users/UsersAccountMenu.jsx'; \ No newline at end of file diff --git a/packages/example-forum/lib/modules/config.js b/packages/example-forum/lib/modules/config.js new file mode 100644 index 00000000..40ae7ca0 --- /dev/null +++ b/packages/example-forum/lib/modules/config.js @@ -0,0 +1,6 @@ +import Users from 'meteor/vulcan:users'; + +Users.avatar.setOptions({ + 'gravatarDefault': 'mm', + 'defaultImageUrl': 'http://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&f=y' +}); diff --git a/packages/example-forum/lib/modules/fragments.js b/packages/example-forum/lib/modules/fragments.js new file mode 100644 index 00000000..bb1e5acc --- /dev/null +++ b/packages/example-forum/lib/modules/fragments.js @@ -0,0 +1,48 @@ +import { registerFragment } from 'meteor/vulcan:core'; + +// ------------------------------ Vote ------------------------------ // + +// note: fragment used by default on the UsersProfile fragment +registerFragment(/* GraphQL */` + fragment VotedItem on Vote { + # vulcan:voting + documentId + power + votedAt + } +`); + +// ------------------------------ Users ------------------------------ // + +// note: fragment used by default on UsersProfile, PostsList & CommentsList fragments +registerFragment(/* GraphQL */` + fragment UsersMinimumInfo on User { + # vulcan:users + _id + slug + username + displayName + emailHash + avatarUrl + pageUrl + } +`); + +registerFragment(/* GraphQL */` + fragment UsersProfile on User { + # vulcan:users + ...UsersMinimumInfo + createdAt + isAdmin + bio + htmlBio + twitterUsername + website + groups + # vulcan:posts + postCount + # vulcan:comments + commentCount + } +`); + diff --git a/packages/example-forum/lib/modules/headtags.js b/packages/example-forum/lib/modules/headtags.js new file mode 100644 index 00000000..ae78576b --- /dev/null +++ b/packages/example-forum/lib/modules/headtags.js @@ -0,0 +1,9 @@ +import { Head, Utils } from 'meteor/vulcan:core'; + +// add permanent markup +Head.link.push({ + name: 'rss', + rel: 'alternate', + type: 'application/rss+xml', + href: `${Utils.getSiteUrl()}feed.xml` +}); diff --git a/packages/example-forum/lib/modules/i18n.js b/packages/example-forum/lib/modules/i18n.js new file mode 100644 index 00000000..85872032 --- /dev/null +++ b/packages/example-forum/lib/modules/i18n.js @@ -0,0 +1,91 @@ +import { addStrings } from 'meteor/vulcan:core'; + +addStrings('en', { + + 'posts.new_post': 'New Post', + 'posts.edit': 'Edit', + 'posts.edit_success': 'Post “{title}” edited.', + 'posts.delete': 'Delete', + 'posts.delete_confirm': 'Delete post “{title}”?', + 'posts.delete_success': 'Post “{title}” deleted.', + 'posts.title': 'Title', + 'posts.url': 'URL', + 'posts.body': 'Body', + 'posts.categories': 'Categories', + 'posts.thumbnailUrl': 'Thumbnail URL', + 'posts.status': 'Status', + 'posts.sticky': 'Sticky', + 'posts.load_more': 'Load More', + 'posts.load_more_days': 'Load More Days', + 'posts.no_more': 'No more posts.', + 'posts.no_results': 'No posts to display.', + 'posts.search': 'Search', + 'posts.view': 'View', + 'posts.top': 'Top', + 'posts.new': 'New', + 'posts.best': 'Best', + 'posts.pending': 'Pending', + 'posts.rejected': 'Rejected', + 'posts.scheduled': 'Scheduled', + 'posts.daily': 'Daily', + 'posts.clear_thumbnail': 'Clear Thumbnail', + 'posts.clear_thumbnail?': 'Clear thumbnail?', + 'posts.enter_thumbnail_url': 'Enter URL', + 'posts.created_message': 'Post created.', + 'posts.rate_limit_error': 'Please wait {value} seconds before posting again.', + 'posts.sign_up_or_log_in_first': 'Please sign up or log in first.', + 'posts.postedAt': 'Posted at', + 'posts.dateNotDefined': 'Date not defined', + 'posts.subscribe': 'Subscribe', + 'posts.unsubscribe': 'Unsubscribe', + 'posts.subscribed': 'You have subscribed to “{name}” comments.', + 'posts.unsubscribed': 'You have unsubscribed from “{name}” comments.', + 'posts.subscribed_posts' : 'Posts subscribed to', + 'posts.link_already_posted': 'This link has already been posted.', + 'posts.max_per_day': 'Sorry you cannot submit more than {value} posts per day.', + 'posts.like': 'Like', + + 'comments.comments': 'Comments', + 'comments.count': '{count, plural, =0 {No comments} one {# comment} other {# comments}}', + 'comments.count_0': 'No comments', + 'comments.count_1': '1 comment', + 'comments.count_2': '{count} comments', + 'comments.new': 'New Comment', + 'comments.no_comments': 'No comments to display.', + 'comments.reply': 'Reply', + 'comments.edit': 'Edit', + 'comments.delete': 'Delete', + 'comments.delete_confirm': 'Delete this comment?', + 'comments.delete_success': 'Comment deleted.', + 'comments.please_log_in': 'Please log in to comment.', + 'comments.parentCommentId': 'Parent Comment ID', + 'comments.topLevelCommentId': 'Top Level Comment ID', + 'comments.body': 'Body', + 'comments.rate_limit_error': 'Please wait {value} seconds before commenting again.', + + 'categories': 'Categories', + 'categories.all': 'All Categories', + 'categories.edit': 'Edit Category', + 'categories.edit_success': 'Category “{name}” edited.', + 'categories.new': 'New Category', + 'categories.new_success': 'Category “{name}” created.', + 'categories.delete': 'Delete Category', + 'categories.name': 'Name', + 'categories.description': 'Description', + 'categories.order': 'Order', + 'categories.slug': 'Slug', + 'categories.image': 'Image', + 'categories.parentId': 'Parent ID', + 'categories.subscribe': 'Subscribe to this category\'s posts', + 'categories.unsubscribe': 'Unsubscribe to this category\'s posts', + 'categories.subscribed': 'You have subscribed to “{name}” posts.', + 'categories.unsubscribed': 'You have unsubscribed from “{name}” posts.', + 'categories.subscribed_categories' : 'Categories subscribed to', + 'categories.delete_confirm': 'Delete category “{title}”?', + 'categories.delete_success': 'Category “{name}” deleted.', + 'categories.invalid': 'Invalid category', + + 'admin.categories': 'Categories (admin)', + 'admin.users': 'Users (admin)', + +}); diff --git a/packages/example-forum/lib/modules/index.js b/packages/example-forum/lib/modules/index.js new file mode 100644 index 00000000..1a64d1a6 --- /dev/null +++ b/packages/example-forum/lib/modules/index.js @@ -0,0 +1,13 @@ +import './voting.js'; +import './fragments.js'; +import './components.js'; +import './config.js'; +import './routes.js'; +import './headtags.js'; +import './i18n.js'; + +export { Posts } from './posts/index.js'; +export { Categories } from './categories/index.js'; +export { Comments } from './comments/index.js'; + +import './notifications/index.js'; diff --git a/packages/example-forum/lib/modules/notifications/custom_fields.js b/packages/example-forum/lib/modules/notifications/custom_fields.js new file mode 100644 index 00000000..285fe2c6 --- /dev/null +++ b/packages/example-forum/lib/modules/notifications/custom_fields.js @@ -0,0 +1,66 @@ +import Users from 'meteor/vulcan:users'; + +const notificationsGroup = { + name: "notifications", + order: 2 +}; + +// Add notifications options to user profile settings +Users.addField([ + { + fieldName: 'notifications_users', + fieldSchema: { + label: 'New users', + type: Boolean, + optional: true, + defaultValue: false, + input: "checkbox", + canRead: ['guests'], + canCreate: ['admins'], + canUpdate: ['admins'], + group: notificationsGroup, + } + }, + { + fieldName: 'notifications_posts', + fieldSchema: { + label: 'New posts', + type: Boolean, + optional: true, + defaultValue: false, + input: "checkbox", + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + group: notificationsGroup, + } + }, + { + fieldName: 'notifications_comments', + fieldSchema: { + label: 'Comments on my posts', + type: Boolean, + optional: true, + defaultValue: false, + input: "checkbox", + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + group: notificationsGroup, + } + }, + { + fieldName: 'notifications_replies', + fieldSchema: { + label: 'Replies to my comments', + type: Boolean, + optional: true, + defaultValue: false, + input: "checkbox", + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + group: notificationsGroup, + } + } +]); diff --git a/packages/example-forum/lib/modules/notifications/emails.js b/packages/example-forum/lib/modules/notifications/emails.js new file mode 100644 index 00000000..265ff63d --- /dev/null +++ b/packages/example-forum/lib/modules/notifications/emails.js @@ -0,0 +1,192 @@ +/* + +Emails + +*/ + +import VulcanEmail from 'meteor/vulcan:email'; + +/* + +Test + +*/ + +VulcanEmail.addEmails({ + + test: { + template: "test", + path: "/email/test", + data() { + return {date: new Date()}; + }, + subject() { + return "This is a test"; + }, + } + +}); + +/* + +Users + +*/ + +VulcanEmail.addEmails({ + + newUser: { + template: "newUser", + path: "/email/new-user/:_id?", + subject() { + return "A new user has been created"; + }, + query: ` + query UsersSingleQuery($documentId: String){ + UsersSingle(documentId: $documentId){ + displayName + pageUrl + } + } + ` + }, + + accountApproved: { + template: "accountApproved", + path: "/email/account-approved/:_id?", + subject() { + return "Your account has been approved."; + }, + query: ` + query UsersSingleQuery($documentId: String){ + UsersSingle(documentId: $documentId){ + displayName + } + SiteData{ + title + url + } + } + ` + } + +}); + +/* + +Posts + +*/ + +const postsQuery = ` + query PostsSingleQuery($documentId: String){ + PostsSingle(documentId: $documentId){ + title + url + pageUrl + linkUrl + htmlBody + thumbnailUrl + user{ + pageUrl + displayName + } + } + } +` + +const dummyPost = {title: '[title]', user: {displayName: '[user]'}}; + +VulcanEmail.addEmails({ + + newPost: { + template: "newPost", + path: "/email/new-post/:_id?", + subject(data) { + const post = _.isEmpty(data) ? dummyPost : data.PostsSingle; + return post.user.displayName+' has created a new post: '+post.title; + }, + query: postsQuery + }, + + newPendingPost: { + template: "newPendingPost", + path: "/email/new-pending-post/:_id?", + subject(data) { + const post = _.isEmpty(data) ? dummyPost : data.PostsSingle; + return post.user.displayName+' has a new post pending approval: '+post.title; + }, + query: postsQuery + }, + + postApproved: { + template: "postApproved", + path: "/email/post-approved/:_id?", + subject(data) { + const post = _.isEmpty(data) ? dummyPost : data.PostsSingle; + return 'Your post “'+post.title+'” has been approved'; + }, + query: postsQuery + } + +}); + +/* + +Comments + +*/ + +const commentsQuery = ` + query CommentsSingleQuery($documentId: String){ + CommentsSingle(documentId: $documentId){ + pageUrl + htmlBody + post{ + pageUrl + title + } + user{ + pageUrl + displayName + } + } + } +` + +const dummyComment = {post: {title: '[title]'}, user: {displayName: '[user]'}}; + +VulcanEmail.addEmails({ + + newComment: { + template: "newComment", + path: "/email/new-comment/:_id?", + subject(data) { + const comment = _.isEmpty(data) ? dummyComment : data.CommentsSingle; + return comment.user.displayName+' left a new comment on your post "' + comment.post.title + '"'; + }, + query: commentsQuery + }, + + newReply: { + template: "newReply", + path: "/email/new-reply/:_id?", + subject(data) { + const comment = _.isEmpty(data) ? dummyComment : data.CommentsSingle; + return comment.user.displayName+' replied to your comment on "'+comment.post.title+'"'; + }, + query: commentsQuery + }, + + newCommentSubscribed: { + template: "newComment", + path: "/email/new-comment-subscribed/:_id?", + subject(data) { + const comment = _.isEmpty(data) ? dummyComment : data.CommentsSingle; + return comment.user.displayName+' left a new comment on "' + comment.post.title + '"'; + }, + query: commentsQuery + } + +}); + diff --git a/packages/example-forum/lib/modules/notifications/index.js b/packages/example-forum/lib/modules/notifications/index.js new file mode 100644 index 00000000..78a491c9 --- /dev/null +++ b/packages/example-forum/lib/modules/notifications/index.js @@ -0,0 +1,2 @@ +import './custom_fields.js'; +import './emails.js'; \ No newline at end of file diff --git a/packages/example-forum/lib/modules/posts/admin.js b/packages/example-forum/lib/modules/posts/admin.js new file mode 100644 index 00000000..3b606209 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/admin.js @@ -0,0 +1,25 @@ +/* + +Admin dashboard extension + +*/ + +import { extendFragment, addAdminColumn, addStrings } from 'meteor/vulcan:core'; +import AdminUsersPosts from '../../components/admin/AdminUsersPosts'; + +extendFragment('UsersAdmin', ` + posts(limit: 5){ + ...PostsPage + } +`); + +addAdminColumn({ + name: 'posts', + order: 50, + component: AdminUsersPosts +}); + + +addStrings('en', { + 'admin.users.posts': 'Posts', +}); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/collection.js b/packages/example-forum/lib/modules/posts/collection.js similarity index 81% rename from packages/vulcan-posts/lib/collection.js rename to packages/example-forum/lib/modules/posts/collection.js index 32527213..92a6ae16 100644 --- a/packages/vulcan-posts/lib/collection.js +++ b/packages/example-forum/lib/modules/posts/collection.js @@ -1,15 +1,18 @@ +/* + +Posts collection + +*/ + import schema from './schema.js'; -import mutations from './mutations.js'; -import resolvers from './resolvers.js'; -// import views from './views.js'; -import { createCollection } from 'meteor/vulcan:core'; +import { createCollection, getDefaultResolvers, getDefaultMutations } from 'meteor/vulcan:core'; import Users from 'meteor/vulcan:users'; /** * @summary The global namespace for Posts. * @namespace Posts */ -const Posts = createCollection({ +export const Posts = createCollection({ collectionName: 'Posts', @@ -17,9 +20,9 @@ const Posts = createCollection({ schema, - resolvers, + resolvers: getDefaultResolvers('Posts'), - mutations, + mutations: getDefaultMutations('Posts'), }); @@ -69,6 +72,4 @@ Posts.checkAccess = (currentUser, post) => { const status = _.findWhere(Posts.statuses, {value: post.status}); return Users.canDo(currentUser, `posts.view.${status.label}`); } -} - -export default Posts; +} \ No newline at end of file diff --git a/packages/example-forum/lib/modules/posts/custom_fields.js b/packages/example-forum/lib/modules/posts/custom_fields.js new file mode 100644 index 00000000..bb060188 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/custom_fields.js @@ -0,0 +1,89 @@ +/* + +Custom fields on Users collection + +*/ + +import Users from 'meteor/vulcan:users'; +import SimpleSchema from 'simpl-schema'; + +Users.addField([ + /** + Count of the user's posts + */ + { + fieldName: 'postCount', + fieldSchema: { + type: Number, + optional: true, + defaultValue: 0, + canRead: ['guests'], + } + }, + /** + The user's associated posts (GraphQL only) + */ + { + fieldName: 'posts', + fieldSchema: { + type: Object, + optional: true, + canRead: ['guests'], + resolveAs: { + arguments: 'limit: Int = 5', + type: '[Post]', + resolver: (user, { limit }, { currentUser, Users, Posts }) => { + const posts = Posts.find({ userId: user._id }, { limit }).fetch(); + + // restrict documents fields + const viewablePosts = _.filter(posts, post => Posts.checkAccess(currentUser, post)); + const restrictedPosts = Users.restrictViewableFields(currentUser, Posts, viewablePosts); + return restrictedPosts; + } + } + } + }, + /** + User's bio (Markdown version) + */ + { + fieldName: 'bio', + fieldSchema: { + type: String, + optional: true, + input: "textarea", + canCreate: ['members'], + canUpdate: ['members'], + canRead: ['guests'], + order: 30, + searchable: true, + } + }, + /** + User's bio (Markdown version) + */ + { + fieldName: 'htmlBio', + fieldSchema: { + type: String, + optional: true, + canRead: ['guests'], + } + }, + /** + A link to the user's homepage + */ + { + fieldName: 'website', + fieldSchema: { + type: String, + regEx: SimpleSchema.RegEx.Url, + optional: true, + input: "text", + canCreate: ['members'], + canUpdate: ['members'], + canRead: ['guests'], + order: 50, + } + } +]); diff --git a/packages/example-forum/lib/modules/posts/embedly.js b/packages/example-forum/lib/modules/posts/embedly.js new file mode 100644 index 00000000..dd98bbb1 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/embedly.js @@ -0,0 +1,46 @@ +import { Posts } from '../posts/index.js'; + +Posts.addField([ + { + fieldName: 'url', + fieldSchema: { + input: 'EmbedURL', // we are just extending the field url, not replacing it + } + }, + { + fieldName: 'thumbnailUrl', + fieldSchema: { + type: String, + optional: true, + canCreate: ['members'], + canUpdate: ['members'], + canRead: ['guests'], + hidden: true + } + }, + { + fieldName: 'media', + fieldSchema: { + type: Object, + optional: true, + blackbox: true, + canRead: ['guests'], + } + }, + { + fieldName: 'sourceName', + fieldSchema: { + type: String, + optional: true, + canRead: ['guests'], + } + }, + { + fieldName: 'sourceUrl', + fieldSchema: { + type: String, + optional: true, + canRead: ['guests'], + } + } +]); \ No newline at end of file diff --git a/packages/example-forum/lib/modules/posts/fragments.js b/packages/example-forum/lib/modules/posts/fragments.js new file mode 100644 index 00000000..8ffa4aef --- /dev/null +++ b/packages/example-forum/lib/modules/posts/fragments.js @@ -0,0 +1,49 @@ +import { registerFragment } from 'meteor/vulcan:core'; + +registerFragment(/* GraphQL */` + fragment PostsList on Post { + # posts + _id + title + url + slug + postedAt + createdAt + sticky + status + excerpt + viewCount + clickCount + # users + userId + user { + ...UsersMinimumInfo + } + # embedly + thumbnailUrl + # categories + categories { + ...CategoriesMinimumInfo + } + # comments + commentCount + commenters { + ...UsersMinimumInfo + } + # voting + currentUserVotes{ + ...VoteFragment + } + baseScore + score + } +`); + +registerFragment(/* GraphQL */` + fragment PostsPage on Post { + ...PostsList + body + htmlBody + } +`); + diff --git a/packages/vulcan-posts/lib/helpers.js b/packages/example-forum/lib/modules/posts/helpers.js similarity index 77% rename from packages/vulcan-posts/lib/helpers.js rename to packages/example-forum/lib/modules/posts/helpers.js index 74d29a39..bcb61dc1 100644 --- a/packages/vulcan-posts/lib/helpers.js +++ b/packages/example-forum/lib/modules/posts/helpers.js @@ -1,7 +1,18 @@ +/* + +Posts helpers + +*/ + import moment from 'moment'; -import Posts from './collection.js'; +import { Posts } from './collection.js'; import Users from 'meteor/vulcan:users'; -import { Utils, getSetting } from 'meteor/vulcan:core'; +import { Utils, getSetting, registerSetting } from 'meteor/vulcan:core'; + +registerSetting('forum.outsideLinksPointTo', 'link', 'Whether to point RSS links to the linked URL (“link”) or back to the post page (“page”)'); +registerSetting('forum.requirePostsApproval', false, 'Require posts to be approved manually'); +registerSetting('twitterAccount', null, 'Twitter account associated with the app'); +registerSetting('siteUrl', null, 'Main site URL'); ////////////////// // Link Helpers // @@ -21,7 +32,7 @@ Posts.getLink = function (post, isAbsolute = false, isRedirected = true) { * @param {Object} post */ Posts.getShareableLink = function (post) { - return getSetting("outsideLinksPointTo", "link") === "link" ? Posts.getLink(post) : Posts.getPageUrl(post, true); + return getSetting('forum.outsideLinksPointTo', 'link') === 'link' ? Posts.getLink(post) : Posts.getPageUrl(post, true); }; /** @@ -29,7 +40,7 @@ Posts.getShareableLink = function (post) { * @param {Object} post */ Posts.getLinkTarget = function (post) { - return !!post.url ? "_blank" : ""; + return !!post.url ? '_blank' : ''; }; /** @@ -37,7 +48,7 @@ Posts.getLinkTarget = function (post) { * @param {Object} post */ Posts.getPageUrl = function(post, isAbsolute = false){ - const prefix = isAbsolute ? Utils.getSiteUrl().slice(0,-1) : ""; + const prefix = isAbsolute ? Utils.getSiteUrl().slice(0,-1) : ''; return `${prefix}/posts/${post._id}/${post.slug}`; }; @@ -63,9 +74,9 @@ Posts.getAuthorName = function (post) { * @param {Object} user */ Posts.getDefaultStatus = function (user) { - const canPostApproved = typeof user === 'undefined' ? false : Users.canDo(user, "posts.new.approved"); - if (!getSetting('requirePostsApproval', false) || canPostApproved) { - // if user can post straight to "approved", or else post approval is not required + const canPostApproved = typeof user === 'undefined' ? false : Users.canDo(user, 'posts.new.approved'); + if (!getSetting('forum.requirePostsApproval', false) || canPostApproved) { + // if user can post straight to 'approved', or else post approval is not required return Posts.config.STATUS_APPROVED; } else { return Posts.config.STATUS_PENDING; @@ -115,7 +126,7 @@ Posts.checkForSameUrl = function (url) { * @summary When on a post page, return the current post */ Posts.current = function () { - return Posts.findOne("foo"); + return Posts.findOne('foo'); }; /** @@ -123,7 +134,7 @@ Posts.current = function () { * @param {Object} post */ Posts.isVideo = function (post) { - return post.media && post.media.type === "video"; + return post.media && post.media.type === 'video'; }; /** @@ -142,7 +153,7 @@ Posts.getThumbnailUrl = (post) => { * @param {Object} post */ Posts.getTwitterShareUrl = post => { - const via = getSetting("twitterAccount", null) ? `&via=${getSetting("twitterAccount")}` : ""; + const via = getSetting('twitterAccount', null) ? `&via=${getSetting('twitterAccount')}` : ''; return `https://twitter.com/intent/tweet?text=${ encodeURIComponent(post.title) }%20${ encodeURIComponent(Posts.getLink(post, true)) }${via}`; }; @@ -165,7 +176,7 @@ Posts.getEmailShareUrl = post => { ${post.title} ${Posts.getLink(post, true, false)} -(found via ${getSetting("siteUrl")}) +(found via ${getSetting('siteUrl')}) `; return `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; }; diff --git a/packages/example-forum/lib/modules/posts/index.js b/packages/example-forum/lib/modules/posts/index.js new file mode 100644 index 00000000..0f02a968 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/index.js @@ -0,0 +1,12 @@ +export * from './collection.js'; + +import './fragments.js'; +import './custom_fields.js'; +import './parameters.js'; +import './views.js'; +import './helpers.js'; +import './permissions.js'; +import './redux.js'; +import './admin.js'; +import './newsletter.js'; +import './embedly.js'; diff --git a/packages/example-forum/lib/modules/posts/newsletter.js b/packages/example-forum/lib/modules/posts/newsletter.js new file mode 100644 index 00000000..cb66bfd9 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/newsletter.js @@ -0,0 +1,93 @@ +/* + +Newsletter setup + +*/ + +import VulcanEmail from 'meteor/vulcan:email'; +import { addCallback } from 'meteor/vulcan:core'; +// email test routes (make available to client & server) +import Newsletters from 'meteor/vulcan:newsletter'; +import { Posts } from './collection.js'; +import moment from 'moment'; + +VulcanEmail.addEmails({ + + newsletter: { + template: 'newsletter', + path: '/email/newsletter', + subject(data) { + return _.isEmpty(data) ? '[Generated on server]' : Newsletters.getSubject(data.PostsList); + }, + data() { + return { + date: moment().format('MMMM D YYYY') + } + }, + query: ` + query NewsletterQuery($terms: JSON){ + SiteData{ + title + } + PostsList(terms: $terms){ + + _id + title + url + pageUrl + linkUrl + domain + htmlBody + thumbnailUrl + commentsCount + postedAtFormatted + + user{ + pageUrl + displayName + } + + comments(limit: 3){ + user{ + displayName + avatarUrl + pageUrl + } + htmlBody + postedAt + } + + } + } + `, + isValid(data) { + return data.PostsList && data.PostsList.length; + }, + testVariables() { + return { + terms : { + view: 'newsletter' + } + } + } + }, + + newsletterConfirmation: { + template: 'newsletterConfirmation', + path: '/email/newsletter-confirmation', + subject() { + return 'Newsletter confirmation'; + } + } + +}); + +function MarkPostsAsScheduled (email) { + const postsIds = _.pluck(email.data.PostsList, '_id'); + // eslint-disable-next-line no-console + console.log(postsIds) + const updated = Posts.update({_id: {$in: postsIds}}, {$set: {scheduledAt: new Date()}}, {multi: true}) // eslint-disable-line + // eslint-disable-next-line no-console + console.log(`updated ${updated} posts`) +} +addCallback('newsletter.send.async', MarkPostsAsScheduled); diff --git a/packages/example-forum/lib/modules/posts/parameters.js b/packages/example-forum/lib/modules/posts/parameters.js new file mode 100644 index 00000000..d0c3a7cc --- /dev/null +++ b/packages/example-forum/lib/modules/posts/parameters.js @@ -0,0 +1,89 @@ +/* + +Posts parameters + +*/ + +import { Injected } from 'meteor/meteorhacks:inject-initial'; +import moment from 'moment'; +import { addCallback } from 'meteor/vulcan:core'; + +// Add 'after' and 'before' properties to terms which can be used to limit posts in time. +function PostsAddBeforeAfterParameters (parameters, terms, apolloClient) { + + // console.log('// addBeforeAfterParameters') + + if (typeof parameters.selector.postedAt === 'undefined') { + + let postedAt = {}, mAfter, mBefore, startOfDay, endOfDay, clientTimezoneOffset, serverTimezoneOffset, timeDifference; + + /* + + If we're on the client, add the time difference between client and server + + Example: client is on Japanese time (+9 hours), + server on UCT (Greenwich) time (+0 hours), for a total difference of +9 hours. + + So the time '00:00, UCT' is equivalent to '09:00, JST'. + + So if we want to express the timestamp '00:00, UCT' on the client, + we *add* 9 hours to '00:00, JST' on the client to get '09:00, JST' and + sync up both times. + + */ + + if (Meteor.isClient) { + clientTimezoneOffset = -1 * new Date().getTimezoneOffset(); + serverTimezoneOffset = -1 * Injected.obj('serverTimezoneOffset').offset; + timeDifference = clientTimezoneOffset - serverTimezoneOffset; + + // console.log('client time:'+clientTimezoneOffset); + // console.log('server time:'+serverTimezoneOffset); + // console.log('difference: '+timeDifference); + } + + if (terms.after) { + + // console.log('// after: '+terms.after); + + mAfter = moment(terms.after, 'YYYY-MM-DD'); + startOfDay = mAfter.startOf('day'); + + // console.log('// normal ', mAfter.toDate(), mAfter.valueOf()); + // console.log('// startOfDay ', startOfDay.toDate(), startOfDay.valueOf()); + + if (Meteor.isClient) { + startOfDay.add(timeDifference, 'minutes'); + // console.log('// after add ', startOfDay.toDate(), startOfDay.valueOf()); + // note: on the client, dates are stored as strings, + // so use strings for MongoDB filtering options too + postedAt.$gte = startOfDay.toISOString(); + } else { + postedAt.$gte = startOfDay.toDate(); + } + + } + + if (terms.before) { + + mBefore = moment(terms.before, 'YYYY-MM-DD'); + endOfDay = mBefore.endOf('day'); + + if (Meteor.isClient) { + endOfDay.add(timeDifference, 'minutes'); + postedAt.$lt = endOfDay.toISOString(); + } else { + postedAt.$lt = endOfDay.toDate(); + } + + } + + if (!_.isEmpty(postedAt)) { + parameters.selector.postedAt = postedAt; + } + + } + + return parameters; +} +addCallback('posts.parameters', PostsAddBeforeAfterParameters); diff --git a/packages/example-forum/lib/modules/posts/permissions.js b/packages/example-forum/lib/modules/posts/permissions.js new file mode 100644 index 00000000..fa6b9b17 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/permissions.js @@ -0,0 +1,32 @@ +/* + +Posts permissions + +*/ + +import Users from 'meteor/vulcan:users'; + +const guestsActions = [ + 'posts.view.approved' +]; +Users.groups.guests.can(guestsActions); + +const membersActions = [ + 'posts.new', + 'posts.edit.own', + 'posts.remove.own', + 'posts.upvote', + 'posts.downvote', +]; +Users.groups.members.can(membersActions); + +const adminActions = [ + 'posts.view.pending', + 'posts.view.rejected', + 'posts.view.spam', + 'posts.view.deleted', + 'posts.new.approved', + 'posts.edit.all', + 'posts.remove.all' +]; +Users.groups.admins.can(adminActions); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/redux.js b/packages/example-forum/lib/modules/posts/redux.js similarity index 96% rename from packages/vulcan-posts/lib/redux.js rename to packages/example-forum/lib/modules/posts/redux.js index 7c31fcca..09e41995 100644 --- a/packages/vulcan-posts/lib/redux.js +++ b/packages/example-forum/lib/modules/posts/redux.js @@ -1,3 +1,9 @@ +/* + +Redux + +*/ + import { addAction, addReducer } from 'meteor/vulcan:core'; addAction({ diff --git a/packages/example-forum/lib/modules/posts/schema.js b/packages/example-forum/lib/modules/posts/schema.js new file mode 100644 index 00000000..80d13237 --- /dev/null +++ b/packages/example-forum/lib/modules/posts/schema.js @@ -0,0 +1,462 @@ +/* + +Posts schema + +*/ + +import Users from 'meteor/vulcan:users'; +import { Utils, getSetting, registerSetting, getCollection } from 'meteor/vulcan:core'; +import moment from 'moment'; +import marked from 'marked'; + +registerSetting('forum.postExcerptLength', 30, 'Length of posts excerpts in words'); + +/** + * @summary Posts config namespace + * @type {Object} + */ +const formGroups = { + admin: { + name: 'admin', + order: 2 + } +}; + +/** + * @summary Posts schema + * @type {Object} + */ +const schema = { + /** + ID + */ + _id: { + type: String, + optional: true, + canRead: ['guests'], + }, + /** + Timetstamp of post creation + */ + createdAt: { + type: Date, + optional: true, + canRead: ['admins'], + onCreate: () => { + return new Date(); + } + }, + /** + Timestamp of post first appearing on the site (i.e. being approved) + */ + postedAt: { + type: Date, + optional: true, + canRead: ['guests'], + canCreate: ['admins'], + canUpdate: ['admins'], + input: 'datetime', + group: formGroups.admin, + onCreate: ({document: post, currentUser}) => { + // Set the post's postedAt if it's going to be approved + if (!post.postedAt && getCollection('Posts').getDefaultStatus(currentUser) === getCollection('Posts').config.STATUS_APPROVED) { + return new Date(); + } + }, + onUpdate: ({data, document: post}) => { + // Set the post's postedAt if it's going to be approved + if (!post.postedAt && data.status === getCollection('Posts').config.STATUS_APPROVED) { + return new Date(); + } + } + }, + /** + URL + */ + url: { + type: String, + optional: true, + max: 500, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + input: 'url', + order: 10, + searchable: true, + query: ` + SiteData{ + logoUrl + title + } + `, + }, + /** + Title + */ + title: { + type: String, + optional: false, + max: 500, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + input: 'text', + order: 20, + searchable: true + }, + /** + Slug + */ + slug: { + type: String, + optional: true, + canRead: ['guests'], + onCreate: ({document: post}) => { + return Utils.slugify(post.title); + }, + onUpdate: ({data}) => { + if (data.title) { + return Utils.slugify(data.title); + } + } + }, + /** + Post body (markdown) + */ + body: { + type: String, + optional: true, + max: 3000, + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + input: 'textarea', + order: 30 + }, + /** + HTML version of the post body + */ + htmlBody: { + type: String, + optional: true, + canRead: ['guests'], + onCreate: ({document: post}) => { + if (post.body) { + return Utils.sanitize(marked(post.body)); + } + }, + onUpdate: ({data}) => { + if (data.body) { + return Utils.sanitize(marked(data.body)); + } + } + }, + /** + Post Excerpt + */ + excerpt: { + type: String, + optional: true, + canRead: ['guests'], + searchable: true, + onCreate: ({document: post}) => { + if (post.body) { + // excerpt length is configurable via the settings (30 words by default, ~255 characters) + const excerptLength = getSetting('forum.postExcerptLength', 30); + return Utils.trimHTML(Utils.sanitize(marked(post.body)), excerptLength); + } + }, + onUpdate: ({data}) => { + if (data.body) { + const excerptLength = getSetting('forum.postExcerptLength', 30); + return Utils.trimHTML(Utils.sanitize(marked(data.body)), excerptLength); + } + } + }, + /** + Count of how many times the post's page was viewed + */ + viewCount: { + type: Number, + optional: true, + canRead: ['admins'], + defaultValue: 0 + }, + /** + Timestamp of the last comment + */ + lastCommentedAt: { + type: Date, + optional: true, + canRead: ['guests'], + }, + /** + Count of how many times the post's link was clicked + */ + clickCount: { + type: Number, + optional: true, + canRead: ['admins'], + defaultValue: 0 + }, + /** + The post's status. One of pending (`1`), approved (`2`), or deleted (`3`) + */ + status: { + type: Number, + optional: true, + canRead: ['guests'], + canCreate: ['admins'], + canUpdate: ['admins'], + input: 'select', + onCreate: ({document: document, currentUser}) => { + if (!document.status) { + return getCollection('Posts').getDefaultStatus(currentUser); + } + }, + onUpdate: ({data, currentUser}) => { + // if for some reason post status has been removed, give it default status + if (data.status === null) { + return getCollection('Posts').getDefaultStatus(currentUser); + } + }, + options: () => getCollection('Posts').statuses, + group: formGroups.admin + }, + /** + Whether a post is scheduled in the future or not + */ + isFuture: { + type: Boolean, + optional: true, + canRead: ['guests'], + onCreate: ({document: post}) => { + // Set the post's isFuture to true if necessary + if (post.postedAt) { + const postTime = new Date(post.postedAt).getTime(); + const currentTime = new Date().getTime() + 1000; + return postTime > currentTime; // round up to the second + } + }, + onUpdate: ({data, document: post}) => { + // Set the post's isFuture to true if necessary + if (data.postedAt) { + const postTime = new Date(data.postedAt).getTime(); + const currentTime = new Date().getTime() + 1000; + if (postTime > currentTime) { + // if a post's postedAt date is in the future, set isFuture to true + return true; + } else if (post.isFuture) { + // else if a post has isFuture to true but its date is in the past, set isFuture to false + return false; + } + } + } + }, + /** + Whether the post is sticky (pinned to the top of posts lists) + */ + sticky: { + type: Boolean, + optional: true, + defaultValue: false, + canRead: ['guests'], + canCreate: ['admins'], + canUpdate: ['admins'], + input: 'checkbox', + group: formGroups.admin, + onCreate: ({document: post}) => { + if(!post.sticky) { + return false; + } + }, + onUpdate: ({data}) => { + if (!data.sticky) { + return false; + } + } + }, + /** + Save info for later spam checking on a post. We will use this for the akismet package + */ + userIP: { + type: String, + optional: true, + canRead: ['admins'], + }, + userAgent: { + type: String, + optional: true, + canRead: ['admins'], + }, + referrer: { + type: String, + optional: true, + canRead: ['admins'], + }, + /** + The post author's name + */ + author: { + type: String, + optional: true, + canRead: ['guests'], + onUpdate: ({data}) => { + // if userId is changing, change the author name too + if (data.userId) { + return Users.getDisplayNameById(data.userId) + } + } + }, + /** + The post author's `_id`. + */ + userId: { + type: String, + optional: true, + input: 'select', + canRead: ['guests'], + canCreate: ['members'], + hidden: true, + resolveAs: { + fieldName: 'user', + type: 'User', + resolver: async (post, args, context) => { + if (!post.userId) return null; + const user = await context.Users.loader.load(post.userId); + return context.Users.restrictViewableFields(context.currentUser, context.Users, user); + }, + addOriginalField: true + }, + }, + + /** + Used to keep track of when a post has been included in a newsletter + */ + scheduledAt: { + type: Date, + optional: true, + canRead: ['admins'], + }, + + // GraphQL-only fields + + domain: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, context) => { + return Utils.getDomain(post.url); + }, + } + }, + + pageUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, { Posts }) => { + return Posts.getPageUrl(post, true); + }, + } + }, + + linkUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, { Posts }) => { + return post.url ? Utils.getOutgoingUrl(post.url) : Posts.getPageUrl(post, true); + }, + } + }, + + postedAtFormatted: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, context) => { + return moment(post.postedAt).format('dddd, MMMM Do YYYY'); + } + } + }, + + commentsCount: { + type: Number, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'Int', + resolver: (post, args, { Comments }) => { + const commentsCount = Comments.find({ postId: post._id }).count(); + return commentsCount; + }, + } + }, + + comments: { + type: Object, + optional: true, + canRead: ['guests'], + resolveAs: { + arguments: 'limit: Int = 5', + type: '[Comment]', + resolver: (post, { limit }, { currentUser, Users, Comments }) => { + const comments = Comments.find({ postId: post._id }, { limit }).fetch(); + + // restrict documents fields + const viewableComments = _.filter(comments, comments => Comments.checkAccess(currentUser, comments)); + const restrictedComments = Users.restrictViewableFields(currentUser, Comments, viewableComments); + + return restrictedComments; + } + } + }, + + emailShareUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, { Posts }) => { + return Posts.getEmailShareUrl(post); + } + } + }, + + twitterShareUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, { Posts }) => { + return Posts.getTwitterShareUrl(post); + } + } + }, + + facebookShareUrl: { + type: String, + optional: true, + canRead: ['guests'], + resolveAs: { + type: 'String', + resolver: (post, args, { Posts }) => { + return Posts.getFacebookShareUrl(post); + } + } + }, + +}; + +export default schema; diff --git a/packages/vulcan-posts/lib/views.js b/packages/example-forum/lib/modules/posts/views.js similarity index 56% rename from packages/vulcan-posts/lib/views.js rename to packages/example-forum/lib/modules/posts/views.js index a1b0ce0b..0d46d92a 100644 --- a/packages/vulcan-posts/lib/views.js +++ b/packages/example-forum/lib/modules/posts/views.js @@ -1,5 +1,7 @@ import Users from 'meteor/vulcan:users'; -import Posts from './collection.js' +import { Posts } from './collection.js' +import moment from 'moment'; +import Newsletters from 'meteor/vulcan:newsletter'; /** * @summary Base parameters that will be common to all other view unless specific properties are overwritten @@ -14,7 +16,7 @@ Posts.addDefaultView(terms => ({ /** * @summary Top view */ -Posts.addView("top", terms => ({ +Posts.addView('top', terms => ({ options: { sort: {sticky: -1, score: -1} } @@ -23,7 +25,7 @@ Posts.addView("top", terms => ({ /** * @summary New view */ -Posts.addView("new", terms => ({ +Posts.addView('new', terms => ({ options: { sort: {sticky: -1, postedAt: -1} } @@ -32,7 +34,7 @@ Posts.addView("new", terms => ({ /** * @summary Best view */ -Posts.addView("best", terms => ({ +Posts.addView('best', terms => ({ options: { sort: {sticky: -1, baseScore: -1} } @@ -41,7 +43,7 @@ Posts.addView("best", terms => ({ /** * @summary Pending view */ -Posts.addView("pending", terms => ({ +Posts.addView('pending', terms => ({ selector: { status: Posts.config.STATUS_PENDING }, @@ -53,7 +55,7 @@ Posts.addView("pending", terms => ({ /** * @summary Rejected view */ -Posts.addView("rejected", terms => ({ +Posts.addView('rejected', terms => ({ selector: { status: Posts.config.STATUS_REJECTED }, @@ -65,7 +67,7 @@ Posts.addView("rejected", terms => ({ /** * @summary Scheduled view */ -Posts.addView("scheduled", terms => ({ +Posts.addView('scheduled', terms => ({ selector: { status: Posts.config.STATUS_APPROVED, isFuture: true @@ -78,7 +80,7 @@ Posts.addView("scheduled", terms => ({ /** * @summary User posts view */ -Posts.addView("userPosts", terms => ({ +Posts.addView('userPosts', terms => ({ selector: { userId: terms.userId, status: Posts.config.STATUS_APPROVED, @@ -95,10 +97,10 @@ Posts.addView("userPosts", terms => ({ /** * @summary User upvoted posts view */ -Posts.addView("userUpvotedPosts", (terms, apolloClient) => { +Posts.addView('userUpvotedPosts', (terms, apolloClient) => { var user = apolloClient ? Users.findOneInStore(apolloClient.store, terms.userId) : Users.findOne(terms.userId); - var postsIds = _.pluck(user.upvotedPosts, "itemId"); + var postsIds = _.pluck(user.upvotedPosts, 'documentId'); return { selector: {_id: {$in: postsIds}, userId: {$ne: terms.userId}}, // exclude own posts options: {limit: 5, sort: {postedAt: -1}} @@ -108,13 +110,37 @@ Posts.addView("userUpvotedPosts", (terms, apolloClient) => { /** * @summary User downvoted posts view */ -Posts.addView("userDownvotedPosts", (terms, apolloClient) => { +Posts.addView('userDownvotedPosts', (terms, apolloClient) => { var user = apolloClient ? Users.findOneInStore(apolloClient.store, terms.userId) : Users.findOne(terms.userId); - var postsIds = _.pluck(user.downvotedPosts, "itemId"); + var postsIds = _.pluck(user.downvotedPosts, 'documentId'); // TODO: sort based on votedAt timestamp and not postedAt, if possible return { selector: {_id: {$in: postsIds}}, options: {limit: 5, sort: {postedAt: -1}} }; }); + +/** + * @summary Newsletter posts view + */ +// create new 'newsletter' view for all posts from the past X days that haven't been scheduled yet +Posts.addView('newsletter', terms => { + const lastNewsletter = Newsletters.findOne({}, {sort: {createdAt: -1}}); + + // if there is a last newsletter and it was sent less than 7 days ago use its date, else default to posts from the last 7 days + const lastWeek = moment().subtract(7, 'days'); + const lastNewsletterIsAfterLastWeek = lastNewsletter && moment(lastNewsletter.createdAt).isAfter(lastWeek); + const after = lastNewsletterIsAfterLastWeek ? lastNewsletter.createdAt : lastWeek.toDate(); + + return { + selector: { + scheduledAt: {$exists: false}, + postedAt: {$gte: after} + }, + options: { + sort: {baseScore: -1}, + limit: terms.limit + } + } +}); diff --git a/packages/example-forum/lib/modules/routes.js b/packages/example-forum/lib/modules/routes.js new file mode 100644 index 00000000..1702ead9 --- /dev/null +++ b/packages/example-forum/lib/modules/routes.js @@ -0,0 +1,11 @@ +import { addRoute } from 'meteor/vulcan:core'; + +addRoute([ + {name:'posts.list', path: '/', componentName: 'PostsHome'}, // index route + {name:'posts.daily', path:'daily', componentName: 'PostsDaily'}, + {name:'posts.single', path:'posts/:_id(/:slug)', componentName: 'PostsSingle'}, + {name:'users.single', path:'users/:slug', componentName: 'UsersSingle'}, + {name:'users.account', path:'account', componentName: 'UsersAccount'}, + {name:'users.edit', path:'users/:slug/edit', componentName: 'UsersAccount'}, + {name:'admin.categories', path:'admin/categories', componentName: 'CategoriesDashboard'}, +]); diff --git a/packages/example-forum/lib/modules/voting.js b/packages/example-forum/lib/modules/voting.js new file mode 100644 index 00000000..52ecafc3 --- /dev/null +++ b/packages/example-forum/lib/modules/voting.js @@ -0,0 +1,6 @@ +import { makeVoteable } from 'meteor/vulcan:voting'; +import { Posts } from './posts/index.js'; +import { Comments } from './comments/index.js'; + +makeVoteable(Posts); +makeVoteable(Comments); \ No newline at end of file diff --git a/packages/example-forum/lib/server/api.js b/packages/example-forum/lib/server/api.js new file mode 100644 index 00000000..393f0e2a --- /dev/null +++ b/packages/example-forum/lib/server/api.js @@ -0,0 +1,90 @@ +import { Posts } from '../modules/posts/index.js'; +import { Comments } from '../modules/comments/index.js'; +import Users from 'meteor/vulcan:users'; +import { Utils } from 'meteor/vulcan:core'; +import { Picker } from 'meteor/meteorhacks:picker'; + +export const servePostsApi = (terms) => { + var posts = []; + + if (!terms.limit) { + terms.limit = 50; + } + + var parameters = Posts.getParameters(terms); + + const postsCursor = Posts.find(parameters.selector, parameters.options); + + postsCursor.forEach(function(post) { + var url = Posts.getLink(post); + var postOutput = { + title: post.title, + headline: post.title, // for backwards compatibility + author: post.author, + date: post.postedAt, + url: url, + pageUrl: Posts.getPageUrl(post, true), + guid: post._id + }; + + if(post.body) + postOutput.body = post.body; + + if(post.url) + postOutput.domain = Utils.getDomain(url); + + if (post.thumbnailUrl) { + postOutput.thumbnailUrl = Utils.addHttp(post.thumbnailUrl); + } + + var twitterName = Users.getTwitterNameById(post.userId); + if(twitterName) + postOutput.twitterName = twitterName; + + var comments = []; + + Comments.find({postId: post._id}, {sort: {postedAt: -1}, limit: 50}).forEach(function(comment) { + var commentProperties = { + body: comment.body, + author: comment.author, + date: comment.postedAt, + guid: comment._id, + parentCommentId: comment.parentCommentId + }; + comments.push(commentProperties); + }); + + var commentsToDelete = []; + + comments.forEach(function(comment, index) { + if (comment.parentCommentId) { + var parent = comments.filter(function(obj) { + return obj.guid === comment.parentCommentId; + })[0]; + if (parent) { + parent.replies = parent.replies || []; + parent.replies.push(JSON.parse(JSON.stringify(comment))); + commentsToDelete.push(index); + } + } + }); + + commentsToDelete.reverse().forEach(function(index) { + comments.splice(index,1); + }); + + postOutput.comments = comments; + + posts.push(postOutput); + }); + + return JSON.stringify(posts); +}; + +// for backwards compatibility's sake, accept a "limit" segment +Picker.route('/api/:limit?', function(params, req, res, next) { + if (typeof params.limit !== "undefined") { + params.query.limit = params.limit; + } + res.end(servePostsApi(params.query)); +}); diff --git a/packages/vulcan-categories/lib/callbacks.js b/packages/example-forum/lib/server/categories/callbacks.js similarity index 58% rename from packages/vulcan-categories/lib/callbacks.js rename to packages/example-forum/lib/server/categories/callbacks.js index 29190d3b..9780db61 100644 --- a/packages/vulcan-categories/lib/callbacks.js +++ b/packages/example-forum/lib/server/categories/callbacks.js @@ -1,13 +1,12 @@ -import Posts from "meteor/vulcan:posts"; -import Categories from "./collection.js"; -import { addCallback, Utils } from 'meteor/vulcan:core'; +/* -// add callback that adds categories CSS classes -function addCategoryClass (postClass, post) { - var classArray = _.map(Posts.getCategories(post), function (category){return "category-"+category.slug;}); - return postClass + " " + classArray.join(' '); -} -addCallback("postClass", addCategoryClass); + +Callbacks to validate categories and generate category slugs + +*/ + +import { addCallback } from 'meteor/vulcan:core'; +import { Categories } from '../../modules/categories/collection.js'; // ------- Categories Check -------- // @@ -27,35 +26,17 @@ var checkCategories = function (post) { } }; -function postsNewCheckCategories (post) { +function PostsNewCheckCategories (post) { checkCategories(post); return post; } -addCallback("posts.new.sync", postsNewCheckCategories); +addCallback("posts.new.sync", PostsNewCheckCategories); -function postEditCheckCategories (modifier) { +function PostEditCheckCategories (modifier) { checkCategories(modifier.$set); return modifier; } -addCallback("posts.edit.sync", postEditCheckCategories); - -function categoriesNewGenerateSlug (category) { - // if no slug has been provided, generate one - const slug = category.slug || Utils.slugify(category.name); - category.slug = Utils.getUnusedSlug(Categories, slug); - return category; -} -addCallback("categories.new.sync", categoriesNewGenerateSlug); - -function categoriesEditGenerateSlug (modifier, document) { - // if slug is changing - if (modifier.$set && modifier.$set.slug && modifier.$set.slug !== document.slug) { - const slug = modifier.$set.slug; - modifier.$set.slug = Utils.getUnusedSlug(Categories, slug); - } - return modifier; -} -addCallback("categories.edit.sync", categoriesEditGenerateSlug); +addCallback("posts.edit.sync", PostEditCheckCategories); // TODO: debug this diff --git a/packages/example-forum/lib/server/categories/index.js b/packages/example-forum/lib/server/categories/index.js new file mode 100644 index 00000000..a9a682c0 --- /dev/null +++ b/packages/example-forum/lib/server/categories/index.js @@ -0,0 +1,3 @@ + +import './callbacks.js'; +import './indexes.js'; diff --git a/packages/example-forum/lib/server/categories/indexes.js b/packages/example-forum/lib/server/categories/indexes.js new file mode 100644 index 00000000..c340d3e2 --- /dev/null +++ b/packages/example-forum/lib/server/categories/indexes.js @@ -0,0 +1,3 @@ +import { Posts } from '../../modules/posts/index.js'; + +Posts._ensureIndex({'categories': 1}); diff --git a/packages/example-forum/lib/server/comments/callbacks/notifications.js b/packages/example-forum/lib/server/comments/callbacks/notifications.js new file mode 100644 index 00000000..3db16faa --- /dev/null +++ b/packages/example-forum/lib/server/comments/callbacks/notifications.js @@ -0,0 +1,54 @@ +/* + +Comment notification callbacks + +*/ + +import Users from 'meteor/vulcan:users'; +import { addCallback } from 'meteor/vulcan:core'; +import { createNotification } from '../../email/notifications.js'; +import { Posts } from '../../../modules/posts/index.js'; +import { Comments } from '../../../modules/comments/index.js'; + +// add new comment notification callback on comment submit +function CommentsNewNotifications (comment) { + + // note: dummy content has disableNotifications set to true + if(Meteor.isServer && !comment.disableNotifications) { + + const post = Posts.findOne(comment.postId); + const postAuthor = Users.findOne(post.userId); + + + let userIdsNotified = []; + + // 1. Notify author of post (if they have new comment notifications turned on) + // but do not notify author of post if they're the ones posting the comment + if (Users.getSetting(postAuthor, 'notifications_comments', false) && comment.userId !== postAuthor._id) { + createNotification(post.userId, 'newComment', {documentId: comment._id}); + userIdsNotified.push(post.userId); + } + + // 2. Notify author of comment being replied to + if (!!comment.parentCommentId) { + + const parentComment = Comments.findOne(comment.parentCommentId); + + // do not notify author of parent comment if they're also post author or comment author + // (someone could be replying to their own comment) + if (parentComment.userId !== post.userId && parentComment.userId !== comment.userId) { + + const parentCommentAuthor = Users.findOne(parentComment.userId); + + // do not notify parent comment author if they have reply notifications turned off + if (Users.getSetting(parentCommentAuthor, 'notifications_replies', false)) { + createNotification(parentComment.userId, 'newReply', {documentId: parentComment._id}); + userIdsNotified.push(parentComment.userId); + } + } + + } + + } +} +addCallback('comments.new.async', CommentsNewNotifications); diff --git a/packages/example-forum/lib/server/comments/callbacks/other.js b/packages/example-forum/lib/server/comments/callbacks/other.js new file mode 100644 index 00000000..893b93f7 --- /dev/null +++ b/packages/example-forum/lib/server/comments/callbacks/other.js @@ -0,0 +1,107 @@ +import Users from 'meteor/vulcan:users'; +import { addCallback, runCallbacksAsync, removeMutation } from 'meteor/vulcan:core'; + +import { Posts } from '../../../modules/posts/index.js'; +import { Comments } from '../../../modules/comments/index.js'; + +////////////////////////////////////////////////////// +// comments.new.sync // +////////////////////////////////////////////////////// + +function CommentsNewOperations (comment) { + + var userId = comment.userId; + + // increment comment count + Users.update({_id: userId}, { + $inc: {'commentCount': 1} + }); + + // update post + Posts.update(comment.postId, { + $inc: {commentCount: 1}, + $set: {lastCommentedAt: new Date()}, + $addToSet: {commenters: userId} + }); + + return comment; +} +addCallback('comments.new.sync', CommentsNewOperations); + +////////////////////////////////////////////////////// +// comments.new.async // +////////////////////////////////////////////////////// + + +/** + * @summary Run the 'upvote.async' callbacks *once* the item exists in the database + * @param {object} item - The item being operated on + * @param {object} user - The user doing the operation + * @param {object} collection - The collection the item belongs to + */ +function UpvoteAsyncCallbacksAfterDocumentInsert(item, user, collection) { + runCallbacksAsync('upvote.async', item, user, collection, 'upvote'); +} + +addCallback('comments.new.async', UpvoteAsyncCallbacksAfterDocumentInsert); + +////////////////////////////////////////////////////// +// comments.remove.async // +////////////////////////////////////////////////////// + +function CommentsRemovePostCommenters (comment, currentUser) { + const { userId, postId } = comment; + + // dec user's comment count + Users.update({_id: userId}, { + $inc: {'commentCount': -1} + }); + + const postComments = Comments.find({postId}, {sort: {postedAt: -1}}).fetch(); + + const commenters = _.uniq(postComments.map(comment => comment.userId)); + const lastCommentedAt = postComments[0] && postComments[0].postedAt; + + // update post with a decremented comment count, a unique list of commenters and corresponding last commented at date + Posts.update(postId, { + $inc: {commentCount: -1}, + $set: {lastCommentedAt, commenters}, + }); + + return comment; +} + +addCallback('comments.remove.async', CommentsRemovePostCommenters); + +function CommentsRemoveChildrenComments (comment, currentUser) { + + const childrenComments = Comments.find({parentCommentId: comment._id}).fetch(); + + childrenComments.forEach(childComment => { + removeMutation({ + action: 'comments.remove', + collection: Comments, + documentId: childComment._id, + currentUser: currentUser, + validate: false + }); + }); + + return comment; +} + +addCallback('comments.remove.async', CommentsRemoveChildrenComments); + +////////////////////////////////////////////////////// +// other // +////////////////////////////////////////////////////// + +function UsersRemoveDeleteComments (user, options) { + if (options.deleteComments) { + Comments.remove({userId: user._id}); + } else { + // not sure if anything should be done in that scenario yet + // Comments.update({userId: userId}, {$set: {author: '\[deleted\]'}}, {multi: true}); + } +} +addCallback('users.remove.async', UsersRemoveDeleteComments); diff --git a/packages/example-forum/lib/server/comments/callbacks/validation.js b/packages/example-forum/lib/server/comments/callbacks/validation.js new file mode 100644 index 00000000..6948255c --- /dev/null +++ b/packages/example-forum/lib/server/comments/callbacks/validation.js @@ -0,0 +1,19 @@ +import Users from 'meteor/vulcan:users'; +import { addCallback, getSetting, registerSetting } from 'meteor/vulcan:core'; +import { Comments } from '../../../modules/comments/index.js'; + +registerSetting('forum.commentInterval', 15, 'How long users should wait in between comments (in seconds)'); + +function CommentsNewRateLimit (comment, user) { + if (!Users.isAdmin(user)) { + const timeSinceLastComment = Users.timeSinceLast(user, Comments); + const commentInterval = Math.abs(parseInt(getSetting('forum.commentInterval',15))); + + // check that user waits more than 15 seconds between comments + if((timeSinceLastComment < commentInterval)) { + throw new Error(Utils.encodeIntlError({id: 'comments.rate_limit_error', value: commentInterval-timeSinceLastComment})); + } + } + return comment; +} +addCallback('comments.new.validate', CommentsNewRateLimit); diff --git a/packages/example-forum/lib/server/comments/callbacks/voting.js b/packages/example-forum/lib/server/comments/callbacks/voting.js new file mode 100644 index 00000000..cbac25c2 --- /dev/null +++ b/packages/example-forum/lib/server/comments/callbacks/voting.js @@ -0,0 +1,16 @@ +import Users from 'meteor/vulcan:users'; +import { addCallback } from 'meteor/vulcan:core'; +import { Comments } from '../../../modules/comments/index.js'; + +import { performVoteServer } from 'meteor/vulcan:voting'; + +/** + * @summary Make users upvote their own new comments + */ +function CommentsNewUpvoteOwnComment(comment) { + var commentAuthor = Users.findOne(comment.userId); + const votedComent = performVoteServer({ document: comment, voteType: 'upvote', collection: Comments, user: commentAuthor }) + return {...comment, ...votedComent}; +} + +addCallback('comments.new.after', CommentsNewUpvoteOwnComment); \ No newline at end of file diff --git a/packages/example-forum/lib/server/comments/index.js b/packages/example-forum/lib/server/comments/index.js new file mode 100644 index 00000000..510fa9a4 --- /dev/null +++ b/packages/example-forum/lib/server/comments/index.js @@ -0,0 +1,4 @@ +import './callbacks/notifications.js'; +import './callbacks/other.js'; +import './callbacks/validation.js'; +import './callbacks/voting.js'; diff --git a/packages/example-forum/lib/server/email/notifications.js b/packages/example-forum/lib/server/email/notifications.js new file mode 100644 index 00000000..fd970604 --- /dev/null +++ b/packages/example-forum/lib/server/email/notifications.js @@ -0,0 +1,25 @@ +import Users from 'meteor/vulcan:users'; +import VulcanEmail from 'meteor/vulcan:email'; +import { getSetting, registerSetting } from 'meteor/vulcan:core'; + +registerSetting('emailNotifications', true, 'Enable email notifications'); + +export const createNotification = (userIds, notificationName, variables) => { + + if (getSetting('emailNotifications', true)) { + // if userIds is not an array, wrap it in one + if (!Array.isArray(userIds)) userIds = [userIds]; + + const emailName = notificationName; + + userIds.forEach(userId => { + const to = Users.getEmail(Users.findOne(userId)); + if (to) { + VulcanEmail.buildAndSend({ to, emailName, variables }); + } else { + console.log(`// Couldn't send notification: user ${user._id} doesn't have an email`); // eslint-disable-line + } + }); + } + +}; diff --git a/packages/example-forum/lib/server/email/templates.js b/packages/example-forum/lib/server/email/templates.js new file mode 100644 index 00000000..c725f2a1 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates.js @@ -0,0 +1,16 @@ +import VulcanEmail from 'meteor/vulcan:email'; + +VulcanEmail.addTemplates({ + test: Assets.getText("lib/server/email/templates/common/test.handlebars"), + wrapper: Assets.getText("lib/server/email/templates/common/wrapper.handlebars"), + newPost: Assets.getText("lib/server/email/templates/posts/newPost.handlebars"), + newPendingPost: Assets.getText("lib/server/email/templates/posts/newPendingPost.handlebars"), + postApproved: Assets.getText("lib/server/email/templates/posts/postApproved.handlebars"), + newComment: Assets.getText("lib/server/email/templates/comments/newComment.handlebars"), + newReply: Assets.getText("lib/server/email/templates/comments/newReply.handlebars"), + accountApproved: Assets.getText("lib/server/email/templates/users/accountApproved.handlebars"), + newUser: Assets.getText("lib/server/email/templates/users/newUser.handlebars"), + newsletter: Assets.getText("lib/server/email/templates/newsletter/newsletter.handlebars"), + newsletterConfirmation: Assets.getText("lib/server/email/templates/newsletter/newsletterConfirmation.handlebars"), + postItem: Assets.getText("lib/server/email/templates/newsletter/postItem.handlebars"), +}); \ No newline at end of file diff --git a/packages/example-forum/lib/server/email/templates/comments/newComment.handlebars b/packages/example-forum/lib/server/email/templates/comments/newComment.handlebars new file mode 100644 index 00000000..13a0c853 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/comments/newComment.handlebars @@ -0,0 +1,13 @@ + +{{CommentsSingle.user.displayName}} +left a new comment on +{{CommentsSingle.post.title}}: + +

+ +
+{{{CommentsSingle.htmlBody}}} +
+
+ +Reply

\ No newline at end of file diff --git a/packages/example-forum/lib/server/email/templates/comments/newReply.handlebars b/packages/example-forum/lib/server/email/templates/comments/newReply.handlebars new file mode 100644 index 00000000..d810a130 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/comments/newReply.handlebars @@ -0,0 +1,12 @@ +{{CommentsSingle.user.displayName}} +has replied to your comment on +{{CommentsSingle.post.title}}: + +

+ +
+{{{CommentsSingle.htmlBody}}} +
+
+ +Reply

\ No newline at end of file diff --git a/packages/vulcan-email-templates/lib/server/emails/common/test.handlebars b/packages/example-forum/lib/server/email/templates/common/test.handlebars similarity index 100% rename from packages/vulcan-email-templates/lib/server/emails/common/test.handlebars rename to packages/example-forum/lib/server/email/templates/common/test.handlebars diff --git a/packages/vulcan-email-templates/lib/server/emails/common/wrapper.handlebars b/packages/example-forum/lib/server/email/templates/common/wrapper.handlebars similarity index 100% rename from packages/vulcan-email-templates/lib/server/emails/common/wrapper.handlebars rename to packages/example-forum/lib/server/email/templates/common/wrapper.handlebars diff --git a/packages/example-forum/lib/server/email/templates/newsletter/newsletter.handlebars b/packages/example-forum/lib/server/email/templates/newsletter/newsletter.handlebars new file mode 100644 index 00000000..1ced22e6 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/newsletter/newsletter.handlebars @@ -0,0 +1,154 @@ + + +Recently on {{SiteData.title}} +– {{date}} +

+ + +
\ No newline at end of file diff --git a/packages/vulcan-email-templates/lib/server/emails/newsletter/newsletterConfirmation.handlebars b/packages/example-forum/lib/server/email/templates/newsletter/newsletterConfirmation.handlebars similarity index 100% rename from packages/vulcan-email-templates/lib/server/emails/newsletter/newsletterConfirmation.handlebars rename to packages/example-forum/lib/server/email/templates/newsletter/newsletterConfirmation.handlebars diff --git a/packages/vulcan-email-templates/lib/server/emails/newsletter/postItem.handlebars b/packages/example-forum/lib/server/email/templates/newsletter/postItem.handlebars similarity index 100% rename from packages/vulcan-email-templates/lib/server/emails/newsletter/postItem.handlebars rename to packages/example-forum/lib/server/email/templates/newsletter/postItem.handlebars diff --git a/packages/example-forum/lib/server/email/templates/posts/newPendingPost.handlebars b/packages/example-forum/lib/server/email/templates/posts/newPendingPost.handlebars new file mode 100644 index 00000000..8aeea9eb --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/posts/newPendingPost.handlebars @@ -0,0 +1,18 @@ + +{{PostsSingle.user.displayName}} +has a new post pending approval: +{{#if PostsSingle.url}} + {{PostsSingle.title}} +{{else}} + {{PostsSingle.title}} +{{/if}} +

+ +{{#if PostsSingle.htmlBody}} +
+ {{{PostsSingle.htmlBody}}} +
+
+{{/if}} + +Go to post

diff --git a/packages/example-forum/lib/server/email/templates/posts/newPost.handlebars b/packages/example-forum/lib/server/email/templates/posts/newPost.handlebars new file mode 100644 index 00000000..22a4e1ff --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/posts/newPost.handlebars @@ -0,0 +1,18 @@ + +{{PostsSingle.user.displayName}} +has created a new post: +{{#if PostsSingle.url}} + {{PostsSingle.title}} +{{else}} + {{PostsSingle.title}} +{{/if}} +

+ +{{#if PostsSingle.htmlBody}} +
+ {{{PostsSingle.htmlBody}}} +
+
+{{/if}} + +Discuss

diff --git a/packages/vulcan-email-templates/lib/server/emails/posts/postApproved.handlebars b/packages/example-forum/lib/server/email/templates/posts/postApproved.handlebars similarity index 54% rename from packages/vulcan-email-templates/lib/server/emails/posts/postApproved.handlebars rename to packages/example-forum/lib/server/email/templates/posts/postApproved.handlebars index 3bbc777e..1a34520a 100644 --- a/packages/vulcan-email-templates/lib/server/emails/posts/postApproved.handlebars +++ b/packages/example-forum/lib/server/email/templates/posts/postApproved.handlebars @@ -2,5 +2,5 @@ Congratulations, your post has been approved:

-{{postTitle}} +{{PostsSingle.title}}

\ No newline at end of file diff --git a/packages/example-forum/lib/server/email/templates/users/accountApproved.handlebars b/packages/example-forum/lib/server/email/templates/users/accountApproved.handlebars new file mode 100644 index 00000000..c0780618 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/users/accountApproved.handlebars @@ -0,0 +1,3 @@ +{{UsersSingle.displayName}}, welcome to {{SiteData.title}}!

+ +You've just been invited. Start posting.

\ No newline at end of file diff --git a/packages/example-forum/lib/server/email/templates/users/newUser.handlebars b/packages/example-forum/lib/server/email/templates/users/newUser.handlebars new file mode 100644 index 00000000..28b5c114 --- /dev/null +++ b/packages/example-forum/lib/server/email/templates/users/newUser.handlebars @@ -0,0 +1 @@ +A new user account has been created: {{UsersSingle.displayName}}

\ No newline at end of file diff --git a/packages/example-forum/lib/server/main.js b/packages/example-forum/lib/server/main.js new file mode 100644 index 00000000..e181d427 --- /dev/null +++ b/packages/example-forum/lib/server/main.js @@ -0,0 +1,21 @@ +// Modules + +export * from '../modules/index.js'; + +export * from './email/notifications.js'; + +// Server + +import './email/templates.js'; + +import './seed/seed_posts.js'; +import './seed/seed_categories.js'; + +import './comments/index.js'; + +import './categories/index.js'; + +import './posts/index.js'; + +import './api.js'; +import './rss.js'; \ No newline at end of file diff --git a/packages/example-forum/lib/server/posts/callbacks/embedly.js b/packages/example-forum/lib/server/posts/callbacks/embedly.js new file mode 100644 index 00000000..51e43105 --- /dev/null +++ b/packages/example-forum/lib/server/posts/callbacks/embedly.js @@ -0,0 +1,87 @@ +/* + +Callbacks to add media/thumbnail after submit and on edit + +*/ + +import { addCallback, getSetting } from 'meteor/vulcan:core'; +import { Embed } from 'meteor/vulcan:embed'; + +const embedProvider = getSetting('embedProvider'); + +// For security reason, we make the media property non-modifiable by the client and +// we use a separate server-side API call to set it (and the thumbnail object if it hasn't already been set) + +// Async variant that directly modifies the post object with update() +function AddMediaAfterSubmit (post) { + + if(post.url){ + + const data = Embed[embedProvider].getData(post.url); + + if (data) { + + // only add a thumbnailUrl if there isn't one already + if (!post.thumbnailUrl && data.thumbnailUrl) { + post.thumbnailUrl = data.thumbnailUrl; + } + + // add media if necessary + if (data.media && data.media.html) { + post.media = data.media; + } + + // add source name & url if they exist + if (data.sourceName && data.sourceUrl) { + post.sourceName = data.sourceName; + post.sourceUrl = data.sourceUrl; + } + + } + + } + + return post; +} +addCallback('posts.new.sync', AddMediaAfterSubmit); + +function updateMediaOnEdit (modifier, post) { + + const newUrl = modifier.$set.url; + + if(newUrl && newUrl !== post.url){ + + const data = Embed[embedProvider].getData(newUrl); + + if(data) { + + if (data.media && data.media.html) { + if (modifier.$unset.media) { + delete modifier.$unset.media + } + modifier.$set.media = data.media; + } + + // add source name & url if they exist + if (data.sourceName && data.sourceUrl) { + modifier.$set.sourceName = data.sourceName; + modifier.$set.sourceUrl = data.sourceUrl; + } + + } + } + return modifier; +} +addCallback('posts.edit.sync', updateMediaOnEdit); + +const addMediaAfterSubmit = AddMediaAfterSubmit; + +const regenerateThumbnail = function (post) { + delete post.thumbnailUrl; + delete post.media; + delete post.sourceName; + delete post.sourceUrl; + addMediaAfterSubmit(post); +}; + +export { addMediaAfterSubmit, updateMediaOnEdit, regenerateThumbnail } diff --git a/packages/example-forum/lib/server/posts/callbacks/notifications.js b/packages/example-forum/lib/server/posts/callbacks/notifications.js new file mode 100644 index 00000000..e039ca4e --- /dev/null +++ b/packages/example-forum/lib/server/posts/callbacks/notifications.js @@ -0,0 +1,43 @@ +/* + +Notifications for new posts and post approval. + +*/ + +import { Posts } from '../../../modules/posts/index.js' +import Users from 'meteor/vulcan:users'; +import { Connectors, addCallback } from 'meteor/vulcan:core'; +import { createNotification } from '../../email/notifications.js'; + +/** + * @summary Add notification callback when a post is approved + */ +function PostsApprovedNotification (post) { + createNotification(post.userId, 'postApproved', {documentId: post._id}); +} +addCallback('posts.approve.async', PostsApprovedNotification); + + +/** + * @summary Add new post notification callback on post submit + */ +function PostsNewNotifications (post) { + + const adminUsers = Connectors.find(Users, { isAdmin: true }, { fields: { _id: 1 }}); + let adminIds = _.pluck(adminUsers, '_id'); + let notifiedUserIds = _.pluck(Users.find({'notifications_posts': true}, {fields: {_id:1}}).fetch(), '_id'); + + // remove post author ID from arrays + adminIds = _.without(adminIds, post.userId); + notifiedUserIds = _.without(notifiedUserIds, post.userId); + + if (post.status === Posts.config.STATUS_PENDING && !!adminIds.length) { + // if post is pending, only notify admins + createNotification(adminIds, 'newPendingPost', {documentId: post._id}); + } else if (!!notifiedUserIds.length) { + // if post is approved, notify everybody + createNotification(notifiedUserIds, 'newPost', {documentId: post._id}); + } + +} +addCallback('posts.new.async', PostsNewNotifications); diff --git a/packages/example-forum/lib/server/posts/callbacks/other.js b/packages/example-forum/lib/server/posts/callbacks/other.js new file mode 100644 index 00000000..92141c29 --- /dev/null +++ b/packages/example-forum/lib/server/posts/callbacks/other.js @@ -0,0 +1,107 @@ +/* + +Callbacks to: + +- Increment a user's post count +- Run post approved callbacks +- Update a user's post count +- Remove a user's posts when it's deleted +- Track clicks + +*/ + +import { Posts } from '../../../modules/posts/index.js' +import Users from 'meteor/vulcan:users'; +import { Connectors, addCallback, getSetting, registerSetting, runCallbacks, runCallbacksAsync } from 'meteor/vulcan:core'; +import Events from 'meteor/vulcan:events'; + +registerSetting('forum.trackClickEvents', true, 'Track clicks to posts pages'); + +/** + * @summary Increment the user's post count + */ +function PostsNewIncrementPostCount(post) { + var userId = post.userId; + Users.update({ _id: userId }, { $inc: { 'postCount': 1 } }); +} +addCallback('posts.new.async', PostsNewIncrementPostCount); + +////////////////////////////////////////////////////// +// posts.edit.sync // +////////////////////////////////////////////////////// + +function PostsEditRunPostApprovedSyncCallbacks(modifier, post) { + if (modifier.$set && Posts.isApproved(modifier.$set) && !Posts.isApproved(post)) { + modifier = runCallbacks('posts.approve.sync', modifier, post); + } + return modifier; +} +addCallback('posts.edit.sync', PostsEditRunPostApprovedSyncCallbacks); + +////////////////////////////////////////////////////// +// posts.edit.async // +////////////////////////////////////////////////////// + +function PostsEditRunPostApprovedAsyncCallbacks(post, oldPost) { + if (Posts.isApproved(post) && !Posts.isApproved(oldPost)) { + runCallbacksAsync('posts.approve.async', post); + } +} +addCallback('posts.edit.async', PostsEditRunPostApprovedAsyncCallbacks); + +////////////////////////////////////////////////////// +// posts.remove.sync // +////////////////////////////////////////////////////// + +function PostsRemoveOperations(post) { + Users.update({ _id: post.userId }, { $inc: { 'postCount': -1 } }); + return post; +} +addCallback('posts.remove.sync', PostsRemoveOperations); + +////////////////////////////////////////////////////// +// users.remove.async // +////////////////////////////////////////////////////// + +function UsersRemoveDeletePosts(user, options) { + if (options.deletePosts) { + Posts.remove({ userId: user._id }); + } else { + // not sure if anything should be done in that scenario yet + // Posts.update({userId: userId}, {$set: {author: '\[deleted\]'}}, {multi: true}); + } +} +addCallback('users.remove.async', UsersRemoveDeletePosts); + +////////////////////////////////////////////////////// +// posts.click.async // +////////////////////////////////////////////////////// + +// /** +// * @summary Increase the number of clicks on a post +// * @param {string} postId – the ID of the post being edited +// * @param {string} ip – the IP of the current user +// */ + +function PostsClickTracking(post, ip) { + if (getSetting('forum.trackClickEvents', true)) { + Events.track('post.click', { title: post.title, postId: post._id }); + Connectors.update(Posts, post._id, { $inc: { clickCount: 1 } }); + } +} + +// track links clicked, locally in Events collection +// note: this event is not sent to segment cause we cannot access the current user +// in our server-side route /out -> sending an event would create a new anonymous +// user: the free limit of 1,000 unique users per month would be reached quickly +addCallback('posts.click.async', PostsClickTracking); + +////////////////////////////////////////////////////// +// posts.approve.sync // +////////////////////////////////////////////////////// + +function PostsApprovedSetPostedAt(modifier, post) { + modifier.$set.postedAt = new Date(); + return modifier; +} +addCallback('posts.approve.sync', PostsApprovedSetPostedAt); diff --git a/packages/example-forum/lib/server/posts/callbacks/validation.js b/packages/example-forum/lib/server/posts/callbacks/validation.js new file mode 100644 index 00000000..ffcdb449 --- /dev/null +++ b/packages/example-forum/lib/server/posts/callbacks/validation.js @@ -0,0 +1,83 @@ +/* + +Post validation and rate limiting callbacks + +*/ + +import { Posts } from '../../../modules/posts/index.js' +import Users from 'meteor/vulcan:users'; +import { addCallback, getSetting, registerSetting } from 'meteor/vulcan:core'; +import { createError } from 'apollo-errors'; + +registerSetting('forum.postInterval', 30, 'How long users should wait between each posts, in seconds'); +registerSetting('forum.maxPostsPerDay', 5, 'Maximum number of posts a user can create in a day'); + +/** + * @summary Rate limiting + */ +function PostsNewRateLimit (post, user) { + + if(!Users.isAdmin(user)){ + + var timeSinceLastPost = Users.timeSinceLast(user, Posts), + numberOfPostsInPast24Hours = Users.numberOfItemsInPast24Hours(user, Posts), + postInterval = Math.abs(parseInt(getSetting('forum.postInterval', 30))), + maxPostsPer24Hours = Math.abs(parseInt(getSetting('forum.maxPostsPerDay', 5))); + + // check that user waits more than X seconds between posts + if(timeSinceLastPost < postInterval){ + const RateLimitError = createError('posts.rate_limit_error', {message: 'posts.rate_limit_error'}); + throw new RateLimitError({data: {break: true, id: 'posts.rate_limit_error', properties: { value: postInterval-timeSinceLastPost }}}); + + } + // check that the user doesn't post more than Y posts per day + if(numberOfPostsInPast24Hours >= maxPostsPer24Hours){ + const RateLimitError = createError('posts.max_per_day', {message: 'posts.max_per_day'}); + throw new RateLimitError({data: {break: true, id: 'posts.max_per_day', properties: { value: maxPostsPer24Hours }}}); + } + } + + return post; +} +addCallback('posts.new.validate', PostsNewRateLimit); + +/** + * @summary Check for duplicate links + */ +function PostsNewDuplicateLinksCheck (post, user) { + if(!!post.url && Posts.checkForSameUrl(post.url)) { + const DuplicateError = createError('posts.link_already_posted', {message: 'posts.link_already_posted'}); + throw new DuplicateError({ + data: { + break: true, + id: 'posts.link_already_posted', + path: 'url', + properties: { url: post.url }, + }, + }); + } + return post; +} +addCallback('posts.new.sync', PostsNewDuplicateLinksCheck); + + +/** + * @summary Check for duplicate links + */ +function PostsEditDuplicateLinksCheck (modifier, post) { + if(post.url !== modifier.$set.url && !!modifier.$set.url) { + if (Posts.checkForSameUrl(modifier.$set.url)){ + const DuplicateError = createError('posts.link_already_posted', {message: 'posts.link_already_posted'}); + throw new DuplicateError({ + data: { + break: true, + id: 'posts.link_already_posted', + path: 'url', + properties: { url: post.url }, + }, + }); + } + } + return modifier; +} +addCallback('posts.edit.sync', PostsEditDuplicateLinksCheck); diff --git a/packages/example-forum/lib/server/posts/callbacks/voting.js b/packages/example-forum/lib/server/posts/callbacks/voting.js new file mode 100644 index 00000000..131e2927 --- /dev/null +++ b/packages/example-forum/lib/server/posts/callbacks/voting.js @@ -0,0 +1,20 @@ +/* + +Voting callbacks + +*/ + +import { Posts } from '../../../modules/posts/index.js'; +import Users from 'meteor/vulcan:users'; +import { addCallback } from 'meteor/vulcan:core'; +import { performVoteServer } from 'meteor/vulcan:voting'; + +/** + * @summary Make users upvote their own new posts + */ +function PostsNewUpvoteOwnPost(post) { + var postAuthor = Users.findOne(post.userId); + return {...post, ...performVoteServer({ document: post, voteType: 'upvote', collection: Posts, user: postAuthor, updateDocument: true })}; +} + +addCallback('posts.new.after', PostsNewUpvoteOwnPost); diff --git a/packages/vulcan-posts/lib/server/cron.js b/packages/example-forum/lib/server/posts/cron.js similarity index 94% rename from packages/vulcan-posts/lib/server/cron.js rename to packages/example-forum/lib/server/posts/cron.js index 7145ff6a..c423677f 100644 --- a/packages/vulcan-posts/lib/server/cron.js +++ b/packages/example-forum/lib/server/posts/cron.js @@ -1,9 +1,9 @@ import { SyncedCron } from 'meteor/percolatestudio:synced-cron'; // import moment from 'moment'; -import Posts from '../collection.js'; +import { Posts } from '../../modules/posts/index.js'; SyncedCron.options = { - log: false, + log: true, collectionName: 'cronHistory', utc: false, collectionTTL: 172800 diff --git a/packages/example-forum/lib/server/posts/graphql.js b/packages/example-forum/lib/server/posts/graphql.js new file mode 100644 index 00000000..ac2421c4 --- /dev/null +++ b/packages/example-forum/lib/server/posts/graphql.js @@ -0,0 +1,21 @@ +/* + +GraphQL config + +*/ + +import { addGraphQLMutation, addGraphQLResolvers } from 'meteor/vulcan:core'; + +const specificResolvers = { + Mutation: { + increasePostViewCount(root, { postId }, context) { + return context.Posts.update({_id: postId}, { $inc: { viewCount: 1 }}); + } + } +}; + +addGraphQLResolvers(specificResolvers); +addGraphQLMutation('increasePostViewCount(postId: String): Float'); + + + diff --git a/packages/example-forum/lib/server/posts/index.js b/packages/example-forum/lib/server/posts/index.js new file mode 100644 index 00000000..ce6c960a --- /dev/null +++ b/packages/example-forum/lib/server/posts/index.js @@ -0,0 +1,11 @@ +import './cron.js'; +import './out.js'; +import './indexes.js'; +import './graphql.js'; + +import './callbacks/embedly.js'; +import './callbacks/notifications.js'; +import './callbacks/other.js'; +import './callbacks/validation.js'; +import './callbacks/voting.js'; + diff --git a/packages/example-forum/lib/server/posts/indexes.js b/packages/example-forum/lib/server/posts/indexes.js new file mode 100644 index 00000000..210699e5 --- /dev/null +++ b/packages/example-forum/lib/server/posts/indexes.js @@ -0,0 +1,4 @@ +import { Posts } from '../../modules/posts/index.js'; + +Posts._ensureIndex({"status": 1, "isFuture": 1}); +Posts._ensureIndex({"status": 1, "isFuture": 1, "postedAt": 1}); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/server/routes.js b/packages/example-forum/lib/server/posts/out.js similarity index 75% rename from packages/vulcan-posts/lib/server/routes.js rename to packages/example-forum/lib/server/posts/out.js index 2f44198c..101f3a7a 100644 --- a/packages/vulcan-posts/lib/server/routes.js +++ b/packages/example-forum/lib/server/posts/out.js @@ -1,7 +1,7 @@ import { runCallbacksAsync } from 'meteor/vulcan:core'; import escapeStringRegexp from 'escape-string-regexp'; import { Picker } from 'meteor/meteorhacks:picker'; -import Posts from '../collection.js'; +import { Posts } from '../../modules/posts/index.js'; Picker.route('/out', ({ query}, req, res, next) => { if(query.url){ // for some reason, query.url doesn't need to be decoded @@ -14,10 +14,7 @@ Picker.route('/out', ({ query}, req, res, next) => { try { - // decode url just in case - const decodedUrl = decodeURIComponent(query.url); - - const post = Posts.findOne({url: {$regex: escapeStringRegexp(decodedUrl)}}, {sort: {postedAt: -1, createdAt: -1}}); + const post = Posts.findOne({url: {$regex: escapeStringRegexp(query.url)}}, {sort: {postedAt: -1, createdAt: -1}}); if (post) { const ip = req.headers && req.headers['x-forwarded-for'] || req.connection.remoteAddress; @@ -31,9 +28,12 @@ Picker.route('/out', ({ query}, req, res, next) => { res.end(`Invalid URL: ${query.url}`); } } catch (error) { - console.log('// /out error') - console.log(error) - console.log(query) + // eslint-disable-next-line no-console + console.log('// /out error'); + // eslint-disable-next-line no-console + console.log(error); + // eslint-disable-next-line no-console + console.log(query); } } else { res.end("Please provide a URL"); diff --git a/packages/example-forum/lib/server/rss.js b/packages/example-forum/lib/server/rss.js new file mode 100644 index 00000000..800c3977 --- /dev/null +++ b/packages/example-forum/lib/server/rss.js @@ -0,0 +1,103 @@ +import RSS from 'rss'; +import { Posts } from '../modules/posts/index.js'; +import { Comments } from '../modules/comments/index.js'; +import { Utils, getSetting, registerSetting } from 'meteor/vulcan:core'; +import { Picker } from 'meteor/meteorhacks:picker'; + +registerSetting('forum.RSSLinksPointTo', 'link', 'Where to point RSS links to'); + +Posts.addView('rss', Posts.views.new); // default to 'new' view for RSS feed + +const getMeta = (url) => { + const siteUrl = getSetting('siteUrl', Meteor.absoluteUrl()); + + return { + title: getSetting('title'), + description: getSetting('tagline'), + feed_url: siteUrl+url, + site_url: siteUrl, + image_url: siteUrl+'img/favicon.png' + }; +}; + +export const servePostRSS = (terms, url) => { + const feed = new RSS(getMeta(url)); + + let parameters = Posts.getParameters(terms); + delete parameters['options']['sort']['sticky']; + + parameters.options.limit = 50; + + const postsCursor = Posts.find(parameters.selector, parameters.options); + + postsCursor.forEach((post) => { + + const description = !!post.body ? post.body+'

' : ''; + const feedItem = { + title: post.title, + description: description + `Discuss`, + author: post.author, + date: post.postedAt, + guid: post._id, + url: (getSetting('forum.RSSLinksPointTo', 'link') === 'link') ? Posts.getLink(post) : Posts.getPageUrl(post, true) + }; + + if (post.thumbnailUrl) { + const url = Utils.addHttp(post.thumbnailUrl); + feedItem.custom_elements = [{'imageUrl':url}, {'content': url}]; + } + + feed.item(feedItem); + }); + + return feed.xml(); +}; + +export const serveCommentRSS = (terms, url) => { + const feed = new RSS(getMeta(url)); + + const commentsCursor = Comments.find({isDeleted: {$ne: true}}, {sort: {postedAt: -1}, limit: 20}); + + commentsCursor.forEach(function(comment) { + const post = Posts.findOne(comment.postId); + + feed.item({ + title: 'Comment on ' + post.title, + description: `${comment.body}

Discuss`, + author: comment.author, + date: comment.postedAt, + url: Comments.getPageUrl(comment, true), + guid: comment._id + }); + }); + + return feed.xml(); +}; + + +Picker.route('/feed.xml', function(params, req, res, next) { + if (typeof params.query.view === 'undefined') { + params.query.view = 'rss'; + } + res.end(servePostRSS(params.query, 'feed.xml')); +}); + +Picker.route('/rss/posts/new.xml', function(params, req, res, next) { + res.end(servePostRSS({view: 'new'}, '/rss/posts/new.xml')); +}); + +Picker.route('/rss/posts/top.xml', function(params, req, res, next) { + res.end(servePostRSS({view: 'top'}, '/rss/posts/top.xml')); +}); + +Picker.route('/rss/posts/best.xml', function(params, req, res, next) { + res.end(servePostRSS({view: 'best'}, '/rss/posts/best.xml')); +}); + +Picker.route('/rss/category/:slug/feed.xml', function(params, req, res, next) { + res.end(servePostRSS({view: 'new', cat: params.slug}, '/rss/category/:slug/feed.xml')); +}); + +Picker.route('/rss/comments.xml', function(params, req, res, next) { + res.end(serveCommentRSS({}, '/rss/comments.xml')); +}); diff --git a/packages/example-forum/lib/server/seed/seed_categories.js b/packages/example-forum/lib/server/seed/seed_categories.js new file mode 100644 index 00000000..cff803ef --- /dev/null +++ b/packages/example-forum/lib/server/seed/seed_categories.js @@ -0,0 +1,102 @@ +/* global Vulcan */ +import { Promise } from 'meteor/promise'; +import { Utils, newMutation, getSetting } from 'meteor/vulcan:core'; +import { Categories } from '../../modules/categories/index.js'; + +if (getSetting('forum.seedOnStart')) { + const dummyFlag = { + fieldName: 'isDummy', + fieldSchema: { + type: Boolean, + optional: true, + hidden: true, + }, + }; + + Categories.addField(dummyFlag); + + const dummyCategories = [ + { + name: 'Test Category 1', + description: 'The first test category', + order: 4, + slug: 'testcat1', + isDummy: true, + }, + { + name: 'Test Category 2', + description: 'The second test category', + order: 7, + slug: 'testcat2', + isDummy: true, + }, + { + name: 'Test Category 3', + description: 'The third test category', + order: 10, + slug: 'testcat3', + isDummy: true, + }, + ]; + + const originalRemoveGettingStartedContent = Vulcan.removeGettingStartedContent; + + Vulcan.removeGettingStartedContent = () => { + if (originalRemoveGettingStartedContent) { + originalRemoveGettingStartedContent(); + } + + Categories.remove({ isDummy: true }); + // eslint-disable-next-line no-console + console.log('// Getting started content removed from seed_categories'); + }; + + Meteor.startup(() => { + // Load categories from settings, if there are any + if (Meteor.settings && Meteor.settings.categories) { + Meteor.settings.categories.forEach(category => { + + // get slug (or slugified name) + const slug = category.slug || Utils.slugify(category.name); + + // look for existing category with same slug + const existingCategory = Categories.findOne({slug: slug}); + + // look for parent category + if (category.parent) { + const parentCategory = Categories.findOne({slug: category.parent}); + if (parentCategory) { + category.parentId = parentCategory._id; + delete category.parent; + } + } + + if (existingCategory) { + // if category exists, update it with settings data except slug + delete category.slug; + Categories.update(existingCategory._id, { $set: category }); + } else { + // if not, create it + Promise.await(newMutation({ + collection: Categories, + document: category, + validate: false, + })); + + // Categories.insert(category); + // eslint-disable-next-line no-console + console.log(`// Creating category “${category.name}”`); // eslint-disable-line + } + }); + } else if (!Categories.find().count()) { + // eslint-disable-next-line no-console + console.log('// inserting dummy categories'); + // else if there are no categories yet, create dummy categories + Promise.awaitAll(dummyCategories.map(document => newMutation({ + collection: Categories, + document, + validate: false, + }))); + } + }); +} diff --git a/packages/example-forum/lib/server/seed/seed_posts.js b/packages/example-forum/lib/server/seed/seed_posts.js new file mode 100644 index 00000000..20d2d725 --- /dev/null +++ b/packages/example-forum/lib/server/seed/seed_posts.js @@ -0,0 +1,158 @@ +/* global Vulcan */ +import moment from 'moment'; +import { newMutation, registerSetting, getSetting } from 'meteor/vulcan:core'; +import Users from 'meteor/vulcan:users'; +import { Promise } from 'meteor/promise'; +import { Posts } from '../../modules/posts/index.js'; +import { Comments } from '../../modules/comments/index.js'; + +registerSetting('forum.seedOnStart', true, 'Seed the app with dummy content on startup'); + +if (getSetting('forum.seedOnStart')) { + + const dummyFlag = { + fieldName: 'isDummy', + fieldSchema: { + type: Boolean, + optional: true, + hidden: true, + }, + }; + + Users.addField(dummyFlag); + Posts.addField(dummyFlag); + Comments.addField(dummyFlag); + + Posts.addField({ + fieldName: 'dummySlug', + fieldSchema: { + type: String, + optional: true, + hidden: true, // never show this + }, + }); + + const toTitleCase = (str) => { + return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();}); + }; + + const createPost = async (slug, postedAt, username, thumbnail) => { + const currentUser = await Users.rawCollection().findOne({ username: username }); + const document = { + postedAt: postedAt, + body: Assets.getText("lib/assets/content/" + slug + ".md"), + title: toTitleCase(slug.replace(/_/g, ' ')), + dummySlug: slug, + isDummy: true, + userId: currentUser._id, + }; + + if (typeof thumbnail !== "undefined") { + document.thumbnailUrl = "/packages/example-forum/lib/assets/images/" + thumbnail; + } + + return newMutation({ + collection: Posts, + document, + currentUser, + validate: false, + }); + }; + + const createComment = async (slug, username, body, parentBody) => { + const user = await Users.rawCollection().findOne({ username: username }); + const post = await Posts.rawCollection().findOne({ dummySlug: slug }); + const comment = { + postId: post._id, + userId: user._id, + body: body, + isDummy: true, + disableNotifications: true, + }; + const parentComment = await Comments.rawCollection().findOne({ body: parentBody }); + + if (parentComment) { + comment.parentCommentId = parentComment._id; + } + + return newMutation({ + collection: Comments, + document: comment, + currentUser: user, + validate: false, + }); + }; + + const createUser = async (username, email) => { + const document = { + username, + email, + isDummy: true, + }; + + return newMutation({ + collection: Users, + document, + validate: false, + }); + }; + + const createDummyUsers = async () => { + // eslint-disable-next-line no-console + console.log('// inserting dummy users…'); + return Promise.all([ + createUser('Bruce', 'dummyuser1@telescopeapp.org'), + createUser('Arnold', 'dummyuser2@telescopeapp.org'), + createUser('Julia', 'dummyuser3@telescopeapp.org'), + ]); + }; + + const createDummyPosts = async () => { + // eslint-disable-next-line no-console + console.log('// inserting dummy posts'); + + return Promise.all([ + createPost("read_this_first", moment().toDate(), "Bruce", "telescope.png"), + createPost("deploying", moment().subtract(10, 'minutes').toDate(), "Arnold"), + createPost("customizing", moment().subtract(3, 'hours').toDate(), "Julia"), + createPost("getting_help", moment().subtract(1, 'days').toDate(), "Bruce", "stackoverflow.png"), + createPost("removing_getting_started_posts", moment().subtract(2, 'days').toDate(), "Julia"), + ]); + }; + + const createDummyComments = async () => { + // eslint-disable-next-line no-console + console.log('// inserting dummy comments…'); + + return Promise.all([ + createComment("read_this_first", "Bruce", "What an awesome app!"), + createComment("deploying", "Arnold", "Deploy to da choppah!"), + createComment("deploying", "Julia", "Do you really need to say this all the time?", "Deploy to da choppah!"), + createComment("customizing", "Julia", "This is really cool!"), + createComment("removing_getting_started_posts", "Bruce", "Yippee ki-yay!"), + createComment("removing_getting_started_posts", "Arnold", "I'll be back.", "Yippee ki-yay!"), + ]); + }; + + Vulcan.removeGettingStartedContent = () => { + Users.remove({ 'profile.isDummy': true }); + Posts.remove({ isDummy: true }); + Comments.remove({ isDummy: true }); + // eslint-disable-next-line no-console + console.log('// Getting started content removed from seed_posts'); + }; + + // Uses Promise.await to await async functions in a Fiber to make them "Meteor synchronous" + Meteor.startup(() => { + // insert dummy content only if createDummyContent hasn't happened and there aren't any posts or users in the db + if (!Users.find({ isDummy: true }).count()) { + Promise.await(createDummyUsers()); + } + if (!Posts.find().count()) { + Promise.await(createDummyPosts()); + } + if (!Comments.find().count()) { + Promise.await(createDummyComments()); + } + }); +} diff --git a/packages/example-forum/lib/stylesheets/_accounts.scss b/packages/example-forum/lib/stylesheets/_accounts.scss new file mode 100644 index 00000000..17130715 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_accounts.scss @@ -0,0 +1,46 @@ +.users-account-menu{ + .icon{ + display: none; + } +} + +.accounts-ui{ + .buttons{ + a{ + display:block; + text-align: center; + margin-bottom: $vmargin; + } + } + .password-or-service{ + text-align: center; + margin-bottom: $vmargin; + } + .social-buttons{ + @include flex-center; + justify-content: space-between; + button{ + margin-right: $hmargin; + width: 100%; + &.btn-twitter{ + background: #55acee; + border-color: #55acee; + } + &.btn-facebook{ + background: #3b5998; + border-color: #3b5998; + } + &:last-child{ + margin-right: 0 !important; + } + } + } +} + +.password-reset-form{ + text-align: center; +} + +.change-password-link{ + margin-bottom: $vmargin; +} diff --git a/packages/example-forum/lib/stylesheets/_breakpoints.scss b/packages/example-forum/lib/stylesheets/_breakpoints.scss new file mode 100644 index 00000000..b14d5456 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_breakpoints.scss @@ -0,0 +1,28 @@ +$small-break: 600px; +$medium-break: 800px; + +@mixin small(){ // smaller than small-break + @media screen and (max-width: $small-break) { + @content; + } +} +@mixin small-medium(){ // smaller than medium-break + @media screen and (max-width: $medium-break) { + @content; + } +} +@mixin medium(){ // between small and medium-break + @media screen and (min-width: $small-break) and (max-width: $medium-break) { + @content; + } +} +@mixin medium-large(){ // larger than small-break + @media screen and (min-width: $small-break) { + @content; + } +} +@mixin large(){ // larger than medium-break + @media screen and (min-width: $medium-break) { + @content; + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_categories.scss b/packages/example-forum/lib/stylesheets/_categories.scss new file mode 100644 index 00000000..ff4fe4a7 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_categories.scss @@ -0,0 +1,27 @@ +.category-menu-item{ + position: relative; + .categories-delete-link, .edit-category-link{ + position: absolute; + top: 4px; + right: 5px; + } +} + +.categories-new-button{ + text-align: center; + padding: $vmargin/2; + button{ + display: inline-block; + } +} + +.categories-node .categories-node{ + border-left: 10px $lightest-border solid; + padding-left: $hmargin; +} + +.categories-edit-form-admin{ + @include flex-center; + justify-content: space-between; + margin-bottom: $vmargin * 2; +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_cheatsheet.scss b/packages/example-forum/lib/stylesheets/_cheatsheet.scss new file mode 100644 index 00000000..f530a0c6 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_cheatsheet.scss @@ -0,0 +1,35 @@ + +.cheatsheet h1{ + margin-bottom: 20px; +} +.cheatsheet-wrapper{ + display: flex; +} +.cheatsheet-block{ + flex: 1; + margin-right: 20px; +} +.cheatsheet-block:last-of-type{ + margin-right: 0; +} + +.cheatsheet ul{ + list-style-type: none; + margin-bottom: 20px; + padding-left: 0px; +} +.cheatsheet ul li{ + margin-bottom: 10px; +} +.cheatsheet h2{ + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #ddd; +} +.cheatsheet h3{ + margin-bottom: 10px; + font-weight: bold; +} +.cheatsheet code{ + font-size: 13px; +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_colors.scss b/packages/example-forum/lib/stylesheets/_colors.scss new file mode 100644 index 00000000..112f4d95 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_colors.scss @@ -0,0 +1,25 @@ +$light-yellow: #FFFBDB; + +$lightest-grey: #f3f3f3; +$lighter-grey: #eee; +$light-grey: #ddd; + +$light-blue: #DAEDFF; +$blue: #0275d8; +$white: #fff; +$medium-grey: #bbb; +$dark-grey: #888; +$black: #333; + +$red: #E04E4B; + +$header-bg: $light-blue; + +$active-color: $blue; + +$lightest-border: $lightest-grey; +$lighter-border: $lighter-grey; +$light-border: $light-grey; + +$light-text: $medium-grey; +$medium-text: $dark-grey; diff --git a/packages/example-forum/lib/stylesheets/_comments.scss b/packages/example-forum/lib/stylesheets/_comments.scss new file mode 100644 index 00000000..a0453a06 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_comments.scss @@ -0,0 +1,83 @@ +.posts-comments-thread{ + border-top: $border; + margin-top: $vmargin; + padding-top: $vmargin; +} + +.posts-comments-thread-title{ + margin-bottom: $vmargin; +} + +.comments-list{ + margin-bottom: $vmargin; +} + +.comments-node .comments-node, .comments-item-reply{ + border-left: 10px $lightest-border solid; + padding-left: $hmargin; +} + +.comments-item{ + margin-bottom: $vmargin; +} + +.comments-item-body{ + @include border-radius; + // max-width: 800px; + border: $border; + padding: $hmargin $vmargin; +} + +.comments-item-meta{ + @include flex-center; + margin-bottom: $vmargin; + + .comments-item-vote{ + margin-right: 10px; + } + + .avatar{ + margin-right: 5px; + } + .users-name{ + margin-right: $hmargin; + } + .comments-item-date{ + margin-right: $hmargin; + color: $medium-text; + font-size: $small-font; + } + .comment-edit, .comment-delete { + font-size: $small-font; + } + .comment-delete { + margin-left: 8px; + } +} + +.comments-item-text{ +} + +.comments-edit-form, .comments-new-form{ + .input-body{ + margin-bottom: $hmargin; + } + .form-cancel{ + display: inline-block; + margin-left: $hmargin; + } +} + +.comments-item-reply{ + margin-top: $vmargin; +} + +.comment-actions{ + @include flex-center; + button{ + margin-right: $hmargin; + } + a{ + display: block; + } +} diff --git a/packages/example-forum/lib/stylesheets/_global.scss b/packages/example-forum/lib/stylesheets/_global.scss new file mode 100644 index 00000000..bd2fdada --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_global.scss @@ -0,0 +1,34 @@ +body{ + font-family: "Source Sans Pro", "Helvetica Neue", "Helvetica", sans-serif; +} + +.main, .footer, .header{ + margin-left: auto; + margin-right: auto; + padding: 0 $hmargin; + max-width: 1200px; +} + +.main{ + margin-bottom: $vmargin; +} + +a{ + cursor: pointer; +} + +.page-title{ + margin-bottom: $vmargin*2; +} + +.message.error { + color: $red; +} + +.menu-node-children{ + .dropdown-item{ + a{ + padding-left: 20px; + } + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_header.scss b/packages/example-forum/lib/stylesheets/_header.scss new file mode 100644 index 00000000..c04a6389 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_header.scss @@ -0,0 +1,81 @@ +.header-wrapper{ + background: $header-bg; + margin-bottom: $vmargin; +} + +.header{ + @include medium-large{ + @include flex-center; + justify-content: space-between; + } + padding-top: $vmargin; + padding-bottom: $vmargin; +} + +.logo{ + @include small{ + text-align: center; + } + @include medium-large{ + display: flex; + align-items: baseline; + .logo-text{ + margin-right: $hmargin; + } + .logo-image{ + margin-right: $hmargin; + img{ + max-width: 150px; + max-height: 100px; + } + } + } + .tagline{ + font-size: 1.35rem; + } +} + +.nav{ + @include flex-center; + @include small{ + border-top: 1px solid $medium-grey; + margin-top: $vmargin; + padding-top: $vmargin; + justify-content: space-between; + } + @include medium-large{ + .nav-user{ + margin-right: $hmargin; + } + } + .nav-new-post{ + @include small{ + + } + } +} + +.users-account-menu, .users-menu{ + .dropdown-toggle, .dropdown-toggle-inner{ + @include flex-center; + img{ + margin-right: 5px; + } + } +} + +.accounts-ui{ + padding: $vmargin; + min-width: 240px; + .buttons{ + .btn{ + display: block; + width: 100%; + text-align: center; + margin-bottom: $vmargin; + &:last-child{ + margin-bottom: 0; + } + } + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_newsletter.scss b/packages/example-forum/lib/stylesheets/_newsletter.scss new file mode 100644 index 00000000..e54fd2fd --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_newsletter.scss @@ -0,0 +1,36 @@ +.newsletter{ + background: $white; + border: 1px solid $light-border; + border-radius: 3px; + position: relative; + @include medium-large{ + @include flex-center; + justify-content: center; + } + padding: $vmargin $hmargin; + margin-bottom: $vmargin; + .newsletter-tagline{ + font-size: 1.35rem; + @include small{ + text-align: center; + margin-bottom: $vmargin; + } + @include medium-large{ + margin: 0 $hmargin 0 0; + } + } + .newsletter-button{ + // min-width: 100px; + } + .newsletter-form{ + @include flex-center; + .form-control{ + margin: 0 5px 0 0; + } + } + .newsletter-close{ + position: absolute; + top: 5px; + right: 5px; + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_other.scss b/packages/example-forum/lib/stylesheets/_other.scss new file mode 100644 index 00000000..b5f41cb8 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_other.scss @@ -0,0 +1,39 @@ +.footer{ + text-align: center; + margin-bottom: $vmargin; +} + +.search-form{ + .form-group{ + margin-bottom: 0; + } +} + +.modal-form-title{ + border-bottom: $border; + padding-bottom: $vmargin; + margin-bottom: $vmargin; + line-height: 1; +} + +.input-categories{ + .col-sm-9{ + display: flex; + flex-wrap: wrap; + .checkbox{ + width: 50%; + } + } +} + +.private{ + text-transform: uppercase; + font-size: $smaller-font; + color: $red; +} + +.dropdown-item.active{ + a{ + color: $white; + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_posts.scss b/packages/example-forum/lib/stylesheets/_posts.scss new file mode 100644 index 00000000..df62b04e --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_posts.scss @@ -0,0 +1,312 @@ + +////////////////////////////////////////////////////// +// Post List // +////////////////////////////////////////////////////// + +.posts-list-header{ + @include medium-large{ + @include flex-center; + justify-content: space-between; + } + margin-bottom: $vmargin; +} + +.posts-list-header-categories{ + @include small{ + margin: 0 auto; + margin-bottom: $vmargin; + text-align: center; + .btn-group{ + margin-right: 0; + width: 100%; + } + .dropdown-toggle{ + width: 100%; + } + } +} + +.posts-views{ + @include small{ + margin-bottom: $vmargin; + .btn-group{ + margin-right: 0; + width: 100%; + } + .dropdown-toggle{ + width: 100%; + } + } + a.btn{ + @include border; + // @include border-radius; + // padding: 3px 5px; + // margin-right: 5px; + &.posts-view-inactive{ + @include activeHover; + } + &.posts-view-active{ + background: $light-blue; + } + } +} + +.posts-list-header{ + @include medium-large{ + .search-form, .posts-list-header-categories button, .posts-views button{ + width: 160px; + } + } +} + +.posts-list-content{ + border-top: $border; + // padding-top: 15px; + // margin-top: 15px; + margin-bottom: $vmargin; +} + +.posts-load-more, .comments-load-more{ + display: block; + text-align: center; + @include border; + @include border-radius; + font-size: $large-font; + padding: 10px 20px; + @include activeHover; + width: 100%; + background: $white; + color: $active-color; +} + +.posts-load-more-loading{ + padding: 21px 0; +} + +////////////////////////////////////////////////////// +// Post Item // +////////////////////////////////////////////////////// + +.posts-item{ + @include medium-large{ + @include flex-center; + } + border-bottom: $border; + padding: $vmargin 5px; + // margin-bottom: 15px; + &.posts-sticky{ + background: $light-yellow; + + } +} + +.posts-item-vote{ + @include small{ + float: right; + } + @include medium-large{ + margin-right: $hmargin; + } +} + +.vote-button{ + @include border; + @include border-radius; + @include flex-center; + flex-direction: column; + justify-content: center; + padding: 3px 5px; + .icon{ + text-align: center; + display: block; + } + .vote-count{ + text-align: center; + display: block; + } + .voted &{ + color: $light-text; + } + &.show-downvote{ + .icon{ + &:hover{ + color: $active-color; + } + } + } + &.hide-downvote{ + @include activeHover; + } + &, &:active, &:hover{ + text-decoration: none; + } +} + +.posts-item-content{ + flex-grow: 1; +} + +.posts-item-title{ + @include small{ + margin-right: 40px; + font-size: $large-font; + margin-bottom: $vmargin/2; + } + @include medium-large{ + @include flex-center; + font-size: $larger-font; + margin-bottom: 5px; + } + .posts-item-title-link{ + @include small{ + display: block; + margin-bottom: $vmargin/2; + } + color: $black; + &, &:active, &:hover{ + text-decoration: none; + } + } +} + +.posts-categories{ + flex-wrap: wrap; + @include medium-large{ + margin-left: 5px; + } + @include flex-center; + a{ + display: block; + font-size: $small-font; + @include border; + @include border-radius; + margin-right: 5px; + padding: 2px 6px; + @include activeHover; + } +} + +.posts-item-meta{ + @include medium-large{ + @include flex-center; + } + font-size: $small-font; + .avatar{ + margin-right: 5px; + } + .users-name, .posts-item-date, .posts-item-comments, .posts-stats, .post-actions{ + margin-right: $hmargin; + } +} + +.posts-item-user{ + @include small{ + margin-bottom: $vmargin/2; + } + @include flex-center; +} + +.posts-item-date, .posts-item-comments{ + @include small{ + margin-bottom: $vmargin/2; + } + color: $medium-text; +} + +.posts-stats{ + @include small{ + margin-bottom: $vmargin/2; + display: inline-block; + } + @include border; + @include border-radius; + font-size: $smaller-font; + .posts-stats-item{ + border-right: $border; + padding: 2px 5px; + display: inline-block; + &:last-child{ + border: none; + } + } +} + +.post-actions{ + @include flex-center; +} + +.posts-commenters{ + @include small{ + display: none; + } + @include flex-center; +} + +.posts-commenters-avatars{ + display: flex; + .avatar{ + margin-right: $hmargin; + } +} + +.posts-thumbnail{ + @include small{ + float: left; + } + display: block; + margin-right: $hmargin; + img{ + display: block; + height: 50px; + width: auto; + } +} + +////////////////////////////////////////////////////// +// Post Page // +////////////////////////////////////////////////////// + +.posts-page{ + .posts-item{ + @include border-radius; + border: $border; + padding: $hmargin; + margin-bottom: $vmargin; + } +} + +.posts-page-body{ + font-size: $large-font; + margin-bottom: $hmargin; +} + +////////////////////////////////////////////////////// +// Other Elements // +////////////////////////////////////////////////////// + +.posts-day{ + margin-bottom: $vmargin * 2; + .posts-load-more{ + border: none; + padding: 0; + &:hover{ + background: inherit; + color: inherit; + border: inherit; + } + } + .posts-no-results{ + margin-top: $vmargin; + } +} + +.posts-edit-form-header{ + @include flex-center; + justify-content: space-between; +} + +.posts-edit-form-admin{ + @include flex-center; + justify-content: space-between; + margin-bottom: $vmargin * 2; +} diff --git a/packages/example-forum/lib/stylesheets/_spinner.scss b/packages/example-forum/lib/stylesheets/_spinner.scss new file mode 100644 index 00000000..772215af --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_spinner.scss @@ -0,0 +1,47 @@ + +.spinner { + height: 10px; + @include flex-center; + justify-content: center; + div { + width: 10px; + height: 10px; + background-color: rgba(0,0,0,0.35); + + border-radius: 100%; + display: inline-block; + -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; + animation: sk-bouncedelay 1.4s infinite ease-in-out both; + margin-right: $hmargin/2; + &:last-child{ + margin-right: 0; + } + } + &.white div{ + background-color: rgba(255,255,255,0.55); + } + .bounce1 { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; + } + + .bounce2 { + -webkit-animation-delay: -0.16s; + animation-delay: -0.16s; + } +} + +@-webkit-keyframes sk-bouncedelay { + 0%, 80%, 100% { -webkit-transform: scale(0) } + 40% { -webkit-transform: scale(1.0) } +} + +@keyframes sk-bouncedelay { + 0%, 80%, 100% { + -webkit-transform: scale(0); + transform: scale(0); + } 40% { + -webkit-transform: scale(1.0); + transform: scale(1.0); + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_users.scss b/packages/example-forum/lib/stylesheets/_users.scss new file mode 100644 index 00000000..0bde5aa4 --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_users.scss @@ -0,0 +1,38 @@ +.accounts-ui{ + .field{ + margin-bottom: $vmargin/2; + } +} + +.users-menu{ + .avatar{ + margin-right: 5px; + } +} + +.avatar-initials{ + background: $light-grey; + color: $dark-grey; + text-align: center; + border-radius: 100%; + text-decoration: none; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + span{ + display: block; + line-height: 1; + } +} + +.users-newsletter-settings { + @include flex-center; + margin-top: $vmargin; +} + +.log-in-message{ + h3{ + text-align: center; + } +} \ No newline at end of file diff --git a/packages/example-forum/lib/stylesheets/_variables.scss b/packages/example-forum/lib/stylesheets/_variables.scss new file mode 100644 index 00000000..c2aab7ba --- /dev/null +++ b/packages/example-forum/lib/stylesheets/_variables.scss @@ -0,0 +1,32 @@ +$hmargin: 10px; +$vmargin: 15px; + +$smaller-font: 0.8rem; +$small-font: 0.9rem; +$medium-font: 1rem; +$large-font: 1.25rem; +$larger-font: 1.35rem; + +$border: 1px solid $light-border; + +@mixin activeHover{ + &:hover{ + background: $active-color; + color: $white; + border-color: $active-color; + text-decoration: none; + } +} + +@mixin flex-center{ + display: flex; + align-items: center; +} + +@mixin border-radius{ + border-radius: .25rem; +} +@mixin border{ + border: $border; +} + diff --git a/packages/example-forum/lib/stylesheets/bootstrap.css b/packages/example-forum/lib/stylesheets/bootstrap.css new file mode 100644 index 00000000..c05fa4ec --- /dev/null +++ b/packages/example-forum/lib/stylesheets/bootstrap.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.2.1 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e")}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E")}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-right{right:0;left:auto}}.dropdown-menu-left{right:auto;left:0}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:first-child{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.dropdown-item:last-child{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(2.875rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.8125rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;color:inherit;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card{overflow:hidden}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion .card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);border-radius:.25rem;box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media screen and (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-3by4::before{padding-top:133.333333%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ diff --git a/packages/example-forum/lib/stylesheets/main.scss b/packages/example-forum/lib/stylesheets/main.scss new file mode 100644 index 00000000..12f2da8a --- /dev/null +++ b/packages/example-forum/lib/stylesheets/main.scss @@ -0,0 +1,52 @@ +// @import '{}/node_modules/bootstrap/scss/bootstrap.scss'; + +@import "breakpoints"; +@import "colors"; +@import "variables"; +@import "spinner"; +@import "global"; + +@import "accounts"; +@import "categories"; +@import "cheatsheet"; +@import "comments"; +@import "header"; +@import "newsletter"; +@import "other"; +@import "posts"; +@import "users"; + +.new-post-button{ + margin-bottom: 15px; +} + +code{ + font-family: monospace; + background: #fafafa; + border: 1px solid #ddd; + padding: 3px 6px; +} + +.avatar{ + height: 20px; + width: 20px; + display: block; + a, span, img{ + display: block; + border-radius: 100%; + height: 100%; + widows: 100%; + } +} + +.sr-only{ + display: none; +} + +.upvoted .upvote{ + opacity: 0.3; +} + +.posts-day h2{ + font-weight: bold; +} \ No newline at end of file diff --git a/packages/example-forum/package.js b/packages/example-forum/package.js new file mode 100644 index 00000000..78e95908 --- /dev/null +++ b/packages/example-forum/package.js @@ -0,0 +1,68 @@ +Package.describe({ + name: "example-forum", + summary: "Vulcan forum package", + version: '1.12.12', + git: "https://github.com/VulcanJS/Vulcan.git" +}); + +Package.onUse(function (api) { + + api.versionsFrom('METEOR@1.5.2'); + + api.use([ + + 'promise', + 'fourseven:scss@4.5.0', + + // vulcan core + 'vulcan:core@1.12.16', + + // vulcan packages + 'vulcan:ui-bootstrap@1.12.16', + 'vulcan:voting@1.12.16', + 'vulcan:accounts@1.12.16', + 'vulcan:email@1.12.16', + 'vulcan:forms@1.12.16', + 'vulcan:newsletter@1.12.16', + 'vulcan:events@1.12.16', + 'vulcan:embed@1.12.16', + 'vulcan:admin@1.12.16', + + ]); + + api.addAssets([ + 'lib/assets/images/stackoverflow.png', + 'lib/assets/images/telescope.png', + ], ['client']); + + api.addAssets([ + 'lib/assets/content/read_this_first.md', + 'lib/assets/content/deploying.md', + 'lib/assets/content/customizing.md', + 'lib/assets/content/getting_help.md', + 'lib/assets/content/removing_getting_started_posts.md', + + 'lib/server/email/templates/common/test.handlebars', + 'lib/server/email/templates/common/wrapper.handlebars', + 'lib/server/email/templates/comments/newComment.handlebars', + 'lib/server/email/templates/comments/newReply.handlebars', + 'lib/server/email/templates/posts/newPendingPost.handlebars', + 'lib/server/email/templates/posts/newPost.handlebars', + 'lib/server/email/templates/posts/postApproved.handlebars', + 'lib/server/email/templates/users/accountApproved.handlebars', + 'lib/server/email/templates/users/newUser.handlebars', + 'lib/server/email/templates/newsletter/newsletter.handlebars', + 'lib/server/email/templates/newsletter/newsletterConfirmation.handlebars', + 'lib/server/email/templates/newsletter/postItem.handlebars', + + ], ['server']); + + api.addFiles([ + // 'lib/stylesheets/bootstrap.css', + 'lib/stylesheets/main.scss' + ], ['client']); + + api.mainModule("lib/server/main.js", "server"); + api.mainModule("lib/client/main.js", "client"); + +}); diff --git a/packages/networkcanvas-styles/lib/stylesheets/main.scss b/packages/networkcanvas-styles/lib/stylesheets/main.scss index 592a0277..a7d9cb76 100644 --- a/packages/networkcanvas-styles/lib/stylesheets/main.scss +++ b/packages/networkcanvas-styles/lib/stylesheets/main.scss @@ -1,3 +1,6 @@ +//@import "_bootstrap.scss"; + + @import "./breakpoints"; @import "./colors"; @import "./variables"; @@ -23,10 +26,7 @@ code { padding: 3px 6px; } -blockquote { - font-size: inherit; // Override bootstrap -} .sr-only{ - display: none; -} + //display: none; +} \ No newline at end of file diff --git a/packages/networkcanvas-styles/lib/stylesheets/stylesheets b/packages/networkcanvas-styles/lib/stylesheets/stylesheets new file mode 120000 index 00000000..bd46ea31 --- /dev/null +++ b/packages/networkcanvas-styles/lib/stylesheets/stylesheets @@ -0,0 +1 @@ +../node_modules/boostrap-sass/assets/stylesheets \ No newline at end of file diff --git a/packages/networkcanvas-styles/package.js b/packages/networkcanvas-styles/package.js index f54c5c61..506a3651 100644 --- a/packages/networkcanvas-styles/package.js +++ b/packages/networkcanvas-styles/package.js @@ -9,8 +9,8 @@ Package.onUse(function (api) { api.versionsFrom(['METEOR@1.0']); api.use([ - 'vulcan:core@1.7.0', - 'fourseven:scss@4.5.0', + 'vulcan:core', + 'fourseven:scss@4.10.0', ]); api.addFiles([ diff --git a/packages/networkcanvas-styles/scss-config.json b/packages/networkcanvas-styles/scss-config.json new file mode 100644 index 00000000..2cd9d14c --- /dev/null +++ b/packages/networkcanvas-styles/scss-config.json @@ -0,0 +1,9 @@ +{ + "enableAutoprefixer": true, + "outputStyle": "compressed", + "sourceComments": true, + "sourceMap": true, + "includePaths": [ +// "node_modules/bootstrap-sass/assets/stylesheets" + ] +} \ No newline at end of file diff --git a/packages/networkcanvas-styles/scss.json b/packages/networkcanvas-styles/scss.json deleted file mode 100644 index 6dbafe64..00000000 --- a/packages/networkcanvas-styles/scss.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "enableAutoprefixer": true, - "outputStyle": "compressed", - "sourceComments": true, - "sourceMap": true -} \ No newline at end of file diff --git a/packages/vulcan-accounts/README.md b/packages/vulcan-accounts/README.md deleted file mode 100644 index 8271d92a..00000000 --- a/packages/vulcan-accounts/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan accounts package (forked from https://github.com/studiointeract/accounts-ui) \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/accounts_ui.js b/packages/vulcan-accounts/imports/accounts_ui.js deleted file mode 100755 index 3d9fb17d..00000000 --- a/packages/vulcan-accounts/imports/accounts_ui.js +++ /dev/null @@ -1,231 +0,0 @@ -import { Accounts } from 'meteor/accounts-base'; -import { - redirect, - validatePassword, - validateEmail, - validateUsername, -} from './helpers.js'; - -/** - * @summary Accounts UI - * @namespace - * @memberOf Accounts - */ -Accounts.ui = {}; - -Accounts.ui._options = { - requestPermissions: [], - requestOfflineToken: {}, - forceApprovalPrompt: {}, - requireEmailVerification: false, - passwordSignupFields: 'USERNAME_AND_EMAIL', - minimumPasswordLength: 7, - loginPath: '/', - signUpPath: null, - resetPasswordPath: null, - profilePath: '/', - changePasswordPath: null, - homeRoutePath: '/', - onSubmitHook: () => {}, - onPreSignUpHook: () => new Promise(resolve => resolve()), - onPostSignUpHook: () => {}, - onEnrollAccountHook: () => redirect(`${Accounts.ui._options.loginPath}`), - onResetPasswordHook: () => redirect(`${Accounts.ui._options.loginPath}`), - onVerifyEmailHook: () => redirect(`${Accounts.ui._options.profilePath}`), - onSignedInHook: () => redirect(`${Accounts.ui._options.homeRoutePath}`), - onSignedOutHook: () => redirect(`${Accounts.ui._options.homeRoutePath}`), - emailPattern: new RegExp('[^@]+@[^@\.]{2,}\.[^\.@]+'), -}; - -/** - * @summary Configure the behavior of [``](#react-accounts-ui). - * @anywhere - * @param {Object} options - * @param {Object} options.requestPermissions Which [permissions](#requestpermissions) to request from the user for each external service. - * @param {Object} options.requestOfflineToken To ask the user for permission to act on their behalf when offline, map the relevant external service to `true`. Currently only supported with Google. See [Meteor.loginWithExternalService](#meteor_loginwithexternalservice) for more details. - * @param {Object} options.forceApprovalPrompt If true, forces the user to approve the app's permissions, even if previously approved. Currently only supported with Google. - * @param {String} options.passwordSignupFields Which fields to display in the user creation form. One of '`USERNAME_AND_EMAIL`' (default), '`USERNAME_AND_OPTIONAL_EMAIL`', '`USERNAME_ONLY`', '`EMAIL_ONLY`'. - */ -Accounts.ui.config = function(options) { - // validate options keys - const VALID_KEYS = [ - 'passwordSignupFields', - 'requestPermissions', - 'requestOfflineToken', - 'forbidClientAccountCreation', - 'requireEmailVerification', - 'minimumPasswordLength', - 'loginPath', - 'signUpPath', - 'resetPasswordPath', - 'profilePath', - 'changePasswordPath', - 'homeRoutePath', - 'onSubmitHook', - 'onPreSignUpHook', - 'onPostSignUpHook', - 'onEnrollAccountHook', - 'onResetPasswordHook', - 'onVerifyEmailHook', - 'onSignedInHook', - 'onSignedOutHook', - 'validateField', - 'emailPattern', - ]; - - _.each(_.keys(options), function (key) { - if (!_.contains(VALID_KEYS, key)) - throw new Error("Accounts.ui.config: Invalid key: " + key); - }); - - // Deal with `passwordSignupFields` - if (options.passwordSignupFields) { - if (_.contains([ - "USERNAME_AND_EMAIL", - "USERNAME_AND_OPTIONAL_EMAIL", - "USERNAME_ONLY", - "EMAIL_ONLY", - ], options.passwordSignupFields)) { - Accounts.ui._options.passwordSignupFields = options.passwordSignupFields; - } - else { - throw new Error("Accounts.ui.config: Invalid option for `passwordSignupFields`: " + options.passwordSignupFields); - } - } - - // Deal with `requestPermissions` - if (options.requestPermissions) { - _.each(options.requestPermissions, function (scope, service) { - if (Accounts.ui._options.requestPermissions[service]) { - throw new Error("Accounts.ui.config: Can't set `requestPermissions` more than once for " + service); - } - else if (!(scope instanceof Array)) { - throw new Error("Accounts.ui.config: Value for `requestPermissions` must be an array"); - } - else { - Accounts.ui._options.requestPermissions[service] = scope; - } - }); - } - - // Deal with `requestOfflineToken` - if (options.requestOfflineToken) { - _.each(options.requestOfflineToken, function (value, service) { - if (service !== 'google') - throw new Error("Accounts.ui.config: `requestOfflineToken` only supported for Google login at the moment."); - - if (Accounts.ui._options.requestOfflineToken[service]) { - throw new Error("Accounts.ui.config: Can't set `requestOfflineToken` more than once for " + service); - } - else { - Accounts.ui._options.requestOfflineToken[service] = value; - } - }); - } - - // Deal with `forceApprovalPrompt` - if (options.forceApprovalPrompt) { - _.each(options.forceApprovalPrompt, function (value, service) { - if (service !== 'google') - throw new Error("Accounts.ui.config: `forceApprovalPrompt` only supported for Google login at the moment."); - - if (Accounts.ui._options.forceApprovalPrompt[service]) { - throw new Error("Accounts.ui.config: Can't set `forceApprovalPrompt` more than once for " + service); - } - else { - Accounts.ui._options.forceApprovalPrompt[service] = value; - } - }); - } - - // Deal with `requireEmailVerification` - if (options.requireEmailVerification) { - if (typeof options.requireEmailVerification != 'boolean') { - throw new Error(`Accounts.ui.config: "requireEmailVerification" not a boolean`); - } - else { - Accounts.ui._options.requireEmailVerification = options.requireEmailVerification; - } - } - - // Deal with `minimumPasswordLength` - if (options.minimumPasswordLength) { - if (typeof options.minimumPasswordLength != 'number') { - throw new Error(`Accounts.ui.config: "minimumPasswordLength" not a number`); - } - else { - Accounts.ui._options.minimumPasswordLength = options.minimumPasswordLength; - } - } - - // Deal with the hooks. - for (let hook of [ - 'onSubmitHook', - 'onPreSignUpHook', - 'onPostSignUpHook', - ]) { - if (options[hook]) { - if (typeof options[hook] != 'function') { - throw new Error(`Accounts.ui.config: "${hook}" not a function`); - } - else { - Accounts.ui._options[hook] = options[hook]; - } - } - } - - // Deal with pattern. - for (let hook of [ - 'emailPattern', - ]) { - if (options[hook]) { - if (!(options[hook] instanceof RegExp)) { - throw new Error(`Accounts.ui.config: "${hook}" not a Regular Expression`); - } - else { - Accounts.ui._options[hook] = options[hook]; - } - } - } - - // deal with the paths. - for (let path of [ - 'loginPath', - 'signUpPath', - 'resetPasswordPath', - 'profilePath', - 'changePasswordPath', - 'homeRoutePath' - ]) { - if (typeof options[path] !== 'undefined') { - if (options[path] !== null && typeof options[path] !== 'string') { - throw new Error(`Accounts.ui.config: ${path} is not a string or null`); - } - else { - Accounts.ui._options[path] = options[path]; - } - } - } - - // deal with redirect hooks. - for (let hook of [ - 'onEnrollAccountHook', - 'onResetPasswordHook', - 'onVerifyEmailHook', - 'onSignedInHook', - 'onSignedOutHook']) { - if (options[hook]) { - if (typeof options[hook] == 'function') { - Accounts.ui._options[hook] = options[hook]; - } - else if (typeof options[hook] == 'string') { - Accounts.ui._options[hook] = () => redirect(options[hook]); - } - else { - throw new Error(`Accounts.ui.config: "${hook}" not a function or an absolute or relative path`); - } - } - } -}; - -export default Accounts; diff --git a/packages/vulcan-accounts/imports/api/server/servicesListPublication.js b/packages/vulcan-accounts/imports/api/server/servicesListPublication.js deleted file mode 100755 index 31b70341..00000000 --- a/packages/vulcan-accounts/imports/api/server/servicesListPublication.js +++ /dev/null @@ -1,13 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { getLoginServices } from '../../helpers.js'; - -Meteor.publish('servicesList', function() { - let services = getLoginServices(); - if (Package['accounts-password']) { - services.push({name: 'password'}); - } - let fields = {}; - // Publish the existing services for a user, only name or nothing else. - services.forEach(service => fields[`services.${service.name}.name`] = 1); - return Meteor.users.find({ _id: this.userId }, { fields: fields}); -}); diff --git a/packages/vulcan-accounts/imports/components.js b/packages/vulcan-accounts/imports/components.js deleted file mode 100644 index a7403d2c..00000000 --- a/packages/vulcan-accounts/imports/components.js +++ /dev/null @@ -1,11 +0,0 @@ -import './ui/components/Button.jsx'; -import './ui/components/Buttons.jsx'; -import './ui/components/Field.jsx'; -import './ui/components/Fields.jsx'; -import './ui/components/Form.jsx'; -import './ui/components/FormMessage.jsx'; -import './ui/components/FormMessages.jsx'; -import './ui/components/LoginForm.jsx'; -import './ui/components/PasswordOrService.jsx'; -import './ui/components/SocialButtons.jsx'; -import './ui/components/ResetPassword.jsx'; diff --git a/packages/vulcan-accounts/imports/helpers.js b/packages/vulcan-accounts/imports/helpers.js deleted file mode 100755 index dd7d3340..00000000 --- a/packages/vulcan-accounts/imports/helpers.js +++ /dev/null @@ -1,118 +0,0 @@ -let browserHistory -try { browserHistory = require('react-router').browserHistory } catch(e) {} -export const loginButtonsSession = Accounts._loginButtonsSession; -export const STATES = { - SIGN_IN: Symbol('SIGN_IN'), - SIGN_UP: Symbol('SIGN_UP'), - PROFILE: Symbol('PROFILE'), - PASSWORD_CHANGE: Symbol('PASSWORD_CHANGE'), - PASSWORD_RESET: Symbol('PASSWORD_RESET'), - ENROLL_ACCOUNT: Symbol('ENROLL_ACCOUNT') -}; - -export function getLoginServices() { - // First look for OAuth services. - const services = Package['accounts-oauth'] ? Accounts.oauth.serviceNames() : []; - - // Be equally kind to all login services. This also preserves - // backwards-compatibility. - services.sort(); - - return _.map(services, function(name){ - return {name: name}; - }); -}; -// Export getLoginServices using old style globals for accounts-base which -// requires it. -this.getLoginServices = getLoginServices; - -export function hasPasswordService() { - // First look for OAuth services. - return !!Package['accounts-password']; -}; - -export function loginResultCallback(service, err) { - if (!err) { - - } else if (err instanceof Accounts.LoginCancelledError) { - // do nothing - } else if (err instanceof ServiceConfiguration.ConfigError) { - - } else { - //loginButtonsSession.errorMessage(err.reason || "Unknown error"); - } - - if (Meteor.isClient) { - if (typeof redirect === 'string'){ - window.location.href = '/'; - } - - if (typeof service === 'function'){ - service(); - } - } -}; - -export function passwordSignupFields() { - return Accounts.ui._options.passwordSignupFields || "USERNAME_AND_EMAIL"; -}; - -export function validateEmail(email, showMessage, clearMessage) { - if (passwordSignupFields() === "USERNAME_AND_OPTIONAL_EMAIL" && email === '') { - return true; - } - if (Accounts.ui._options.emailPattern.test(email)) { - return true; - } else if (!email || email.length === 0) { - showMessage("accounts.error_email_required", 'warning', false, 'email'); - return false; - } else { - showMessage("accounts.error_invalid_email", 'warning', false, 'email'); - return false; - } -} - -export function validatePassword(password = '', showMessage, clearMessage){ - if (password.length >= Accounts.ui._options.minimumPasswordLength) { - return true; - } else { - const errMsg = "accounts.error_minchar" - showMessage(errMsg, 'warning', false, 'password'); - return false; - } -}; - -export function validateUsername(username, showMessage, clearMessage, formState) { - if ( username ) { - return true; - } else { - const fieldName = (passwordSignupFields() === 'USERNAME_ONLY' || formState === STATES.SIGN_UP) ? 'username' : 'usernameOrEmail'; - showMessage("accounts.error_username_required", 'warning', false, fieldName); - return false; - } -} - -export function redirect(redirect) { - if (Meteor.isClient) { - if (window.history) { - // Run after all app specific redirects, i.e. to the login screen. - Meteor.setTimeout(() => { - if (Package['kadira:flow-router']) { - Package['kadira:flow-router'].FlowRouter.go(redirect); - } else if (Package['kadira:flow-router-ssr']) { - Package['kadira:flow-router-ssr'].FlowRouter.go(redirect); - } else if (browserHistory) { - browserHistory.push(redirect); - } else { - window.history.pushState( {} , 'redirect', redirect ); - } - }, 100); - } - } -} - -export function capitalize(string) { - return string.replace(/\-/, ' ').split(' ').map(word => { - return word.charAt(0).toUpperCase() + word.slice(1); - }).join(' '); -} diff --git a/packages/vulcan-accounts/imports/login_session.js b/packages/vulcan-accounts/imports/login_session.js deleted file mode 100755 index 26d38c17..00000000 --- a/packages/vulcan-accounts/imports/login_session.js +++ /dev/null @@ -1,107 +0,0 @@ -import {Accounts} from 'meteor/accounts-base'; -import { - STATES, - loginResultCallback, - getLoginServices -} from './helpers.js'; - -const VALID_KEYS = [ - 'dropdownVisible', - - // XXX consider replacing these with one key that has an enum for values. - 'inSignupFlow', - 'inForgotPasswordFlow', - 'inChangePasswordFlow', - 'inMessageOnlyFlow', - - 'errorMessage', - 'infoMessage', - - // dialogs with messages (info and error) - 'resetPasswordToken', - 'enrollAccountToken', - 'justVerifiedEmail', - 'justResetPassword', - - 'configureLoginServiceDialogVisible', - 'configureLoginServiceDialogServiceName', - 'configureLoginServiceDialogSaveDisabled', - 'configureOnDesktopVisible' -]; - -export const validateKey = function (key) { - if (!_.contains(VALID_KEYS, key)) - throw new Error("Invalid key in loginButtonsSession: " + key); -}; - -export const KEY_PREFIX = "Meteor.loginButtons."; - -// XXX This should probably be package scope rather than exported -// (there was even a comment to that effect here from before we had -// namespacing) but accounts-ui-viewer uses it, so leave it as is for -// now -Accounts._loginButtonsSession = { - set: function(key, value) { - validateKey(key); - if (_.contains(['errorMessage', 'infoMessage'], key)) - throw new Error("Don't set errorMessage or infoMessage directly. Instead, use errorMessage() or infoMessage()."); - - this._set(key, value); - }, - - _set: function(key, value) { - Session.set(KEY_PREFIX + key, value); - }, - - get: function(key) { - validateKey(key); - return Session.get(KEY_PREFIX + key); - } -}; - -if (Meteor.isClient){ - // In the login redirect flow, we'll have the result of the login - // attempt at page load time when we're redirected back to the - // application. Register a callback to update the UI (i.e. to close - // the dialog on a successful login or display the error on a failed - // login). - // - Accounts.onPageLoadLogin(function (attemptInfo) { - // Ignore if we have a left over login attempt for a service that is no longer registered. - if (_.contains(_.pluck(getLoginServices(), "name"), attemptInfo.type)) - loginResultCallback(attemptInfo.type, attemptInfo.error); - }); - - let doneCallback; - - Accounts.onResetPasswordLink(function (token, done) { - Accounts._loginButtonsSession.set('resetPasswordToken', token); - Session.set(KEY_PREFIX + 'state', 'resetPasswordToken'); - doneCallback = done; - - Accounts.ui._options.onResetPasswordHook(); - }); - - Accounts.onEnrollmentLink(function (token, done) { - Accounts._loginButtonsSession.set('enrollAccountToken', token); - Session.set(KEY_PREFIX + 'state', 'enrollAccountToken'); - doneCallback = done; - - Accounts.ui._options.onEnrollAccountHook(); - }); - - Accounts.onEmailVerificationLink(function (token, done) { - Accounts.verifyEmail(token, function (error) { - if (! error) { - Accounts._loginButtonsSession.set('justVerifiedEmail', true); - Session.set(KEY_PREFIX + 'state', 'justVerifiedEmail'); - Accounts.ui._options.onSignedInHook(); - } - else { - Accounts.ui._options.onVerifyEmailHook(); - } - - done(); - }); - }); -} diff --git a/packages/vulcan-accounts/imports/routes.js b/packages/vulcan-accounts/imports/routes.js deleted file mode 100644 index 11d6def9..00000000 --- a/packages/vulcan-accounts/imports/routes.js +++ /dev/null @@ -1,3 +0,0 @@ -import { addRoute } from 'meteor/vulcan:core'; - -addRoute({name: 'resetPassword', path: '/reset-password/:token', componentName: 'AccountsResetPassword'}); diff --git a/packages/vulcan-accounts/imports/ui/components/Button.jsx b/packages/vulcan-accounts/imports/ui/components/Button.jsx deleted file mode 100755 index 5de8219b..00000000 --- a/packages/vulcan-accounts/imports/ui/components/Button.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import Button from 'react-bootstrap/lib/Button'; -import { registerComponent } from 'meteor/vulcan:core'; - -export class AccountsButton extends PureComponent { - render () { - - const { - label, - href = null, - type, - disabled = false, - className, - onClick - } = this.props; - - return type === 'link' ? - { label } : - ; - } -} -AccountsButton.propTypes = { - onClick: PropTypes.func -}; - -registerComponent('AccountsButton', AccountsButton); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/Buttons.jsx b/packages/vulcan-accounts/imports/ui/components/Buttons.jsx deleted file mode 100755 index 60b0f8f1..00000000 --- a/packages/vulcan-accounts/imports/ui/components/Buttons.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import './Button.jsx'; -import { Components, registerComponent } from 'meteor/vulcan:core'; - -export class Buttons extends React.Component { - render () { - let { buttons = {}, className = "buttons" } = this.props; - return ( -
- {Object.keys(buttons).map((id, i) => - - )} -
- ); - } -}; - -registerComponent('AccountsButtons', Buttons); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/Field.jsx b/packages/vulcan-accounts/imports/ui/components/Field.jsx deleted file mode 100755 index 2109be9b..00000000 --- a/packages/vulcan-accounts/imports/ui/components/Field.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import FormControl from 'react-bootstrap/lib/FormControl'; -import { registerComponent } from 'meteor/vulcan:core'; - -export class AccountsField extends PureComponent { - constructor(props) { - super(props); - this.state = { - mount: true - }; - } - - triggerUpdate() { - // Trigger an onChange on inital load, to support browser prefilled values. - const { onChange } = this.props; - if (this.input && onChange) { - onChange({ target: { value: this.input.value } }); - } - } - - componentDidMount() { - this.triggerUpdate(); - } - - componentDidUpdate(prevProps) { - // Re-mount component so that we don't expose browser prefilled passwords if the component was - // a password before and now something else. - if (prevProps.id !== this.props.id) { - this.setState({mount: false}); - } - else if (!this.state.mount) { - this.setState({mount: true}); - this.triggerUpdate(); - } - } - - render() { - const { - id, - hint, - label, - type = 'text', - onChange, - required = false, - className = "field", - defaultValue = "", - message, - } = this.props; - const { mount = true } = this.state; - if (type == 'notice') { - return
{ label }
; - } - return mount ? ( -
- { this.input = ref; }} onChange={ onChange } placeholder={ hint } defaultValue={ defaultValue } /> - {message && ( - - {message.message} - )} -
- ) : null; - } -} -AccountsField.propTypes = { - onChange: PropTypes.func -}; - -registerComponent('AccountsField', AccountsField) \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/Fields.jsx b/packages/vulcan-accounts/imports/ui/components/Fields.jsx deleted file mode 100755 index bb42212a..00000000 --- a/packages/vulcan-accounts/imports/ui/components/Fields.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { Components, registerComponent } from 'meteor/vulcan:core'; - -export class AccountsFields extends React.Component { - render () { - let { fields = {}, className = "fields" } = this.props; - return ( -
- {Object.keys(fields).map((id, i) => - - )} -
- ); - } -} - -registerComponent('AccountsFields', AccountsFields); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/Form.jsx b/packages/vulcan-accounts/imports/ui/components/Form.jsx deleted file mode 100755 index cb701d21..00000000 --- a/packages/vulcan-accounts/imports/ui/components/Form.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { Components, registerComponent } from 'meteor/vulcan:core'; - -export class AccountsForm extends PureComponent { - componentDidMount() { - let form = this.form; - if (form) { - form.addEventListener('submit', (e) => { - e.preventDefault(); - }); - } - } - - render() { - const { - hasPasswordService, - oauthServices, - fields, - buttons, - error, - messages, - ready = true, - className - } = this.props; - const _className = classnames("accounts-ui", { - "ready": ready, - }); - return ( -
this.form = ref} - className={_className} - noValidate - > - - - - - - - ); - } -} -AccountsForm.propTypes = { - oauthServices: PropTypes.object, - fields: PropTypes.object.isRequired, - buttons: PropTypes.object.isRequired, - error: PropTypes.string, - ready: PropTypes.bool -}; - -registerComponent('AccountsForm', AccountsForm); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx b/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx deleted file mode 100755 index db72b174..00000000 --- a/packages/vulcan-accounts/imports/ui/components/FormMessage.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { registerComponent } from 'meteor/vulcan:core'; - -export class AccountsFormMessage extends React.Component { - render () { - let { message, type, className = "message", style = {} } = this.props; - message = _.isObject(message) ? message.message : message; // If message is object, then try to get message from it - return message ? ( -
{ message }
- ) : null; - } -} - -registerComponent('AccountsFormMessage', AccountsFormMessage); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/FormMessages.jsx b/packages/vulcan-accounts/imports/ui/components/FormMessages.jsx deleted file mode 100755 index b17afb88..00000000 --- a/packages/vulcan-accounts/imports/ui/components/FormMessages.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { Component } from 'react'; -import { Components, registerComponent } from 'meteor/vulcan:core'; - -export class AccountsFormMessages extends Component { - render () { - const { messages = [], className = "messages", style = {} } = this.props; - return messages.length > 0 && ( -
- {messages - .filter(message => !('field' in message)) - .map(({ message, type }, i) => - - )} -
- ); - } -} - -registerComponent('AccountsFormMessages', AccountsFormMessages); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/LoginForm.jsx b/packages/vulcan-accounts/imports/ui/components/LoginForm.jsx deleted file mode 100755 index 0492f9c1..00000000 --- a/packages/vulcan-accounts/imports/ui/components/LoginForm.jsx +++ /dev/null @@ -1,940 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Tracker from 'tracker-component'; -import { Accounts } from 'meteor/accounts-base'; -import { KEY_PREFIX } from '../../login_session.js'; -import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import { intlShape } from 'meteor/vulcan:i18n'; -import { withApollo } from 'react-apollo'; - -import { - STATES, - passwordSignupFields, - validateEmail, - validatePassword, - validateUsername, - loginResultCallback, - getLoginServices, - hasPasswordService, - capitalize -} from '../../helpers.js'; - -export class AccountsLoginForm extends Tracker.Component { - constructor(props) { - super(props); - - if (props.formState === STATES.SIGN_IN && Package['accounts-password']) { - console.warn('Do not force the state to SIGN_IN on Accounts.ui.LoginForm, it will make it impossible to reset password in your app, this state is also the default state if logged out, so no need to force it.'); - } - - const currentUser = props.currentUser; - - const resetStoreAndThen = hook => { - return () => { - props.client.resetStore(); - hook(); - } - } - - // Set inital state. - this.state = { - messages: [], - waiting: true, - formState: props.formState ? props.formState : (currentUser ? STATES.PROFILE : STATES.SIGN_IN), - onSubmitHook: props.onSubmitHook || Accounts.ui._options.onSubmitHook, - onSignedInHook: resetStoreAndThen(props.onSignedInHook || Accounts.ui._options.onSignedInHook), - onSignedOutHook: resetStoreAndThen(props.onSignedOutHook || Accounts.ui._options.onSignedOutHook), - onPreSignUpHook: props.onPreSignUpHook || Accounts.ui._options.onPreSignUpHook, - onPostSignUpHook: resetStoreAndThen(props.onPostSignUpHook || Accounts.ui._options.onPostSignUpHook), - }; - } - - componentDidMount() { - let changeState = Session.get(KEY_PREFIX + 'state'); - switch (changeState) { - case 'enrollAccountToken': - this.setState(prevState => ({ - formState: STATES.ENROLL_ACCOUNT - })); - Session.set(KEY_PREFIX + 'state', null); - break; - case 'resetPasswordToken': - this.setState(prevState => ({ - formState: STATES.PASSWORD_CHANGE - })); - Session.set(KEY_PREFIX + 'state', null); - break; - - case 'justVerifiedEmail': - this.setState(prevState => ({ - formState: STATES.PROFILE - })); - Session.set(KEY_PREFIX + 'state', null); - break; - } - - // Add default field values once the form did mount on the client - this.setState(prevState => ({ - ...this.getDefaultFieldValues(), - })); - - // Listen for the user to login/logout. - this.autorun(() => { - - // Add the services list to the user. - this.subscribe('servicesList'); - this.setState({ - currentUser: Accounts.user(), - waiting: !Accounts.loginServicesConfigured() - }); - - }); - } - - componentWillReceiveProps(nextProps, nextContext) { - if (nextProps.formState && nextProps.formState !== this.state.formState) { - this.setState({ - formState: nextProps.formState, - ...this.getDefaultFieldValues(), - }); - } - } - - componentDidUpdate(prevProps, prevState) { - if (typeof this.props.currentUser !== 'undefined') { - if (!prevProps.currentUser !== !this.props.currentUser) { - this.setState({ - formState: this.props.currentUser ? STATES.PROFILE : STATES.SIGN_IN - }); - } - - const loggingInMessage = 'accounts.logging_in'; - - if (this.state.formState == STATES.PROFILE) { - if (!this.props.currentUser && this.state.messages.length === 0) { - // this.showMessage(loggingInMessage); // don't show logging in message for now - } else if (this.props.currentUser && - this.state.messages.find(({ message }) => message === loggingInMessage)) { - this.clearMessage(loggingInMessage); - } - } else if (prevState.formState == STATES.PROFILE && - this.state.messages.find(({ message }) => message === loggingInMessage)) { - this.clearMessage(loggingInMessage); - } - } else { - if (!prevState.currentUser !== !this.state.currentUser) { - this.setState({ - formState: this.state.currentUser ? STATES.PROFILE : STATES.SIGN_IN - }); - } - } - } - - validateField(field, value) { - const { formState } = this.state; - switch(field) { - case 'email': - return validateEmail(value, - this.showMessage.bind(this), - this.clearMessage.bind(this), - ); - case 'password': - return validatePassword(value, - this.showMessage.bind(this), - this.clearMessage.bind(this), - ); - case 'username': - return validateUsername(value, - this.showMessage.bind(this), - this.clearMessage.bind(this), - formState, - ); - } - } - - getUsernameOrEmailField() { - return { - id: 'usernameOrEmail', - hint: this.context.intl.formatMessage({id: 'accounts.enter_username_or_email'}), - label: this.context.intl.formatMessage({id: 'accounts.username_or_email'}), - required: true, - defaultValue: this.state.currentUsername || "", - onChange: this.handleChange.bind(this, 'usernameOrEmail'), - message: this.getMessageForField('usernameOrEmail'), - }; - } - - getUsernameField() { - return { - id: 'username', - hint: this.context.intl.formatMessage({id: 'accounts.enter_username'}), - label: this.context.intl.formatMessage({id: 'accounts.username'}), - required: true, - defaultValue: this.state.currentUsername || "", - onChange: this.handleChange.bind(this, 'username'), - message: this.getMessageForField('username'), - }; - } - - getEmailField() { - return { - id: 'email', - hint: this.context.intl.formatMessage({id: 'accounts.enter_email'}), - label: this.context.intl.formatMessage({id: 'accounts.email'}), - type: 'email', - required: true, - defaultValue: this.state.email || "", - onChange: this.handleChange.bind(this, 'email'), - message: this.getMessageForField('email'), - }; - } - - getPasswordField() { - return { - id: 'password', - hint: this.context.intl.formatMessage({id: 'accounts.enter_password'}), - label: this.context.intl.formatMessage({id: 'accounts.password'}), - type: 'password', - required: true, - defaultValue: this.state.password || "", - onChange: this.handleChange.bind(this, 'password'), - message: this.getMessageForField('password'), - }; - } - - getSetPasswordField() { - return { - id: 'newPassword', - hint: this.context.intl.formatMessage({id: 'accounts.enter_password'}), - label: this.context.intl.formatMessage({id: 'accounts.choose_password'}), - type: 'password', - required: true, - onChange: this.handleChange.bind(this, 'newPassword') - }; - } - - getNewPasswordField() { - return { - id: 'newPassword', - hint: this.context.intl.formatMessage({id: 'accounts.enter_new_password'}), - label: this.context.intl.formatMessage({id: 'accounts.new_password'}), - type: 'password', - required: true, - onChange: this.handleChange.bind(this, 'newPassword'), - message: this.getMessageForField('newPassword'), - }; - } - - handleChange(field, evt) { - let value = evt.target.value; - switch (field) { - case 'password': break; - default: - value = value.trim(); - break; - } - this.setState({ [field]: value }); - this.setDefaultFieldValues({ [field]: value }); - } - - fields() { - const loginFields = []; - const { formState } = this.state; - - if (!hasPasswordService() && getLoginServices().length == 0) { - loginFields.push({ - label: 'No login service added, i.e. accounts-password', - type: 'notice' - }); - } - - if (hasPasswordService() && formState == STATES.SIGN_IN) { - if (_.contains([ - "USERNAME_AND_EMAIL", - "USERNAME_AND_OPTIONAL_EMAIL", - ], passwordSignupFields())) { - loginFields.push(this.getUsernameOrEmailField()); - } - - if (passwordSignupFields() === "USERNAME_ONLY") { - loginFields.push(this.getUsernameField()); - } - - if (_.contains([ - "EMAIL_ONLY", - ], passwordSignupFields())) { - loginFields.push(this.getEmailField()); - } - - loginFields.push(this.getPasswordField()); - } - - if (hasPasswordService() && formState == STATES.SIGN_UP) { - if (_.contains([ - "USERNAME_AND_EMAIL", - "USERNAME_AND_OPTIONAL_EMAIL", - "USERNAME_ONLY", - ], passwordSignupFields())) { - loginFields.push(this.getUsernameField()); - } - - if (_.contains([ - "USERNAME_AND_EMAIL", - "EMAIL_ONLY", - ], passwordSignupFields())) { - loginFields.push(this.getEmailField()); - } - - if (_.contains(["USERNAME_AND_OPTIONAL_EMAIL"], passwordSignupFields())) { - loginFields.push(Object.assign(this.getEmailField(), {required: false})); - } - - loginFields.push(this.getPasswordField()); - } - - if (formState == STATES.PASSWORD_RESET) { - loginFields.push(this.getEmailField()); - } - - if (this.showPasswordChangeForm()) { - if (Meteor.isClient && !Accounts._loginButtonsSession.get('resetPasswordToken')) { - loginFields.push(this.getPasswordField()); - } - loginFields.push(this.getNewPasswordField()); - } - - if (this.showEnrollAccountForm()) { - loginFields.push(this.getSetPasswordField()); - } - - return _.indexBy(loginFields, 'id'); - } - - buttons() { - const { - loginPath = Accounts.ui._options.loginPath, - signUpPath = Accounts.ui._options.signUpPath, - resetPasswordPath = Accounts.ui._options.resetPasswordPath, - changePasswordPath = Accounts.ui._options.changePasswordPath, - profilePath = Accounts.ui._options.profilePath, - } = this.props; - const { formState, waiting } = this.state; - let loginButtons = []; - const currentUser = typeof this.props.currentUser !== 'undefined' - ? this.props.currentUser : this.state.currentUser; - - if (currentUser && formState == STATES.PROFILE) { - loginButtons.push({ - id: 'signOut', - label: this.context.intl.formatMessage({id: 'accounts.sign_out'}), - disabled: waiting, - onClick: this.signOut.bind(this) - }); - } - - if (this.showCreateAccountLink()) { - loginButtons.push({ - id: 'switchToSignUp', - label: this.context.intl.formatMessage({id: 'accounts.sign_up'}), - type: 'link', - href: signUpPath, - onClick: this.switchToSignUp.bind(this) - }); - } - - if (formState == STATES.SIGN_UP || formState == STATES.PASSWORD_RESET) { - loginButtons.push({ - id: 'switchToSignIn', - label: this.context.intl.formatMessage({id: 'accounts.sign_in'}), - type: 'link', - href: loginPath, - onClick: this.switchToSignIn.bind(this) - }); - } - - if (this.showForgotPasswordLink()) { - loginButtons.push({ - id: 'switchToPasswordReset', - label: this.context.intl.formatMessage({id: 'accounts.forgot_password'}), - type: 'link', - href: resetPasswordPath, - onClick: this.switchToPasswordReset.bind(this) - }); - } - - if (currentUser - && formState == STATES.PROFILE - // note: user.services is not published so change password link would never be shown - // && (currentUser.services && 'password' in currentUser.services) - ) { - loginButtons.push({ - id: 'switchToChangePassword', - label: this.context.intl.formatMessage({id: 'accounts.change_password'}), - type: 'link', - href: changePasswordPath, - onClick: this.switchToChangePassword.bind(this) - }); - } - - if (formState == STATES.SIGN_UP) { - loginButtons.push({ - id: 'signUp', - label: this.context.intl.formatMessage({id: 'accounts.sign_up'}), - type: hasPasswordService() ? 'submit' : 'link', - className: 'active', - disabled: waiting, - onClick: hasPasswordService() ? this.signUp.bind(this, {}) : null - }); - } - - if (this.showSignInLink()) { - loginButtons.push({ - id: 'signIn', - label: this.context.intl.formatMessage({id: 'accounts.sign_in'}), - type: hasPasswordService() ? 'submit' : 'link', - className: 'active', - disabled: waiting, - onClick: hasPasswordService() ? this.signIn.bind(this) : null - }); - } - - if (formState == STATES.PASSWORD_RESET) { - loginButtons.push({ - id: 'emailResetLink', - label: this.context.intl.formatMessage({id: 'accounts.reset_your_password'}), - type: 'submit', - disabled: waiting, - onClick: this.passwordReset.bind(this) - }); - } - - if (this.showPasswordChangeForm() || this.showEnrollAccountForm()) { - loginButtons.push({ - id: 'changePassword', - label: (this.showPasswordChangeForm() ? this.context.intl.formatMessage({id: 'accounts.change_password'}) : this.context.intl.formatMessage({id: 'accounts.set_password'})), - type: 'submit', - disabled: waiting, - onClick: this.passwordChange.bind(this) - }); - - if (Accounts.user()) { - loginButtons.push({ - id: 'switchToSignOut', - label: this.context.intl.formatMessage({id: 'accounts.cancel'}), - type: 'link', - href: profilePath, - onClick: this.switchToSignOut.bind(this) - }); - } else { - loginButtons.push({ - id: 'cancelResetPassword', - label: this.context.intl.formatMessage({id: 'accounts.cancel'}), - type: 'link', - onClick: this.cancelResetPassword.bind(this), - }); - } - } - - // Sort the button array so that the submit button always comes first, and - // buttons should also come before links. - loginButtons.sort((a, b) => { - return ( - b.type == 'submit' && - a.type != undefined) - ( - a.type == 'submit' && - b.type != undefined); - }); - - return _.indexBy(loginButtons, 'id'); - } - - showSignInLink(){ - return this.state.formState == STATES.SIGN_IN && Package['accounts-password']; - } - - showPasswordChangeForm() { - return(Package['accounts-password'] - && this.state.formState == STATES.PASSWORD_CHANGE); - } - - showEnrollAccountForm() { - return(Package['accounts-password'] - && this.state.formState == STATES.ENROLL_ACCOUNT); - } - - showCreateAccountLink() { - return this.state.formState == STATES.SIGN_IN && !Accounts._options.forbidClientAccountCreation && Package['accounts-password']; - } - - showForgotPasswordLink() { - return this.state.formState == STATES.SIGN_IN && hasPasswordService() && _.contains( - ["USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "EMAIL_ONLY"], - passwordSignupFields() - ); - } - - /** - * Helper to store field values while using the form. - */ - setDefaultFieldValues(defaults) { - if (typeof defaults !== 'object') { - throw new Error('Argument to setDefaultFieldValues is not of type object'); - } else if (typeof localStorage !== 'undefined' && localStorage) { - localStorage.setItem('accounts_ui', JSON.stringify({ - passwordSignupFields: passwordSignupFields(), - ...this.getDefaultFieldValues(), - ...defaults, - })); - } - } - - /** - * Helper to get field values when switching states in the form. - */ - getDefaultFieldValues() { - if (typeof localStorage !== 'undefined' && localStorage) { - const defaultFieldValues = JSON.parse(localStorage.getItem('accounts_ui') || null); - if (defaultFieldValues - && defaultFieldValues.passwordSignupFields === passwordSignupFields()) { - return defaultFieldValues; - } - } - } - - /** - * Helper to clear field values when signing in, up or out. - */ - clearDefaultFieldValues() { - if (typeof localStorage !== 'undefined' && localStorage) { - localStorage.removeItem('accounts_ui'); - } - } - - switchToSignUp(event) { - event.preventDefault(); - this.setState({ - formState: STATES.SIGN_UP, - ...this.getDefaultFieldValues(), - }); - this.clearMessages(); - } - - switchToSignIn(event) { - event.preventDefault(); - this.setState({ - formState: STATES.SIGN_IN, - ...this.getDefaultFieldValues(), - }); - this.clearMessages(); - } - - switchToPasswordReset(event) { - event.preventDefault(); - this.setState({ - formState: STATES.PASSWORD_RESET, - ...this.getDefaultFieldValues(), - }); - this.clearMessages(); - } - - switchToChangePassword(event) { - event.preventDefault(); - this.setState({ - formState: STATES.PASSWORD_CHANGE, - ...this.getDefaultFieldValues(), - }); - this.clearMessages(); - } - - switchToSignOut(event) { - event.preventDefault(); - this.setState({ - formState: STATES.PROFILE, - }); - this.clearMessages(); - } - - cancelResetPassword(event) { - event.preventDefault(); - Accounts._loginButtonsSession.set('resetPasswordToken', null); - this.setState({ - formState: STATES.SIGN_IN, - messages: [], - }); - } - - signOut() { - Meteor.logout(() => { - this.setState({ - formState: STATES.SIGN_IN, - password: null, - }); - this.state.onSignedOutHook(); - this.clearMessages(); - this.clearDefaultFieldValues(); - }); - } - - signIn() { - const { - username = null, - email = null, - usernameOrEmail = null, - password, - formState, - onSubmitHook - } = this.state; - let error = false; - let loginSelector; - this.clearMessages(); - - if (usernameOrEmail !== null) { - if (!this.validateField('username', usernameOrEmail)) { - if (this.state.formState == STATES.SIGN_UP) { - this.state.onSubmitHook("error.accounts.usernameRequired", this.state.formState); - } - error = true; - } - else { - loginSelector = usernameOrEmail; - } - } else if (username !== null) { - if (!this.validateField('username', username)) { - if (this.state.formState == STATES.SIGN_UP) { - this.state.onSubmitHook("error.accounts.usernameRequired", this.state.formState); - } - error = true; - } - else { - loginSelector = { username: username }; - } - } - else if (usernameOrEmail == null) { - if (!this.validateField('email', email)) { - error = true; - } - else { - loginSelector = { email }; - } - } - if (!this.validateField('password', password)) { - error = true; - } - - if (!error) { - Meteor.loginWithPassword(loginSelector, password, (error, result) => { - onSubmitHook(error,formState); - if (error) { - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage(this.context.intl.formatMessage({id: errorId}) || 'accounts.error_unknown', errorId, 'error'); - } - else { - loginResultCallback(() => this.state.onSignedInHook()); - this.setState({ - formState: STATES.PROFILE, - password: null, - }); - this.clearDefaultFieldValues(); - } - }); - } - } - - oauthButtons() { - const { formState, waiting } = this.state; - let oauthButtons = []; - if (formState == STATES.SIGN_IN || formState == STATES.SIGN_UP ) { - if(Accounts.oauth) { - Accounts.oauth.serviceNames().map((service) => { - oauthButtons.push({ - id: service, - label: capitalize(service), - disabled: waiting, - type: 'button', - className: `btn-${service} ${service}`, - onClick: this.oauthSignIn.bind(this, service) - }); - }); - } - } - return _.indexBy(oauthButtons, 'id'); - } - - oauthSignIn(serviceName) { - const { formState, waiting, currentUser, onSubmitHook } = this.state; - //Thanks Josh Owens for this one. - function capitalService() { - return serviceName.charAt(0).toUpperCase() + serviceName.slice(1); - } - - if(serviceName === 'meteor-developer'){ - serviceName = 'meteorDeveloperAccount'; - } - - const loginWithService = Meteor["loginWith" + capitalService()]; - - let options = {}; // use default scope unless specified - if (Accounts.ui._options.requestPermissions[serviceName]) - options.requestPermissions = Accounts.ui._options.requestPermissions[serviceName]; - if (Accounts.ui._options.requestOfflineToken[serviceName]) - options.requestOfflineToken = Accounts.ui._options.requestOfflineToken[serviceName]; - if (Accounts.ui._options.forceApprovalPrompt[serviceName]) - options.forceApprovalPrompt = Accounts.ui._options.forceApprovalPrompt[serviceName]; - - this.clearMessages(); - loginWithService(options, (error) => { - onSubmitHook(error,formState); - if (error) { - if (error instanceof Accounts.LoginCancelledError) { - // do nothing - } else { - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage((error.reason && this.context.intl.formatMessage({id: errorId})) || 'accounts.error_unknown' && errorId) - } - } else { - this.setState({ formState: STATES.PROFILE }); - this.clearDefaultFieldValues(); - loginResultCallback(() => { - Meteor.setTimeout(() => this.state.onSignedInHook(), 100); - }); - } - }); - - } - - signUp(options = {}) { - const { - username = null, - email = null, - usernameOrEmail = null, - password, - formState, - onSubmitHook - } = this.state; - let error = false; - this.clearMessages(); - - if (username !== null) { - if ( !this.validateField('username', username) ) { - if (this.state.formState == STATES.SIGN_UP) { - this.state.onSubmitHook("error.accounts.usernameRequired", this.state.formState); - } - error = true; - } else { - options.username = username; - } - } else { - if (_.contains([ - "USERNAME_AND_EMAIL", - ], passwordSignupFields()) && !this.validateField('username', username) ) { - if (this.state.formState == STATES.SIGN_UP) { - this.state.onSubmitHook("error.accounts.usernameRequired", this.state.formState); - } - error = true; - } - } - - if (!this.validateField('email', email)){ - error = true; - } else { - options.email = email; - } - - if (!this.validateField('password', password)) { - onSubmitHook("Invalid password", formState); - error = true; - } else { - options.password = password; - } - - const SignUp = function(_options) { - Accounts.createUser(_options, (error) => { - if (error) { - console.log(error) - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage(this.context.intl.formatMessage({id: errorId}) || 'accounts.error_unknown' && errorId, 'error'); - if (this.context.intl.formatMessage({id: `error.accounts_${error.reason}`})) { - onSubmitHook(`error.accounts.${error.reason}`, formState); - } - else { - onSubmitHook('Unknown error', formState); - } - } - else { - onSubmitHook(null, formState); - this.setState({ formState: STATES.PROFILE, password: null }); - let currentUser = Accounts.user(); - loginResultCallback(this.state.onPostSignUpHook.bind(this, _options, currentUser)); - this.clearDefaultFieldValues(); - } - - this.setState({ waiting: false }); - }); - }; - if (!error) { - this.setState({ waiting: true }); - // Allow for Promises to return. - let promise = this.state.onPreSignUpHook(options); - if (promise instanceof Promise) { - promise.then(SignUp.bind(this, options)); - } - else { - SignUp(options); - } - } - } - - passwordReset() { - const { - email = '', - waiting, - formState, - onSubmitHook - } = this.state; - - if (waiting) { - return; - } - - this.clearMessages(); - if (this.validateField('email', email)) { - this.setState({ waiting: true }); - - Accounts.forgotPassword({ email: email }, (error) => { - if (error) { - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage(this.context.intl.formatMessage({id: errorId}) || 'accounts.error_unknown' && errorId, 'error'); - } - else { - this.showMessage('accounts.info_email_sent', 'success', 5000); - this.clearDefaultFieldValues(); - } - onSubmitHook(error, formState); - this.setState({ waiting: false }); - }); - } - } - - passwordChange() { - const { - password, - newPassword, - formState, - onSubmitHook, - onSignedInHook, - } = this.state; - - if (!this.validateField('password', newPassword)){ - onSubmitHook('err.minChar',formState); - return; - } - - let token = Accounts._loginButtonsSession.get('resetPasswordToken'); - if (!token) { - token = Accounts._loginButtonsSession.get('enrollAccountToken'); - } - if (token) { - Accounts.resetPassword(token, newPassword, (error) => { - if (error) { - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage(this.context.intl.formatMessage({id: errorId}) || 'accounts.error_unknown' && errorId, 'error'); - onSubmitHook(error, formState); - } - else { - this.showMessage('accounts.info_password_changed', 'success', 5000); - onSubmitHook(null, formState); - this.setState({ formState: STATES.PROFILE }); - Accounts._loginButtonsSession.set('resetPasswordToken', null); - Accounts._loginButtonsSession.set('enrollAccountToken', null); - onSignedInHook(); - } - }); - } - else { - Accounts.changePassword(password, newPassword, (error) => { - if (error) { - const errorId = `accounts.error_${error.reason.toLowerCase().replace(/ /g, '_')}`; - this.showMessage(this.context.intl.formatMessage({id:errorId}) || 'accounts.error_unknown' && errorId, 'error'); - onSubmitHook(error, formState); - } - else { - this.showMessage('accounts.info_password_changed', 'success', 5000); - onSubmitHook(null, formState); - this.setState({ formState: STATES.PROFILE }); - this.clearDefaultFieldValues(); - } - }); - } - } - - showMessage(message, type, clearTimeout, field){ - if (message) { - this.setState(({ messages = [] }) => { - messages.push({ - message: this.context.intl.formatMessage({id: message}), - type, - ...(field && { field }), - }); - return { messages }; - }); - if (clearTimeout) { - this.hideMessageTimout = setTimeout(() => { - // Filter out the message that timed out. - this.clearMessage(message); - }, clearTimeout); - } - } - } - - getMessageForField(field) { - const { messages = [] } = this.state; - return messages.find(({ field:key }) => key === field); - } - - clearMessage(message) { - if (message) { - this.setState(({ messages = [] }) => ({ - messages: messages.filter(({ message:a }) => a !== message), - })); - } - } - - clearMessages() { - if (this.hideMessageTimout) { - clearTimeout(this.hideMessageTimout); - } - this.setState({ messages: [] }); - } - - componentWillUnmount() { - if (this.hideMessageTimout) { - clearTimeout(this.hideMessageTimout); - } - } - - render() { - this.oauthButtons(); - // Backwords compatibility with v1.2.x. - const { messages = [] } = this.state; - const message = { - deprecated: true, - message: messages.map(({ message }) => message).join(', '), - }; - - return ( - - ); - } -} - -AccountsLoginForm.contextTypes = { - intl: intlShape -} - -registerComponent('AccountsLoginForm', AccountsLoginForm, withCurrentUser, withApollo); diff --git a/packages/vulcan-accounts/imports/ui/components/PasswordOrService.jsx b/packages/vulcan-accounts/imports/ui/components/PasswordOrService.jsx deleted file mode 100755 index 37c20560..00000000 --- a/packages/vulcan-accounts/imports/ui/components/PasswordOrService.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { hasPasswordService } from '../../helpers.js'; -import { registerComponent } from 'meteor/vulcan:core'; -import { intlShape } from 'meteor/vulcan:i18n'; - -export class AccountsPasswordOrService extends PureComponent { - render () { - let { className = "password-or-service", style = {} } = this.props; - const services = Object.keys(this.props.oauthServices).map(service => { - return this.props.oauthServices[service].label; - }); - let labels = services; - if (services.length > 2) { - labels = []; - } - - if (hasPasswordService() && services.length > 0) { - return ( -
- { `${this.context.intl.formatMessage({id: 'accounts.or_use'})} ${ labels.join(' / ') }` } -
- ); - } - return null; - } -} - -AccountsPasswordOrService.propTypes = { - oauthServices: PropTypes.object -}; - -AccountsPasswordOrService.contextTypes = { - intl: intlShape -}; - -registerComponent('AccountsPasswordOrService', AccountsPasswordOrService); \ No newline at end of file diff --git a/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx b/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx deleted file mode 100644 index 202395a5..00000000 --- a/packages/vulcan-accounts/imports/ui/components/ResetPassword.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; -import { intlShape } from 'meteor/vulcan:i18n'; -import { STATES } from '../../helpers.js'; - -class AccountsResetPassword extends PureComponent { - componentDidMount() { - const token = this.props.params.token; - Accounts._loginButtonsSession.set('resetPasswordToken', token); - } - - render() { - if (!this.props.currentUser) { - return ( - - ); - } else { - return ( -
-
{this.context.intl.formatMessage({id: 'accounts.info_password_changed'})}!
-
- ); - } - } -} - -AccountsResetPassword.contextTypes = { - intl: intlShape -} - -AccountsResetPassword.propsTypes = { - currentUser: PropTypes.object, - params: PropTypes.object, -}; - -AccountsResetPassword.displayName = 'AccountsResetPassword'; - -registerComponent('AccountsResetPassword', AccountsResetPassword, withCurrentUser); diff --git a/packages/vulcan-accounts/imports/ui/components/SocialButtons.jsx b/packages/vulcan-accounts/imports/ui/components/SocialButtons.jsx deleted file mode 100755 index 37e2ac2b..00000000 --- a/packages/vulcan-accounts/imports/ui/components/SocialButtons.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import './Button.jsx'; -import { Components, registerComponent } from 'meteor/vulcan:core'; - - -export class AccountsSocialButtons extends React.Component { - render() { - let { oauthServices = {}, className = "social-buttons" } = this.props; - return( -
- {Object.keys(oauthServices).map((id, i) => { - return ; - })} -
- ); - } -} - -registerComponent('AccountsSocialButtons', AccountsSocialButtons); \ No newline at end of file diff --git a/packages/vulcan-accounts/main_client.js b/packages/vulcan-accounts/main_client.js deleted file mode 100755 index e657dfd2..00000000 --- a/packages/vulcan-accounts/main_client.js +++ /dev/null @@ -1,11 +0,0 @@ -import { Accounts } from 'meteor/accounts-base'; -import './imports/accounts_ui.js'; -import './imports/components.js'; -import './imports/login_session.js'; -import './imports/routes.js'; -import { STATES } from './imports/helpers.js'; - -import './imports/ui/components/LoginForm.jsx'; - -export { Accounts, STATES }; -export default Accounts; diff --git a/packages/vulcan-accounts/main_server.js b/packages/vulcan-accounts/main_server.js deleted file mode 100755 index d775f9b6..00000000 --- a/packages/vulcan-accounts/main_server.js +++ /dev/null @@ -1,12 +0,0 @@ -import { Accounts } from 'meteor/accounts-base'; -import './imports/accounts_ui.js'; -import './imports/components.js'; -import './imports/login_session.js'; -import './imports/routes.js'; -import { redirect, STATES } from './imports/helpers.js'; -import './imports/api/server/servicesListPublication.js'; - -import './imports/ui/components/LoginForm.jsx'; - -export { Accounts, redirect, STATES }; -export default Accounts; diff --git a/packages/vulcan-accounts/package.js b/packages/vulcan-accounts/package.js deleted file mode 100755 index c4d4d066..00000000 --- a/packages/vulcan-accounts/package.js +++ /dev/null @@ -1,30 +0,0 @@ -Package.describe({ - name: 'vulcan:accounts', - version: '1.7.0', - summary: 'Accounts UI for React in Meteor 1.3+', - git: 'https://github.com/studiointeract/accounts-ui', - documentation: 'README.md' -}); - -Package.onUse(function(api) { - api.versionsFrom('1.3'); - - api.use('vulcan:core@1.7.0'); - - api.use('ecmascript'); - api.use('tracker'); - api.use('underscore'); - api.use('accounts-base'); - api.use('check'); - api.use('random'); - api.use('email'); - api.use('session'); - - api.imply('accounts-base'); - - api.use('accounts-oauth', {weak: true}); - api.use('accounts-password', {weak: true}); - - api.mainModule('main_client.js', 'client'); - api.mainModule('main_server.js', 'server'); -}); diff --git a/packages/vulcan-admin/README.md b/packages/vulcan-admin/README.md deleted file mode 100644 index d2fe98bb..00000000 --- a/packages/vulcan-admin/README.md +++ /dev/null @@ -1 +0,0 @@ -VulcanJS admin package. \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/AdminHome.jsx b/packages/vulcan-admin/lib/components/AdminHome.jsx deleted file mode 100644 index a7e733e7..00000000 --- a/packages/vulcan-admin/lib/components/AdminHome.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { Components, withCurrentUser, AdminColumns } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; -import Users from 'meteor/vulcan:users'; - -import '../modules/columns.js'; - -const AdminHome = ({ currentUser }) => -
-

}> - -
-
- -export default withCurrentUser(AdminHome); \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx deleted file mode 100644 index 41e61ee9..00000000 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersActions.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import Users from 'meteor/vulcan:users'; -import { Components, withRemove } from 'meteor/vulcan:core'; -import Button from 'react-bootstrap/lib/Button'; - -const AdminUsersActions = ({ document: user, removeMutation }) =>{ - - const deleteHandler = e => { - e.preventDefault(); - if (confirm(`Delete user ${Users.getDisplayName(user)}?`)) { - removeMutation({documentId: user._id}); - } - } - - return -} - -const removeOptions = { - collection: Users -} - -export default withRemove(removeOptions)(AdminUsersActions); - diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx deleted file mode 100644 index 683d023b..00000000 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersCreated.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { Components } from 'meteor/vulcan:core'; -import moment from 'moment'; - -const AdminUsersCreated = ({ document: user }) => -
- {moment(new Date(user.createdAt)).format('MM/DD/YY')} -
- -export default AdminUsersCreated; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx deleted file mode 100644 index bdebace4..00000000 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersEmail.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import Users from 'meteor/vulcan:users'; -import { Components } from 'meteor/vulcan:core'; - -const AdminUsersEmail = ({ document: user }) => - {Users.getEmail(user)} - -export default AdminUsersEmail; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx b/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx deleted file mode 100644 index e6342988..00000000 --- a/packages/vulcan-admin/lib/components/users/columns/AdminUsersName.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import Users from 'meteor/vulcan:users'; -import { Components } from 'meteor/vulcan:core'; - -const AdminUsersName = ({ document: user, flash }) => -
- - - - {Users.getDisplayName(user)} - -   - - {_.rest(Users.getGroups(user)).map(group => {group})} - -
- -export default AdminUsersName; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/modules/columns.js b/packages/vulcan-admin/lib/modules/columns.js deleted file mode 100644 index d20cb7d5..00000000 --- a/packages/vulcan-admin/lib/modules/columns.js +++ /dev/null @@ -1,23 +0,0 @@ -import { addAdminColumn } from 'meteor/vulcan:core'; - -import AdminUsersName from '../components/users/columns/AdminUsersName.jsx'; -import AdminUsersEmail from '../components/users/columns/AdminUsersEmail.jsx'; -import AdminUsersCreated from '../components/users/columns/AdminUsersCreated.jsx'; - -addAdminColumn([ - { - name: 'name', - order: 1, - component: AdminUsersName - }, - { - name: 'email', - order: 10, - component: AdminUsersEmail - }, - { - name: 'created', - order: 20, - component: AdminUsersCreated - }, -]); \ No newline at end of file diff --git a/packages/vulcan-admin/lib/modules/fragments.js b/packages/vulcan-admin/lib/modules/fragments.js deleted file mode 100644 index 138a1f7b..00000000 --- a/packages/vulcan-admin/lib/modules/fragments.js +++ /dev/null @@ -1,20 +0,0 @@ -import { registerFragment } from 'meteor/vulcan:lib'; - -// ------------------------------ Vote ------------------------------ // - -// note: fragment used by default on the UsersProfile fragment -registerFragment(` - fragment UsersAdmin on User { - _id - username - createdAt - isAdmin - displayName - email - emailHash - slug - groups - services - avatarUrl - } -`); diff --git a/packages/vulcan-admin/lib/modules/i18n.js b/packages/vulcan-admin/lib/modules/i18n.js deleted file mode 100644 index 1b07aa96..00000000 --- a/packages/vulcan-admin/lib/modules/i18n.js +++ /dev/null @@ -1,9 +0,0 @@ -import { addStrings } from 'meteor/vulcan:core'; - -addStrings('en', { - 'users.name': 'Name', - 'users.created': 'Created', - 'users.groups': 'Groups', - 'users.actions': 'Actions', - 'users.email': 'Email', -}); \ No newline at end of file diff --git a/packages/vulcan-admin/lib/modules/index.js b/packages/vulcan-admin/lib/modules/index.js deleted file mode 100644 index 9a851d3e..00000000 --- a/packages/vulcan-admin/lib/modules/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import './fragments.js'; -import './routes.js'; -import './i18n.js'; diff --git a/packages/vulcan-admin/lib/modules/routes.js b/packages/vulcan-admin/lib/modules/routes.js deleted file mode 100644 index 59ad9518..00000000 --- a/packages/vulcan-admin/lib/modules/routes.js +++ /dev/null @@ -1,4 +0,0 @@ -import { addRoute, getDynamicComponent } from 'meteor/vulcan:core'; -import React from 'react'; - -addRoute({ name: 'admin', path: '/admin', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))}); diff --git a/packages/vulcan-admin/lib/server/main.js b/packages/vulcan-admin/lib/server/main.js deleted file mode 100644 index 67d11275..00000000 --- a/packages/vulcan-admin/lib/server/main.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../modules/index.js'; \ No newline at end of file diff --git a/packages/vulcan-admin/lib/stylesheets/style.scss b/packages/vulcan-admin/lib/stylesheets/style.scss deleted file mode 100644 index 242550e4..00000000 --- a/packages/vulcan-admin/lib/stylesheets/style.scss +++ /dev/null @@ -1,41 +0,0 @@ -.datatable-users{ - .datatable-search{ - margin-bottom: 10px; - padding: 2px 7px; - } - - .datatable-item-name{ - .modal-trigger{ - display: inline-block; - cursor: pointer; - } - .avatar{ - display: inline-block; - margin-right: 5px; - } - a{ - display: flex; - align-items: center; - div{ - margin-right: 5px; - } - } - } - - .datatable-item-groups{ - code{ - display: inline-block; - margin-right: 5px; - } - } - - .datatable-load-more{ - display: flex; - align-items: center; - justify-content: center; - } - - .avatar img{ - width: 20px; - } -} \ No newline at end of file diff --git a/packages/vulcan-admin/package.js b/packages/vulcan-admin/package.js deleted file mode 100644 index 13ec4ec3..00000000 --- a/packages/vulcan-admin/package.js +++ /dev/null @@ -1,28 +0,0 @@ -Package.describe({ - name: "vulcan:admin", - summary: "Vulcan components package", - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom(['METEOR@1.0']); - - api.use([ - - 'fourseven:scss@4.5.0', - 'dynamic-import@0.1.1', - // Vulcan packages - 'vulcan:core@1.7.0', - - ]); - - api.mainModule("lib/server/main.js", "server"); - api.mainModule("lib/client/main.js", "client"); - - api.addFiles([ - 'lib/stylesheets/style.scss' - ], ['client']); - -}); diff --git a/packages/vulcan-categories/.gitignore b/packages/vulcan-categories/.gitignore deleted file mode 100644 index 677a6fc2..00000000 --- a/packages/vulcan-categories/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.build* diff --git a/packages/vulcan-categories/README.md b/packages/vulcan-categories/README.md deleted file mode 100644 index 74f3b412..00000000 --- a/packages/vulcan-categories/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan tags package, used internally. \ No newline at end of file diff --git a/packages/vulcan-categories/lib/client.js b/packages/vulcan-categories/lib/client.js deleted file mode 100644 index ec7fc6c7..00000000 --- a/packages/vulcan-categories/lib/client.js +++ /dev/null @@ -1,4 +0,0 @@ -import Categories, { getCategories, getCategoriesAsOptions, getCategoriesAsNestedOptions } from './modules.js'; - -export { getCategories, getCategoriesAsOptions, getCategoriesAsNestedOptions }; -export default Categories; diff --git a/packages/vulcan-categories/lib/collection.js b/packages/vulcan-categories/lib/collection.js deleted file mode 100644 index f5ba23da..00000000 --- a/packages/vulcan-categories/lib/collection.js +++ /dev/null @@ -1,24 +0,0 @@ -import schema from './schema.js'; -import mutations from './mutations.js'; -import resolvers from './resolvers.js'; -import { createCollection } from 'meteor/vulcan:core'; - - -/** - * @summary The global namespace for Categories. - * @namespace Categories - */ - const Categories = createCollection({ - - collectionName: 'Categories', - - typeName: 'Category', - - schema, - - resolvers, - - mutations, - - }); -export default Categories; \ No newline at end of file diff --git a/packages/vulcan-categories/lib/custom_fields.js b/packages/vulcan-categories/lib/custom_fields.js deleted file mode 100644 index 1baec12b..00000000 --- a/packages/vulcan-categories/lib/custom_fields.js +++ /dev/null @@ -1,38 +0,0 @@ -import Posts from "meteor/vulcan:posts"; -import { getCategoriesAsOptions } from './schema.js'; - -Posts.addField([ - { - fieldName: 'categories', - fieldSchema: { - type: Array, - control: "checkboxgroup", - optional: true, - insertableBy: ['members'], - editableBy: ['admins'], - viewableBy: ['guests'], - form: { - noselect: true, - type: "bootstrap-category", - order: 50, - options: formProps => getCategoriesAsOptions(formProps.client), - }, - resolveAs: { - fieldName: 'categories', - type: '[Category]', - resolver: async (post, args, {currentUser, Users, Categories}) => { - if (!post.categories) return []; - const categories = _.compact(await Categories.loader.loadMany(post.categories)); - return Users.restrictViewableFields(currentUser, Categories, categories); - } - } - } - }, - { - fieldName: 'categories.$', - fieldSchema: { - type: String, - optional: true - } - } -]); diff --git a/packages/vulcan-categories/lib/modules.js b/packages/vulcan-categories/lib/modules.js deleted file mode 100644 index e33070e8..00000000 --- a/packages/vulcan-categories/lib/modules.js +++ /dev/null @@ -1,12 +0,0 @@ -import Categories from './collection.js'; - -export { getCategories, getCategoriesAsOptions, getCategoriesAsNestedOptions } from './schema.js'; -import './helpers.js'; -import './callbacks.js'; -import './parameters.js'; -import './custom_fields.js'; -import './permissions.js'; -import './resolvers.js'; -import './mutations.js'; - -export default Categories; diff --git a/packages/vulcan-categories/lib/mutations.js b/packages/vulcan-categories/lib/mutations.js deleted file mode 100644 index 2c2f686d..00000000 --- a/packages/vulcan-categories/lib/mutations.js +++ /dev/null @@ -1,88 +0,0 @@ -import { newMutation, editMutation, removeMutation } from 'meteor/vulcan:core'; -import Users from 'meteor/vulcan:users'; - -const performCheck = (mutation, user, document) => { - if (!mutation.check(user, document)) throw new Error(Utils.encodeIntlError({id: `app.mutation_not_allowed`, value: `"${mutation.name}" on _id "${document._id}"`})); -}; - -const mutations = { - - new: { - - name: 'categoriesNew', - - check(user, document) { - if (!user) return false; - return Users.canDo(user, 'categories.new'); - }, - - mutation(root, {document}, context) { - - performCheck(this, context.currentUser, document); - - return newMutation({ - collection: context.Categories, - document: document, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - edit: { - - name: 'categoriesEdit', - - check(user, document) { - if (!user || !document) return false; - return Users.canDo(user, `categories.edit.all`); - }, - - mutation(root, {documentId, set, unset}, context) { - - const document = context.Categories.findOne(documentId); - performCheck(this, context.currentUser, document); - - return editMutation({ - collection: context.Categories, - documentId: documentId, - set: set, - unset: unset, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - remove: { - - name: 'categoriesRemove', - - check(user, document) { - if (!user || !document) return false; - return Users.canDo(user, `categories.remove.all`); - }, - - mutation(root, {documentId}, context) { - - const document = context.Categories.findOne(documentId); - performCheck(this, context.currentUser, document); - - return removeMutation({ - collection: context.Categories, - documentId: documentId, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - -}; - -export default mutations; diff --git a/packages/vulcan-categories/lib/parameters.js b/packages/vulcan-categories/lib/parameters.js deleted file mode 100644 index 9006aa45..00000000 --- a/packages/vulcan-categories/lib/parameters.js +++ /dev/null @@ -1,52 +0,0 @@ -import Categories from './collection.js'; -import { addCallback, getSetting } from 'meteor/vulcan:core'; -import { getCategories } from './schema.js'; - -// Category Default Sorting by Ascending order (1, 2, 3..) -function CategoriesAscOrderSorting(parameters, terms) { - parameters.options.sort = {order: 1}; - - return parameters; -} - -addCallback('categories.parameters', CategoriesAscOrderSorting); - -// Category Posts Parameters -// Add a "categories" property to terms which can be used to filter *all* existing Posts views. -function PostsCategoryParameter(parameters, terms, apolloClient) { - - const cat = terms.cat || terms["cat[]"]; - // filter by category if category slugs are provided - if (cat) { - - let categoriesIds = []; - let selector = {}; - let slugs; - - if (typeof cat === "string") { // cat is a string - selector = {slug: cat}; - slugs = [cat]; - } else if (Array.isArray(cat)) { // cat is an array - selector = {slug: {$in: cat}}; - slugs = cat; - } - - // TODO: use new Apollo imperative API - // get all categories passed in terms - const categories = !!apolloClient ? _.filter(getCategories(apolloClient), category => _.contains(slugs, category.slug) ) : Categories.find(selector).fetch(); - - // for each category, add its ID and the IDs of its children to categoriesId array - categories.forEach(function (category) { - categoriesIds.push(category._id); - // TODO: find a better way to handle child categories - // categoriesIds = categoriesIds.concat(_.pluck(Categories.getChildren(category), "_id")); - }); - - const operator = getSetting('categoriesFilter', 'union') === 'union' ? '$in' : '$all'; - - parameters.selector = Meteor.isClient ? {...parameters.selector, 'categories._id': {$in: categoriesIds}} : {...parameters.selector, categories: {[operator]: categoriesIds}}; - } - return parameters; -} - -addCallback("posts.parameters", PostsCategoryParameter); diff --git a/packages/vulcan-categories/lib/resolvers.js b/packages/vulcan-categories/lib/resolvers.js deleted file mode 100644 index 45a0561e..00000000 --- a/packages/vulcan-categories/lib/resolvers.js +++ /dev/null @@ -1,43 +0,0 @@ - -// root resolvers: basic list, single, and total query resolvers -const resolvers = { - - list: { - - name: 'categoriesList', - - resolver(root, {terms}, context, info) { - let {selector, options} = context.Categories.getParameters(terms); - - options.limit = terms.limit; - options.skip = terms.offset; - options.fields = context.getViewableFields(context.currentUser, context.Categories); - - return context.Categories.find(selector, options).fetch(); - }, - - }, - - single: { - - name: 'categoriesSingle', - - resolver(root, {documentId, slug}, context) { - const selector = documentId ? {_id: documentId} : {slug: slug}; - return context.Categories.findOne(selector, { fields: context.getViewableFields(context.currentUser, context.Categories) }); - }, - - }, - - total: { - - name: 'categoriesTotal', - - resolver(root, args, context) { - return context.Categories.find().count(); - }, - - } -}; - -export default resolvers; diff --git a/packages/vulcan-categories/lib/schema.js b/packages/vulcan-categories/lib/schema.js deleted file mode 100644 index c3e53d7a..00000000 --- a/packages/vulcan-categories/lib/schema.js +++ /dev/null @@ -1,112 +0,0 @@ -import { Utils } from 'meteor/vulcan:core'; - -export function getCategories (apolloClient) { - - // get the current data of the store - const apolloData = apolloClient.store.getState().apollo.data; - - // filter these data based on their typename: we are interested in the categories data - let categories = _.filter(apolloData, (object, key) => { - return object.__typename === 'Category' - }); - - // order categories - categories = _.sortBy(categories, cat => cat.order); - - return categories; -} - -export function getCategoriesAsOptions (apolloClient) { - // give the form component (here: checkboxgroup) exploitable data - return getCategories(apolloClient).map(function (category) { - return { - value: category._id, - label: category.name, - // slug: category.slug, // note: it may be used to look up from prefilled props - }; - }); -} - -export function getCategoriesAsNestedOptions (apolloClient) { - // give the form component (here: checkboxgroup) exploitable data - const formattedCategories = getCategories(apolloClient).map(function (category) { - return { - value: category._id, - label: category.name, - parentId: category.parentId, - _id: category._id - // slug: category.slug, // note: it may be used to look up from prefilled props - }; - }); - const nestedCategories = Utils.unflatten(formattedCategories, {idProperty: '_id', parentIdProperty: 'parentId', childrenProperty: 'options'}); - return nestedCategories; -} - -// category schema -const schema = { - _id: { - type: String, - viewableBy: ['guests'], - optional: true, - }, - name: { - type: String, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - }, - description: { - type: String, - optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - form: { - rows: 3 - } - }, - order: { - type: Number, - optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - }, - slug: { - type: String, - optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - }, - image: { - type: String, - optional: true, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - }, - parentId: { - type: String, - optional: true, - control: "select", - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - resolveAs: { - fieldName: 'parent', - type: 'Category', - resolver: async (category, args, {currentUser, Users, Categories}) => { - if (!category.parentId) return null; - const parent = await Categories.loader.load(category.parentId); - return Users.restrictViewableFields(currentUser, Categories, parent); - }, - addOriginalField: true - }, - form: { - options: formProps => getCategoriesAsOptions(formProps.client) - } - } -}; - -export default schema; diff --git a/packages/vulcan-categories/lib/server.js b/packages/vulcan-categories/lib/server.js deleted file mode 100644 index 4516df5d..00000000 --- a/packages/vulcan-categories/lib/server.js +++ /dev/null @@ -1,8 +0,0 @@ -import Categories, { getCategories, getCategoriesAsOptions, getCategoriesAsNestedOptions } from './modules.js'; -import Posts from 'meteor/vulcan:posts'; -import './server/load_categories.js'; - -Posts._ensureIndex({'categories': 1}); - -export { getCategories, getCategoriesAsOptions, getCategoriesAsNestedOptions }; -export default Categories; diff --git a/packages/vulcan-categories/lib/server/load_categories.js b/packages/vulcan-categories/lib/server/load_categories.js deleted file mode 100644 index 20106744..00000000 --- a/packages/vulcan-categories/lib/server/load_categories.js +++ /dev/null @@ -1,40 +0,0 @@ -import Categories from "../collection.js"; -import { Utils, newMutation } from 'meteor/vulcan:core'; - -// Load categories from settings, if there are any - -if (Meteor.settings && Meteor.settings.categories) { - Meteor.settings.categories.forEach(category => { - - // get slug (or slugified name) - const slug = category.slug || Utils.slugify(category.name); - - // look for existing category with same slug - let existingCategory = Categories.findOne({slug: slug}); - - // look for parent category - if (category.parent) { - const parentCategory = Categories.findOne({slug: category.parent}); - if (parentCategory) { - category.parentId = parentCategory._id; - delete category.parent; - } - } - - if (existingCategory) { - // if category exists, update it with settings data except slug - delete category.slug; - Categories.update(existingCategory._id, {$set: category}); - } else { - // if not, create it - newMutation({ - collection: Categories, - document: category, - validate: false, - }); - - // Categories.insert(category); - console.log(`// Creating category “${category.name}”`); // eslint-disable-line - } - }); -} diff --git a/packages/vulcan-categories/package.js b/packages/vulcan-categories/package.js deleted file mode 100644 index bd4470fc..00000000 --- a/packages/vulcan-categories/package.js +++ /dev/null @@ -1,20 +0,0 @@ -Package.describe({ - name: "vulcan:categories", - summary: "Vulcan tags package", - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom("METEOR@1.0"); - - api.use([ - 'vulcan:core@1.7.0', - 'vulcan:posts@1.7.0', - ]); - - api.mainModule("lib/server.js", "server"); - api.mainModule("lib/client.js", "client"); - -}); \ No newline at end of file diff --git a/packages/vulcan-categories/scss.json b/packages/vulcan-categories/scss.json deleted file mode 100644 index 6dbafe64..00000000 --- a/packages/vulcan-categories/scss.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "enableAutoprefixer": true, - "outputStyle": "compressed", - "sourceComments": true, - "sourceMap": true -} \ No newline at end of file diff --git a/packages/vulcan-comments/README.md b/packages/vulcan-comments/README.md deleted file mode 100644 index 8a8fbd9e..00000000 --- a/packages/vulcan-comments/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan comments package, used internally. \ No newline at end of file diff --git a/packages/vulcan-comments/TESTS.md b/packages/vulcan-comments/TESTS.md deleted file mode 100644 index fb710932..00000000 --- a/packages/vulcan-comments/TESTS.md +++ /dev/null @@ -1,12 +0,0 @@ -### Single Post Page - -- `/posts/:postId` should show a list of comments. -- `/posts/:postId` should subscribe to the `postComments` publication. - -### Comment Submit - -- - -### Comment Edit - -### Comment Delete \ No newline at end of file diff --git a/packages/vulcan-comments/lib/callbacks/callbacks_comments_edit.js b/packages/vulcan-comments/lib/callbacks/callbacks_comments_edit.js deleted file mode 100644 index 19a41afd..00000000 --- a/packages/vulcan-comments/lib/callbacks/callbacks_comments_edit.js +++ /dev/null @@ -1,13 +0,0 @@ -import marked from 'marked'; -import { addCallback, Utils } from 'meteor/vulcan:core'; - -// ------------------------------------- comments.edit.sync -------------------------------- // - -function CommentsEditGenerateHTMLBody (modifier, comment, user) { - // if body is being modified, update htmlBody too - if (modifier.$set && modifier.$set.body) { - modifier.$set.htmlBody = Utils.sanitize(marked(modifier.$set.body)); - } - return modifier; -} -addCallback("comments.edit.sync", CommentsEditGenerateHTMLBody); diff --git a/packages/vulcan-comments/lib/callbacks/callbacks_comments_new.js b/packages/vulcan-comments/lib/callbacks/callbacks_comments_new.js deleted file mode 100644 index 831086ef..00000000 --- a/packages/vulcan-comments/lib/callbacks/callbacks_comments_new.js +++ /dev/null @@ -1,49 +0,0 @@ -import marked from 'marked'; -import Posts from "meteor/vulcan:posts"; -import Comments from '../collection.js'; -import Users from 'meteor/vulcan:users'; -import { addCallback, Utils, getSetting } from 'meteor/vulcan:core'; - -// ------------------------------------- comments.new.validate -------------------------------- // - -function CommentsNewRateLimit (comment, user) { - if (!Users.isAdmin(user)) { - const timeSinceLastComment = Users.timeSinceLast(user, Comments); - const commentInterval = Math.abs(parseInt(getSetting('commentInterval',15))); - - // check that user waits more than 15 seconds between comments - if((timeSinceLastComment < commentInterval)) { - throw new Error(Utils.encodeIntlError({id: "comments.rate_limit_error", value: commentInterval-timeSinceLastComment})); - } - } - return comment; -} -addCallback("comments.new.validate", CommentsNewRateLimit); - -// ------------------------------------- comments.new.sync -------------------------------- // - -function CommentsNewGenerateHTMLBody (comment, user) { - comment.htmlBody = Utils.sanitize(marked(comment.body)); - return comment; -} -addCallback("comments.new.sync", CommentsNewGenerateHTMLBody); - -function CommentsNewOperations (comment) { - - var userId = comment.userId; - - // increment comment count - Users.update({_id: userId}, { - $inc: {'commentCount': 1} - }); - - // update post - Posts.update(comment.postId, { - $inc: {commentCount: 1}, - $set: {lastCommentedAt: new Date()}, - $addToSet: {commenters: userId} - }); - - return comment; -} -addCallback("comments.new.sync", CommentsNewOperations); diff --git a/packages/vulcan-comments/lib/callbacks/callbacks_comments_remove.js b/packages/vulcan-comments/lib/callbacks/callbacks_comments_remove.js deleted file mode 100644 index df5e7012..00000000 --- a/packages/vulcan-comments/lib/callbacks/callbacks_comments_remove.js +++ /dev/null @@ -1,47 +0,0 @@ -import { removeMutation, addCallback } from 'meteor/vulcan:core'; -import Posts from "meteor/vulcan:posts"; -import Comments from '../collection.js'; -import Users from 'meteor/vulcan:users'; - -function CommentsRemovePostCommenters (comment, currentUser) { - const { userId, postId } = comment; - - // dec user's comment count - Users.update({_id: userId}, { - $inc: {'commentCount': -1} - }); - - const postComments = Comments.find({postId}, {sort: {postedAt: -1}}).fetch(); - - const commenters = _.uniq(postComments.map(comment => comment.userId)); - const lastCommentedAt = postComments[0] && postComments[0].postedAt; - - // update post with a decremented comment count, a unique list of commenters and corresponding last commented at date - Posts.update(postId, { - $inc: {commentCount: -1}, - $set: {lastCommentedAt, commenters}, - }); - - return comment; -} - -addCallback("comments.remove.async", CommentsRemovePostCommenters); - -function CommentsRemoveChildrenComments (comment, currentUser) { - - const childrenComments = Comments.find({parentCommentId: comment._id}).fetch(); - - childrenComments.forEach(childComment => { - removeMutation({ - action: 'comments.remove', - collection: Comments, - documentId: childComment._id, - currentUser: currentUser, - validate: false - }); - }); - - return comment; -} - -addCallback("comments.remove.async", CommentsRemoveChildrenComments); diff --git a/packages/vulcan-comments/lib/callbacks/callbacks_other.js b/packages/vulcan-comments/lib/callbacks/callbacks_other.js deleted file mode 100644 index c68a2ecb..00000000 --- a/packages/vulcan-comments/lib/callbacks/callbacks_other.js +++ /dev/null @@ -1,24 +0,0 @@ -import Comments from '../collection.js'; -import { addCallback } from 'meteor/vulcan:core'; - -function UsersRemoveDeleteComments (user, options) { - if (options.deleteComments) { - Comments.remove({userId: user._id}); - } else { - // not sure if anything should be done in that scenario yet - // Comments.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true}); - } -} -addCallback("users.remove.async", UsersRemoveDeleteComments); - -// Add to posts.single publication - -function PostsSingleAddCommentsUsers (users, post) { - // get IDs from all commenters on the post - const comments = Comments.find({postId: post._id}).fetch(); - if (comments.length) { - users = users.concat(_.pluck(comments, "userId")); - } - return users; -} -addCallback("posts.single.getUsers", PostsSingleAddCommentsUsers); diff --git a/packages/vulcan-comments/lib/client.js b/packages/vulcan-comments/lib/client.js deleted file mode 100644 index 0d05e11b..00000000 --- a/packages/vulcan-comments/lib/client.js +++ /dev/null @@ -1,3 +0,0 @@ -import Comments from './modules.js'; - -export default Comments; \ No newline at end of file diff --git a/packages/vulcan-comments/lib/collection.js b/packages/vulcan-comments/lib/collection.js deleted file mode 100644 index 79d55821..00000000 --- a/packages/vulcan-comments/lib/collection.js +++ /dev/null @@ -1,24 +0,0 @@ -import schema from './schema.js'; -import mutations from './mutations.js'; -import resolvers from './resolvers.js'; -import { createCollection } from 'meteor/vulcan:core'; - - -/** - * @summary The global namespace for Comments. - * @namespace Comments - */ - const Comments = createCollection({ - - collectionName: 'Comments', - - typeName: 'Comment', - - schema, - - resolvers, - - mutations, - - }); -export default Comments; diff --git a/packages/vulcan-comments/lib/modules.js b/packages/vulcan-comments/lib/modules.js deleted file mode 100644 index 3e13a24c..00000000 --- a/packages/vulcan-comments/lib/modules.js +++ /dev/null @@ -1,16 +0,0 @@ -import Comments from './collection.js'; - -import './schema.js'; -import './callbacks/callbacks_comments_new.js'; -import './callbacks/callbacks_comments_edit.js'; -import './callbacks/callbacks_comments_remove.js'; -import './callbacks/callbacks_other.js'; -import './views.js'; -import './parameters.js'; -import './helpers.js'; -import './custom_fields.js'; -import './permissions.js'; -import './resolvers.js'; -import './mutations.js'; - -export default Comments; diff --git a/packages/vulcan-comments/lib/mutations.js b/packages/vulcan-comments/lib/mutations.js deleted file mode 100644 index 1d2665fd..00000000 --- a/packages/vulcan-comments/lib/mutations.js +++ /dev/null @@ -1,88 +0,0 @@ -import { newMutation, editMutation, removeMutation, Utils } from 'meteor/vulcan:core'; -import Users from 'meteor/vulcan:users'; - -const performCheck = (mutation, user, document) => { - if (!mutation.check(user, document)) throw new Error(Utils.encodeIntlError({id: `app.mutation_not_allowed`, value: `"${mutation.name}" on _id "${document._id}"`})); -}; - -const mutations = { - - new: { - - name: 'commentsNew', - - check(user, document) { - if (!user) return false; - return Users.canDo(user, 'comments.new'); - }, - - mutation(root, {document}, context) { - - performCheck(this, context.currentUser, document); - - return newMutation({ - collection: context.Comments, - document: document, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - edit: { - - name: 'commentsEdit', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'comments.edit.own') : Users.canDo(user, `comments.edit.all`); - }, - - mutation(root, {documentId, set, unset}, context) { - - const document = context.Comments.findOne(documentId); - performCheck(this, context.currentUser, document); - - return editMutation({ - collection: context.Comments, - documentId: documentId, - set: set, - unset: unset, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - remove: { - - name: 'commentsRemove', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'comments.remove.own') : Users.canDo(user, `comments.remove.all`); - }, - - mutation(root, {documentId}, context) { - - const document = context.Comments.findOne(documentId); - performCheck(this, context.currentUser, document); - - return removeMutation({ - collection: context.Comments, - documentId: documentId, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - -}; - -export default mutations; diff --git a/packages/vulcan-comments/lib/parameters.js b/packages/vulcan-comments/lib/parameters.js deleted file mode 100644 index b4816925..00000000 --- a/packages/vulcan-comments/lib/parameters.js +++ /dev/null @@ -1,18 +0,0 @@ -import { addCallback } from 'meteor/vulcan:core'; - -// limit the number of items that can be requested at once -function CommentsMaxLimit (parameters, terms) { - var maxLimit = 1000; - // if a limit was provided with the terms, add it too (note: limit=0 means "no limit") - if (typeof terms.limit !== 'undefined') { - _.extend(parameters.options, {limit: parseInt(terms.limit)}); - } - - // limit to "maxLimit" items at most when limit is undefined, equal to 0, or superior to maxLimit - if(!parameters.options.limit || parameters.options.limit === 0 || parameters.options.limit > maxLimit) { - parameters.options.limit = maxLimit; - } - return parameters; -} - -addCallback("comments.parameters", CommentsMaxLimit); diff --git a/packages/vulcan-comments/lib/permissions.js b/packages/vulcan-comments/lib/permissions.js deleted file mode 100644 index bbc238ca..00000000 --- a/packages/vulcan-comments/lib/permissions.js +++ /dev/null @@ -1,24 +0,0 @@ -import Users from 'meteor/vulcan:users'; - -const guestsActions = [ - "comments.view" -]; -Users.groups.guests.can(guestsActions); - -const membersActions = [ - "comments.view", - "comments.new", - "comments.edit.own", - "comments.remove.own", - "comments.upvote", - "comments.cancelUpvote", - "comments.downvote", - "comments.cancelDownvote" -]; -Users.groups.members.can(membersActions); - -const adminActions = [ - "comments.edit.all", - "comments.remove.all" -]; -Users.groups.admins.can(adminActions); \ No newline at end of file diff --git a/packages/vulcan-comments/lib/resolvers.js b/packages/vulcan-comments/lib/resolvers.js deleted file mode 100644 index f5750320..00000000 --- a/packages/vulcan-comments/lib/resolvers.js +++ /dev/null @@ -1,48 +0,0 @@ -// root resolvers: basic list, single, and total query resolvers -const resolvers = { - - list: { - - name: 'commentsList', - - resolver(root, {terms}, {currentUser, Users, Comments}) { - - // get selector and options from terms and perform Mongo query - let {selector, options} = Comments.getParameters(terms); - options.skip = terms.offset; - const comments = Comments.find(selector, options).fetch(); - - // restrict documents fields - const restrictedComments = Users.restrictViewableFields(currentUser, Comments, comments); - - // prime the cache - restrictedComments.forEach(comment => Comments.loader.prime(comment._id, comment)); - - return restrictedComments; - }, - - }, - - single: { - - name: 'commentsSingle', - - async resolver(root, {documentId}, {currentUser, Users, Comments}) { - const comment = await Comments.loader.load(documentId); - return Users.restrictViewableFields(currentUser, Comments, comment); - }, - - }, - - total: { - - name: 'commentsTotal', - // broken because it doesn't take any arguments in the query - resolver(root, {terms}, context) { - return context.Comments.find({postId: terms.postId}).count(); - }, - - } -}; - -export default resolvers; diff --git a/packages/vulcan-comments/lib/server.js b/packages/vulcan-comments/lib/server.js deleted file mode 100644 index 0d05e11b..00000000 --- a/packages/vulcan-comments/lib/server.js +++ /dev/null @@ -1,3 +0,0 @@ -import Comments from './modules.js'; - -export default Comments; \ No newline at end of file diff --git a/packages/vulcan-comments/lib/views.js b/packages/vulcan-comments/lib/views.js deleted file mode 100644 index 697e4f6a..00000000 --- a/packages/vulcan-comments/lib/views.js +++ /dev/null @@ -1,22 +0,0 @@ -import Comments from './collection.js'; - -// will be common to all other view unless specific properties are overwritten -Comments.addDefaultView(function (terms) { - return { - options: {limit: 1000} - }; -}); - -Comments.addView("postComments", function (terms) { - return { - selector: {postId: terms.postId}, - options: {sort: {postedAt: 1}} // oldest to newest - }; -}); - -Comments.addView("userComments", function (terms) { - return { - selector: {userId: terms.userId}, - options: {sort: {postedAt: -1}} - }; -}); diff --git a/packages/vulcan-comments/package.js b/packages/vulcan-comments/package.js deleted file mode 100644 index b8dea44c..00000000 --- a/packages/vulcan-comments/package.js +++ /dev/null @@ -1,20 +0,0 @@ -Package.describe({ - name: "vulcan:comments", - summary: "Vulcan comments package", - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom(['METEOR@1.0']); - - api.use([ - 'vulcan:core@1.7.0', - 'vulcan:posts@1.7.0', - ]); - - api.mainModule("lib/server.js", "server"); - api.mainModule("lib/client.js", "client"); - -}); diff --git a/packages/vulcan-core/README.md b/packages/vulcan-core/README.md deleted file mode 100644 index 64fdd9ab..00000000 --- a/packages/vulcan-core/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan core package, used internally. \ No newline at end of file diff --git a/packages/vulcan-core/lib/client/main.js b/packages/vulcan-core/lib/client/main.js deleted file mode 100644 index 094a9a67..00000000 --- a/packages/vulcan-core/lib/client/main.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../modules/index.js'; diff --git a/packages/vulcan-core/lib/modules/callbacks.js b/packages/vulcan-core/lib/modules/callbacks.js deleted file mode 100644 index e70209aa..00000000 --- a/packages/vulcan-core/lib/modules/callbacks.js +++ /dev/null @@ -1,21 +0,0 @@ -import { addCallback, getActions } from 'meteor/vulcan:lib'; - -/* - - Core callbacks - -*/ - -/** - * @summary Clear flash messages marked as seen when the route changes - * @param {Object} Item needed by `runCallbacks` to iterate on, unused here - * @param {Object} Redux store reference instantiated on the current connected client - * @param {Object} Apollo Client reference instantiated on the current connected client - */ -function RouterClearMessages(unusedItem, store, apolloClient) { - store.dispatch(getActions().messages.clearSeen()); - - return unusedItem; -} - -addCallback('router.onUpdate', RouterClearMessages); diff --git a/packages/vulcan-core/lib/modules/components/App.jsx b/packages/vulcan-core/lib/modules/components/App.jsx deleted file mode 100644 index 2cadf5d0..00000000 --- a/packages/vulcan-core/lib/modules/components/App.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Components, registerComponent, getSetting, Strings } from 'meteor/vulcan:lib'; -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { IntlProvider, intlShape} from 'meteor/vulcan:i18n'; -import withCurrentUser from '../containers/withCurrentUser.js'; - -class App extends PureComponent { - - getLocale() { - return getSetting('locale', 'en'); - } - - getChildContext() { - - const messages = Strings[this.getLocale()] || {}; - const intlProvider = new IntlProvider({locale: this.getLocale()}, messages); - const { intl } = intlProvider.getChildContext(); - return { - intl: intl - }; - } - - render() { - - const currentRoute = _.last(this.props.routes); - const LayoutComponent = currentRoute.layoutName ? Components[currentRoute.layoutName] : Components.Layout; - - return ( - -
- - - { this.props.currentUserLoading ? : this.props.children } - -
-
- ); - } -} - -App.propTypes = { - currentUserLoading: PropTypes.bool, -} - -App.childContextTypes = { - intl: intlShape, -} - -App.displayName = 'App'; - -registerComponent('App', App, withCurrentUser); - -export default App; diff --git a/packages/vulcan-core/lib/modules/components/Card.jsx b/packages/vulcan-core/lib/modules/components/Card.jsx deleted file mode 100644 index f61aadc9..00000000 --- a/packages/vulcan-core/lib/modules/components/Card.jsx +++ /dev/null @@ -1,143 +0,0 @@ -import { registerComponent, Components } from 'meteor/vulcan:lib'; -import { intlShape, FormattedMessage } from 'meteor/vulcan:i18n'; -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import moment from 'moment'; -import Button from 'react-bootstrap/lib/Button'; - -const getLabel = (fieldName, collection, intl) => { - const schema = collection.simpleSchema()._schema; - const fieldSchema = schema[fieldName]; - return intl.formatMessage({id: `${collection._name}.${fieldName}`, defaultMessage: fieldSchema.label}); -} - -const getTypeName = (fieldName, collection) => { - const schema = collection.simpleSchema()._schema; - const fieldSchema = schema[fieldName]; - const type = fieldSchema.type.singleType; - const typeName = typeof type === 'function' ? type.name : type; - return typeName; -} - -const parseImageUrl = value => { - const isImage = ['.png', '.jpg', '.gif'].indexOf(value.substr(-4)) !== -1 || ['.webp', '.jpeg' ].indexOf(value.substr(-5)) !== -1; - return isImage ? - {value}/ : - ; -} - -const LimitedString = ({ string }) => -
- {string.indexOf(' ') === -1 && string.length > 30 ? - {string.substr(0,30)}… : - {string} - } -
- -const getFieldValue = (value, typeName) => { - - if (!value) { - return '' - } - - if (Array.isArray(value)) { - typeName = 'Array'; - } - - switch (typeName) { - - case 'Boolean': - case 'boolean': - case 'Number': - case 'number': - case 'SimpleSchema.Integer': - return {value}; - - case 'Array': - return
    {value.map((item, index) =>
  1. {getFieldValue(item, typeof item)}
  2. )}
- - case 'Object': - case 'object': - return ( - - - {_.map(value, (value, key) => - - - - - )} - -
{key}{getFieldValue(value, typeof value)}
- ) - - case 'Date': - return moment(new Date(value)).format('dddd, MMMM Do YYYY, h:mm:ss'); - - default: - return parseImageUrl(value); - } -} - -const CardItem = ({label, value, typeName}) => - - {label} - {getFieldValue(value, typeName)} - - -const CardEdit = (props, context) => - - - }> - - - - - -CardEdit.contextTypes = { intl: intlShape }; - -const CardEditForm = ({ collection, document, closeModal }) => - { - closeModal(); - }} - /> - -const Card = ({className, collection, document, currentUser, fields}, {intl}) => { - - const fieldNames = fields ? fields : _.without(_.keys(document), '__typename'); - const canEdit = currentUser && collection.options.mutations.edit.check(currentUser, document); - - return ( -
- - - {canEdit ? : null} - {fieldNames.map((fieldName, index) => - - )} - -
-
- ); -}; - -Card.displayName = "Card"; - -Card.propTypes = { - className: PropTypes.string, - collection: PropTypes.object, - document: PropTypes.object, - currentUser: PropTypes.object, - fields: PropTypes.array, -} - -Card.contextTypes = { - intl: intlShape -} - -registerComponent('Card', Card); \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/components/Datatable.jsx b/packages/vulcan-core/lib/modules/components/Datatable.jsx deleted file mode 100644 index 137664fa..00000000 --- a/packages/vulcan-core/lib/modules/components/Datatable.jsx +++ /dev/null @@ -1,214 +0,0 @@ -import { registerComponent, Components } from 'meteor/vulcan:lib'; -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import withCurrentUser from '../containers/withCurrentUser.js'; -import withList from '../containers/withList.js'; -import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -import Button from 'react-bootstrap/lib/Button'; - -/* - -Datatable Component - -*/ - -// see: http://stackoverflow.com/questions/1909441/jquery-keyup-delay -const delay = (function(){ - var timer = 0; - return function(callback, ms){ - clearTimeout (timer); - timer = setTimeout(callback, ms); - }; -})(); - -class Datatable extends PureComponent { - - constructor() { - super(); - this.updateQuery = this.updateQuery.bind(this); - this.state = { - value: '', - query: '' - } - } - - updateQuery(e) { - e.persist() - e.preventDefault(); - this.setState({ - value: e.target.value - }); - delay(() => { - this.setState({ - query: e.target.value - }); - }, 700 ); - } - - render() { - - const options = { - collection: this.props.collection, - ...this.props.options - } - - const DatatableWithList = withList(options)(Components.DatatableContents); - - return ( -
- - -
- ) - } -} - -Datatable.propTypes = { - collection: PropTypes.object, - columns: PropTypes.array, - options: PropTypes.object, - showEdit: PropTypes.bool, -} -registerComponent('Datatable', Datatable, withCurrentUser); - -/* - -DatatableHeader Component - -*/ -const DatatableHeader = ({ collection, column }, { intl }) => { - const schema = collection.simpleSchema()._schema; - const columnName = typeof column === 'string' ? column : column.name; - - /* - - use either: - - 1. the column name translation - 2. the column name label in the schema (if the column name matches a schema field) - 3. the raw column name. - - */ - const formattedLabel = intl.formatMessage({ id: `${collection._name}.${columnName}`, defaultMessage: schema[columnName] ? schema[columnName].label : columnName }); - return {formattedLabel}; -} - -DatatableHeader.contextTypes = { - intl: intlShape -}; - -registerComponent('DatatableHeader', DatatableHeader); - -/* - -DatatableContents Component - -*/ - -const DatatableContents = (props) => { - const {collection, columns, results, loading, loadMore, count, totalCount, networkStatus, showEdit} = props; - - if (loading) { - return ; - } - - const isLoadingMore = networkStatus === 2; - const hasMore = totalCount > results.length; - - return ( -
- - - - {_.sortBy(columns, column => column.order).map((column, index) => )} - {showEdit ? : null} - - - - {results.map((document, index) => )} - -
-
- {hasMore ? - isLoadingMore ? - - : - : null - } -
-
- ) -} -registerComponent('DatatableContents', DatatableContents); - -/* - -DatatableRow Component - -*/ -const DatatableRow = ({ collection, columns, document, showEdit }, { intl }) => { - return ( - - - {_.sortBy(columns, column => column.order).map((column, index) => )} - - {showEdit ? - - } - > - - - - : null} - - - ) -} -registerComponent('DatatableRow', DatatableRow); - -DatatableRow.contextTypes = { - intl: intlShape -}; -/* - -DatatableEditForm Component - -*/ -const DatatableEditForm = ({ collection, document, closeModal }) => - { - closeModal(); - }} - /> -registerComponent('DatatableEditForm', DatatableEditForm); - - -/* - -DatatableCell Component - -*/ -const DatatableCell = ({ column, document }) => { - const Component = column.component || Components[column.componentName] || Components.DatatableDefaultCell; - - return ( - - ) -} -registerComponent('DatatableCell', DatatableCell); - -/* - -DatatableDefaultCell Component - -*/ - -const DatatableDefaultCell = ({ column, document }) => -
{typeof column === 'string' ? document[column] : document[column.name]}
- -registerComponent('DatatableDefaultCell', DatatableDefaultCell); diff --git a/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx b/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx deleted file mode 100644 index 63126b5c..00000000 --- a/packages/vulcan-core/lib/modules/components/DynamicLoading.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { Components, registerComponent } from 'meteor/vulcan:lib'; - -const DynamicLoading = ({ isLoading, pastDelay, error }) => { - if (isLoading && pastDelay) { - return ; - } else if (error && !isLoading) { - console.log(error) - return

Error!

; - } else { - return null; - } -} - -registerComponent('DynamicLoading', DynamicLoading); - -export default DynamicLoading; \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/components/Error404.jsx b/packages/vulcan-core/lib/modules/components/Error404.jsx deleted file mode 100644 index 79f9efe5..00000000 --- a/packages/vulcan-core/lib/modules/components/Error404.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import { registerComponent } from 'meteor/vulcan:lib'; -import React from 'react'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; - -const Error404 = () => { - return ( -
-

-
- ) -} - -Error404.displayName = "Error404"; - -registerComponent('Error404', Error404); - -export default Error404; \ No newline at end of file diff --git a/packages/vulcan-core/lib/modules/components/HeadTags.jsx b/packages/vulcan-core/lib/modules/components/HeadTags.jsx deleted file mode 100644 index 88a5d223..00000000 --- a/packages/vulcan-core/lib/modules/components/HeadTags.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import Helmet from 'react-helmet'; -import { registerComponent, Utils, getSetting, Head } from 'meteor/vulcan:lib'; - -class HeadTags extends PureComponent { - render() { - - const url = !!this.props.url ? this.props.url : Utils.getSiteUrl(); - const title = !!this.props.title ? this.props.title : getSetting("title", "My App"); - const description = !!this.props.description ? this.props.description : getSetting("tagline") || getSetting("description"); - - // default image meta: logo url, else site image defined in settings - let image = !!getSetting("siteImage") ? getSetting("siteImage"): getSetting("logoUrl"); - - // overwrite default image if one is passed as props - if (!!this.props.image) { - image = this.props.image; - } - - // add site url base if the image is stored locally - if (!!image && image.indexOf('//') === -1) { - image = Utils.getSiteUrl() + image; - } - - return ( -
- - - {title} - - - - - - {/* facebook */} - - - - - - - {/* twitter */} - - - - - - - - - {Head.meta.map((tag, index) => )} - {Head.link.map((tag, index) => )} - {Head.script.map((tag, index) => `; - }, - - // get data from res._injectPayload - getData(res, key) { - if (res._injectPayload) { - // same as _.clone(res._injectPayload[key]); - const data = res._injectPayload[key]; - const clonedData = EJSON.parse(EJSON.stringify(data)); - return clonedData; - } - return null; - }, -}; - -// **injectDataMiddleware, Notes that it must after router connect handler** -webAppConnectHandlersUse(function injectDataMiddleware(req, res, next) { - if (res._injectHtml) { - req.dynamicHead = req.dynamicHead || ''; - req.dynamicHead += res._injectHtml; - } - next(); -}, { order: 900 }); diff --git a/packages/vulcan-lib/lib/server/intl_polyfill.js b/packages/vulcan-lib/lib/server/intl_polyfill.js deleted file mode 100644 index d9e092de..00000000 --- a/packages/vulcan-lib/lib/server/intl_polyfill.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - -intl polyfill. See https://github.com/andyearnshaw/Intl.js/ - -*/ - -import { getSetting } from '../modules/settings.js'; - -var areIntlLocalesSupported = require('intl-locales-supported'); - -var localesMyAppSupports = [ - getSetting("locale", "en") -]; - -if (global.Intl) { - // Determine if the built-in `Intl` has the locale data we need. - if (!areIntlLocalesSupported(localesMyAppSupports)) { - // `Intl` exists, but it doesn't have the data we need, so load the - // polyfill and replace the constructors with need with the polyfill's. - var IntlPolyfill = require('intl'); - Intl.NumberFormat = IntlPolyfill.NumberFormat; - Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat; - } -} else { - // No `Intl`, so use and load the polyfill. - global.Intl = require('intl'); -} \ No newline at end of file diff --git a/packages/vulcan-lib/lib/server/main.js b/packages/vulcan-lib/lib/server/main.js deleted file mode 100644 index 9705c0a6..00000000 --- a/packages/vulcan-lib/lib/server/main.js +++ /dev/null @@ -1,9 +0,0 @@ -import './oauth_config.js'; -import './apollo_server.js'; -import './intl_polyfill.js'; - -export * from './meteor_patch.js'; -export * from '../modules/index.js'; -export * from './mutations.js'; -export * from './render_context.js'; -export * from './inject_data.js'; diff --git a/packages/vulcan-lib/lib/server/meteor_patch.js b/packages/vulcan-lib/lib/server/meteor_patch.js deleted file mode 100644 index d5bdaaf7..00000000 --- a/packages/vulcan-lib/lib/server/meteor_patch.js +++ /dev/null @@ -1,81 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { WebApp } from 'meteor/webapp'; - -import { Utils } from '../modules/index.js'; - -// clever webAppConnectHandlersUse -export const webAppConnectHandlersUse = (name, route, fn, options) => { - // init - if (typeof name === 'function') { - options = route; - fn = name; - route = '/'; - name = undefined; - } else if (name[0] === '/') { - options = fn; - fn = route; - route = name; - name = undefined; - } else if (typeof route === 'function') { - options = fn; - fn = route; - route = '/'; - } - options = options || {}; - route = options.route ? options.route : route; - - // newfn - let done = false; - const newfn = (req, res, next) => { - if (!fn.stack && !fn._router && done && options.once) { - next(); - return; - } - done = true; - - fn(req, res, next); - - if (!fn.stack && !fn._router && options.autoNext) { - next(); - } - }; - - // use it - let connectHandlers; - if (options.raw) { - connectHandlers = WebApp.rawConnectHandlers; - } else { - connectHandlers = WebApp.connectHandlers; - } - connectHandlers.use(route, newfn); - - // get handle - let handle; - if (options.unshift) { - const item = connectHandlers.stack.pop(); - connectHandlers.stack.unshift(item); - handle = connectHandlers.stack[0].handle; - } else { - handle = connectHandlers.stack[connectHandlers.stack.length - 1].handle; - } - - // copy options to handle - Object.keys(options).forEach((key) => { - handle[key] = options[key]; - }); -}; - -webAppConnectHandlersUse(function sortConnectHandlersMiddleware(req, res, next) { - WebApp.rawConnectHandlers.stack.forEach((item) => { - if (isNaN(item.handle.order)) { - item.handle.order = 100; - } - }); - WebApp.connectHandlers.stack.forEach((item) => { - if (isNaN(item.handle.order)) { - item.handle.order = 100; - } - }); - WebApp.rawConnectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order); - WebApp.connectHandlers.stack.sort((a, b) => a.handle.order - b.handle.order); -}, { order: 0, autoNext: true, once: true, unshift: true }); diff --git a/packages/vulcan-lib/lib/server/mutations.js b/packages/vulcan-lib/lib/server/mutations.js deleted file mode 100644 index d33b81f6..00000000 --- a/packages/vulcan-lib/lib/server/mutations.js +++ /dev/null @@ -1,220 +0,0 @@ -/* - -Mutations have four steps: - -1. Validation - -If the mutation call is not trusted (i.e. it comes from a GraphQL mutation), -we'll run all validate steps: - -- Check that the current user has permission to insert/edit each field. -- Add userId to document (insert only). -- Run validation callbacks. - -2. Sync Callbacks - -The second step is to run the mutation argument through all the sync callbacks. - -3. Operation - -We then perform the insert/update/remove operation. - -4. Async Callbacks - -Finally, *after* the operation is performed, we execute any async callbacks. -Being async, they won't hold up the mutation and slow down its response time -to the client. - -*/ - -import { Utils, runCallbacks, runCallbacksAsync } from '../modules/index.js'; - -export const newMutation = ({ collection, document, currentUser, validate, context }) => { - - // console.log("// newMutation") - // console.log(collection._name) - // console.log(document) - - // we don't want to modify the original document - let newDocument = Object.assign({}, document); - - const collectionName = collection._name; - const schema = collection.simpleSchema()._schema; - - // if document is not trusted, run validation steps - if (validate) { - - // validate document - collection.simpleSchema().validate(document); - - // check that the current user has permission to insert each field - _.keys(newDocument).forEach(fieldName => { - var field = schema[fieldName]; - if (!field || !context.Users.canInsertField (currentUser, field)) { - throw new Error(Utils.encodeIntlError({id: 'app.disallowed_property_detected', value: fieldName})); - } - }); - - // run validation callbacks - newDocument = runCallbacks(`${collectionName}.new.validate`, newDocument, currentUser); - } - - // check if userId field is in the schema and add it to document if needed - const userIdInSchema = Object.keys(schema).find(key => key === 'userId'); - if (!!userIdInSchema && !newDocument.userId) newDocument.userId = currentUser._id; - - // run onInsert step - _.keys(schema).forEach(fieldName => { - if (schema[fieldName].onInsert) { - const autoValue = schema[fieldName].onInsert(newDocument, currentUser); - if (autoValue) { - newDocument[fieldName] = autoValue; - } - } - }); - - // TODO: find that info in GraphQL mutations - // if (Meteor.isServer && this.connection) { - // post.userIP = this.connection.clientAddress; - // post.userAgent = this.connection.httpHeaders["user-agent"]; - // } - - // run sync callbacks - newDocument = runCallbacks(`${collectionName}.new.sync`, newDocument, currentUser); - - // add _id to document - newDocument._id = collection.insert(newDocument); - - // get fresh copy of document from db - const insertedDocument = collection.findOne(newDocument._id); - - // run async callbacks - // note: query for document to get fresh document with collection-hooks effects applied - runCallbacksAsync(`${collectionName}.new.async`, insertedDocument, currentUser, collection); - - // console.log("// new mutation finished:") - // console.log(newDocument) - - return newDocument; -} - -export const editMutation = ({ collection, documentId, set, unset = {}, currentUser, validate, context }) => { - - // console.log("// editMutation") - // console.log(collection._name) - // console.log(documentId) - // console.log(set) - // console.log(unset) - - const collectionName = collection._name; - const schema = collection.simpleSchema()._schema; - - // build mongo modifier from arguments - let modifier = {$set: set, $unset: unset}; - - // get original document from database - let document = collection.findOne(documentId); - - // if document is not trusted, run validation steps - if (validate) { - - // validate modifiers - collection.simpleSchema().validate({$set: set, $unset: unset}, { modifier: true }); - - // check that the current user has permission to edit each field - const modifiedProperties = _.keys(set).concat(_.keys(unset)); - modifiedProperties.forEach(function (fieldName) { - var field = schema[fieldName]; - if (!field || !context.Users.canEditField(currentUser, field, document)) { - throw new Error(Utils.encodeIntlError({id: 'app.disallowed_property_detected', value: fieldName})); - } - }); - - // run validation callbacks - modifier = runCallbacks(`${collectionName}.edit.validate`, modifier, document, currentUser); - } - - // run onEdit step - _.keys(schema).forEach(fieldName => { - - if (schema[fieldName].onEdit) { - const autoValue = schema[fieldName].onEdit(modifier, document, currentUser); - if (typeof autoValue !== 'undefined') { - if (autoValue === null) { - // if any autoValue returns null, then unset the field - modifier.$unset[fieldName] = true; - } else { - modifier.$set[fieldName] = autoValue; - } - } - } - }); - - // run sync callbacks (on mongo modifier) - modifier = runCallbacks(`${collectionName}.edit.sync`, modifier, document, currentUser); - - // remove empty modifiers - if (_.isEmpty(modifier.$set)) { - delete modifier.$set; - } - if (_.isEmpty(modifier.$unset)) { - delete modifier.$unset; - } - - // update document - collection.update(documentId, modifier, {removeEmptyStrings: false}); - - // get fresh copy of document from db - const newDocument = collection.findOne(documentId); - - // clear cache if needed - if (collection.loader) { - collection.loader.clear(documentId); - } - - // run async callbacks - runCallbacksAsync(`${collectionName}.edit.async`, newDocument, document, currentUser, collection); - - // console.log("// edit mutation finished") - // console.log(modifier) - // console.log(newDocument) - - return newDocument; -} - -export const removeMutation = ({ collection, documentId, currentUser, validate, context }) => { - - // console.log("// removeMutation") - // console.log(collection._name) - // console.log(documentId) - - const collectionName = collection._name; - const schema = collection.simpleSchema()._schema; - - let document = collection.findOne(documentId); - - // if document is not trusted, run validation callbacks - if (validate) { - document = runCallbacks(`${collectionName}.remove.validate`, document, currentUser); - } - - // run onRemove step - _.keys(schema).forEach(fieldName => { - if (schema[fieldName].onRemove) { - schema[fieldName].onRemove(document, currentUser); - } - }); - - runCallbacks(`${collectionName}.remove.sync`, document, currentUser); - - collection.remove(documentId); - - // clear cache if needed - if (collection.loader) { - collection.loader.clear(documentId); - } - - runCallbacksAsync(`${collectionName}.remove.async`, document, currentUser, collection); - - return document; -} diff --git a/packages/vulcan-lib/lib/server/oauth_config.js b/packages/vulcan-lib/lib/server/oauth_config.js deleted file mode 100644 index d0693b0e..00000000 --- a/packages/vulcan-lib/lib/server/oauth_config.js +++ /dev/null @@ -1,9 +0,0 @@ -const services = Meteor.settings.oAuth; - -if (services) { - _.keys(services).forEach(serviceName => { - ServiceConfiguration.configurations.upsert({service: serviceName}, { - $set: services[serviceName] - }); - }); -} diff --git a/packages/vulcan-lib/lib/server/render_context.js b/packages/vulcan-lib/lib/server/render_context.js deleted file mode 100644 index 5e67c616..00000000 --- a/packages/vulcan-lib/lib/server/render_context.js +++ /dev/null @@ -1,176 +0,0 @@ -import { createMemoryHistory } from 'react-router'; -import { compose } from 'redux'; -import cookieParser from 'cookie-parser'; - -import { Meteor } from 'meteor/meteor'; -import { DDP } from 'meteor/ddp'; -import { Accounts } from 'meteor/accounts-base'; -import { RoutePolicy } from 'meteor/routepolicy'; -import { WebApp } from 'meteor/webapp'; - -import { - createApolloClient, - configureStore, getActions, getReducers, getMiddlewares, - Utils, -} from '../modules/index.js'; - -import { webAppConnectHandlersUse } from './meteor_patch.js'; - -const Fiber = Npm.require('fibers'); - -// check the req url -function isAppUrl(req) { - const url = req.url; - if (url === '/favicon.ico' || url === '/robots.txt') { - return false; - } - - if (url === '/app.manifest') { - return false; - } - - // Avoid serving app HTML for declared routes such as /sockjs/. - if (RoutePolicy.classify(url)) { - return false; - } - - // we only need to support HTML pages only for browsers - // Facebook's scraper uses a request header Accepts: */* - // so allow either - const facebookAcceptsHeader = new RegExp("/*\/*/"); - return /html/.test(req.headers.accept) || facebookAcceptsHeader.test(req.headers.accept); -} - -// for meteor.user -const LoginContext = function LoginContext(loginToken) { - this._loginToken = loginToken; - - // get the user - if (Meteor.users) { - // check to make sure, we've the loginToken, - // otherwise a random user will fetched from the db - let user; - if (loginToken) { - const hashedToken = loginToken && Accounts._hashLoginToken(loginToken); - const query = { 'services.resume.loginTokens.hashedToken': hashedToken }; - const options = { fields: { _id: 1 } }; - user = Meteor.users.findOne(query, options); - } - - if (user) { - this.userId = user._id; - } - } -}; - -// for req.cookies -webAppConnectHandlersUse(cookieParser(), { order: 10, name: 'cookieParserMiddleware' }); - -// initRenderContextMiddleware -webAppConnectHandlersUse(Meteor.bindEnvironment(function initRenderContextMiddleware(req, res, next) { - // check the req url - if (!isAppUrl(req)) { - next(); - return; - } - - // init - const history = createMemoryHistory(req.url); - const loginToken = req.cookies && req.cookies.meteor_login_token; - const apolloClient = createApolloClient({ loginToken: loginToken }); - let actions = {}; - let reducers = { apollo: apolloClient.reducer() }; - let middlewares = [Utils.defineName(apolloClient.middleware(), 'apolloClientMiddleware')]; - - // renderContext object - req.renderContext = { - history, - loginToken, - apolloClient, - addAction(addedAction) { // context.addAction: add action to renderContext - actions = { ...actions, ...addedAction }; - return this.getActions(); - }, - getActions() { // SSR actions = server actions + renderContext actions - return { ...getActions(), ...actions }; - }, - addReducer(addedReducer) { // context.addReducer: add reducer to renderContext - reducers = { ...reducers, ...addedReducer }; - return this.getReducers(); - }, - getReducers() { // SSR reducers = server reducers + renderContext reducers - return { ...getReducers(), ...reducers }; - }, - addMiddleware(middlewareOrMiddlewareArray) { // context.addMiddleware: add middleware to renderContext - const addedMiddleware = Array.isArray(middlewareOrMiddlewareArray) ? middlewareOrMiddlewareArray : [middlewareOrMiddlewareArray]; - middlewares = [...middlewares, ...addedMiddleware]; - return this.getMiddlewares(); - }, - getMiddlewares() { // SSR middlewares = server middlewares + renderContext middlewares - return [...getMiddlewares(), ...middlewares]; - }, - }; - - // create store - req.renderContext.store = configureStore(req.renderContext.getReducers, {}, (store) => { - let chain, newDispatch; - return next => (action) => { - if (!chain) { - chain = req.renderContext.getMiddlewares().map(middleware => middleware(store)); - newDispatch = compose(...chain)(next) - } - return newDispatch(action); - }; - }) - - // for meteor.user - req.loginContext = new LoginContext(loginToken); - - next(); -}), { order: 20 }); - -// render context object -export const renderContext = new Meteor.EnvironmentVariable(); - -// render context get function -export const getRenderContext = () => renderContext.get(); - -// withRenderContextEnvironment -export const withRenderContextEnvironment = (fn, options = {}) => { - // set newfn - const newfn = (req, res, next) => { - if (!isAppUrl(req)) { - next(); - return; - } - - Fiber.current._meteor_dynamics = Fiber.current._meteor_dynamics || []; - Fiber.current._meteor_dynamics[DDP._CurrentInvocation.slot] = req.loginContext; - Fiber.current._meteor_dynamics[renderContext.slot] = req.renderContext; - - fn(req.renderContext, req, res, next); - - if (options.autoNext) { - next(); - } - }; - - // get evfn - const evfn = Meteor.bindEnvironment(newfn); - - // use it - WebApp.connectHandlers.use(evfn); - - // get handle - const handle = WebApp.connectHandlers.stack[WebApp.connectHandlers.stack.length - 1].handle; - - // copy options to handle - Object.keys(options).forEach((key) => { - handle[key] = options[key]; - }); -}; - -// withRenderContext make it easy to access context -export const withRenderContext = (func, options = {}) => { - withRenderContextEnvironment(func, { ...options, autoNext: true }); -}; diff --git a/packages/vulcan-lib/package.js b/packages/vulcan-lib/package.js deleted file mode 100644 index 982bed95..00000000 --- a/packages/vulcan-lib/package.js +++ /dev/null @@ -1,51 +0,0 @@ -Package.describe({ - name: 'vulcan:lib', - summary: 'Vulcan libraries.', - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom(['METEOR@1.0']); - - var packages = [ - - 'buffer@0.0.0', // see https://github.com/meteor/meteor/issues/8645 - - // Meteor packages - - 'meteor-base@1.1.0', - 'mongo', - 'tracker', - 'service-configuration', - 'standard-minifiers@1.1.0', - 'modules@0.9.2', - 'accounts-base', - 'check', - 'http', - 'email', - 'ecmascript@0.8.2', - 'service-configuration', - 'shell-server@0.2.4', - - // Third-party packages - - // 'aldeed:collection2-core@2.0.0', - 'meteorhacks:picker@1.0.3', - 'percolatestudio:synced-cron@1.1.0', - 'meteorhacks:inject-initial@1.0.4', - ]; - - api.use(packages); - - api.imply(packages); - - api.export([ - 'Vulcan' - ]); - - api.mainModule('lib/server/main.js', 'server'); - api.mainModule('lib/client/main.js', 'client'); - -}); diff --git a/packages/vulcan-notifications/package.js b/packages/vulcan-notifications/package.js index b341688e..cee26858 100644 --- a/packages/vulcan-notifications/package.js +++ b/packages/vulcan-notifications/package.js @@ -10,14 +10,9 @@ Package.onUse(function (api) { api.versionsFrom("METEOR@1.0"); api.use([ - 'vulcan:core@1.7.0', - 'vulcan:email@1.7.0', + 'vulcan:core', + 'vulcan:email', ]); - - api.use([ - 'vulcan:posts@1.7.0', - 'vulcan:comments@1.7.0', - ], {weak: true}); api.mainModule('lib/modules.js', ['client', 'server']); diff --git a/packages/vulcan-posts/README.md b/packages/vulcan-posts/README.md deleted file mode 100644 index b77379a7..00000000 --- a/packages/vulcan-posts/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan posts package, used internally. \ No newline at end of file diff --git a/packages/vulcan-posts/TESTS.md b/packages/vulcan-posts/TESTS.md deleted file mode 100644 index 7c191460..00000000 --- a/packages/vulcan-posts/TESTS.md +++ /dev/null @@ -1,34 +0,0 @@ - -### Client-Side Operations - -#### Insert - -- `Posts.insert()` should always fail (no `allow/deny` validators are set on `Posts` collection for `insert` operation). - -#### Update on allowed property - -`Posts.update(postId, {$set: {title: "Hello World"}})` should return 1 only if current user is admin, or current user owns the post. - -#### Update on admin-only property - -`Posts.update(postId, {$set: {status: 1}})` should return 1 only if current user is admin. - -#### Update on disallowed property - -`Posts.update(postId, {$set: {baseScore: 999}})` should always throw an error. - - - -### Meteor Method Calls - -#### submitPost - -#### editPost - - - -### Server-Side Calls - -#### Posts.submit() - -`Posts.submit(post)` should insert a post in the database. \ No newline at end of file diff --git a/packages/vulcan-posts/lib/admin.js b/packages/vulcan-posts/lib/admin.js deleted file mode 100644 index 62f1e3c7..00000000 --- a/packages/vulcan-posts/lib/admin.js +++ /dev/null @@ -1,14 +0,0 @@ -import { extendFragment, addAdminColumn } from 'meteor/vulcan:core'; -import AdminUsersPosts from './components/AdminUsersPosts'; - -extendFragment('UsersAdmin', ` - posts(limit: 5){ - ...PostsPage - } -`); - -addAdminColumn({ - name: 'posts', - order: 50, - component: AdminUsersPosts -}); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/callbacks/callbacks_other.js b/packages/vulcan-posts/lib/callbacks/callbacks_other.js deleted file mode 100644 index 2a514259..00000000 --- a/packages/vulcan-posts/lib/callbacks/callbacks_other.js +++ /dev/null @@ -1,78 +0,0 @@ -import Posts from '../collection.js' -import Users from 'meteor/vulcan:users'; -import { addCallback, getSetting } from 'meteor/vulcan:core'; -import Events from 'meteor/vulcan:events'; - -// ------------------------------------- posts.remove.sync -------------------------------- // - -function PostsRemoveOperations (post) { - Users.update({_id: post.userId}, {$inc: {"postCount": -1}}); - return post; -} -addCallback("posts.remove.sync", PostsRemoveOperations); - -// ------------------------------------- posts.approve.async -------------------------------- // - -/** - * @summary set postedAt when a post is approved and it doesn't have a postedAt date - */ -function PostsSetPostedAt (modifier, post) { - if (!modifier.$set.postedAt && !post.postedAt) { - modifier.$set.postedAt = new Date(); - if (modifier.$unset) { - delete modifier.$unset.postedAt; - } - } - return modifier; -} -addCallback("posts.approve.sync", PostsSetPostedAt); - -// ------------------------------------- users.remove.async -------------------------------- // - -function UsersRemoveDeletePosts (user, options) { - if (options.deletePosts) { - Posts.remove({userId: user._id}); - } else { - // not sure if anything should be done in that scenario yet - // Posts.update({userId: userId}, {$set: {author: "\[deleted\]"}}, {multi: true}); - } -} -addCallback("users.remove.async", UsersRemoveDeletePosts); - - -// /** -// * @summary Increase the number of clicks on a post -// * @param {string} postId – the ID of the post being edited -// * @param {string} ip – the IP of the current user -// */ -Posts.increaseClicks = (post, ip) => { - const clickEvent = { - name: 'click', - properties: { - postId: post._id, - ip: ip - } - }; - - if (getSetting('trackClickEvents', true)) { - // make sure this IP hasn't previously clicked on this post - const existingClickEvent = Events.findOne({name: 'click', 'properties.postId': post._id, 'properties.ip': ip}); - - if(!existingClickEvent) { - Events.log(clickEvent); - return Posts.update(post._id, { $inc: { clickCount: 1 }}); - } - } else { - return Posts.update(post._id, { $inc: { clickCount: 1 }}); - } -}; - -function PostsClickTracking(post, ip) { - return Posts.increaseClicks(post, ip); -} - -// track links clicked, locally in Events collection -// note: this event is not sent to segment cause we cannot access the current user -// in our server-side route /out -> sending an event would create a new anonymous -// user: the free limit of 1,000 unique users per month would be reached quickly -addCallback('posts.click.async', PostsClickTracking); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/callbacks/callbacks_posts_edit.js b/packages/vulcan-posts/lib/callbacks/callbacks_posts_edit.js deleted file mode 100644 index 02fe2f31..00000000 --- a/packages/vulcan-posts/lib/callbacks/callbacks_posts_edit.js +++ /dev/null @@ -1,101 +0,0 @@ -import marked from 'marked'; -import Posts from '../collection.js' -import { runCallbacks, runCallbacksAsync, addCallback, getSetting, Utils } from 'meteor/vulcan:core'; - -////////////////////////////////////////////////////// -// posts.edit.sync // -////////////////////////////////////////////////////// - - -/** - * @summary Force sticky to default to false when it's not specified - * (simpleSchema's defaultValue does not work on edit, so do it manually in callback) - */ -function PostsEditForceStickyToFalse (modifier, post) { - if (!modifier.$set.sticky) { - if (modifier.$unset && modifier.$unset.sticky) { - delete modifier.$unset.sticky; - } - modifier.$set.sticky = false; - } - return modifier; -} -addCallback("posts.edit.sync", PostsEditForceStickyToFalse); - -/** - * @summary Set status - */ -function PostsEditSetIsFuture (modifier, post) { - const postTime = new Date(modifier.$set.postedAt).getTime(); - const currentTime = new Date().getTime() + 1000; // why "+ 1000" ?? - if (modifier.$set.postedAt) { - if (postTime > currentTime) { - // if a post's postedAt date is in the future, set isFuture to true - modifier.$set.isFuture = true; - } else if (post.isFuture) { - // else if a post has isFuture to true but its date is in the past, set isFuture to false - modifier.$set.isFuture = false; - } - } - return modifier; -} -addCallback("posts.edit.sync", PostsEditSetIsFuture); - - -function PostsEditRunPostApprovedSyncCallbacks (modifier, post) { - if (modifier.$set && Posts.isApproved(modifier.$set) && !Posts.isApproved(post)) { - modifier = runCallbacks("posts.approve.sync", modifier, post); - } - return modifier; -} -addCallback("posts.edit.sync", PostsEditRunPostApprovedSyncCallbacks); - -/** - * @summary If title is changing, return new slug - */ -function PostsEditSlugify (modifier, post) { - if (modifier.$set && modifier.$set.title) { - modifier.$set.slug = Utils.slugify(modifier.$set.title); - } - return modifier; -} - -addCallback("posts.edit.sync", PostsEditSlugify); - -/** - * @summary If body is changing, update related fields (htmlBody & excerpt) - */ -function PostsEditHTMLContent (modifier, post) { - if (modifier.$set && typeof modifier.$set.body !== 'undefined') { - // excerpt length is configurable via the settings (30 words by default, ~255 characters) - const excerptLength = getSetting('postExcerptLength', 30); - - // extend the modifier - modifier.$set = { - ...modifier.$set, - htmlBody: Utils.sanitize(marked(modifier.$set.body)), - excerpt: Utils.trimHTML(Utils.sanitize(marked(modifier.$set.body)), excerptLength), - }; - } else if (modifier.$unset && modifier.$unset.body) { - // extend the modifier - modifier.$unset = { - ...modifier.$unset, - htmlBody: true, - excerpt: true, - }; - } - - return modifier; -} -addCallback("posts.edit.sync", PostsEditHTMLContent); - -////////////////////////////////////////////////////// -// posts.edit.async // -////////////////////////////////////////////////////// - -function PostsEditRunPostApprovedAsyncCallbacks (post, oldPost) { - if (Posts.isApproved(post) && !Posts.isApproved(oldPost)) { - runCallbacksAsync("posts.approve.async", post); - } -} -addCallback("posts.edit.async", PostsEditRunPostApprovedAsyncCallbacks); diff --git a/packages/vulcan-posts/lib/callbacks/callbacks_posts_new.js b/packages/vulcan-posts/lib/callbacks/callbacks_posts_new.js deleted file mode 100644 index c5a0eda2..00000000 --- a/packages/vulcan-posts/lib/callbacks/callbacks_posts_new.js +++ /dev/null @@ -1,116 +0,0 @@ -import Posts from '../collection.js' -import marked from 'marked'; -import Users from 'meteor/vulcan:users'; -import { addCallback, getSetting, Utils } from 'meteor/vulcan:core'; -import { createError } from 'apollo-errors'; - -////////////////////////////////////////////////////// -// posts.new.validate // -////////////////////////////////////////////////////// - - -/** - * @summary Rate limiting - */ -function PostsNewRateLimit (post, user) { - - if(!Users.isAdmin(user)){ - - var timeSinceLastPost = Users.timeSinceLast(user, Posts), - numberOfPostsInPast24Hours = Users.numberOfItemsInPast24Hours(user, Posts), - postInterval = Math.abs(parseInt(getSetting('postInterval', 30))), - maxPostsPer24Hours = Math.abs(parseInt(getSetting('maxPostsPerDay', 5))); - - // check that user waits more than X seconds between posts - if(timeSinceLastPost < postInterval){ - const RateLimitError = createError('posts.rate_limit_error', {message: 'posts.rate_limit_error'}); - throw new RateLimitError({data: {break: true, value: postInterval-timeSinceLastPost}}); - } - // check that the user doesn't post more than Y posts per day - if(numberOfPostsInPast24Hours >= maxPostsPer24Hours){ - const RateLimitError = createError('posts.max_per_day', {message: 'posts.max_per_day'}); - throw new RateLimitError({data: {break: true, value: maxPostsPer24Hours}}); - } - } - - return post; -} -addCallback("posts.new.validate", PostsNewRateLimit); - -////////////////////////////////////////////////////// -// posts.new.sync // -////////////////////////////////////////////////////// - - -/** - * @summary Set the post's postedAt if it's going to be approved - */ -function PostsSetPostedAt (post, user) { - if (!post.postedAt && Posts.getDefaultStatus(user) === Posts.config.STATUS_APPROVED) post.postedAt = new Date(); - return post; -} -addCallback("posts.new.sync", PostsSetPostedAt); - -/** - * @summary Set the post's isFuture to true if necessary - */ -function PostsNewSetFuture (post, user) { - post.isFuture = post.postedAt && new Date(post.postedAt).getTime() > new Date(post.createdAt).getTime() + 1000; // round up to the second - return post; -} -addCallback("posts.new.sync", PostsNewSetFuture); - -/** - * @summary Force sticky to default to false when it's not specified - */ -function PostsNewSetStickyToFalse (post, user) { - if (!post.sticky) { - post.sticky = false; - } - return post; -} -addCallback("posts.new.sync", PostsNewSetStickyToFalse); - - -/** - * @summary Set the post's slug based on its title - */ -function PostsNewSlugify (post) { - post.slug = Utils.slugify(post.title); - return post; -} -addCallback("posts.new.sync", PostsNewSlugify); - -/** - * @summary Set the post's HTML content & the excerpt based on its possible body - */ -function PostsNewHTMLContent (post) { - if (post.body) { - // excerpt length is configurable via the settings (30 words by default, ~255 characters) - const excerptLength = getSetting('postExcerptLength', 30); - - // extend the post document - post = { - ...post, - htmlBody: Utils.sanitize(marked(post.body)), - excerpt: Utils.trimHTML(Utils.sanitize(marked(post.body)), excerptLength), - }; - } - - return post; -} -addCallback("posts.new.sync", PostsNewHTMLContent); - -////////////////////////////////////////////////////// -// posts.new.async // -////////////////////////////////////////////////////// - - -/** - * @summary Increment the user's post count - */ -function PostsNewIncrementPostCount (post) { - var userId = post.userId; - Users.update({_id: userId}, {$inc: {"postCount": 1}}); -} -addCallback("posts.new.async", PostsNewIncrementPostCount); diff --git a/packages/vulcan-posts/lib/client.js b/packages/vulcan-posts/lib/client.js deleted file mode 100644 index 32600f82..00000000 --- a/packages/vulcan-posts/lib/client.js +++ /dev/null @@ -1 +0,0 @@ -export * from './modules.js'; \ No newline at end of file diff --git a/packages/vulcan-posts/lib/components/AdminUsersPosts.jsx b/packages/vulcan-posts/lib/components/AdminUsersPosts.jsx deleted file mode 100644 index f8409dd5..00000000 --- a/packages/vulcan-posts/lib/components/AdminUsersPosts.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Posts from 'meteor/vulcan:posts'; -import { Link } from 'react-router'; - -const AdminUsersPosts = ({ document: user }) => -
    - {user.posts && user.posts.map(post => -
  • {post.title}
  • - )} -
- -export default AdminUsersPosts; diff --git a/packages/vulcan-posts/lib/custom_fields.js b/packages/vulcan-posts/lib/custom_fields.js deleted file mode 100644 index 0be636bf..00000000 --- a/packages/vulcan-posts/lib/custom_fields.js +++ /dev/null @@ -1,41 +0,0 @@ -import Users from "meteor/vulcan:users"; - -Users.addField([ - /** - Count of the user's posts - */ - { - fieldName: "postCount", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - The user's associated posts (GraphQL only) - */ - { - fieldName: "posts", - fieldSchema: { - type: Array, - optional: true, - viewableBy: ['guests'], - resolveAs: { - fieldName: 'posts', - arguments: 'limit: Int = 5', - type: '[Post]', - resolver: (user, { limit }, { currentUser, Users, Posts }) => { - const posts = Posts.find({ userId: user._id }, { limit }).fetch(); - - // restrict documents fields - const viewablePosts = _.filter(posts, post => Posts.checkAccess(currentUser, post)); - const restrictedPosts = Users.restrictViewableFields(currentUser, Posts, viewablePosts); - - return restrictedPosts; - } - } - } - } -]); diff --git a/packages/vulcan-posts/lib/i18n.js b/packages/vulcan-posts/lib/i18n.js deleted file mode 100644 index c389ccb4..00000000 --- a/packages/vulcan-posts/lib/i18n.js +++ /dev/null @@ -1,5 +0,0 @@ -import { addStrings } from 'meteor/vulcan:core'; - -addStrings('en', { - 'admin.users.posts': 'Posts', -}); \ No newline at end of file diff --git a/packages/vulcan-posts/lib/modules.js b/packages/vulcan-posts/lib/modules.js deleted file mode 100644 index 2a96f4ae..00000000 --- a/packages/vulcan-posts/lib/modules.js +++ /dev/null @@ -1,19 +0,0 @@ -import Posts from './collection.js'; - -import './schema.js'; -import './custom_fields.js'; -import './parameters.js'; -import './views.js'; -import './helpers.js'; -import './callbacks/callbacks_posts_new.js'; -import './callbacks/callbacks_posts_edit.js'; -import './callbacks/callbacks_other.js'; -import './permissions.js'; -import './resolvers.js'; -import './mutations.js'; -import './redux.js'; -import './i18n.js'; -import './admin.js'; - -export { default as AdminUsersPosts } from './components/AdminUsersPosts'; -export default Posts; \ No newline at end of file diff --git a/packages/vulcan-posts/lib/mutations.js b/packages/vulcan-posts/lib/mutations.js deleted file mode 100644 index a771d6d4..00000000 --- a/packages/vulcan-posts/lib/mutations.js +++ /dev/null @@ -1,87 +0,0 @@ -import { newMutation, editMutation, removeMutation, addGraphQLMutation, Utils } from 'meteor/vulcan:core'; -import Users from 'meteor/vulcan:users'; - - -const mutations = { - - new: { - - name: 'postsNew', - - check(user, document) { - if (!user) return false; - return Users.canDo(user, 'posts.new'); - }, - - mutation(root, {document}, context) { - - Utils.performCheck(this.check, context.currentUser, document); - - return newMutation({ - collection: context.Posts, - document: document, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - edit: { - - name: 'postsEdit', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'posts.edit.own') : Users.canDo(user, `posts.edit.all`); - }, - - mutation(root, {documentId, set, unset}, context) { - - const document = context.Posts.findOne(documentId); - Utils.performCheck(this.check, context.currentUser, document); - - return editMutation({ - collection: context.Posts, - documentId: documentId, - set: set, - unset: unset, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - remove: { - - name: 'postsRemove', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'posts.remove.own') : Users.canDo(user, `posts.remove.all`); - }, - - mutation(root, {documentId}, context) { - - const document = context.Posts.findOne(documentId); - Utils.performCheck(this.check, context.currentUser, document); - - return removeMutation({ - collection: context.Posts, - documentId: documentId, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - -}; - -addGraphQLMutation('increasePostViewCount(postId: String): Float'); - -export default mutations; diff --git a/packages/vulcan-posts/lib/parameters.js b/packages/vulcan-posts/lib/parameters.js deleted file mode 100644 index 4180c34d..00000000 --- a/packages/vulcan-posts/lib/parameters.js +++ /dev/null @@ -1,117 +0,0 @@ -import { Injected } from 'meteor/meteorhacks:inject-initial'; -import moment from 'moment'; -import { addCallback, Utils } from 'meteor/vulcan:core'; - -// Add "after" and "before" properties to terms which can be used to limit posts in time. -function addTimeParameter (parameters, terms, apolloClient) { - - // console.log("// addTimeParameter") - - if (typeof parameters.selector.postedAt === "undefined") { - - let postedAt = {}, mAfter, mBefore, startOfDay, endOfDay, clientTimezoneOffset, serverTimezoneOffset, timeDifference; - - /* - - If we're on the client, add the time difference between client and server - - Example: client is on Japanese time (+9 hours), - server on UCT (Greenwich) time (+0 hours), for a total difference of +9 hours. - - So the time "00:00, UCT" is equivalent to "09:00, JST". - - So if we want to express the timestamp "00:00, UCT" on the client, - we *add* 9 hours to "00:00, JST" on the client to get "09:00, JST" and - sync up both times. - - */ - - if (Meteor.isClient) { - clientTimezoneOffset = -1 * new Date().getTimezoneOffset(); - serverTimezoneOffset = -1 * Injected.obj('serverTimezoneOffset').offset; - timeDifference = clientTimezoneOffset - serverTimezoneOffset; - - // console.log("client time:"+clientTimezoneOffset); - // console.log("server time:"+serverTimezoneOffset); - // console.log("difference: "+timeDifference); - } - - if (terms.after) { - - // console.log("// after: "+terms.after); - - mAfter = moment(terms.after, "YYYY-MM-DD"); - startOfDay = mAfter.startOf('day'); - - // console.log("// normal ", mAfter.toDate(), mAfter.valueOf()); - // console.log("// startOfDay ", startOfDay.toDate(), startOfDay.valueOf()); - - if (Meteor.isClient) { - startOfDay.add(timeDifference, "minutes"); - // console.log("// after add ", startOfDay.toDate(), startOfDay.valueOf()); - // note: on the client, dates are stored as strings, - // so use strings for MongoDB filtering options too - postedAt.$gte = startOfDay.toISOString(); - } else { - postedAt.$gte = startOfDay.toDate(); - } - - } - - if (terms.before) { - - mBefore = moment(terms.before, "YYYY-MM-DD"); - endOfDay = mBefore.endOf('day'); - - if (Meteor.isClient) { - endOfDay.add(timeDifference, "minutes"); - postedAt.$lt = endOfDay.toISOString(); - } else { - postedAt.$lt = endOfDay.toDate(); - } - - } - - if (!_.isEmpty(postedAt)) { - parameters.selector.postedAt = postedAt; - } - - } - - return parameters; -} -addCallback("posts.parameters", addTimeParameter); - -// limit the number of items that can be requested at once -function limitPosts (parameters, terms, apolloClient) { - var maxLimit = 200; - - // 1. set default limit to 10 - let limit = 10; - - // 2. look for limit on terms.limit - if (terms.limit) { - limit = parseInt(terms.limit); - } - - // 3. look for limit on terms.options.limit - if (terms.options && terms.options.limit) { - limit = parseInt(terms.options.limit); - } - - // 4. make sure limit is not greater than 200 - if (limit > maxLimit) { - limit = maxLimit; - } - - // 5. initialize parameters.options if needed - if (!parameters.options) { - parameters.options = {}; - } - - // 6. set limit - parameters.options.limit = limit; - - return parameters; -} -addCallback("posts.parameters", limitPosts); diff --git a/packages/vulcan-posts/lib/permissions.js b/packages/vulcan-posts/lib/permissions.js deleted file mode 100644 index 2a39df8b..00000000 --- a/packages/vulcan-posts/lib/permissions.js +++ /dev/null @@ -1,24 +0,0 @@ -import Users from 'meteor/vulcan:users'; - -const guestsActions = [ - "posts.view.approved" -]; -Users.groups.guests.can(guestsActions); - -const membersActions = [ - "posts.new", - "posts.edit.own", - "posts.remove.own", -]; -Users.groups.members.can(membersActions); - -const adminActions = [ - "posts.view.pending", - "posts.view.rejected", - "posts.view.spam", - "posts.view.deleted", - "posts.new.approved", - "posts.edit.all", - "posts.remove.all" -]; -Users.groups.admins.can(adminActions); diff --git a/packages/vulcan-posts/lib/resolvers.js b/packages/vulcan-posts/lib/resolvers.js deleted file mode 100644 index e2253e4b..00000000 --- a/packages/vulcan-posts/lib/resolvers.js +++ /dev/null @@ -1,77 +0,0 @@ -import { addGraphQLResolvers, Utils } from 'meteor/vulcan:core'; - -const specificResolvers = { - Mutation: { - increasePostViewCount(root, { postId }, context) { - return context.Posts.update({_id: postId}, { $inc: { viewCount: 1 }}); - } - } -}; - -addGraphQLResolvers(specificResolvers); - -const resolvers = { - - list: { - - name: 'postsList', - - resolver(root, {terms}, {currentUser, Users, Posts}, info) { - - // get selector and options from terms and perform Mongo query - let {selector, options} = Posts.getParameters(terms); - options.skip = terms.offset; - - if (terms.query) { - // Hijack the generated params to use mongo text search - delete selector['$or']; - selector['$text'] = { $search: terms.query }; - - options.fields = options.fields || {}; - options.fields.score = { $meta: "textScore" }; - options.sort = { score: { $meta: "textScore" } }; - } - - const posts = Posts.find(selector, options).fetch(); - - // restrict documents fields - const viewablePosts = _.filter(posts, post => Posts.checkAccess(currentUser, post)); - const restrictedPosts = Users.restrictViewableFields(currentUser, Posts, viewablePosts); - - // prime the cache - restrictedPosts.forEach(post => Posts.loader.prime(post._id, post)); - - return restrictedPosts; - }, - - }, - - single: { - - name: 'postsSingle', - - async resolver(root, {documentId, slug}, {currentUser, Users, Posts}) { - - // don't use Dataloader if post is selected by slug - const post = documentId ? await Posts.loader.load(documentId) : Posts.findOne({slug}); - - Utils.performCheck(Posts.checkAccess, currentUser, post, Posts, documentId); - - return Users.restrictViewableFields(currentUser, Posts, post); - }, - - }, - - total: { - - name: 'postsTotal', - - resolver(root, {terms}, {Posts}) { - const {selector} = Posts.getParameters(terms); - return Posts.find(selector).count(); - }, - - } -}; - -export default resolvers; diff --git a/packages/vulcan-posts/lib/schema.js b/packages/vulcan-posts/lib/schema.js deleted file mode 100644 index 2bce58b5..00000000 --- a/packages/vulcan-posts/lib/schema.js +++ /dev/null @@ -1,250 +0,0 @@ -import Users from 'meteor/vulcan:users'; -import Posts from './collection.js'; - -/** - * @summary Posts config namespace - * @type {Object} - */ -const formGroups = { - admin: { - name: "admin", - order: 2 - } -}; - -/** - * @summary Posts schema - * @type {Object} - */ -const schema = { - /** - ID - */ - _id: { - type: String, - optional: true, - viewableBy: ['guests'], - }, - /** - Timetstamp of post creation - */ - createdAt: { - type: Date, - optional: true, - viewableBy: ['admins'], - onInsert: (document, currentUser) => { - return new Date(); - } - }, - /** - Timestamp of post first appearing on the site (i.e. being approved) - */ - postedAt: { - type: Date, - optional: true, - viewableBy: ['guests'], - insertableBy: ['admins'], - editableBy: ['admins'], - control: "datetime", - group: formGroups.admin - }, - /** - URL - */ - // url: { - // type: String, - // optional: true, - // max: 500, - // viewableBy: ['guests'], - // insertableBy: ['members'], - // editableBy: ['members'], - // control: "url", - // order: 10, - // searchable: true - // }, - /** - Title - */ - title: { - type: String, - optional: false, - max: 500, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - control: "text", - order: 20, - searchable: true - }, - /** - Slug - */ - slug: { - type: String, - optional: true, - viewableBy: ['guests'], - }, - /** - Post body (markdown) - */ - body: { - type: String, - optional: true, - max: 3000, - viewableBy: ['guests'], - insertableBy: ['members'], - editableBy: ['members'], - control: "textarea", - order: 30 - }, - /** - HTML version of the post body - */ - htmlBody: { - type: String, - optional: true, - viewableBy: ['guests'], - }, - /** - Post Excerpt - */ - excerpt: { - type: String, - optional: true, - viewableBy: ['guests'], - searchable: true - }, - /** - Count of how many times the post's page was viewed - */ - viewCount: { - type: Number, - optional: true, - viewableBy: ['admins'], - defaultValue: 0 - }, - /** - Timestamp of the last comment - */ - lastCommentedAt: { - type: Date, - optional: true, - viewableBy: ['guests'], - }, - /** - Count of how many times the post's link was clicked - */ - clickCount: { - type: Number, - optional: true, - viewableBy: ['admins'], - defaultValue: 0 - }, - /** - The post's status. One of pending (`1`), approved (`2`), or deleted (`3`) - */ - status: { - type: Number, - optional: true, - viewableBy: ['guests'], - insertableBy: ['admins'], - editableBy: ['admins'], - control: "select", - onInsert: document => { - if (document.userId && !document.status) { - const user = Users.findOne(document.userId); - return Posts.getDefaultStatus(user); - } - }, - form: { - noselect: true, - options: () => Posts.statuses, - group: 'admin' - }, - group: formGroups.admin - }, - /** - Whether a post is scheduled in the future or not - */ - isFuture: { - type: Boolean, - optional: true, - viewableBy: ['guests'], - }, - /** - Whether the post is sticky (pinned to the top of posts lists) - */ - sticky: { - type: Boolean, - optional: true, - defaultValue: false, - viewableBy: ['guests'], - insertableBy: ['admins'], - editableBy: ['admins'], - control: "checkbox", - group: formGroups.admin - }, - /** - Whether the post is inactive. Inactive posts see their score recalculated less often - */ - inactive: { - type: Boolean, - optional: true, - defaultValue: false - }, - /** - Save info for later spam checking on a post. We will use this for the akismet package - */ - userIP: { - type: String, - optional: true, - viewableBy: ['admins'], - }, - userAgent: { - type: String, - optional: true, - viewableBy: ['admins'], - }, - referrer: { - type: String, - optional: true, - viewableBy: ['admins'], - }, - /** - The post author's name - */ - author: { - type: String, - optional: true, - viewableBy: ['guests'], - onEdit: (modifier, document, currentUser) => { - // if userId is changing, change the author name too - if (modifier.$set && modifier.$set.userId) { - return Users.getDisplayNameById(modifier.$set.userId) - } - } - }, - /** - The post author's `_id`. - */ - userId: { - type: String, - optional: true, - control: "select", - viewableBy: ['guests'], - insertableBy: ['members'], - hidden: true, - resolveAs: { - fieldName: 'user', - type: 'User', - resolver: async (post, args, context) => { - if (!post.userId) return null; - const user = await context.Users.loader.load(post.userId); - return context.Users.restrictViewableFields(context.currentUser, context.Users, user); - }, - addOriginalField: true - }, - } -}; - -export default schema; diff --git a/packages/vulcan-posts/lib/server.js b/packages/vulcan-posts/lib/server.js deleted file mode 100644 index 9639e25e..00000000 --- a/packages/vulcan-posts/lib/server.js +++ /dev/null @@ -1,16 +0,0 @@ -import Posts from './modules.js'; - -import './server/routes.js'; -import './server/cron.js'; - -// index -Posts._ensureIndex({"status": 1, "isFuture": 1}); -Posts._ensureIndex({"status": 1, "isFuture": 1, "postedAt": 1}); - -// text index for '$text' query -Posts._ensureIndex( - { title: "text", body: "text" }, - { weights: { title: 4 } } -); - -export * from './modules.js'; diff --git a/packages/vulcan-posts/package.js b/packages/vulcan-posts/package.js deleted file mode 100644 index 84181990..00000000 --- a/packages/vulcan-posts/package.js +++ /dev/null @@ -1,20 +0,0 @@ -Package.describe({ - name: "vulcan:posts", - summary: "Vulcan posts package", - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom(['METEOR@1.0']); - - api.use([ - 'vulcan:core@1.7.0', - 'vulcan:events@1.7.0', - ]); - - api.mainModule("lib/server.js", "server"); - api.mainModule("lib/client.js", "client"); - -}); diff --git a/packages/vulcan-subscribe/lib/callbacks.js b/packages/vulcan-subscribe/lib/callbacks.js index 1a9efe69..bd88c627 100644 --- a/packages/vulcan-subscribe/lib/callbacks.js +++ b/packages/vulcan-subscribe/lib/callbacks.js @@ -1,4 +1,4 @@ -import { createNotification } from 'meteor/vulcan:notifications'; +import { createNotification } from 'meteor/example-forum'; import Users from 'meteor/vulcan:users'; import { addCallback } from 'meteor/vulcan:core'; import { performSubscriptionAction } from './mutations'; @@ -7,9 +7,10 @@ import { performSubscriptionAction } from './mutations'; // note: even if all these callbacks are async, they are imported on the client so they pop in the cheatsheet when debug is enabled // note: leverage weak dependencies on packages -const Comments = Package['vulcan:comments'] ? Package['vulcan:comments'].default : null; -const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; -const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; +// const Comments = Package['vulcan:comments'] ? Package['vulcan:comments'].default : null; +// const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; +// const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; +import {Comments, Posts, Categories} from "meteor/example-forum"; /** * @summary Automatically subscribe a user to their own posts if their user settings permit diff --git a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx index cc1fe522..d4e69d2b 100644 --- a/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx +++ b/packages/vulcan-subscribe/lib/components/SubscribeTo.jsx @@ -12,68 +12,72 @@ const getSubscribeAction = subscribed => subscribed ? 'unsubscribe' : 'subscribe class SubscribeToActionHandler extends PureComponent { - constructor(props, context) { - super(props, context); + constructor(props, context) { + super(props, context); - this.onSubscribe = this.onSubscribe.bind(this); + this.onSubscribe = this.onSubscribe.bind(this); - this.state = { - subscribed: !!Users.isSubscribedTo(props.currentUser, props.document, props.documentType), - }; - } + this.state = { + subscribed: !!Users.isSubscribedTo(props.currentUser, props.document, props.documentType), + }; + } - async onSubscribe(e) { - try { - e.preventDefault(); + async onSubscribe(e) { + try { + e.preventDefault(); - const { document, documentType } = this.props; - const action = getSubscribeAction(this.state.subscribed); + const { document, documentType } = this.props; + const action = getSubscribeAction(this.state.subscribed); - // todo: change the mutation to auto-update the user in the store - await this.setState(prevState => ({subscribed: !prevState.subscribed})); + // todo: change the mutation to auto-update the user in the store + await this.setState(prevState => ({subscribed: !prevState.subscribed})); - // mutation name will be for example postsSubscribe - await this.props[`${documentType + Utils.capitalize(action)}`]({documentId: document._id}); + // mutation name will be for example postsSubscribe + await this.props[`${documentType + Utils.capitalize(action)}`]({documentId: document._id}); - // success message will be for example posts.subscribed - this.props.flash(this.context.intl.formatMessage( - {id: `${documentType}.${action}d`}, - // handle usual name properties - {name: document.name || document.title || document.displayName} - ), "success"); + this.props.flash(this.context.intl.formatMessage( + { id: `${documentType}.${action}d`, properties: {name: document.name || document.title || document.displayName}, + type: 'success' + }) + ); - } catch(error) { - this.props.flash(error.message, "error"); + } catch(error) { + console.log(error); + this.props.flash({ + id: `subscribe.error_${error.name}`, + message: error.message, + type: 'error', + }); + } } - } - render() { - const { currentUser, document, documentType } = this.props; - const { subscribed } = this.state; + render() { + const { currentUser, document, documentType } = this.props; + const { subscribed } = this.state; - const action = `${documentType}.${getSubscribeAction(subscribed)}`; + const action = `${documentType}.${getSubscribeAction(subscribed)}`; - // can't subscribe to yourself - if (!currentUser || !document || (documentType === 'users' && document._id === currentUser._id)) { - return null; - } + // can't subscribe to yourself + if (!currentUser || !document || (documentType === 'users' && document._id === currentUser._id)) { + return null; + } - const className = classNames(this.props.className, { 'subscribed': subscribed }); + const className = classNames(this.props.className, { 'subscribed': subscribed }); - return Users.canDo(currentUser, action) ? : null; - } + return Users.canDo(currentUser, action) ? : null; + } } SubscribeToActionHandler.propTypes = { - document: PropTypes.object.isRequired, - className: PropTypes.string, - currentUser: PropTypes.object, + document: PropTypes.object.isRequired, + className: PropTypes.string, + currentUser: PropTypes.object, }; SubscribeToActionHandler.contextTypes = { - intl: intlShape + intl: intlShape }; const subscribeMutationContainer = ({documentType, actionName}) => graphql(gql` @@ -84,24 +88,24 @@ const subscribeMutationContainer = ({documentType, actionName}) => graphql(gql` } } `, { - props: ({ownProps, mutate}) => ({ - [documentType + actionName]: vars => { - return mutate({ - variables: vars, - }); - }, - }), + props: ({ownProps, mutate}) => ({ + [documentType + actionName]: vars => { + return mutate({ + variables: vars, + }); + }, + }), }); const SubscribeTo = props => { - const documentType = Utils.getCollectionNameFromTypename(props.document.__typename); + const documentType = Utils.getCollectionNameFromTypename(props.document.__typename); - const withSubscribeMutations = ['Subscribe', 'Unsubscribe'].map(actionName => subscribeMutationContainer({documentType, actionName})); + const withSubscribeMutations = ['Subscribe', 'Unsubscribe'].map(actionName => subscribeMutationContainer({documentType, actionName})); - const EnhancedHandler = compose(...withSubscribeMutations)(SubscribeToActionHandler); + const EnhancedHandler = compose(...withSubscribeMutations)(SubscribeToActionHandler); - return ; + return ; }; registerComponent('SubscribeTo', SubscribeTo, withCurrentUser, withMessages); diff --git a/packages/vulcan-subscribe/lib/custom_fields.js b/packages/vulcan-subscribe/lib/custom_fields.js index e3019035..0f704124 100644 --- a/packages/vulcan-subscribe/lib/custom_fields.js +++ b/packages/vulcan-subscribe/lib/custom_fields.js @@ -1,8 +1,10 @@ import Users from "meteor/vulcan:users"; // note: leverage weak dependencies on packages -const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; -const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; +// const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; +// const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; + +import {Posts, Categories} from "meteor/example-forum"; Users.addField([ { diff --git a/packages/vulcan-subscribe/lib/fragments.js b/packages/vulcan-subscribe/lib/fragments.js index 25be3bae..d6a7d26c 100644 --- a/packages/vulcan-subscribe/lib/fragments.js +++ b/packages/vulcan-subscribe/lib/fragments.js @@ -1,5 +1,6 @@ -import { extendFragment } from 'meteor/vulcan:core'; +import { extendFragment, registerFragment } from 'meteor/vulcan:core'; -extendFragment('UsersCurrent', ` - subscribedItems -`); \ No newline at end of file +extendFragment(` + fragment UsersCurrent on User { + subscribedItems + }`); diff --git a/packages/vulcan-subscribe/lib/mutations.js b/packages/vulcan-subscribe/lib/mutations.js index c5cee5b1..eae396c9 100644 --- a/packages/vulcan-subscribe/lib/mutations.js +++ b/packages/vulcan-subscribe/lib/mutations.js @@ -174,14 +174,15 @@ subscribeMutationsGenerator(Users); // note: leverage weak dependencies on packages -const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; +// const Posts = Package['vulcan:posts'] ? Package['vulcan:posts'].default : null; // check if vulcan:posts exists, if yes, add the mutations to Posts +import { Posts, Categories } from 'meteor/example-forum'; if (!!Posts) { subscribeMutationsGenerator(Posts); } // check if vulcan:categories exists, if yes, add the mutations to Categories -const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; +// const Categories = Package['vulcan:categories'] ? Package['vulcan:categories'].default : null; if (!!Categories) { subscribeMutationsGenerator(Categories); } diff --git a/packages/vulcan-subscribe/package.js b/packages/vulcan-subscribe/package.js index d1650e33..adef6651 100644 --- a/packages/vulcan-subscribe/package.js +++ b/packages/vulcan-subscribe/package.js @@ -8,19 +8,12 @@ Package.describe({ Package.onUse(function (api) { - api.versionsFrom("METEOR@1.0"); + api.versionsFrom("METEOR@1.5.2"); api.use([ - 'vulcan:core@1.7.0', - 'vulcan:notifications@1.7.0', - // dependencies on posts, categories are done with nested imports to reduce explicit dependencies + 'vulcan:core', + 'vulcan:users' ]); - - api.use([ - 'vulcan:posts@1.7.0', - 'vulcan:comments@1.7.0', - 'vulcan:categories@1.7.0', - ], {weak: true}); api.mainModule("lib/modules.js", ["client"]); api.mainModule("lib/modules.js", ["server"]); diff --git a/packages/vulcan-users/README.md b/packages/vulcan-users/README.md deleted file mode 100644 index 221773d2..00000000 --- a/packages/vulcan-users/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan users package, used internally. \ No newline at end of file diff --git a/packages/vulcan-users/TESTS.md b/packages/vulcan-users/TESTS.md deleted file mode 100644 index 0669f9cf..00000000 --- a/packages/vulcan-users/TESTS.md +++ /dev/null @@ -1 +0,0 @@ -### Creating An Account \ No newline at end of file diff --git a/packages/vulcan-users/lib/avatar.js b/packages/vulcan-users/lib/avatar.js deleted file mode 100644 index 35a01683..00000000 --- a/packages/vulcan-users/lib/avatar.js +++ /dev/null @@ -1,339 +0,0 @@ -import Users from './collection.js'; -import CryptoJS from 'crypto-js'; - -Users.avatar = { - - /** - * `cleantString` remove starting and trailing whitespaces - * and lowercase the input - * @param {String} string input string that may contain leading and trailing - * whitespaces and uppercase letters - * @return {String} output cleaned string - */ - cleanString: function (string) { - return string.trim().toLowerCase() - }, - - /** - * `isHash` check if a string match the MD5 form : - * 32 chars string containing letters from `a` to `f` - * and digits from `0` to `9` - * @param {String} string that might be a hash - * @return {Boolean} - */ - isHash: function (string) { - var self = this - return /^[a-f0-9]{32}$/i.test(self.cleanString(string)) - }, - - /** - * `hash` takes an input and run it through `CryptoJS.MD5` - * @see https://atmospherejs.com/jparker/crypto-md5 - * @param {String} string input string - * @return {String} md5 hash of the input - */ - hash: function (string) { - var self = this - return CryptoJS.MD5(self.cleanString(string)).toString() - }, - - /** - * `imageUrl` will provide the url for the avatar, given an email or a hash - * and a set of options to be passed to the gravatar API - * @see https://en.gravatar.com/site/implement/images/ - * @param {String} emailOrHash email or pregenerated MD5 hash to query - * gravatar with. - * @param {Object} options options to be passed to gravatar in the query - * string. The `secure` will be used to determine which base url to use. - * @return {String} complete url to the avatar - */ - imageUrl: function (emailOrHash, options) { - var self = this - options = options || {} - - // Want HTTPS ? - var url = options.secure - ? 'https://secure.gravatar.com/avatar/' - : 'http://www.gravatar.com/avatar/' - delete options.secure - - // Is it an MD5 already ? - url += self.isHash(emailOrHash) - ? emailOrHash - : self.hash(emailOrHash) - - // Have any options to pass ? - var params = _.map(options, function (val, key) { - return key + '=' + encodeURIComponent(val) - }).join('&') - - return (params.length > 0) - ? url + '?' + params - : url - }, - - // Default functionality. You can override these options by calling - // Users.avatar.setOptions (do not set this.options directly) - - options: { - - // Determines the type of fallback to use when no image can be found via - // linked services (Gravatar included): - // 'default image' (the default option, which will show either the image - // specified by defaultImageUrl, the package's default image, or a Gravatar - // default image) - // OR - // 'initials' (show the user's initials). - fallbackType: '', - - // Default avatar image's URL. It can be a relative path - // (relative to website's base URL, e.g. 'images/defaultthis.png'). - defaultImageUrl: 'http://www.gravatar.com/avatar/?d=identicon', - - // This property name will be used to fetch an avatar url from the user's profile - // (e.g. 'avatar'). If this property is set and a property of that name exists - // on the user's profile (e.g. user.profile.avatar) that property will be used - // as the avatar url. - customImageProperty: '', - - // Gravatar default option to use (overrides default image URL) - // Options are available at: - // https://secure.gravatar.com/site/implement/images/#default-image - gravatarDefault: '', - - // This property is used to prefix the CSS classes of the DOM elements. - // If no value is set, then the default CSS class assigned to all DOM elements are prefixed with 'avatar' as default. - // If a value is set to, 'foo' for example, the resulting CSS classes are prefixed with 'foo'. - cssClassPrefix: '', - - // This property defines the various image sizes available - imageSizes: { - 'large': 80, - 'small': 30, - 'extra-small': 20 - }, - - // Default background color when displaying the initials. - // Can also be set to a function to map an user object to a background color. - backgroundColor: '#aaa', - - // Default text color when displaying the initials. - // Can also be set to a function to map an user object to a text color. - textColor: '#fff', - - // Generate the required CSS and include it in the head of your application. - // Setting this to false will exclude the generated CSS and leave the - // avatar unstyled by the package. - generateCSS: true - }, - - // Sets the Avatar options. You must use this setter function rather than assigning directly to - // this.options, otherwise the stylesheet won't be generated. - - setOptions: function(options) { - this.options = _.extend(this.options, options); - }, - - // Returns the cssClassPrefix property from options - getCssClassPrefix: function () { - return this.options.cssClassPrefix ? this.options.cssClassPrefix : 'avatar'; - }, - - // Returns a background color for initials - getBackgroundColor: function (user) { - if (_.isString(this.options.backgroundColor)) - return this.options.backgroundColor; - else if (_.isFunction(this.options.backgroundColor)) - return this.options.backgroundColor(user); - }, - - // Returns a text color for initials - getTextColor: function (user) { - if (_.isString(this.options.textColor)) - return this.options.textColor; - else if (_.isFunction(this.options.textColor)) - return this.options.textColor(user); - }, - - // Get the initials of the user - getInitials: function (user) { - - var initials = ''; - var name = ''; - var parts = []; - - if (user && user.profile && user.profile.firstName) { - initials = user.profile.firstName.charAt(0).toUpperCase(); - - if (user.profile.lastName) { - initials += user.profile.lastName.charAt(0).toUpperCase(); - } - else if (user.profile.familyName) { - initials += user.profile.familyName.charAt(0).toUpperCase(); - } - else if (user.profile.secondName) { - initials += user.profile.secondName.charAt(0).toUpperCase(); - } - } - else { - if (user && user.profile && user.profile.name) { - name = user.profile.name; - } - else if (user && user.username) { - name = user.username; - } - - parts = name.split(' '); - // Limit getInitials to first and last initial to avoid problems with - // very long multi-part names (e.g. 'Jose Manuel Garcia Galvez') - initials = _.first(parts).charAt(0).toUpperCase(); - if (parts.length > 1) { - initials += _.last(parts).charAt(0).toUpperCase(); - } - } - - return initials; - }, - - // Get the url of the user's avatar - // XXX: this.getUrl is a reactive function only when no user argument is specified. - getUrl: function (user) { - - // Default to the currently logged in user, unless otherwise specified. - if (!user) return null; - - var url = ''; - var defaultUrl, svc; - - if (user) { - svc = this.getService(user); - if (svc === 'facebook') { - // use larger image (~200x200) - url = 'https://graph.facebook.com/' + user.services.facebook.id + '/picture/?type=large'; - } - else if (svc === 'google') { - url = user.services.google.picture; - } - else if (svc === 'github') { - url = 'https://avatars.githubusercontent.com/' + user.services.github.username + '?s=200'; - } - else if (svc === 'instagram') { - url = user.services.instagram.profile_picture; - } - else if (svc === 'linkedin') { - url = user.services.linkedin.pictureUrl; - } - else if (svc === 'custom') { - url = this.getCustomUrl(user); - } - else if (svc === 'none') { - defaultUrl = this.options.defaultImageUrl; - // If it's a relative path (no '//' anywhere), complete the URL - if (defaultUrl.indexOf('//') === -1) { - // remove starting slash if it exists - if (defaultUrl.charAt(0) === '/') defaultUrl = defaultUrl.substr(1); - // Then add the relative path to the server's base URL - defaultUrl = Meteor.absoluteUrl(defaultUrl); - } - url = this.getGravatarUrl(user, defaultUrl); - } - } - - return url; - }, - - getService: function (user) { - var services = user && user.services || {}; - if (this.getCustomUrl(user)) { return 'custom'; } - var service = _.find([['twitter', 'profile_image_url_https'], ['facebook', 'id'], ['google', 'picture'], ['github', 'username'], ['instagram', 'profile_picture'], ['linkedin', 'pictureUrl']], function(s) { return !!services[s[0]] && s[1].length && !!services[s[0]][s[1]]; }); - if(!service) - return 'none'; - else - return service[0]; - }, - - computeUrl: function(prop, user) { - if (typeof prop === 'function') { - prop = prop.call(user); - } - if (prop && typeof prop === 'string') { - return prop; - } - }, - - getDescendantProp: function (obj, desc) { - var arr = desc.split('.'); - while(arr.length && (obj = obj[arr.shift()])); - return obj; - }, - - getCustomUrl: function (user) { - - var customProp = user && this.options.customImageProperty; - if (typeof customProp === 'function') { - return this.computeUrl(customProp, user); - } else if (customProp) { - return this.computeUrl(this.getDescendantProp(user, customProp), user); - } - }, - - getGravatarUrl: function (user, defaultUrl) { - var gravatarDefault; - var validGravatars = ['404', 'mm', 'identicon', 'monsterid', 'wavatar', 'retro', 'blank']; - - // Initials are shown when Gravatar returns 404. - if (this.options.fallbackType !== 'initials') { - var valid = _.contains(validGravatars, this.options.gravatarDefault); - gravatarDefault = valid ? this.options.gravatarDefault : defaultUrl; - } - else { - gravatarDefault = '404'; - } - - var emailOrHash = this.getUserEmail(user) || Users.getEmailHash(user); - // var secure = true; - var options = { - // NOTE: Gravatar's default option requires a publicly accessible URL, - // so it won't work when your app is running on localhost and you're - // using an image with either the standard default image URL or a custom - // defaultImageUrl that is a relative path (e.g. 'images/defaultthis.png'). - size: 200, // use 200x200 like twitter and facebook above (might be useful later) - default: gravatarDefault, - secure: true - }; - return emailOrHash ? this.imageUrl(emailOrHash, options) : null; - - }, - - // Get the user's email address - getUserEmail: function (user) { - var emails = _.pluck(user.emails, 'address'); - return emails[0] || null; - }, - - // Returns the size class to use for an avatar - getSizeClass: function(context) { - // Defaults are 'large', 'small', 'extra-small', but user can add new ones - return this.options.imageSizes[context.size] ? this.getCssClassPrefix() + '-' + context.size : ''; - }, - - // Returns the shape class for an avatar - getShapeClass: function (context) { - var valid = ['rounded', 'circle']; - return _.contains(valid, context.shape) ? this.getCssClassPrefix() + '-' + context.shape : ''; - }, - - // Returns the custom class(es) for an avatar - getCustomClasses: function (context) { - return context.class ? context.class : ''; - }, - - // Returns the initials text for an avatar - getInitialsText: function(user, context) { - return context.initials || this.getInitials(user); - } - -}; - -// This will be replaced if the user calls setOptions in their own code -Users.avatar.setOptions({}); diff --git a/packages/vulcan-users/lib/callbacks.js b/packages/vulcan-users/lib/callbacks.js deleted file mode 100644 index 8f509349..00000000 --- a/packages/vulcan-users/lib/callbacks.js +++ /dev/null @@ -1,71 +0,0 @@ -import Users from './collection.js'; -import marked from 'marked'; -import { addCallback, Utils, runCallbacksAsync } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core isn't loaded yet - -////////////////////////////////////////////////////// -// Callbacks // -////////////////////////////////////////////////////// - -function hasCompletedProfile (user) { - return Users.hasCompletedProfile(user); -} -addCallback("users.profileCompleted.sync", hasCompletedProfile); - -// remove this to get rid of dependency on vulcan:email - -// function usersNewAdminUserCreationNotification (user) { -// // send notifications to admins -// const admins = Users.adminUsers(); -// admins.forEach(function(admin) { -// if (Users.getSetting(admin, "notifications_users", false)) { -// const emailProperties = Users.getNotificationProperties(user); -// const html = VulcanEmail.getTemplate('newUser')(emailProperties); -// VulcanEmail.send(Users.getEmail(admin), `New user account: ${emailProperties.displayName}`, VulcanEmail.buildTemplate(html)); -// } -// }); -// return user; -// } -// addCallback("users.new.sync", usersNewAdminUserCreationNotification); - -function usersEditGenerateHtmlBio (modifier) { - if (modifier.$set && modifier.$set.bio) { - modifier.$set.htmlBio = Utils.sanitize(marked(modifier.$set.bio)); - } - return modifier; -} -addCallback("users.edit.sync", usersEditGenerateHtmlBio); - -function usersEditCheckEmail (modifier, user) { - // if email is being modified, update user.emails too - if (modifier.$set && modifier.$set.email) { - - const newEmail = modifier.$set.email; - - // check for existing emails and throw error if necessary - const userWithSameEmail = Users.findByEmail(newEmail); - if (userWithSameEmail && userWithSameEmail._id !== user._id) { - throw new Error(Utils.encodeIntlError({id:"users.email_already_taken", value: newEmail})); - } - - // if user.emails exists, change it too - if (!!user.emails) { - user.emails[0].address = newEmail; - modifier.$set.emails = user.emails; - } - - // update email hash - modifier.$set.emailHash = Users.avatar.hash(newEmail); - - } - return modifier; -} -addCallback("users.edit.sync", usersEditCheckEmail); - -// when a user is edited, check if their profile is now complete -function usersCheckCompletion (newUser, oldUser) { - if (!Users.hasCompletedProfile(oldUser) && Users.hasCompletedProfile(newUser)) { - runCallbacksAsync("users.profileCompleted.async", newUser); - } -} -addCallback("users.edit.async", usersCheckCompletion); - diff --git a/packages/vulcan-users/lib/client.js b/packages/vulcan-users/lib/client.js deleted file mode 100644 index 32600f82..00000000 --- a/packages/vulcan-users/lib/client.js +++ /dev/null @@ -1 +0,0 @@ -export * from './modules.js'; \ No newline at end of file diff --git a/packages/vulcan-users/lib/collection.js b/packages/vulcan-users/lib/collection.js deleted file mode 100644 index a1a89804..00000000 --- a/packages/vulcan-users/lib/collection.js +++ /dev/null @@ -1,28 +0,0 @@ -import schema from './schema.js'; -import mutations from './mutations.js'; -import resolvers from './resolvers.js'; -import { createCollection, addGraphQLQuery } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core isn't loaded yet - -/** - * @summary Vulcan Users namespace - * @namespace Users - */ -const Users = createCollection({ - - collection: Meteor.users, - - collectionName: 'Users', - - typeName: 'User', - - schema, - - resolvers, - - mutations, - -}); - -addGraphQLQuery(`currentUser: User`); - -export default Users; diff --git a/packages/vulcan-users/lib/fragments.js b/packages/vulcan-users/lib/fragments.js deleted file mode 100644 index 9026da73..00000000 --- a/packages/vulcan-users/lib/fragments.js +++ /dev/null @@ -1,20 +0,0 @@ -import { registerFragment } from 'meteor/vulcan:lib'; - -// ------------------------------ Vote ------------------------------ // - -// note: fragment used by default on the UsersProfile fragment -registerFragment(` - fragment UsersCurrent on User { - _id - username - createdAt - isAdmin - displayName - email - emailHash - slug - groups - services - avatarUrl - } -`); diff --git a/packages/vulcan-users/lib/helpers.js b/packages/vulcan-users/lib/helpers.js deleted file mode 100644 index 50c0bad6..00000000 --- a/packages/vulcan-users/lib/helpers.js +++ /dev/null @@ -1,219 +0,0 @@ -import { Utils } from 'meteor/vulcan:lib'; -import Users from './collection.js'; -import moment from 'moment'; -import _ from 'underscore'; - -//////////////////// -// User Getters // -//////////////////// - -/** - * @summary Get a user - * @param {String} userOrUserId - */ -Users.getUser = function (userOrUserId) { - if (typeof userOrUserId === "undefined") { - if (!Meteor.user()) { - throw new Error(); - } else { - return Meteor.user(); - } - } else if (typeof userOrUserId === "string") { - return Users.findOne(userOrUserId); - } else { - return userOrUserId; - } -}; - -/** - * @summary Get a user's username (unique, no special characters or spaces) - * @param {Object} user - */ -Users.getUserName = function (user) { - try { - if (user.username) - return user.username; - } - catch (error){ - console.log(error); // eslint-disable-line - return null; - } -}; -Users.getUserNameById = function (userId) {return Users.getUserName(Users.findOne(userId))}; - -/** - * @summary Get a user's display name (not unique, can take special characters and spaces) - * @param {Object} user - */ -Users.getDisplayName = function (user) { - if (typeof user === "undefined") { - return ""; - } else { - return (user.displayName) ? user.displayName : Users.getUserName(user); - } -}; -Users.getDisplayNameById = function (userId) {return Users.getDisplayName(Users.findOne(userId));}; - -/** - * @summary Get a user's profile URL - * @param {Object} user (note: we only actually need either the _id or slug properties) - * @param {Boolean} isAbsolute - */ -Users.getProfileUrl = function (user, isAbsolute) { - if (typeof user === "undefined") { - return ""; - } - isAbsolute = typeof isAbsolute === "undefined" ? false : isAbsolute; // default to false - var prefix = isAbsolute ? Utils.getSiteUrl().slice(0,-1) : ""; - if (user.slug) { - return `${prefix}/users/${user.slug}`; - } else { - return ""; - } -}; - -/** - * @summary Get a user's account edit URL - * @param {Object} user (note: we only actually need either the _id or slug properties) - * @param {Boolean} isAbsolute - */ -Users.getEditUrl = function (user, isAbsolute) { - return `${Users.getProfileUrl(user, isAbsolute)}/edit`; -}; - -/** - * @summary Get a user's email - * @param {Object} user - */ -Users.getEmail = function (user) { - if(user.email){ - return user.email; - }else{ - return null; - } -}; -Users.getEmailById = function (userId) {return Users.getEmail(Users.findOne(userId));}; - -/** - * @summary Get a user's email hash - * @param {Object} user - */ -Users.getEmailHash = function (user) { - return user.emailHash; -}; -Users.getEmailHashById = function (userId) {return Users.getEmailHash(Users.findOne(userId));}; - -/** - * @summary Get a user setting - * @param {Object} user - * @param {String} settingName - * @param {Object} defaultValue - */ -Users.getSetting = function (user = null, settingName, defaultValue = null) { - if (user) { - const settingValue = Users.getProperty(user, settingName); - return typeof settingValue === 'undefined' ? defaultValue : settingValue; - } else { - return defaultValue; - } -}; - -//////////////////// -// User Checks // -//////////////////// - -/** - * @summary Check if the user has completed their profile. - * @param {Object} user - */ -Users.hasCompletedProfile = function (user) { - - if (!user) return false; - - return _.every(Users.getRequiredFields(), function (fieldName) { - return !!Utils.getNestedProperty(user, fieldName); - }); - -}; - -/////////////////// -// Other Helpers // -/////////////////// - -Users.findLast = function (user, collection) { - return collection.findOne({userId: user._id}, {sort: {createdAt: -1}}); -}; - -Users.timeSinceLast = function (user, collection){ - var now = new Date().getTime(); - var last = this.findLast(user, collection); - if(!last) - return 999; // if this is the user's first post or comment ever, stop here - return Math.abs(Math.floor((now-last.createdAt)/1000)); -}; - -Users.numberOfItemsInPast24Hours = function (user, collection) { - var mNow = moment(); - var items = collection.find({ - userId: user._id, - createdAt: { - $gte: mNow.subtract(24, 'hours').toDate() - } - }); - return items.count(); -}; - -Users.getProperty = function (object, property) { - // recursive function to get nested properties - var array = property.split('.'); - if(array.length > 1){ - var parent = array.shift(); - // if our property is not at this level, call function again one level deeper if we can go deeper, else return undefined - return (typeof object[parent] === "undefined") ? undefined : this.getProperty(object[parent], array.join('.')); - }else{ - // else return property - return object[array[0]]; - } -}; - -Users.setSetting = (user, settingName, value) => { - // all users settings should begin with the prexi : user.setting namespace, so add "" if needed - var field = settingName.slice(0,2) === "" ? settingName : "" + settingName; - - var modifier = {$set: {}}; - modifier.$set[field] = value; - - Users.update(user._id, modifier); -} - - -//////////////////// -// More Helpers // -//////////////////// - -// helpers that don't take a user as argument - -/** - * @summary @method Users.getRequiredFields - * Get a list of all fields required for a profile to be complete. - */ -Users.getRequiredFields = function () { - var schema = Users.simpleSchema()._schema; - var fields = _.filter(_.keys(schema), function (fieldName) { - var field = schema[fieldName]; - return !!field.mustComplete; - }); - return fields; -}; - -Users.adminUsers = function (options) { - return this.find({isAdmin : true}, options).fetch(); -}; - -Users.getCurrentUserEmail = function () { - return Meteor.user() ? Users.getEmail(Meteor.user()) : ''; -}; - -Users.findByEmail = function (email) { - return Users.findOne({"email": email}); -}; diff --git a/packages/vulcan-users/lib/modules.js b/packages/vulcan-users/lib/modules.js deleted file mode 100644 index 4faaa249..00000000 --- a/packages/vulcan-users/lib/modules.js +++ /dev/null @@ -1,10 +0,0 @@ -import Users from './collection.js'; - -import './callbacks.js'; -import './helpers.js'; -import './avatar.js'; -import './permissions.js'; -import './fragments.js'; -import './views.js'; - -export default Users; diff --git a/packages/vulcan-users/lib/mutations.js b/packages/vulcan-users/lib/mutations.js deleted file mode 100644 index 5954423a..00000000 --- a/packages/vulcan-users/lib/mutations.js +++ /dev/null @@ -1,88 +0,0 @@ -import { newMutation, editMutation, removeMutation, Utils } from 'meteor/vulcan:lib'; -import Users from './collection'; // circular dependency? - -const performCheck = (mutation, user, document) => { - if (!mutation.check(user, document)) throw new Error(Utils.encodeIntlError({id: `app.mutation_not_allowed`, value: `"${mutation.name}" on _id "${document._id}"`})); -}; - -const mutations = { - - new: { - - name: 'usersNew', - - check(user, document) { - if (!user) return false; - return Users.canDo(user, 'users.new'); - }, - - mutation(root, {document}, context) { - - performCheck(this, context.currentUser, document); - - return newMutation({ - collection: context.Users, - document: document, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - edit: { - - name: 'usersEdit', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'users.edit.own') : Users.canDo(user, `users.edit.all`); - }, - - mutation(root, {documentId, set, unset}, context) { - - const document = context.Users.findOne(documentId); - performCheck(this, context.currentUser, document); - - return editMutation({ - collection: context.Users, - documentId: documentId, - set: set, - unset: unset, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - - remove: { - - name: 'usersRemove', - - check(user, document) { - if (!user || !document) return false; - return Users.owns(user, document) ? Users.canDo(user, 'users.remove.own') : Users.canDo(user, `users.remove.all`); - }, - - mutation(root, {documentId}, context) { - - const document = context.Users.findOne(documentId); - performCheck(this, context.currentUser, document); - - return removeMutation({ - collection: context.Users, - documentId: documentId, - currentUser: context.currentUser, - validate: true, - context, - }); - }, - - }, - -}; - -export default mutations; diff --git a/packages/vulcan-users/lib/permissions.js b/packages/vulcan-users/lib/permissions.js deleted file mode 100644 index c76c9e7f..00000000 --- a/packages/vulcan-users/lib/permissions.js +++ /dev/null @@ -1,297 +0,0 @@ -import Users from './collection.js'; -import { Utils } from 'meteor/vulcan:lib'; - -/** - * @summary Users.groups object - */ -Users.groups = {}; - -/** - * @summary Group class - */ -class Group { - - constructor() { - this.actions = []; - } - - can(actions) { - actions = Array.isArray(actions) ? actions : [actions]; - this.actions = this.actions.concat(actions); - } - - cannot(actions) { - actions = Array.isArray(actions) ? actions : [actions]; - this.actions = _.difference(this.actions, actions); - } - -} - -//////////////////// -// Helpers // -//////////////////// - -/** - * @summary create a new group - * @param {String} groupName - */ -Users.createGroup = groupName => { - Users.groups[groupName] = new Group(); -}; - -/** - * @summary get a list of a user's groups - * @param {Object} user - */ -Users.getGroups = user => { - - let userGroups = []; - - if (!user) { // guests user - - userGroups = ["guests"]; - - } else { - - userGroups = ["members"]; - - if (user.groups) { // custom groups - userGroups = userGroups.concat(user.groups); - } - - if (Users.isAdmin(user)) { // admin - userGroups.push("admins"); - } - - } - - return userGroups; - -}; - -/** - * @summary get a list of all the actions a user can perform - * @param {Object} user - */ -Users.getActions = user => { - let userGroups = Users.getGroups(user); - if (!userGroups.includes('guests')) { - // always give everybody permission for guests actions, too - userGroups.push('guests'); - } - let groupActions = userGroups.map(groupName => { - // note: make sure groupName corresponds to an actual group - const group = Users.groups[groupName]; - return group && group.actions; - }); - return _.unique(_.flatten(groupActions)); -}; - -/** - * @summary check if a user is a member of a group - * @param {Array} user - * @param {String} group or array of groups - */ -Users.isMemberOf = (user, groupOrGroups) => { - const groups = Array.isArray(groupOrGroups) ? groupOrGroups : [groupOrGroups]; - - // everybody is considered part of the guests group - if (groups.indexOf('guests') !== -1) return true; - - // every logged in user is part of the members group - if (groups.indexOf('members') !== -1) return !!user; - - // the admin group have their own function - if (groups.indexOf('admin') !== -1) return Users.isAdmin(user); - - // else test for the `groups` field - return _.intersection(Users.getGroups(user), groups).length > 0; -}; - -/** - * @summary check if a user can perform a specific action - * @param {Object} user - * @param {String} action - */ -Users.canDo = (user, action) => { - return Users.isAdmin(user) || Users.getActions(user).indexOf(action) !== -1; -}; - -// DEPRECATED -// TODO: remove this -/** - * @summary Check if a user can edit a document - * @param {Object} user - The user performing the action - * @param {Object} document - The document being edited - */ -// Users.canEdit = function (user, document) { - -// user = (typeof user === 'undefined') ? Meteor.user() : user; - -// // note(apollo): use of `__typename` given by react-apollo -// //const collectionName = document.getCollectionName(); -// const collectionName = document.__typename ? Utils.getCollectionNameFromTypename(document.__typename) : document.getCollectionName(); - -// if (!user || !document) { -// return false; -// } - -// if (document.hasOwnProperty('isDeleted') && document.isDeleted) return false; - -// if (Users.owns(user, document)) { -// // if this is user's document, check if user can edit own documents -// return Users.canDo(user, `${collectionName}.edit.own`); -// } else { -// // if this is not user's document, check if they can edit all documents -// return Users.canDo(user, `${collectionName}.edit.all`); -// } - -// }; - -/** - * @summary Check if a user owns a document - * @param {Object|string} userOrUserId - The user or their userId - * @param {Object} document - The document to check (post, comment, user object, etc.) - */ -Users.owns = function (user, document) { - try { - if (!!document.userId) { - // case 1: document is a post or a comment, use userId to check - return user._id === document.userId; - } else { - // case 2: document is a user, use _id or slug to check - return document.slug ? user.slug === document.slug : user._id === document._id; - } - } catch (e) { - return false; // user not logged in - } -}; - -/** - * @summary Check if a user is an admin - * @param {Object|string} userOrUserId - The user or their userId - */ -Users.isAdmin = function (userOrUserId) { - try { - var user = Users.getUser(userOrUserId); - return !!user && !!user.isAdmin; - } catch (e) { - return false; // user not logged in - } -}; -Users.isAdminById = Users.isAdmin; - -/** - * @summary Check if a user can view a field - * @param {Object} user - The user performing the action - * @param {Object} field - The field being edited or inserted - */ -Users.canViewField = function (user, field, document) { - if (field.viewableBy) { - return typeof field.viewableBy === 'function' ? field.viewableBy(user, document) : Users.isMemberOf(user, field.viewableBy) - } - return false; -}; - -/** - * @summary Get a list of fields viewable by a user - * @param {Object} user - The user performing the action - * @param {Object} collection - The collection - * @param {Object} document - Optionally, get a list for a specific document - */ -Users.getViewableFields = function (user, collection, document) { - return Utils.arrayToFields(_.compact(_.map(collection.simpleSchema()._schema, - (field, fieldName) => { - return Users.canViewField(user, field, document) ? fieldName : null; - } - ))); -} - -// collection helper -Users.helpers({ - getViewableFields(collection, document) { - return Users.getViewableFields(this, collection, document); - } -}); - -/** - * @summary For a given document or list of documents, keep only fields viewable by current user - * @param {Object} user - The user performing the action - * @param {Object} collection - The collection - * @param {Object} document - The document being returned by the resolver - */ -Users.restrictViewableFields = function (user, collection, docOrDocs) { - - if (!docOrDocs) return {}; - - const restrictDoc = document => { - - // get array of all keys viewable by user - const viewableKeys = _.keys(Users.getViewableFields(user, collection, document)); - const restrictedDocument = _.clone(document); - - // loop over each property in the document and delete it if it's not viewable - _.forEach(restrictedDocument, (value, key) => { - if (!viewableKeys.includes(key)) { - delete restrictedDocument[key]; - } - }); - - return restrictedDocument; - - }; - - return Array.isArray(docOrDocs) ? docOrDocs.map(restrictDoc) : restrictDoc(docOrDocs); - -} - -/** - * @summary Check if a user can submit a field - * @param {Object} user - The user performing the action - * @param {Object} field - The field being edited or inserted - */ -Users.canInsertField = function (user, field) { - if (user && field.insertableBy) { - return typeof field.insertableBy === 'function' ? field.insertableBy(user) : Users.isMemberOf(user, field.insertableBy) - } - return false; -}; - -/** @function - * Check if a user can edit a field - * @param {Object} user - The user performing the action - * @param {Object} field - The field being edited or inserted - */ -Users.canEditField = function (user, field, document) { - if (user && field.editableBy) { - return typeof field.editableBy === 'function' ? field.editableBy(user, document) : Users.isMemberOf(user, field.editableBy) - } - return false; -}; - -//////////////////// -// Initialize // -//////////////////// - -/** - * @summary initialize the 3 out-of-the-box groups - */ -Users.createGroup("guests"); // non-logged-in users -Users.createGroup("members"); // regular users - -const membersActions = [ - "users.new", - "users.edit.own", - "users.remove.own" -]; -Users.groups.members.can(membersActions); - -Users.createGroup("admins"); // admin users - -const adminActions = [ - "users.new", - "users.edit.all", - "users.remove.all", - "settings.edit" -]; -Users.groups.admins.can(adminActions); diff --git a/packages/vulcan-users/lib/resolvers.js b/packages/vulcan-users/lib/resolvers.js deleted file mode 100644 index 29c522a4..00000000 --- a/packages/vulcan-users/lib/resolvers.js +++ /dev/null @@ -1,73 +0,0 @@ -import { addGraphQLResolvers } from 'meteor/vulcan:lib'; -import Users from './collection.js'; - -const specificResolvers = { - Query: { - currentUser(root, args, context) { - let user = null; - if (context && context.userId) { - user = context.Users.findOne(context.userId); - - if (user.services) { - Object.keys(user.services).forEach((key) => { - user.services[key] = {} - }); - } - } - return user; - }, - }, -}; - -addGraphQLResolvers(specificResolvers); - -const resolvers = { - - list: { - - name: 'usersList', - - resolver(root, {terms}, {currentUser, Users}, info) { - - // get selector and options from terms and perform Mongo query - let {selector, options} = Users.getParameters(terms); - options.limit = (terms.limit < 1 || terms.limit > 100) ? 100 : terms.limit; - options.skip = terms.offset; - const users = Users.find(selector, options).fetch(); - - // restrict documents fields - const restrictedUsers = Users.restrictViewableFields(currentUser, Users, users); - - // prime the cache - restrictedUsers.forEach(user => Users.loader.prime(user._id, user)); - - return restrictedUsers; - }, - - }, - - single: { - - name: 'usersSingle', - - async resolver(root, {documentId, slug}, {currentUser, Users}) { - // don't use Dataloader if user is selected by slug - const user = documentId ? await Users.loader.load(documentId) : Users.findOne({slug}); - return Users.restrictViewableFields(currentUser, Users, user); - }, - - }, - - total: { - - name: 'usersTotal', - - resolver(root, {terms}, context) { - const {selector} = context.Users.getParameters(terms); - return context.Users.find(selector).count(); - }, - - } -}; - -export default resolvers; diff --git a/packages/vulcan-users/lib/schema.js b/packages/vulcan-users/lib/schema.js deleted file mode 100644 index 73a1d2bf..00000000 --- a/packages/vulcan-users/lib/schema.js +++ /dev/null @@ -1,271 +0,0 @@ -import SimpleSchema from 'simpl-schema'; -import Users from './collection.js'; -import { Utils } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core isn't loaded yet - -/////////////////////////////////////// -// Order for the Schema is as follows. Change as you see fit: -// 00. -// 10. Display Name -// 20. Email -// 30. Bio -// 40. Slug -// 50. Contact Info -// 60. Twitter username (removed) -// 70. -// 80. -// 90. -// 100. -// Anything else.. -/////////////////////////////////////// - -const adminGroup = { - name: "admin", -}; - -const ownsOrIsAdmin = (user, document) => { - return Users.owns(user, document) || Users.isAdmin(user); -}; - -/** - * @summary Users schema - * @type {Object} - */ -const schema = { - _id: { - type: String, - optional: true, - viewableBy: ['guests'], - }, - username: { - type: String, - optional: true, - viewableBy: ['guests'], - insertableBy: ['guests'], - searchable: true - }, - emails: { - type: Array, - optional: true, - }, - 'emails.$': { - type: Object, - optional: true, - }, - "emails.$.address": { - type: String, - regEx: SimpleSchema.RegEx.Email, - optional: true, - }, - "emails.$.verified": { - type: Boolean, - optional: true, - }, - createdAt: { - type: Date, - optional: true, - viewableBy: ['guests'], - onInsert: () => { - return new Date(); - } - }, - isAdmin: { - type: Boolean, - label: "Admin", - control: "checkbox", - optional: true, - insertableBy: ['admins'], - editableBy: ['admins'], - viewableBy: ['guests'], - group: adminGroup, - onInsert: user => { - // if this is not a dummy account, and is the first user ever, make them an admin - const realUsersCount = Users.find({'isDummy': {$ne: true}}).count(); - return (!user.isDummy && realUsersCount === 0) ? true : false; - } - }, - profile: { - type: Object, - optional: true, - blackbox: true, - insertableBy: ['guests'], - }, - // telescope-specific data, kept for backward compatibility and migration purposes - telescope: { - type: Object, - blackbox: true, - optional: true, - }, - services: { - type: Object, - optional: true, - blackbox: true, - }, - /** - The name displayed throughout the app. Can contain spaces and special characters, doesn't need to be unique - */ - displayName: { - type: String, - optional: true, - control: "text", - insertableBy: ['members'], - editableBy: ['members'], - viewableBy: ['guests'], - order: 10, - onInsert: (user, options) => { - const profileName = Utils.getNestedProperty(user, 'profile.name'); - const linkedinFirstName = Utils.getNestedProperty(user, 'services.linkedin.firstName'); - if (profileName) return profileName; - if (linkedinFirstName) return `${linkedinFirstName} ${Utils.getNestedProperty(user, 'services.linkedin.lastName')}`; - if (user.username) return user.username; - return undefined; - }, - searchable: true - }, - /** - Bio (Markdown version) - */ - bio: { - type: String, - optional: true, - control: "textarea", - insertableBy: ['members'], - editableBy: ['members'], - viewableBy: ['guests'], - order: 30, - searchable: true - }, - /** - The user's email. Modifiable. - */ - email: { - type: String, - optional: true, - regEx: SimpleSchema.RegEx.Email, - mustComplete: true, - control: "text", - insertableBy: ['guests'], - editableBy: ['members'], - viewableBy: ownsOrIsAdmin, - order: 20, - onInsert: (user) => { - // look in a few places for the user email - const meteorEmails = Utils.getNestedProperty(user, 'services.meteor-developer.emails'); - const facebookEmail = Utils.getNestedProperty(user, 'services.facebook.email'); - const githubEmail = Utils.getNestedProperty(user, 'services.github.email'); - const googleEmail = Utils.getNestedProperty(user, 'services.google.email'); - const linkedinEmail = Utils.getNestedProperty(user, 'services.linkedin.emailAddress'); - - if (meteorEmails) return _.findWhere(meteorEmails, { primary: true }).address; - if (facebookEmail) return facebookEmail; - if (githubEmail) return githubEmail; - if (googleEmail) return googleEmail; - if (linkedinEmail) return linkedinEmail; - return undefined; - }, - searchable: true - // unique: true // note: find a way to fix duplicate accounts before enabling this - }, - /** - A hash of the email, used for Gravatar // TODO: change this when email changes - */ - emailHash: { - type: String, - optional: true, - viewableBy: ['guests'], - onInsert: user => { - if (user.email) { - return Users.avatar.hash(user.email); - } - } - }, - avatarUrl: { - type: String, - optional: true, - viewableBy: ['guests'], - onInsert: user => { - const facebookId = Utils.getNestedProperty(user, 'services.facebook.id'); - if (facebookId) return `https://graph.facebook.com/${facebookId}/picture?type=large`; - return undefined; - - }, - resolveAs: { - fieldName: 'avatarUrl', - type: 'String', - resolver: async (user, args, context) => { - if (user.avatarUrl) { - return user.avatarUrl; - } else { - // user has already been cleaned up by Users.restrictViewableFields, so we - // reload the full user object from the cache to access user.services - const fullUser = await Users.loader.load(user._id); - return Users.avatar.getUrl(fullUser); - } - } - } - }, - /** - The HTML version of the bio field - */ - htmlBio: { - type: String, - optional: true, - viewableBy: ['guests'], - }, - /** - The user's karma - */ - karma: { - type: Number, - optional: true, - viewableBy: ['guests'], - }, - /** - The user's profile URL slug // TODO: change this when displayName changes - */ - slug: { - type: String, - optional: true, - viewableBy: ['guests'], - order: 40, - onInsert: user => { - // create a basic slug from display name and then modify it if this slugs already exists; - const basicSlug = Utils.slugify(user.displayName); - return Utils.getUnusedSlug(Users, basicSlug); - } - }, - /** - Contact info for the user - */ - contactInfo: { - type: String, - optional: true, - control: "text", - insertableBy: ['members'], - editableBy: ['members'], - viewableBy: ['guests'], - order: 50, - }, - /** - Groups - */ - groups: { - type: Array, - optional: true, - control: "checkboxgroup", - insertableBy: ['admins'], - editableBy: ['admins'], - viewableBy: ['guests'], - form: { - options: function () { - const groups = _.without(_.keys(Users.groups), "guests", "members", "admins"); - return groups.map(group => {return {value: group, label: group};}); - } - }, - }, - 'groups.$': { - type: String, - optional: true - } -}; - -export default schema; diff --git a/packages/vulcan-users/lib/server.js b/packages/vulcan-users/lib/server.js deleted file mode 100644 index a7d5e244..00000000 --- a/packages/vulcan-users/lib/server.js +++ /dev/null @@ -1,6 +0,0 @@ -import './server/on_create_user.js'; -import './server/urls.js'; -import './server/graphql_context.js'; - -export {default as createUser} from './server/create_user.js'; -export * from './modules.js'; diff --git a/packages/vulcan-users/lib/server/create_user.js b/packages/vulcan-users/lib/server/create_user.js deleted file mode 100644 index e6dde996..00000000 --- a/packages/vulcan-users/lib/server/create_user.js +++ /dev/null @@ -1,20 +0,0 @@ -import Users from '../collection.js'; -import { newMutation } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core isn't loaded yet - -const createUser = user => { - - // if user has an email, copy it over to emails array - if(user.email) { - user.emails = [{address: user.email, verified: false}]; - } - - user.services = {}; - - newMutation({ - collection: Users, - document: user, - validate: false - }); -} - -export default createUser; \ No newline at end of file diff --git a/packages/vulcan-users/lib/server/graphql_context.js b/packages/vulcan-users/lib/server/graphql_context.js deleted file mode 100644 index 1c3a249b..00000000 --- a/packages/vulcan-users/lib/server/graphql_context.js +++ /dev/null @@ -1,4 +0,0 @@ -import { addToGraphQLContext } from 'meteor/vulcan:lib'; -import Users from '../modules.js'; - -addToGraphQLContext({ getViewableFields: Users.getViewableFields }) diff --git a/packages/vulcan-users/lib/server/on_create_user.js b/packages/vulcan-users/lib/server/on_create_user.js deleted file mode 100644 index 7de77241..00000000 --- a/packages/vulcan-users/lib/server/on_create_user.js +++ /dev/null @@ -1,50 +0,0 @@ -import Users from '../collection.js'; -import { runCallbacks, runCallbacksAsync, Utils } from 'meteor/vulcan:lib'; // import from vulcan:lib because vulcan:core isn't loaded yet - -function onCreateUserCallback (options, user) { - - const schema = Users.simpleSchema()._schema; - - delete options.password; // we don't need to store the password digest - delete options.username; // username is already in user object - - // validate options since they can't be trusted - Users.simpleSchema().validate(options); - - // check that the current user has permission to insert each option field - _.keys(options).forEach(fieldName => { - var field = schema[fieldName]; - if (!field || !Users.canInsertField (user, field)) { - throw new Error(Utils.encodeIntlError({id: 'app.disallowed_property_detected', value: fieldName})); - } - }); - - // extend user with options - user = Object.assign(user, options); - - // run validation callbacks - user = runCallbacks(`users.new.validate`, user); - - // run onInsert step - _.keys(schema).forEach(fieldName => { - if (!user[fieldName] && schema[fieldName].onInsert) { - const autoValue = schema[fieldName].onInsert(user, options); - if (autoValue) { - user[fieldName] = autoValue; - } - } - }); - - user = runCallbacks("users.new.sync", user); - - runCallbacksAsync("users.new.async", user); - - // check if all required fields have been filled in. If so, run profile completion callbacks - if (Users.hasCompletedProfile(user)) { - runCallbacksAsync("users.profileCompleted.async", user); - } - - return user; -} - -Accounts.onCreateUser(onCreateUserCallback); \ No newline at end of file diff --git a/packages/vulcan-users/lib/server/urls.js b/packages/vulcan-users/lib/server/urls.js deleted file mode 100644 index d86b0be9..00000000 --- a/packages/vulcan-users/lib/server/urls.js +++ /dev/null @@ -1 +0,0 @@ -Accounts.urls.resetPassword = (token) => Meteor.absoluteUrl(`reset-password/${token}`); diff --git a/packages/vulcan-users/lib/views.js b/packages/vulcan-users/lib/views.js deleted file mode 100644 index d447d488..00000000 --- a/packages/vulcan-users/lib/views.js +++ /dev/null @@ -1,7 +0,0 @@ -import Users from './collection.js'; - -Users.addView('usersAdmin', terms => ({ - options: { - sort: {createdAt: -1} - } -})); \ No newline at end of file diff --git a/packages/vulcan-users/package.js b/packages/vulcan-users/package.js deleted file mode 100644 index 7c3a8faa..00000000 --- a/packages/vulcan-users/package.js +++ /dev/null @@ -1,19 +0,0 @@ -Package.describe({ - name: 'vulcan:users', - summary: 'Vulcan permissions.', - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom(['METEOR@1.0']); - - api.use([ - 'vulcan:lib@1.7.0' - ]); - - api.mainModule("lib/server.js", "server"); - api.mainModule("lib/client.js", "client"); - -}); diff --git a/packages/vulcan-voting/README.md b/packages/vulcan-voting/README.md deleted file mode 100644 index d4ad37c4..00000000 --- a/packages/vulcan-voting/README.md +++ /dev/null @@ -1 +0,0 @@ -Vulcan scoring package, used internally. \ No newline at end of file diff --git a/packages/vulcan-voting/lib/callbacks.js b/packages/vulcan-voting/lib/callbacks.js deleted file mode 100644 index ac761475..00000000 --- a/packages/vulcan-voting/lib/callbacks.js +++ /dev/null @@ -1,169 +0,0 @@ -import { addCallback, runCallbacksAsync, Utils } from 'meteor/vulcan:core'; -import Users from 'meteor/vulcan:users'; -import Posts from 'meteor/vulcan:posts'; -import Comments from 'meteor/vulcan:comments'; -import { operateOnItem, getVotePower } from './vote.js'; -import { updateScore } from './scoring.js'; - -/* - -### posts.new.sync - -- PostsNewUpvoteOwnPost - -### comments.new.sync - -- CommentsNewUpvoteOwnComment - -### upvote.async -### downvote.async -### cancelUpvote.async -### cancelDownvote.async - -- updateItemScore -- updateUser -- updateKarma - -### posts.new.async -### comments.new.async - -- UpvoteAsyncCallbacksAfterDocumentInsert - -*/ - - -// -------------------------- posts.new.sync ------------------------------- // - -/** - * @summary Make users upvote their own new posts - */ -function PostsNewUpvoteOwnPost(post) { - var postAuthor = Users.findOne(post.userId); - return {...post, ...operateOnItem(Posts, post, postAuthor, 'upvote', false)}; -} - -addCallback("posts.new.sync", PostsNewUpvoteOwnPost); - - -// ----------------------- comments.new.sync ------------------------------- // - -/** - * @summary Make users upvote their own new comments - */ -function CommentsNewUpvoteOwnComment(comment) { - var commentAuthor = Users.findOne(comment.userId); - return {...comment, ...operateOnItem(Comments, comment, commentAuthor, 'upvote', false)}; -} - -addCallback("comments.new.sync", CommentsNewUpvoteOwnComment); - - -// ----------------------------- vote.async ------------------------------- // - -/** - * @summary Update an item's (post or comment) score - * @param {object} item - The item being operated on - * @param {object} user - The user doing the operation - * @param {object} collection - The collection the item belongs to - * @param {string} operation - The operation being performed - */ -function updateItemScore(item, user, collection, operation, context) { - updateScore({collection: collection, item: item, forceUpdate: true}); -} - -addCallback("upvote.async", updateItemScore); -addCallback("downvote.async", updateItemScore); -addCallback("cancelUpvote.async", updateItemScore); -addCallback("cancelDownvote.async", updateItemScore); - - -/** - * @summary Update the profile of the user doing the operation - * @param {object} item - The item being operated on - * @param {object} user - The user doing the operation - * @param {object} collection - The collection the item belongs to - * @param {string} operation - The operation being performed - */ -function updateUser(item, user, collection, operation, context) { - - // uncomment for debug - // console.log(item); - // console.log(user); - // console.log(collection._name); - // console.log(operation); - - const update = {}; - const votePower = getVotePower(user); - const vote = { - itemId: item._id, - votedAt: new Date(), - power: votePower - }; - - const collectionName = Utils.capitalize(collection._name); - - switch (operation) { - case "upvote": - update.$addToSet = {[`upvoted${collectionName}`]: vote}; - break; - case "downvote": - update.$addToSet = {[`downvoted${collectionName}`]: vote}; - break; - case "cancelUpvote": - update.$pull = {[`upvoted${collectionName}`]: {itemId: item._id}}; - break; - case "cancelDownvote": - update.$pull = {[`downvoted${collectionName}`]: {itemId: item._id}}; - break; - } - - Users.update({_id: user._id}, update); - -} - -addCallback("upvote.async", updateUser); -addCallback("downvote.async", updateUser); -addCallback("cancelUpvote.async", updateUser); -addCallback("cancelDownvote.async", updateUser); - - -/** - * @summary Update the karma of the item's owner - * @param {object} item - The item being operated on - * @param {object} user - The user doing the operation - * @param {object} collection - The collection the item belongs to - * @param {string} operation - The operation being performed - */ -function updateKarma(item, user, collection, operation, context) { - - const votePower = getVotePower(user); - const karmaAmount = (operation === "upvote" || operation === "cancelDownvote") ? votePower : -votePower; - - // only update karma is the operation isn't done by the item's author - if (item.userId !== user._id) { - Users.update({_id: item.userId}, {$inc: {"karma": karmaAmount}}); - } - -} - -addCallback("upvote.async", updateKarma); -addCallback("downvote.async", updateKarma); -addCallback("cancelUpvote.async", updateKarma); -addCallback("cancelDownvote.async", updateKarma); - - -// ----------------------- posts.new.async --------------------------------- // -// ----------------------- comments.new.async ------------------------------ // - -/** - * @summary Run the "upvote.async" callbacks *once* the item exists in the database - * @param {object} item - The item being operated on - * @param {object} user - The user doing the operation - * @param {object} collection - The collection the item belongs to - */ -function UpvoteAsyncCallbacksAfterDocumentInsert(item, user, collection) { - runCallbacksAsync("upvote.async", item, user, collection, 'upvote'); -} - -addCallback("posts.new.async", UpvoteAsyncCallbacksAfterDocumentInsert); -addCallback("comments.new.async", UpvoteAsyncCallbacksAfterDocumentInsert); diff --git a/packages/vulcan-voting/lib/client.js b/packages/vulcan-voting/lib/client.js deleted file mode 100644 index 32600f82..00000000 --- a/packages/vulcan-voting/lib/client.js +++ /dev/null @@ -1 +0,0 @@ -export * from './modules.js'; \ No newline at end of file diff --git a/packages/vulcan-voting/lib/containers/withVote.js b/packages/vulcan-voting/lib/containers/withVote.js deleted file mode 100644 index f6adcc09..00000000 --- a/packages/vulcan-voting/lib/containers/withVote.js +++ /dev/null @@ -1,59 +0,0 @@ -import React, { PropTypes, Component } from 'react'; -import { graphql } from 'react-apollo'; -import gql from 'graphql-tag'; -import { operateOnItem } from '../vote.js'; - -const withVote = component => { - - return graphql(gql` - mutation vote($documentId: String, $voteType: String, $collectionName: String) { - vote(documentId: $documentId, voteType: $voteType, collectionName: $collectionName) { - ... on Post { - _id - upvotes - upvoters { - _id - } - downvotes - downvoters { - _id - } - baseScore - } - ... on Comment { - _id - upvotes - upvoters { - _id - } - downvotes - downvoters { - _id - } - baseScore - } - } - } - `, { - props: ({ownProps, mutate}) => ({ - vote: ({document, voteType, collection, currentUser}) => { - const voteResult = operateOnItem(collection, document, currentUser, voteType, true); - return mutate({ - variables: { - documentId: document._id, - voteType, - collectionName: collection._name, - }, - optimisticResponse: { - __typename: 'Mutation', - vote: { - ...voteResult, - }, - } - }) - } - }), - })(component); -} - -export default withVote; \ No newline at end of file diff --git a/packages/vulcan-voting/lib/cron.js b/packages/vulcan-voting/lib/cron.js deleted file mode 100644 index dd039c37..00000000 --- a/packages/vulcan-voting/lib/cron.js +++ /dev/null @@ -1,41 +0,0 @@ -import Posts from "meteor/vulcan:posts"; -import Comments from "meteor/vulcan:comments"; -import { getSetting } from 'meteor/vulcan:core'; -import { updateScore } from './scoring.js'; - -// TODO use a node cron or at least synced-cron -Meteor.startup(function () { - const scoreInterval = parseInt(getSetting("scoreUpdateInterval")) || 30; - if (scoreInterval > 0) { - - // active items get updated every N seconds - Meteor.setInterval(function () { - let updatedPosts = 0; - let updatedComments = 0; - - // console.log('tick ('+scoreInterval+')'); - Posts.find({'status': 2,'inactive': {$ne : true}}).forEach(function (post) { // only run scoring on approved posts - updatedPosts += updateScore({collection: Posts, item: post}); - }); - Comments.find({'inactive': {$ne : true}}).forEach(function (comment) { - updatedComments += updateScore({collection: Comments, item: comment}); - }); - // console.log("Updated "+updatedPosts+"/"+Posts.find().count()+" Posts") - // console.log("Updated "+updatedComments+"/"+Comments.find().count()+" Comments") - }, scoreInterval * 1000); - - // inactive items get updated every hour - Meteor.setInterval(function () { - let updatedPosts = 0; - let updatedComments = 0; - - Posts.find({'inactive': true}).forEach(function (post) { - updatedPosts += updateScore({collection: Posts, item: post}); - }); - Comments.find({'inactive': true}).forEach(function (comment) { - updatedComments += updateScore({collection: Comments, item: comment}); - }); - }, 3600 * 1000); - - } -}); diff --git a/packages/vulcan-voting/lib/custom_fields.js b/packages/vulcan-voting/lib/custom_fields.js deleted file mode 100644 index ba28eb75..00000000 --- a/packages/vulcan-voting/lib/custom_fields.js +++ /dev/null @@ -1,345 +0,0 @@ -import SimpleSchema from 'simpl-schema'; - -import Users from "meteor/vulcan:users"; -import Posts from "meteor/vulcan:posts"; -import Comments from "meteor/vulcan:comments"; - -/** - * @summary Vote schema - * @type {SimpleSchema} - */ -const voteSchema = new SimpleSchema({ - itemId: { - type: String - }, - power: { - type: Number, - optional: true - }, - votedAt: { - type: Date, - optional: true - } -}); - -Users.addField([ - /** - An array containing comments upvotes - */ - { - fieldName: 'upvotedComments', - fieldSchema: { - type: Array, - optional: true, - viewableBy: Users.owns, - resolveAs: { - fieldName: 'upvotedComments', - type: '[Vote]', - resolver: async (user, args, {currentUser, Users, Comments}) => { - if (!user.upvotedComments) return []; - const comments = await Comments.loader.loadMany(user.upvotedComments); - return Users.restrictViewableFields(currentUser, Comments, comments); - } - }, - } - }, - { - fieldName: 'upvotedComments.$', - fieldSchema: { - type: voteSchema, - optional: true - } - }, - /** - An array containing posts upvotes - */ - { - fieldName: 'upvotedPosts', - fieldSchema: { - type: Array, - optional: true, - viewableBy: Users.owns, - resolveAs: { - fieldName: 'upvotedPosts', - type: '[Vote]', - resolver: async (user, args, {currentUser, Users, Posts}) => { - if (!user.upvotedPosts) return []; - const posts = await Posts.loader.loadMany(user.upvotedPosts); - return Users.restrictViewableFields(currentUser, Posts, posts); - } - }, - } - }, - { - fieldName: 'upvotedPosts.$', - fieldSchema: { - type: voteSchema, - optional: true - } - }, - /** - An array containing comment downvotes - */ - { - fieldName: 'downvotedComments', - fieldSchema: { - type: Array, - optional: true, - viewableBy: Users.owns, - resolveAs: { - fieldName: 'downvotedComments', - type: '[Vote]', - resolver: async (user, args, {currentUser, Users, Comments}) => { - if (!user.downvotedComments) return []; - const comments = await Comments.loader.loadMany(user.downvotedComments); - return Users.restrictViewableFields(currentUser, Comments, comments); - } - }, - } - }, - { - fieldName: 'downvotedComments.$', - fieldSchema: { - type: voteSchema, - optional: true - } - }, - /** - An array containing posts downvotes - */ - { - fieldName: 'downvotedPosts', - fieldSchema: { - type: Array, - optional: true, - viewableBy: Users.owns, - resolveAs: { - fieldName: 'downvotedPosts', - type: '[Vote]', - resolver: async (user, args, {currentUser, Users, Posts}) => { - if (!user.downvotedPosts) return []; - const posts = await Posts.loader.loadMany(user.downvotedPosts); - return Users.restrictViewableFields(currentUser, Posts, posts); - } - }, - } - }, - { - fieldName: 'downvotedPosts.$', - fieldSchema: { - type: voteSchema, - optional: true - } - } -]); - -Posts.addField([ - /** - How many upvotes the post has received - */ - { - fieldName: "upvotes", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - An array containing the `_id`s of the post's upvoters - */ - { - fieldName: "upvoters", - fieldSchema: { - type: Array, - optional: true, - viewableBy: ['guests'], - resolveAs: { - fieldName: 'upvoters', - type: '[User]', - resolver: async (post, args, {currentUser, Users}) => { - if (!post.upvoters) return []; - const upvoters = await Users.loader.loadMany(post.upvoters); - return Users.restrictViewableFields(currentUser, Users, upvoters); - }, - }, - } - }, - { - fieldName: "upvoters.$", - fieldSchema: { - type: String, - optional: true - } - }, - /** - How many downvotes the post has received - */ - { - fieldName: "downvotes", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - An array containing the `_id`s of the post's downvoters - */ - { - fieldName: "downvoters", - fieldSchema: { - type: Array, - optional: true, - viewableBy: ['guests'], - resolveAs: { - fieldName: 'downvoters', - type: '[User]', - resolver: async (post, args, {currentUser, Users}) => { - if (!post.downvoters) return []; - const downvoters = await Users.loader.loadMany(post.downvoters); - return Users.restrictViewableFields(currentUser, Users, downvoters); - }, - }, - } - }, - { - fieldName: "downvoters.$", - fieldSchema: { - type: String, - optional: true, - } - }, - /** - The post's base score (not factoring in the post's age) - */ - { - fieldName: "baseScore", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - The post's current score (factoring in age) - */ - { - fieldName: "score", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, -]); - -Comments.addField([ - /** - The number of upvotes the comment has received - */ - { - fieldName: "upvotes", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - An array containing the `_id`s of upvoters - */ - { - fieldName: "upvoters", - fieldSchema: { - type: Array, - optional: true, - viewableBy: ['guests'], - resolveAs: { - fieldName: 'upvoters', - type: '[User]', - resolver: async (comment, args, {currentUser, Users}) => { - if (!comment.upvoters) return []; - const upvoters = await Users.loader.loadMany(comment.upvoters); - return Users.restrictViewableFields(currentUser, Users, upvoters); - }, - }, - } - }, - { - fieldName: "upvoters.$", - fieldSchema: { - type: String, - optional: true - } - }, - /** - The number of downvotes the comment has received - */ - { - fieldName: "downvotes", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - An array containing the `_id`s of downvoters - */ - { - fieldName: "downvoters", - fieldSchema: { - type: Array, - optional: true, - viewableBy: ['guests'], - resolveAs: { - fieldName:'downvoters', - type: '[User]', - resolver: async (comment, args, {currentUser, Users}) => { - if (!comment.downvoters) return []; - const downvoters = await Users.loader.loadMany(comment.downvoters); - return Users.restrictViewableFields(currentUser, Users, downvoters); - }, - }, - } - }, - { - fieldName: "downvoters.$", - fieldSchema: { - type: String, - optional: true, - } - }, - /** - The comment's base score (not factoring in the comment's age) - */ - { - fieldName: "baseScore", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, - /** - The comment's current score (factoring in age) - */ - { - fieldName: "score", - fieldSchema: { - type: Number, - optional: true, - defaultValue: 0, - viewableBy: ['guests'], - } - }, -]); - diff --git a/packages/vulcan-voting/lib/graphql.js b/packages/vulcan-voting/lib/graphql.js deleted file mode 100644 index 26a20c12..00000000 --- a/packages/vulcan-voting/lib/graphql.js +++ /dev/null @@ -1,46 +0,0 @@ -import { addGraphQLSchema, addGraphQLResolvers, addGraphQLMutation, Utils } from 'meteor/vulcan:core'; -import { mutateItem } from './vote.js'; - -const voteSchema = ` - type Vote { - itemId: String - power: Float - votedAt: String - } - - union Votable = Post | Comment -`; - -addGraphQLSchema(voteSchema); - -const resolverMap = { - Votable: { - __resolveType(obj, context, info){ - if(obj.title){ - return 'Post'; - } - - if(obj.postId){ - return 'Comment'; - } - - return null; - }, - }, -}; - -addGraphQLResolvers(resolverMap); - -addGraphQLMutation('vote(documentId: String, voteType: String, collectionName: String) : Votable'); - -const voteResolver = { - Mutation: { - vote(root, {documentId, voteType, collectionName}, context) { - const collection = context[Utils.capitalize(collectionName)]; - const document = collection.findOne(documentId); - return context.Users.canDo(context.currentUser, `${collectionName.toLowerCase()}.${voteType}`) ? mutateItem(collection, document, context.currentUser, voteType, false) : false; - }, - }, -}; - -addGraphQLResolvers(voteResolver); diff --git a/packages/vulcan-voting/lib/helpers.js b/packages/vulcan-voting/lib/helpers.js deleted file mode 100644 index 0c962f97..00000000 --- a/packages/vulcan-voting/lib/helpers.js +++ /dev/null @@ -1,24 +0,0 @@ - -/** - * @summary Check if a user has upvoted a document - * @param {Object} user - * @param {Object} document - * @returns {Boolean} - */ -const hasUpvoted = (user, document) => { - // note(apollo): check upvoters depending if the document is queried by mongo directly or fetched by an apollo resolver - return user && document.upvoters && !!document.upvoters.find(u => u && (typeof u === 'string' ? u === user._id : u._id === user._id)); -}; - -/** - * @summary Check if a user has downvoted a document - * @param {Object} user - * @param {Object} document - * @returns {Boolean} - */ -const hasDownvoted = (user, document) => { - // note(apollo): check downvoters depending if the document is queried by mongo directly or fetched by an apollo resolver - return user && document.downvoters && !!document.downvoters.find(u => u && (typeof u === 'string' ? u === user._id : u._id === user._id)); -}; - -export { hasUpvoted, hasDownvoted } \ No newline at end of file diff --git a/packages/vulcan-voting/lib/modules.js b/packages/vulcan-voting/lib/modules.js deleted file mode 100644 index 2459e56a..00000000 --- a/packages/vulcan-voting/lib/modules.js +++ /dev/null @@ -1,11 +0,0 @@ -import './graphql.js'; -import './custom_fields.js'; -import './permissions.js'; -import './scoring.js'; -import './callbacks.js'; - -import withVote from './containers/withVote.js'; - -export { withVote }; -export { hasUpvoted, hasDownvoted } from './helpers.js'; -export { operateOnItem, mutateItem } from './vote.js'; diff --git a/packages/vulcan-voting/lib/permissions.js b/packages/vulcan-voting/lib/permissions.js deleted file mode 100644 index 80b37a1a..00000000 --- a/packages/vulcan-voting/lib/permissions.js +++ /dev/null @@ -1,13 +0,0 @@ -import Users from 'meteor/vulcan:users'; - -const membersActions = [ - "posts.upvote", - "posts.cancelUpvote", - "posts.downvote", - "posts.cancelDownvote", - "comments.upvote", - "comments.cancelUpvote", - "comments.downvote", - "comments.cancelDownvote" -]; -Users.groups.members.can(membersActions); diff --git a/packages/vulcan-voting/lib/scoring.js b/packages/vulcan-voting/lib/scoring.js deleted file mode 100644 index aec57a1d..00000000 --- a/packages/vulcan-voting/lib/scoring.js +++ /dev/null @@ -1,59 +0,0 @@ -export const updateScore = ({collection, item, forceUpdate}) => { - - // Status Check - - if (!!item.status && item.status !== 2) // if item has a status and is not approved, don't update its score - return 0; - - // Age Check - - // If for some reason item doesn't have a "postedAt" property, abort - if (!item.postedAt) - return 0; - - const postedAt = item.postedAt.valueOf(); - const now = new Date().getTime(); - const age = now - postedAt; - const ageInHours = age / (60 * 60 * 1000); - - if (postedAt > now) // if post has been scheduled in the future, don't update its score - return 0; - - // For performance reasons, the database is only updated if the difference between the old score and the new score - // is meaningful enough. To find out, we calculate the "power" of a single vote after n days. - // We assume that after n days, a single vote will not be powerful enough to affect posts' ranking order. - // Note: sites whose posts regularly get a lot of votes can afford to use a lower n. - - // n = number of days after which a single vote will not have a big enough effect to trigger a score update - // and posts can become inactive - const n = 30; - // x = score increase amount of a single vote after n days (for n=100, x=0.000040295) - const x = 1/Math.pow(n*24+2,1.3); - // time decay factor - const f = 1.3; - - // use baseScore if defined, if not just use 0 - const baseScore = item.baseScore || 0; - - // HN algorithm - const newScore = baseScore / Math.pow(ageInHours + 2, f); - - // console.log(now) - // console.log(age) - // console.log(ageInHours) - // console.log(baseScore) - // console.log(newScore) - - // Note: before the first time updateScore runs on a new item, its score will be at 0 - const scoreDiff = Math.abs(item.score - newScore); - - // only update database if difference is larger than x to avoid unnecessary updates - if (forceUpdate || scoreDiff > x) { - collection.update(item._id, {$set: {score: newScore, inactive: false}}); - return 1; - } else if(ageInHours > n*24) { - // only set a post as inactive if it's older than n days - collection.update(item._id, {$set: {inactive: true}}); - } - return 0; -}; diff --git a/packages/vulcan-voting/lib/server.js b/packages/vulcan-voting/lib/server.js deleted file mode 100644 index 9924b62b..00000000 --- a/packages/vulcan-voting/lib/server.js +++ /dev/null @@ -1,3 +0,0 @@ -import './cron.js'; - -export * from './modules.js'; diff --git a/packages/vulcan-voting/lib/vote.js b/packages/vulcan-voting/lib/vote.js deleted file mode 100644 index 2ebec84d..00000000 --- a/packages/vulcan-voting/lib/vote.js +++ /dev/null @@ -1,143 +0,0 @@ -import Users from 'meteor/vulcan:users'; -import { hasUpvoted, hasDownvoted } from './helpers.js'; -import { runCallbacks, runCallbacksAsync } from 'meteor/vulcan:core'; -import update from 'immutability-helper'; - -// The equation to determine voting power. Defaults to returning 1 for everybody -export const getVotePower = function (user) { - return 1; -}; - -const keepVoteProperties = item => _.pick(item, '__typename', '_id', 'upvoters', 'downvoters', 'upvotes', 'downvotes', 'baseScore'); - -/* - -Runs all the operation and returns an objects without affecting the db. - -*/ -export const operateOnItem = function (collection, originalItem, user, operation, isClient = false) { - - user = typeof user === "undefined" ? Meteor.user() : user; - - let item = { - upvotes: 0, - downvotes: 0, - upvoters: [], - downvoters: [], - baseScore: 0, - ...originalItem, - }; // we do not want to affect the original item directly - - const votePower = getVotePower(user); - const hasUpvotedItem = hasUpvoted(user, item); - const hasDownvotedItem = hasDownvoted(user, item); - const collectionName = collection._name; - const canDo = Users.canDo(user, `${collectionName}.${operation}`); - - // console.log('// operateOnItem') - // console.log('isClient: ', isClient) - // console.log('collection: ', collectionName) - // console.log('operation: ', operation) - // console.log('item: ', item) - // console.log('user: ', user) - // console.log('hasUpvotedItem: ', hasUpvotedItem) - // console.log('hasDownvotedItem: ', hasDownvotedItem) - // console.log('canDo: ', canDo) - - // make sure item and user are defined, and user can perform the operation - if ( - !item || - !user || - !canDo || - operation === "upvote" && hasUpvotedItem || - operation === "downvote" && hasDownvotedItem || - operation === "cancelUpvote" && !hasUpvotedItem || - operation === "cancelDownvote" && !hasDownvotedItem - ) { - throw new Error(`Cannot perform operation "${collectionName}.${operation}"`); - } - - // ------------------------------ Sync Callbacks ------------------------------ // - - item = runCallbacks(operation, item, user, operation, isClient); - - /* - - voters arrays have different structures on client and server: - - - client: [{__typename: "User", _id: 'foo123'}] - - server: ['foo123'] - - */ - - const voter = isClient ? {__typename: "User", _id: user._id} : user._id; - const filterFunction = isClient ? u => u._id !== user._id : u => u !== user._id; - - switch (operation) { - - case "upvote": - if (hasDownvotedItem) { - item = operateOnItem(collection, item, user, "cancelDownvote", isClient); - } - - item = update(item, { - upvoters: {$push: [voter]}, - upvotes: {$set: item.upvotes + 1}, - baseScore: {$set: item.baseScore + votePower}, - }); - - break; - - case "downvote": - if (hasUpvotedItem) { - item = operateOnItem(collection, item, user, "cancelUpvote", isClient); - } - - item = update(item, { - downvoters: {$push: [voter]}, - downvotes: {$set: item.downvotes + 1}, - baseScore: {$set: item.baseScore - votePower}, - }); - - break; - - case "cancelUpvote": - item = update(item, { - upvoters: {$set: item.upvoters.filter(filterFunction)}, - upvotes: {$set: item.upvotes - 1}, - baseScore: {$set: item.baseScore - votePower}, - }); - break; - - case "cancelDownvote": - - item = update(item, { - downvoters: {$set: item.downvoters.filter(filterFunction)}, - downvotes: {$set: item.downvotes - 1}, - baseScore: {$set: item.baseScore + votePower}, - }); - - break; - } - - // console.log('new item', item); - - return item; -}; - -/* - -Call operateOnItem, update the db with the result, run callbacks. - -*/ -export const mutateItem = function (collection, originalItem, user, operation) { - const newItem = operateOnItem(collection, originalItem, user, operation, false); - newItem.inactive = false; - - collection.update({_id: newItem._id}, newItem, {bypassCollection2:true}); - - // --------------------- Server-Side Async Callbacks --------------------- // - runCallbacksAsync(operation+".async", newItem, user, collection, operation); - - return newItem; -} diff --git a/packages/vulcan-voting/package.js b/packages/vulcan-voting/package.js deleted file mode 100644 index 52bc29bc..00000000 --- a/packages/vulcan-voting/package.js +++ /dev/null @@ -1,21 +0,0 @@ -Package.describe({ - name: "vulcan:voting", - summary: "Vulcan scoring package.", - version: '1.7.0', - git: "https://github.com/VulcanJS/Vulcan.git" -}); - -Package.onUse(function (api) { - - api.versionsFrom("METEOR@1.0"); - - api.use([ - 'vulcan:core@1.7.0', - 'vulcan:posts@1.7.0', - 'vulcan:comments@1.7.0' - ], ['client', 'server']); - - api.mainModule("lib/server.js", "server"); - api.mainModule("lib/client.js", "client"); - -}); diff --git a/yarn.lock b/yarn.lock index baa2852f..3200f4cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,158 +2,144 @@ # yarn lockfile v1 -"@types/express-serve-static-core@*": - version "4.0.40" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.40.tgz#168e82978bffc81ee7737bc60728d64733a4f37b" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0": + version "7.3.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a" + integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA== dependencies: - "@types/node" "*" + regenerator-runtime "^0.12.0" -"@types/express@^4.0.35": - version "4.0.35" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.35.tgz#6267c7b60a51fac473467b3c4a02cd1e441805fe" +"@react-bootstrap/react-popper@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@react-bootstrap/react-popper/-/react-popper-1.2.1.tgz#4edf4851d5b4dcf2eb6b264ebbed1a7b7654177b" + integrity sha512-4l3q7LcZEhrSkI4d3Ie3g4CdrXqqTexXX4PFT45CB0z5z2JUbaxgRwKNq7r5j2bLdVpZm+uvUGqxJw8d9vgbJQ== + dependencies: + babel-runtime "6.x.x" + create-react-context "^0.2.1" + popper.js "^1.14.4" + prop-types "^15.6.1" + typed-styles "^0.0.5" + warning "^3.0.0" + +"@segment/loosely-validate-event@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@segment/loosely-validate-event/-/loosely-validate-event-1.1.2.tgz#d77840999e3f7e43e74b3b0d43391c1526f793b8" + integrity sha1-13hAmZ4/fkPnSzsNQzkcFSb3k7g= dependencies: - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" + component-type "^1.2.1" + join-component "^1.1.0" "@types/graphql@0.10.2": version "0.10.2" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" + integrity sha512-Ayw0w+kr8vYd8DToiMXjcHxXv1ljWbqX2mnLwXDxkBgog3vywGriC0JZ+npsuohKs3+E88M8OOtobo4g0X3SIA== -"@types/graphql@^0.8.5", "@types/graphql@^0.8.6": - version "0.8.6" - resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.8.6.tgz#b34fb880493ba835b0c067024ee70130d6f9bb68" - -"@types/mime@*": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" - -"@types/node@*": - version "6.0.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.60.tgz#e7e134ebc674ae6ed93c36c767739b110d2c57fc" - -"@types/serve-static@*": - version "1.7.31" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.31.tgz#15456de8d98d6b4cff31be6c6af7492ae63f521a" - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -Base64@~0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" - -abab@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= dependencies: - mime-types "~2.1.11" + mime-types "~2.1.18" negotiator "0.6.1" -acorn-globals@^1.0.4: - version "1.0.9" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" - dependencies: - acorn "^2.1.0" - -acorn-jsx@^3.0.0, acorn-jsx@^3.0.1: +acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= dependencies: acorn "^3.0.4" -acorn@^2.1.0, acorn@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" - -acorn@^3.0.4, acorn@^3.1.0: +acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= -acorn@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - -agent-base@2: - version "2.0.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.0.1.tgz#bd8f9e86a8eb221fffa07bd14befd55df142815e" - dependencies: - extend "~3.0.0" - semver "~5.0.1" +acorn@^5.5.0: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== ajv-keywords@^1.0.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.0.tgz#c11e6859eafff83e0dafc416929472eca946aa2c" + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= ajv@^4.7.0: - version "4.10.4" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.10.4.tgz#c0974dd00b3464984892d6010aa9c2c945933254" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -alter@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" +ajv@^6.5.5: + version "6.8.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.8.1.tgz#0890b93742985ebf8973cd365c5b23920ce3cb20" + integrity sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ== dependencies: - stable "~0.1.3" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" analytics-node@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/analytics-node/-/analytics-node-2.1.1.tgz#bb92b26b35db7cd64333e20475d38b742cc3eaae" + version "2.4.1" + resolved "https://registry.yarnpkg.com/analytics-node/-/analytics-node-2.4.1.tgz#1f96c8eb887b6c47691044ac7fc9a1231fb020f7" + integrity sha1-H5bI64h7bEdpEESsf8mhIx+wIPc= dependencies: - clone "~2.1.0" + "@segment/loosely-validate-event" "^1.1.2" + clone "^2.1.1" commander "^2.9.0" - component-type "~1.2.1" crypto-token "^1.0.1" - debug "^2.2.0" - join-component "~1.1.0" - lodash "~4.17.2" - superagent "^3.0.0" - superagent-proxy "^1.0.0" + debug "^2.6.2" + lodash "^4.17.4" + remove-trailing-slash "^0.1.0" + superagent "^3.5.0" superagent-retry "^0.6.0" ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= ansi-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -any-promise@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" +ansi-styles@^3.1.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +apollo-cache-control@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.1.1.tgz#173d14ceb3eb9e7cb53de7eb8b61bee6159d4171" + integrity sha512-XJQs167e9u+e5ybSi51nGYr70NPBbswdvTEHtbtXbwkZ+n9t0SLPvUcoqceayOSwjK1XYOdU/EKPawNdb3rLQA== + dependencies: + graphql-extensions "^0.0.x" apollo-client@^1.2.2, apollo-client@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.9.1.tgz#9e6a383605572c755038cf5d7fdac9382bcdc040" + version "1.9.3" + resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.9.3.tgz#37000b3c801f4571b7b089739e696a158896aeab" + integrity sha512-JABKKbqvcw8DJm3YUkEmyx1SK74i+/DesEtAtyocJi10LLmeMQYQFpg8W3BG1tZsYEQ3owEmPbsdNGTly+VOQg== dependencies: apollo-link-core "^0.5.0" graphql "^0.10.0" @@ -165,419 +151,405 @@ apollo-client@^1.2.2, apollo-client@^1.4.0: optionalDependencies: "@types/graphql" "0.10.2" -apollo-errors@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/apollo-errors/-/apollo-errors-1.4.0.tgz#7fb0c58c4ab1712cf5568315e88493aaee38697d" +apollo-engine-binary-darwin@^0.2017.11-84-gb299b9188: + version "0.2017.11-84-gb299b9188" + resolved "https://registry.yarnpkg.com/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.2017.11-84-gb299b9188.tgz#1e863513bb4dce66edd7fe9f12014d55a1c9bed6" + integrity sha512-8zKIFo6ldSwT1npHU4gjHMDEJQuN/CG3MCnx5xY5MGSSkqlqNZZ8njYgXe4qLEjewLMwRTXapcnCw7E2+H1RYQ== + +apollo-engine-binary-linux@^0.2017.11-84-gb299b9188: + version "0.2017.11-84-gb299b9188" + resolved "https://registry.yarnpkg.com/apollo-engine-binary-linux/-/apollo-engine-binary-linux-0.2017.11-84-gb299b9188.tgz#02c4e96b1689fd9d1302535d57de8e60bc763688" + integrity sha512-Y+DYYoR24yi73+Kt03Nr7IXNoMJw6faEgdUpysMdnkIdmqaFfcKj3KH0auzVBhPyVcJo+iRTKqXdnMzjnQxrsg== + +apollo-engine-binary-windows@^0.2017.11-84-gb299b9188: + version "0.2017.11-84-gb299b9188" + resolved "https://registry.yarnpkg.com/apollo-engine-binary-windows/-/apollo-engine-binary-windows-0.2017.11-84-gb299b9188.tgz#706bda576e004c0763a95fd1570f7c7a9718a5df" + integrity sha512-ecpP1HrlP+eb5mNQuz7ObzMWtGJA78UrPlzGRes1KiKJ/c8e1UrrAWI/wuI0Ry7fIKYA6dUzxJ4fHR5TEnMAVA== + +apollo-engine@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/apollo-engine/-/apollo-engine-0.5.6.tgz#b1f1d816df7f598e1dcb453013990991d1491ca3" + integrity sha512-uE7eheOEQO5TY3TZVa2it/wq6DdtjzULXYrRPufzaCw6+L9lABt2P4KPJ5kIpEr5Xku+6dX6QEuRfQgleDbhOg== + dependencies: + request "^2.81.0" + stream-line-wrapper "^0.1.1" + optionalDependencies: + apollo-engine-binary-darwin "^0.2017.11-84-gb299b9188" + apollo-engine-binary-linux "^0.2017.11-84-gb299b9188" + apollo-engine-binary-windows "^0.2017.11-84-gb299b9188" + +apollo-errors@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/apollo-errors/-/apollo-errors-1.9.0.tgz#f1ed0ca0a6be5cd2f24e2eaa7b0860a10146ff51" + integrity sha512-XVukHd0KLvgY6tNjsPS3/Re3U6RQlTKrTbIpqqeTMo2N34uQMr+H1UheV21o8hOZBAFosvBORVricJiP5vfmrw== dependencies: - es6-error "^4.0.0" + assert "^1.4.1" + extendable-error "^0.1.5" apollo-link-core@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/apollo-link-core/-/apollo-link-core-0.5.0.tgz#dc87da1aaa63b029321ae70938dc26257f5ab8c6" + version "0.5.4" + resolved "https://registry.yarnpkg.com/apollo-link-core/-/apollo-link-core-0.5.4.tgz#8efd4cd747959872a32f313f0ccfc2a76b396668" + integrity sha512-OxL0Kjizb0eS2ObldDqJEs/tFN9xI9RZuTJcaszgGy+xudoPXhIMCHMr7hGZhy0mK+U+BbBULZJw4YQU4J0ODQ== dependencies: graphql "^0.10.3" graphql-tag "^2.4.2" - zen-observable-ts "^0.4.0" + zen-observable-ts "^0.4.4" + +apollo-link@^1.2.2: + version "1.2.8" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.8.tgz#0f252adefd5047ac1a9f35ba9439d216587dcd84" + integrity sha512-lfzGRxhK9RmiH3HPFi7TIEBhhDY9M5a2ZDnllcfy5QDk7cCQHQ1WQArcw1FK0g1B+mV4Kl72DSrlvZHZJEolrA== + dependencies: + zen-observable-ts "^0.8.15" + +apollo-server-core@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-1.4.0.tgz#4faff7f110bfdd6c3f47008302ae24140f94c592" + integrity sha512-BP1Vh39krgEjkQxbjTdBURUjLHbFq1zeOChDJgaRsMxGtlhzuLWwwC6lLdPatN8jEPbeHq8Tndp9QZ3iQZOKKA== + dependencies: + apollo-cache-control "^0.1.0" + apollo-tracing "^0.1.0" + graphql-extensions "^0.0.x" + +apollo-server-express@^1.3.6: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-1.4.0.tgz#7d7c58d6d6f9892b83fe575669093bb66738b125" + integrity sha512-zkH00nxhLnJfO0HgnNPBTfZw8qI5ILaPZ5TecMCI9+Y9Ssr2b0bFr9pBRsXy9eudPhI+/O4yqegSUsnLdF/CPw== + dependencies: + apollo-server-core "^1.4.0" + apollo-server-module-graphiql "^1.4.0" + +apollo-server-module-graphiql@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec" + integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA== + +apollo-tracing@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.1.4.tgz#5b8ae1b01526b160ee6e552a7f131923a9aedcc7" + integrity sha512-Uv+1nh5AsNmC3m130i2u3IqbS+nrxyVV3KYimH5QKsdPjxxIQB3JAT+jJmpeDxBel8gDVstNmCh82QSLxLSIdQ== + dependencies: + graphql-extensions "~0.0.9" + +apollo-utilities@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.1.2.tgz#aa5eca9d1f1eb721c381a22e0dde03559d856db3" + integrity sha512-EjDx8vToK+zkWIxc76ZQY/irRX52puNg04xf/w8R0kVTDAgHuVfnFVC01O5vE25kFnIaa5em0pFI0p9b6YMkhQ== + dependencies: + fast-json-stable-stringify "^2.0.0" + tslib "^1.9.3" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: +array-uniq@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array.prototype.find@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.1.tgz#1557f888df6c57e4d1256f20852d687a25b51fde" + version "2.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" + integrity sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA= dependencies: define-properties "^1.1.2" - es-abstract "^1.5.0" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + es-abstract "^1.7.0" asap@~2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1.js@^4.0.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" -asn1@0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7" - asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" assert-err@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assert-err/-/assert-err-1.1.0.tgz#c05062799a1d97d3f5eaa258e3242aab499fc8ef" + integrity sha1-wFBieZodl9P16qJY4yQqq0mfyO8= dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -assert-plus@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -assert-plus@^1.0.0: +assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assert@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= dependencies: util "0.10.3" -ast-traverse@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" - -ast-types@0.8.12: - version "0.8.12" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" - -ast-types@0.9.4, ast-types@0.x.x: - version "0.9.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.4.tgz#410d1f81890aeb8e0a38621558ba5869ae53c91b" - -async@^0.9.0, async@~0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - -async@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" +async@^2.1.2, async@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + dependencies: + lodash "^4.17.10" -async@~0.2.6: +async@~0.2.10: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= attr-accept@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.0.tgz#b5cd35227f163935a8f1de10ed3eba16941f6be6" + version "1.1.3" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52" + integrity sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ== + dependencies: + core-js "^2.5.0" autoprefixer@^6.3.6: - version "6.6.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.6.1.tgz#11a4077abb4b313253ec2f6e1adb91ad84253519" + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + integrity sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ= dependencies: - browserslist "~1.5.1" - caniuse-db "^1.0.30000604" + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^5.2.8" + postcss "^5.2.16" postcss-value-parser "^3.2.3" -aws-sign2@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.5.0.tgz#c57103f7a17fc037f02d7c2e64b602ea223f7d63" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws4@^1.2.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== -babel-code-frame@^6.16.0, babel-code-frame@^6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.20.0.tgz#b968f839090f9a8bc6d41938fb96cb84f7387b26" +babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= dependencies: - chalk "^1.1.0" + chalk "^1.1.3" esutils "^2.0.2" - js-tokens "^2.0.0" - -babel-core@^6.16.0, babel-core@^6.18.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.21.0.tgz#75525480c21c803f826ef3867d22c19f080a3724" - dependencies: - babel-code-frame "^6.20.0" - babel-generator "^6.21.0" - babel-helpers "^6.16.0" - babel-messages "^6.8.0" - babel-register "^6.18.0" - babel-runtime "^6.20.0" - babel-template "^6.16.0" - babel-traverse "^6.21.0" - babel-types "^6.21.0" - babylon "^6.11.0" - convert-source-map "^1.1.0" - debug "^2.1.1" - json5 "^0.5.0" - lodash "^4.2.0" - minimatch "^3.0.2" - path-is-absolute "^1.0.0" - private "^0.1.6" - slash "^1.0.0" - source-map "^0.5.0" + js-tokens "^3.0.2" babel-eslint@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.1.tgz#8a6a884f085aa7060af69cfc77341c2f99370fb2" - dependencies: - babel-code-frame "^6.16.0" - babel-traverse "^6.15.0" - babel-types "^6.15.0" - babylon "^6.13.0" - lodash.pickby "^4.6.0" - -babel-generator@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.21.0.tgz#605f1269c489a1c75deeca7ea16d43d4656c8494" - dependencies: - babel-messages "^6.8.0" - babel-runtime "^6.20.0" - babel-types "^6.21.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.2.0" - source-map "^0.5.0" - -babel-helpers@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.16.0.tgz#1095ec10d99279460553e67eb3eee9973d3867e3" - dependencies: - babel-runtime "^6.0.0" - babel-template "^6.16.0" - -babel-messages@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" - dependencies: - babel-runtime "^6.0.0" - -babel-register@6.16.3: - version "6.16.3" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.16.3.tgz#7b0c0ca7bfdeb9188ba4c27e5fcb7599a497c624" - dependencies: - babel-core "^6.16.0" - babel-runtime "^6.11.6" - core-js "^2.4.0" - home-or-tmp "^1.0.0" - lodash "^4.2.0" - mkdirp "^0.5.1" - path-exists "^1.0.0" - source-map-support "^0.4.2" - -babel-register@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.18.0.tgz#892e2e03865078dd90ad2c715111ec4449b32a68" + version "7.2.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" + integrity sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc= dependencies: - babel-core "^6.18.0" - babel-runtime "^6.11.6" - core-js "^2.4.0" - home-or-tmp "^2.0.0" - lodash "^4.2.0" - mkdirp "^0.5.1" - source-map-support "^0.4.2" + babel-code-frame "^6.22.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" + babylon "^6.17.0" -babel-runtime@6.11.6: - version "6.11.6" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.11.6.tgz#6db707fef2d49c49bfa3cb64efdb436b518b8222" +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.9.5" + babel-runtime "^6.22.0" -"babel-runtime@>=0.14.7 || ^15.0.0-rc.2", babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.9.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.20.0.tgz#87300bdcf4cd770f09bf0048c64204e17806d16f" +babel-runtime@6.x.x, "babel-runtime@>=0.14.7 || ^15.0.0-rc.2", babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= dependencies: core-js "^2.4.0" - regenerator-runtime "^0.10.0" - -babel-runtime@^5.6.18: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" - dependencies: - core-js "^1.0.0" - -babel-template@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" - dependencies: - babel-runtime "^6.9.0" - babel-traverse "^6.16.0" - babel-types "^6.16.0" - babylon "^6.11.0" - lodash "^4.2.0" - -babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.21.0.tgz#69c6365804f1a4f69eb1213f85b00a818b8c21ad" - dependencies: - babel-code-frame "^6.20.0" - babel-messages "^6.8.0" - babel-runtime "^6.20.0" - babel-types "^6.21.0" - babylon "^6.11.0" - debug "^2.2.0" - globals "^9.0.0" - invariant "^2.2.0" - lodash "^4.2.0" - -babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.21.0.tgz#314b92168891ef6d3806b7f7a917fdf87c11a4b2" - dependencies: - babel-runtime "^6.20.0" + regenerator-runtime "^0.11.0" + +babel-traverse@^6.23.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.23.0, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^1.0.1" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.17.0, babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== -babylon@^6.11.0, babylon@^6.13.0: - version "6.15.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" +bail@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" + integrity sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg== -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base64-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" - -batch@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== bcrypt-pbkdf@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" -bcrypt@^0.8.7: - version "0.8.7" - resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-0.8.7.tgz#bc3875a9afd0a7b2cd231a6a7f218a5ce156b093" +bcrypt@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-3.0.4.tgz#1c881379ddf21bcade56e3172669d27152d90d50" + integrity sha512-XqmCym97kT6l+jFEKeFvGuNE9aVEFDGsLMv+tIBTXkJI1sHS0g8s7VQEPJagSMPwWiB5Vpr2kVzVKc/YfwWthA== dependencies: - bindings "1.2.1" - nan "2.3.5" + nan "2.12.1" + node-pre-gyp "0.12.0" -bindings@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" +bluebird@^2.10.2, bluebird@^2.6.2, bluebird@^2.8.1: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.6" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" - -body-parser@^1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67" - dependencies: - bytes "2.4.0" - content-type "~1.0.2" - debug "~2.2.0" - depd "~1.1.0" - http-errors "~1.5.0" - iconv-lite "0.4.13" + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.18.3, body-parser@^1.18.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" on-finished "~2.3.0" - qs "6.2.0" - raw-body "~2.1.7" - type-is "~1.6.13" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -boom@0.4.x: - version "0.4.2" - resolved "https://registry.yarnpkg.com/boom/-/boom-0.4.2.tgz#7a636e9ded4efcefb19cef4947a3c67dfaee911b" - dependencies: - hoek "0.9.x" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -bootstrap-sass@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.7.tgz#6596c7ab40f6637393323ab0bc80d064fc630498" +bootstrap-sass@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.4.0.tgz#b1c330a56782347f626d31d497fa4aea16b3f99b" + integrity sha512-qdUyw4KmNNPSIdBadn+eyuuQFH0LsZlRCs6tor1zN8sQas7mnY5JNfemauraOdNPiFQd2gFeeo3gZjZZCuohZg== -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" -breakable@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" - brorand@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== dependencies: - buffer-xor "^1.0.2" + buffer-xor "^1.0.3" cipher-base "^1.0.0" create-hash "^1.1.0" - evp_bytestokey "^1.0.0" + evp_bytestokey "^1.0.3" inherits "^2.0.1" + safe-buffer "^5.0.1" browserify-cipher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" evp_bytestokey "^1.0.0" browserify-des@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== dependencies: cipher-base "^1.0.1" des.js "^1.0.0" inherits "^2.0.1" + safe-buffer "^5.1.2" browserify-rsa@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= dependencies: bn.js "^4.1.0" randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= dependencies: bn.js "^4.1.1" browserify-rsa "^4.0.0" @@ -590,88 +562,95 @@ browserify-sign@^4.0.0: browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0= dependencies: pako "~0.2.0" -browserslist@~1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.5.2.tgz#1c82fde0ee8693e6d15c49b7bff209dc06298c56" +browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk= dependencies: - caniuse-db "^1.0.30000604" + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== -buffer-xor@^1.0.2: +buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= buffer@^4.9.1: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" isarray "^1.0.0" buffer@^5.0.3: - version "5.0.7" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.7.tgz#570a290b625cf2603290c1149223d27ccf04db97" + version "5.2.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" + integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -bytebuffer@~5: - version "5.0.1" - resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" - dependencies: - long "~3" +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= -bytes@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= dependencies: callsites "^0.2.0" callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= -camelcase@^1.0.2, camelcase@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -caniuse-db@^1.0.30000604: - version "1.0.30000606" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000606.tgz#69a72b40926a698fd4887bab9051575e0e3fb43b" +camelize@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= -caseless@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" +caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000935" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000935.tgz#0b4210ae207013c76bc1a2e0e903b15bec742a08" + integrity sha512-HFqvW9MZZcWD02F3J2GV2ggQyIXiDr7DRPlOWSKVIihu8J1dtsYFqtMjmFqiYamfOmY4NHyn7xFaWAHBtFWgjQ== caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" +chalk@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.2.0.tgz#477b3bf2f9b8fd5ca9e429747e37f724ee7af240" + integrity sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g== dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -679,157 +658,207 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + change-emitter@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.2.tgz#6b88ca4d5d864e516f913421b11899a860aee8db" + version "0.1.6" + resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" + integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU= -cheerio@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" - dependencies: - css-select "~1.0.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "~3.8.1" - lodash "^3.2.0" +character-entities-legacy@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" + integrity sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA== -cheerio@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" +character-entities@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" + integrity sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ== + +character-reference-invalid@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" + integrity sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ== + +cheerio@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4= dependencies: css-select "~1.2.0" dom-serializer "~0.1.0" entities "~1.1.1" - htmlparser2 "~3.8.1" - lodash "^4.1.0" - optionalDependencies: - jsdom "^7.0.2" - -chrono-node@^1.2.3: - version "1.3.5" - resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-1.3.5.tgz#a2495298a32da82bcc01ad9be7d77efa5e244122" - dependencies: - moment "^2.10.3" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== -cipher-base@^1.0.0, cipher-base@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" + safe-buffer "^5.0.1" circular-json@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" - -classnames@^2.1.2, classnames@^2.1.3, classnames@^2.2.3, classnames@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" - -clean-css@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-1.1.7.tgz#601ef9cf7642b982cb33efc9488a6444c986686e" - dependencies: - commander "2.0.x" - -cli-color@^0.3.2: version "0.3.3" - resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-0.3.3.tgz#12d5bdd158ff8a0b0db401198913c03df069f6f5" - dependencies: - d "~0.1.1" - es5-ext "~0.10.6" - memoizee "~0.3.8" - timers-ext "0.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== + +classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== cli-cursor@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= dependencies: restore-cursor "^1.0.1" cli-width@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" +clipboard@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" + integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ== dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" -clone@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" - -clone@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.0.tgz#9c715bfbd39aa197c8ee0f8e65c3912ba34f8cd6" +clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -co@~3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" - dependencies: - delayed-stream "~1.0.0" +collapse-white-space@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" + integrity sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw== -combined-stream@~0.0.4: - version "0.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-0.0.7.tgz#0137e657baa5a7541c57ac37ac5fc07d73b4dc1f" +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: - delayed-stream "0.0.5" + color-name "1.1.3" -commander@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.0.0.tgz#d1b86f901f8b64bd941bdeadaf924530393be928" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +combined-stream2@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/combined-stream2/-/combined-stream2-1.1.2.tgz#f6e14b7a015666f8c7b0a1fac506240164ac3570" + integrity sha1-9uFLegFWZvjHsKH6xQYkAWSsNXA= + dependencies: + bluebird "^2.8.1" + debug "^2.1.1" + stream-length "^1.0.1" -commander@2.9.0, commander@^2.5.0, commander@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== dependencies: - graceful-readlink ">= 1.0.0" + delayed-stream "~1.0.0" -commoner@~0.10.3: - version "0.10.8" - resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" +comma-separated-tokens@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz#b13793131d9ea2d2431cf5b507ddec258f0ce0db" + integrity sha512-Cg90/fcK93n0ecgYTAz1jaA3zvnQ0ExlmKY1rdbyHqAx6BHxwoJc+J7HDu0iuQ7ixEs1qaa+WyQ6oeuBpYP1iA== dependencies: - commander "^2.5.0" - detective "^4.3.1" - glob "^5.0.15" - graceful-fs "^4.1.2" - iconv-lite "^0.4.5" - mkdirp "^0.5.0" - private "^0.1.6" - q "^1.1.2" - recast "^0.11.17" + trim "0.0.1" + +commander@^2.15.1, commander@^2.19.0, commander@^2.9.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +commander@~2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== component-emitter@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -component-type@~1.2.1: +component-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" + integrity sha1-ikeQFwAjjk/DIml3EjAibyS0Fak= + +compressible@~2.0.14: + version "2.0.15" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.15.tgz#857a9ab0a7e5a07d8d837ed43fe2defff64fe212" + integrity sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw== + dependencies: + mime-db ">= 1.36.0 < 2" + +compression@^1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" + integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.4.6, concat-stream@^1.4.7: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: + buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" @@ -837,32 +866,39 @@ concat-stream@^1.4.6, concat-stream@^1.4.7: console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= dependencies: date-now "^0.1.4" +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" - -content-type@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= -convert-source-map@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== cookie-parser@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5" + integrity sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU= dependencies: cookie "0.3.1" cookie-signature "1.0.6" @@ -870,80 +906,112 @@ cookie-parser@^1.4.3: cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.3.1, cookie@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= -cookiejar@^2.0.6: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.0.tgz#86549689539b6d0e269b6637a304be508194d898" +cookiejar@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= -core-js@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" +core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3: + version "2.6.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d" + integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A== -core-util-is@~1.0.0: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= create-ecdh@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== dependencies: bn.js "^4.1.0" elliptic "^6.0.0" -create-hash@^1.1.0, create-hash@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== dependencies: cipher-base "^1.0.1" inherits "^2.0.1" - ripemd160 "^1.0.0" - sha.js "^2.3.6" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== dependencies: + cipher-base "^1.0.3" create-hash "^1.1.0" inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" -create-react-class@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" +create-react-class@^15.5.1, create-react-class@^15.5.2: + version "15.6.3" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" + integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== dependencies: fbjs "^0.8.9" loose-envify "^1.3.1" object-assign "^4.1.1" -cross-spawn-async@^2.1.8: - version "2.2.5" - resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" +create-react-context@<=0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca" + integrity sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A== dependencies: - lru-cache "^4.0.0" - which "^1.2.8" + fbjs "^0.8.0" + gud "^1.0.0" -cryptiles@0.2.x: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-0.2.2.tgz#ed91ff1f17ad13d3748288594f8a48a0d26f325c" +create-react-context@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3" + integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag== dependencies: - boom "0.4.x" + fbjs "^0.8.0" + gud "^1.0.0" -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" +cross-fetch@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-0.0.8.tgz#01ed94dc407df2c00f1807fde700a7cfa48a205c" + integrity sha1-Ae2U3EB98sAPGAf95wCnz6SKIFw= + dependencies: + node-fetch "1.7.3" + whatwg-fetch "2.0.3" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: - boom "2.x.x" + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" crypto-browserify@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" @@ -955,31 +1023,27 @@ crypto-browserify@^3.11.0: pbkdf2 "^3.0.3" public-encrypt "^4.0.0" randombytes "^2.0.0" + randomfill "^1.0.3" crypto-js@^3.1.9-1: version "3.1.9-1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" + integrity sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg= crypto-token@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/crypto-token/-/crypto-token-1.0.1.tgz#27c6482faf3b63c2f5da11577f8304346fe797a5" + integrity sha1-J8ZIL687Y8L12hFXf4MENG/nl6U= css-color-keywords@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" - -css-select@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" - dependencies: - boolbase "~1.0.0" - css-what "1.0" - domutils "1.4" - nth-check "~1.0.0" + integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= dependencies: boolbase "~1.0.0" css-what "2.1" @@ -987,165 +1051,127 @@ css-select@~1.2.0: nth-check "~1.0.1" css-to-react-native@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.0.4.tgz#cf4cc407558b3474d4ba8be1a2cd3b6ce713101b" + version "2.3.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.3.0.tgz#bf80d24ec4a08e430306ef429c0586e6ed5485f7" + integrity sha512-IhR7bNIrCFwbJbKZOAjNDZdwpsbjTN6f1agXeELHDqg1wHPA8c2QLruttKOW7hgMGetkfraRJCIEMrptifBfVw== dependencies: + camelize "^1.0.0" css-color-keywords "^1.0.0" - fbjs "^0.8.5" postcss-value-parser "^3.3.0" -css-what@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" - css-what@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - -cssom@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.0.tgz#386d5135528fe65c1ee1bc7c4e55a38854dbcf7a" - -cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" - -"cssstyle@>= 0.2.29 < 0.3.0": - version "0.2.37" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" - dependencies: - cssom "0.3.x" - -ctype@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f" + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== -d@^0.1.1, d@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8= dependencies: - es5-ext "~0.10.2" + es5-ext "^0.10.9" damerau-levenshtein@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.3.tgz#ae4f4ce0b62acae10ff63a01bb08f652f5213af2" + version "1.0.4" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" + integrity sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ= dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@0: - version "0.0.4" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" - -dataloader@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.3.0.tgz#6fec5be4b30a712e4afd30b86b4334566b97673b" +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== -datauri@~0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/datauri/-/datauri-0.2.1.tgz#f4e8addbb3e54e3dc12d1c88543b8b0b1bf692fa" +datauri@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/datauri/-/datauri-1.1.0.tgz#c6184ff6b928ede4e41ccc23ab954c7839c4fb39" + integrity sha512-0q+cTTKx7q8eDteZRIQLTFJuiIsVing17UbWTPssY4JLSMaYsk/VKpNulBDo9NSgQWcvlPrkEHW8kUO67T/7mQ== dependencies: - mimer "*" - templayed "*" + image-size "^0.6.2" + mimer "^0.3.2" + semver "^5.5.0" date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= -debug@2, debug@2.2.0, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" +debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.6.2, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "0.7.1" + ms "2.0.0" -decamelize@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= -deep-extend@0.4.1, deep-extend@^0.4.0, deep-extend@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= deepmerge@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.3.1.tgz#682ba92402574115b865edce525665814296a39b" + version "1.5.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" + integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== -define-properties@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== dependencies: - foreach "^2.0.5" - object-keys "^1.0.8" + object-keys "^1.0.12" -defined@^1.0.0: +delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -defs@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" - dependencies: - alter "~0.2.0" - ast-traverse "~0.1.1" - breakable "~1.0.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - simple-fmt "~0.1.0" - simple-is "~0.2.0" - stringmap "~0.2.2" - stringset "~0.2.1" - tryor "~0.1.2" - yargs "~3.27.0" - -degenerator@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" - dependencies: - ast-types "0.x.x" - escodegen "1.x.x" - esprima "3.x.x" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delayed-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f" +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== -delayed-stream@~1.0.0: +delegates@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= deprecated-decorator@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" + integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -1153,23 +1179,17 @@ des.js@^1.0.0: destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - dependencies: - repeating "^2.0.0" - -detective@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" - dependencies: - acorn "^3.1.0" - defined "^1.0.0" +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= diffie-hellman@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" @@ -1178,216 +1198,252 @@ diffie-hellman@^5.0.0: doctrine@1.5.0, doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= dependencies: esutils "^2.0.2" isarray "^1.0.0" -dom-helpers@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-2.4.0.tgz#9bb4b245f637367b1fa670274272aa28fe06c367" +doctrine@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" -dom-helpers@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.0.tgz#70af056e6b1507a71084abc1aac0f93a23f7ea1c" +dom-helpers@^3.2.1, dom-helpers@^3.3.1, dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= dependencies: domelementtype "~1.1.1" entities "~1.1.1" domain-browser@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= -domhandler@2.3, domhandler@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== dependencies: domelementtype "1" -domutils@1.4: - version "1.4.3" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= dependencies: + dom-serializer "0" domelementtype "1" -domutils@1.5, domutils@1.5.1, domutils@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== dependencies: dom-serializer "0" domelementtype "1" +dot-object@^1.7.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/dot-object/-/dot-object-1.7.1.tgz#ec2bc9aeeb86bdeecf3d78235c1ee09bb93992e6" + integrity sha512-XJWGenQT/O9TOOip9JzY6Gtk73Cp8qnGnObeSvwdHA9h3YHXgma6qobPRHc8fxHugRpXiHWJ9XiWasXn+Dk2Kg== + dependencies: + commander "^2.19.0" + glob "^7.1.3" + ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" + safer-buffer "^2.1.0" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.2.7: + version "1.3.113" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9" + integrity sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g== elliptic@^6.0.0: - version "6.3.2" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.2.tgz#e4c81e0829cf0a65ab70e998b8232723b5c1bc48" + version "6.4.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" + integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== dependencies: bn.js "^4.4.0" brorand "^1.0.1" hash.js "^1.0.0" + hmac-drbg "^1.0.0" inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" -encodeurl@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= dependencies: iconv-lite "~0.4.13" -entities@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" - entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== -es-abstract@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.6.1.tgz#bb8a2064120abcf928a086ea3d9043114285ec99" +enzyme-adapter-react-16@^1.0.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz#6d49a3a31c3a0fccf527610f31b837e0f307128a" + integrity sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg== + dependencies: + enzyme-adapter-utils "^1.10.0" + function.prototype.name "^1.1.0" + object.assign "^4.1.0" + object.values "^1.1.0" + prop-types "^15.6.2" + react-is "^16.7.0" + react-test-renderer "^16.0.0-0" + +enzyme-adapter-utils@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz#5836169f68b9e8733cb5b69cad5da2a49e34f550" + integrity sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA== + dependencies: + function.prototype.name "^1.1.0" + object.assign "^4.1.0" + object.fromentries "^2.0.0" + prop-types "^15.6.2" + semver "^5.6.0" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.0" - is-callable "^1.1.3" - is-regex "^1.0.3" + is-arrayish "^0.2.1" -es-to-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" +es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== dependencies: - is-callable "^1.1.1" - is-date-object "^1.0.1" - is-symbol "^1.0.1" + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" -es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.5, es5-ext@~0.10.6, es5-ext@~0.10.7: - version "0.10.12" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== dependencies: - es6-iterator "2" - es6-symbol "~3.1" - -es6-error@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.0.2.tgz#eec5c726eacef51b7f6b73c20db6e1b13b069c98" + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" -es6-iterator@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" +es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.47" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.47.tgz#d24232e1380daad5449a817be19bde9729024a11" + integrity sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw== dependencies: - d "^0.1.1" - es5-ext "^0.10.7" - es6-symbol "3" + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + next-tick "1" -es6-iterator@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-0.1.3.tgz#d6f58b8c4fc413c249b4baa19768f8e4d7c8944e" +es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= dependencies: - d "~0.1.1" - es5-ext "~0.10.5" - es6-symbol "~2.0.1" + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" es6-map@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.4.tgz#a34b147be224773a4d7da8072794cefa3632b897" - dependencies: - d "~0.1.1" - es5-ext "~0.10.11" - es6-iterator "2" - es6-set "~0.1.3" - es6-symbol "~3.1.0" - event-emitter "~0.3.4" - -es6-set@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8" + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= dependencies: - d "~0.1.1" - es5-ext "~0.10.11" - es6-iterator "2" - es6-symbol "3" - event-emitter "~0.3.4" + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" -es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= dependencies: - d "~0.1.1" - es5-ext "~0.10.11" + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" -es6-symbol@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-2.0.1.tgz#761b5c67cfd4f1d18afb234f691d678682cb3bf3" +es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= dependencies: - d "~0.1.1" - es5-ext "~0.10.5" + d "1" + es5-ext "~0.10.14" es6-weak-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" - dependencies: - d "^0.1.1" - es5-ext "^0.10.8" - es6-iterator "2" - es6-symbol "3" - -es6-weak-map@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-0.1.4.tgz#706cef9e99aa236ba7766c239c8b9e286ea7d228" + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + integrity sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8= dependencies: - d "~0.1.1" - es5-ext "~0.10.6" - es6-iterator "~0.1.3" - es6-symbol "~2.0.1" + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -escape-regexp-component@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz#9c63b6d0b25ff2a88c3adbd18c5b61acc3b9faa2" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@1.x.x, escodegen@^1.6.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.2.0" - -escope@3.6.0, escope@^3.6.0: +escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= dependencies: es6-map "^0.1.3" es6-weak-map "^2.0.1" @@ -1397,97 +1453,105 @@ escope@3.6.0, escope@^3.6.0: eslint-config-airbnb-base@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-10.0.1.tgz#f17d4e52992c1d45d1b7713efbcd5ecd0e7e0506" + integrity sha1-8X1OUpksHUXRt3E++81ezQ5+BQY= eslint-config-airbnb@^13.0.0: version "13.0.0" resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-13.0.0.tgz#688d15d3c276c0c753ae538c92a44397d76ae46e" + integrity sha1-aI0V08J2wMdTrlOMkqRDl9dq5G4= dependencies: eslint-config-airbnb-base "^10.0.0" eslint-config-meteor@0.0.9: version "0.0.9" - resolved "http://registry.npmjs.org/eslint-config-meteor/-/eslint-config-meteor-0.0.9.tgz#6be219420ba4a3ea023db30a866e60ef487652fa" + resolved "https://registry.yarnpkg.com/eslint-config-meteor/-/eslint-config-meteor-0.0.9.tgz#6be219420ba4a3ea023db30a866e60ef487652fa" + integrity sha1-a+IZQguko+oCPbMKhm5g70h2Uvo= eslint-import-resolver-meteor@^0.3.3: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-meteor/-/eslint-import-resolver-meteor-0.3.4.tgz#e34ba534bf995420eb4cd223302e7a5967eee539" + integrity sha1-40ulNL+ZVCDrTNIjMC56WWfu5Tk= dependencies: object-assign "^4.0.1" resolve "^1.1.6" -eslint-import-resolver-node@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== dependencies: - debug "^2.2.0" - object-assign "^4.0.1" - resolve "^1.1.6" + debug "^2.6.9" + resolve "^1.5.0" -eslint-module-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" +eslint-module-utils@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz#546178dab5e046c8b562bbb50705e2456d7bda49" + integrity sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w== dependencies: - debug "2.2.0" - pkg-dir "^1.0.0" + debug "^2.6.8" + pkg-dir "^2.0.0" eslint-plugin-babel@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-3.3.0.tgz#2f494aedcf6f4aa4e75b9155980837bc1fbde193" + integrity sha1-L0lK7c9vSqTnW5FVmAg3vB+94ZM= -eslint-plugin-import@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz#72ba306fad305d67c4816348a4699a4229ac8b4e" +eslint-plugin-import@^2.11.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz#97ac3e75d0791c4fac0e15ef388510217be7f66f" + integrity sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A== dependencies: - builtin-modules "^1.1.1" contains-path "^0.1.0" - debug "^2.2.0" + debug "^2.6.9" doctrine "1.5.0" - eslint-import-resolver-node "^0.2.0" - eslint-module-utils "^2.0.0" - has "^1.0.1" - lodash.cond "^4.3.0" - minimatch "^3.0.3" - pkg-up "^1.0.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.3.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.9.0" eslint-plugin-jsx-a11y@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-2.2.3.tgz#4e35cb71b8a7db702ac415c806eb8e8d9ea6c65d" + integrity sha1-TjXLcbin23AqxBXIBuuOjZ6mxl0= dependencies: damerau-levenshtein "^1.0.0" jsx-ast-utils "^1.0.0" object-assign "^4.0.1" -eslint-plugin-meteor@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-meteor/-/eslint-plugin-meteor-4.0.1.tgz#442ef4fe66f9b21ae40a89826784121150cb17ff" +eslint-plugin-meteor@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-meteor/-/eslint-plugin-meteor-4.2.2.tgz#8620b0c3764dda33accb8a32ae19a096134b4d81" + integrity sha512-VLvsBdnwTlFjLmpKUCdoy4Sm1mlzeh8FcIXkyxJw2MaZMFHprIjbolZD0JguhP8yK3UwZelZaCIyvk2tidwIbA== dependencies: - babel-register "6.16.3" - babel-runtime "6.11.6" - escope "3.6.0" - invariant "2.2.1" - lodash.find "4.6.0" - lodash.memoize "4.1.2" - path-exists "3.0.0" + invariant "2.2.4" eslint-plugin-react@^6.7.1: - version "6.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.9.0.tgz#54c2e9906b76f9d10142030bdc34e9d6840a0bb2" + version "6.10.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz#c5435beb06774e12c7db2f6abaddcbf900cd3f78" + integrity sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g= dependencies: array.prototype.find "^2.0.1" doctrine "^1.2.2" + has "^1.0.1" jsx-ast-utils "^1.3.4" + object.assign "^4.0.4" eslint@^3.10.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.13.1.tgz#564d2646b5efded85df96985332edd91a23bff25" + version "3.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" + integrity sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw= dependencies: babel-code-frame "^6.16.0" chalk "^1.1.3" - concat-stream "^1.4.6" + concat-stream "^1.5.2" debug "^2.1.1" - doctrine "^1.2.2" + doctrine "^2.0.0" escope "^3.6.0" - espree "^3.3.1" + espree "^3.4.0" + esquery "^1.0.0" estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" @@ -1516,135 +1580,161 @@ eslint@^3.10.1: text-table "~0.2.0" user-home "^2.0.0" -espree@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.3.2.tgz#dbf3fadeb4ecb4d4778303e50103b3d36c88b89c" +espree@^3.4.0: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== dependencies: - acorn "^4.0.1" + acorn "^5.5.0" acorn-jsx "^3.0.0" -esprima-fb@~15001.1001.0-dev-harmony-fb: - version "15001.1001.0-dev-harmony-fb" - resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" - -esprima@3.x.x, esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esprima@^2.6.0, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esquery@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" esrecurse@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== dependencies: - estraverse "~4.1.0" - object-assign "^4.0.1" - -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + estraverse "^4.1.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -estraverse@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= -etag@~1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-emitter@~0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.4.tgz#8d63ddfb4cfe1fae3b32ca265c4c720222080bb5" +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= dependencies: - d "~0.1.1" - es5-ext "~0.10.7" + d "1" + es5-ext "~0.10.14" events@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= -evp_bytestokey@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: - create-hash "^1.1.1" + md5.js "^1.3.4" + safe-buffer "^5.1.1" exenv@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= -express@^4.14.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" +express@^4.16.3: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== dependencies: - accepts "~1.3.3" + accepts "~1.3.5" array-flatten "1.1.1" - content-disposition "0.5.1" - content-type "~1.0.2" + body-parser "1.18.3" + content-disposition "0.5.2" + content-type "~1.0.4" cookie "0.3.1" cookie-signature "1.0.6" - debug "~2.2.0" - depd "~1.1.0" - encodeurl "~1.0.1" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" escape-html "~1.0.3" - etag "~1.7.0" - finalhandler "0.5.0" - fresh "0.3.0" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.2" path-to-regexp "0.1.7" - proxy-addr "~1.1.2" - qs "6.2.0" + proxy-addr "~2.0.4" + qs "6.5.2" range-parser "~1.2.0" - send "0.14.1" - serve-static "~1.11.1" - type-is "~1.6.13" - utils-merge "1.0.0" - vary "~1.1.0" + safe-buffer "5.1.2" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0, extend@^3.0.1, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extend@3, extend@^3.0.0, extend@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" +extendable-error@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/extendable-error/-/extendable-error-0.1.5.tgz#122308a7097bc89a263b2c4fbf089c78140e3b6d" + integrity sha1-EiMIpwl7yJomOyxPvwiceBQOO20= -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fbjs@^0.8.1, fbjs@^0.8.4: - version "0.8.8" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.8.tgz#02f1b6e0ea0d46c24e0b51a2d24df069563a5ad6" +fault@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa" + integrity sha512-o2eo/X2syzzERAtN5LcGbiVQ0WwZSlN3qLtadwAz3X8Bu+XWD16dja/KMsjZLiQr+BLGPDnHGkc4yUJf1Xpkpw== dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" + format "^0.2.2" -fbjs@^0.8.5, fbjs@^0.8.9: - version "0.8.14" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" +fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= dependencies: core-js "^1.0.0" isomorphic-fetch "^2.1.1" @@ -1652,11 +1742,12 @@ fbjs@^0.8.5, fbjs@^0.8.9: object-assign "^4.1.0" promise "^7.1.1" setimmediate "^1.0.5" - ua-parser-js "^0.7.9" + ua-parser-js "^0.7.18" figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" @@ -1664,384 +1755,396 @@ figures@^1.3.5: file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= dependencies: flat-cache "^1.2.1" object-assign "^4.0.1" -file-uri-to-path@0: - version "0.0.2" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-0.0.2.tgz#37cdd1b5b905404b3f05e1b23645be694ff70f82" - -finalhandler@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== dependencies: - debug "~2.2.0" + debug "2.6.9" + encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" - statuses "~1.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" unpipe "~1.0.0" -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" + locate-path "^2.0.0" flat-cache@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + version "1.3.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" + integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== dependencies: circular-json "^0.3.1" - del "^2.0.2" graceful-fs "^4.1.2" + rimraf "~2.6.2" write "^0.2.1" -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - -forever-agent@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.5.2.tgz#6d0e09c4921f94a27f63d3b49c5feff1ea4c5130" +flat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data-to-object@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/form-data-to-object/-/form-data-to-object-0.2.0.tgz#f7a8e68ddd910a1100a65e25ac6a484143ff8168" + integrity sha1-96jmjd2RChEApl4lrGpIQUP/gWg= -form-data@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.2.0.tgz#26f8bc26da6440e299cbdcfb69035c4f77a6e466" - dependencies: - async "~0.9.0" - combined-stream "~0.0.4" - mime-types "~2.0.3" - -form-data@^2.1.1, form-data@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" +form-data@^2.3.1, form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" - combined-stream "^1.0.5" + combined-stream "^1.0.6" mime-types "^2.1.12" -form-data@~0.1.0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.1.4.tgz#91abd788aba9702b1aabfa8bc01031a2ac9e3b12" - dependencies: - async "~0.9.0" - combined-stream "~0.0.4" - mime "~1.2.11" +format@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= -formidable@^1.0.17: - version "1.1.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" +formidable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" + integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== -formsy-react-components@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/formsy-react-components/-/formsy-react-components-0.8.1.tgz#2ce4dc17edb9c73e389c1e720532325e08f9f128" +formsy-react-components@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/formsy-react-components/-/formsy-react-components-1.0.0.tgz#1ccbeea658957ee75df96a65795e8e4245eb1834" + integrity sha1-HMvupliVfudd+WpleV6OQkXrGDQ= dependencies: - classnames "^2.1.3" + classnames "^2.2.5" + lodash.debounce "^4.0.6" + prop-types "^15.5.10" -formsy-react@^0.18.1: - version "0.18.1" - resolved "https://registry.yarnpkg.com/formsy-react/-/formsy-react-0.18.1.tgz#c52ee95baef2896f1547ce6df8dae5fcdbd9ab40" +formsy-react@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/formsy-react/-/formsy-react-1.1.5.tgz#ee0911bb70712eb6fb9924d56fdb974a19006955" + integrity sha512-nNWe4Vbp6aDQ/zSxJ7gVQgD5+avFRVbydcjA2Om42flxpQFrKE54AAbuyEj3Jvv+2b9LVl+WLMAPalyvLjwNcQ== dependencies: form-data-to-object "^0.2.0" + prop-types "^15.5.10" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -fresh@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + dependencies: + minipass "^2.2.1" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -ftp@~0.3.5: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.0.2, function-bind@^1.1.0: +function.prototype.name@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" + integrity sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + is-callable "^1.1.3" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" generate-object-property@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= dependencies: is-property "^1.0.0" -get-uri@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-1.1.0.tgz#7375d04daf7fcb584b3632679cbdf339b51bb149" - dependencies: - data-uri-to-buffer "0" - debug "2" - extend "3" - file-uri-to-path "0" - ftp "~0.3.5" - readable-stream "2" - getpass@^0.1.1: - version "0.1.6" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob@^7.0.0, glob@^7.0.3, glob@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.2" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" -globals@^9.0.0, globals@^9.14.0: - version "9.14.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" +globals@^9.14.0, globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" + delegate "^3.1.2" graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== graphql-anywhere@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-3.0.1.tgz#73531db861174c8f212eafb9f8e84944b38b4e5a" + version "3.1.0" + resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-3.1.0.tgz#3ea0d8e8646b5cee68035016a9a7557c15c21e96" + integrity sha1-PqDY6GRrXO5oA1AWqadVfBXCHpY= graphql-date@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/graphql-date/-/graphql-date-1.0.2.tgz#b196571a6c9a54dc1b650e3e81b6b105865af49b" + version "1.0.3" + resolved "https://registry.yarnpkg.com/graphql-date/-/graphql-date-1.0.3.tgz#31ce05ae40ed8c8ceb040364060109771e712e91" + integrity sha1-Mc4FrkDtjIzrBANkBgEJdx5xLpE= dependencies: assert-err "^1.0.0" -graphql-server-core@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.6.0.tgz#468625cba4a00f80275c065432faa481985f5a65" - optionalDependencies: - "@types/graphql" "^0.8.5" - -graphql-server-express@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/graphql-server-express/-/graphql-server-express-0.6.0.tgz#516090b4add82e6ed2c4a70dd2d925031fdcc289" +graphql-extensions@^0.0.x, graphql-extensions@~0.0.9: + version "0.0.10" + resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.0.10.tgz#34bdb2546d43f6a5bc89ab23c295ec0466c6843d" + integrity sha512-TnQueqUDCYzOSrpQb3q1ngDSP2otJSF+9yNLrQGPzkMsvnQ+v6e2d5tl+B35D4y+XpmvVnAn4T3ZK28mkILveA== dependencies: - graphql-server-core "^0.6.0" - graphql-server-module-graphiql "^0.6.0" - optionalDependencies: - "@types/express" "^4.0.35" - "@types/graphql" "^0.8.6" - -graphql-server-module-graphiql@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/graphql-server-module-graphiql/-/graphql-server-module-graphiql-0.6.0.tgz#e37634b05f000731981e8ed13103f9a5861e5da0" + core-js "^2.5.3" + source-map-support "^0.5.1" -graphql-tag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.0.0.tgz#f3efe3b4d64f33bfe8479ae06a461c9d72f2a6fe" - -graphql-tag@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.4.2.tgz#6a63297d8522d03a2b72d26f1b239aab343840cd" +graphql-tag@^2.0.0, graphql-tag@^2.4.2, graphql-tag@^2.9.2: + version "2.10.1" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02" + integrity sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg== -graphql-tools@^0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-0.10.1.tgz#274aa338d50b1c0b3ed6936eafd8ed3a19ed1828" +graphql-tools@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-3.1.1.tgz#d593358f01e7c8b1671a17b70ddb034dea9dbc50" + integrity sha512-yHvPkweUB0+Q/GWH5wIG60bpt8CTwBklCSzQdEHmRUgAdEQKxw+9B7zB3dG7wB3Ym7M7lfrS4Ej+jtDZfA2UXg== dependencies: + apollo-link "^1.2.2" + apollo-utilities "^1.0.1" deprecated-decorator "^0.1.6" - lodash "^4.3.0" - uuid "^3.0.1" - optionalDependencies: - "@types/graphql" "^0.8.5" - -graphql-tools@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-0.8.4.tgz#3cf4b2a650e0322b655ca274ec24914b608fb562" - dependencies: - deprecated-decorator "^0.1.6" - lodash "^4.3.0" - uuid "^3.0.1" - optionalDependencies: - typed-graphql "^1.0.2" + iterall "^1.1.3" + uuid "^3.1.0" graphql-type-json@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/graphql-type-json/-/graphql-type-json-0.1.4.tgz#89f13f5d32ce08c9a76c79fdf9c1968384d81a4e" + integrity sha1-ifE/XTLOCMmnbHn9+cGWg4TYGk4= -graphql@^0.10.0, graphql@^0.10.3: +graphql@^0.10.0, graphql@^0.10.3, graphql@^0.10.5: version "0.10.5" resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.10.5.tgz#c9be17ca2bdfdbd134077ffd9bbaa48b8becd298" + integrity sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ== dependencies: iterall "^1.1.0" -graphql@^0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.9.6.tgz#514421e9d225c29dfc8fd305459abae58815ef2c" - dependencies: - iterall "^1.0.0" +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== handlebars@^4.0.5: - version "4.0.6" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" + version "4.1.0" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" + integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== dependencies: - async "^1.4.0" + async "^2.5.0" optimist "^0.6.1" - source-map "^0.4.4" + source-map "^0.6.1" optionalDependencies: - uglify-js "^2.6" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + uglify-js "^3.1.4" -har-validator@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" - dependencies: - chalk "^1.1.1" - commander "^2.9.0" - is-my-json-valid "^2.12.4" - pinkie-promise "^2.0.0" +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" + ajv "^6.5.5" + har-schema "^2.0.0" has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= -hash.js@^1.0.0: +has@^1.0.1, has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= dependencies: inherits "^2.0.1" + safe-buffer "^5.0.1" -hawk@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-1.0.0.tgz#b90bb169807285411da7ffcb8dd2598502d3b52d" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: - boom "0.4.x" - cryptiles "0.2.x" - hoek "0.9.x" - sntp "0.2.x" + inherits "^2.0.3" + minimalistic-assert "^1.0.1" -hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" +hast-util-parse-selector@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.1.tgz#4ddbae1ae12c124e3eb91b581d2556441766f0ab" + integrity sha512-Xyh0v+nHmQvrOqop2Jqd8gOdyQtE8sIP9IQf7mlVDqp924W4w/8Liuguk2L2qei9hARnQSG2m+wAOCxM7npJVw== + +hastscript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.0.0.tgz#fee10382c1bc4ba3f1be311521d368c047d2c43a" + integrity sha512-xJtuJ8D42Xtq5yJrnDg/KAIxl2cXBXKoiIJwmWX9XMf8113qHTGl/Bf7jEsxmENJ4w6q4Tfl8s/Y6mEZo8x8qw== dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.2.0" + property-information "^5.0.1" + space-separated-tokens "^1.0.0" -he@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.0.tgz#29319d49beec13a9b1f3c4f9b2a6dde4859bb2a7" +he@^1.0.0, he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -he@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +highlight.js@~9.12.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= history@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/history/-/history-3.2.1.tgz#71c7497f4e6090363d19a6713bb52a1bfcdd99aa" + version "3.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-3.3.0.tgz#fcedcce8f12975371545d735461033579a6dae9c" + integrity sha1-/O3M6PEpdTcVRdc1RhAzV5ptrpw= dependencies: invariant "^2.2.1" loose-envify "^1.2.0" query-string "^4.2.2" warning "^3.0.0" -hoek@0.9.x: - version "0.9.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-0.9.1.tgz#3d322462badf07716ea7eb85baf88079cddce505" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^1.0.0, hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: +hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" + integrity sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs= -hoist-non-react-statics@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.0.tgz#ede16318c2ff1f9fe3a025396ba06fd4c44608bb" +hoist-non-react-statics@^2.2.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.5: + version "2.5.5" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" + integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -home-or-tmp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" +hoist-non-react-statics@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== dependencies: - os-tmpdir "^1.0.1" - user-home "^1.1.1" + react-is "^16.7.0" -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== html-to-text@^2.1.0: version "2.1.3" resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-2.1.3.tgz#e50dbe4e4079912dbedcde05d278388edb8eea91" + integrity sha1-5Q2+TkB5kS2+3N4F0ng4jtuO6pE= dependencies: he "^1.0.0" htmlparser "^1.7.7" @@ -2049,136 +2152,139 @@ html-to-text@^2.1.0: underscore "^1.8.3" underscore.string "^3.2.3" -htmlparser2@^3.9.0: - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" +htmlparser2@^3.10.0, htmlparser2@^3.9.1, htmlparser2@^3.9.2: + version "3.10.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" + integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== dependencies: domelementtype "^1.3.0" domhandler "^2.3.0" domutils "^1.5.1" entities "^1.1.1" inherits "^2.0.1" - readable-stream "^2.0.2" - -htmlparser2@~3.8.1: - version "3.8.3" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" - dependencies: - domelementtype "1" - domhandler "2.3" - domutils "1.5" - entities "1.0" - readable-stream "1.1" + readable-stream "^3.0.6" htmlparser@^1.7.7: version "1.7.7" resolved "https://registry.yarnpkg.com/htmlparser/-/htmlparser-1.7.7.tgz#19e7b3997ff6fbac99ae5a7d2766489efe7e2d0e" + integrity sha1-GeezmX/2+6yZrlp9J2ZInv5+LQ4= -http-browserify@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/http-browserify/-/http-browserify-1.7.0.tgz#33795ade72df88acfbfd36773cefeda764735b20" - dependencies: - Base64 "~0.2.0" - inherits "~2.0.1" - -http-errors@~1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= dependencies: + depd "~1.1.2" inherits "2.0.3" - setprototypeof "1.0.2" - statuses ">= 1.3.1 < 2" - -http-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" - dependencies: - agent-base "2" - debug "2" - extend "3" - -http-signature@~0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-0.10.1.tgz#4fbdac132559aa8323121e540779c0a012b27e66" - dependencies: - asn1 "0.1.11" - assert-plus "^0.1.5" - ctype "0.5.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: - assert-plus "^0.2.0" + assert-plus "^1.0.0" jsprim "^1.2.2" sshpk "^1.7.0" https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + integrity sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI= -https-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== dependencies: - agent-base "2" - debug "2" - extend "3" + safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13: - version "0.4.13" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" +iconv-lite@^0.4.4, iconv-lite@~0.4.13: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.4: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" ignore@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435" + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== -immutability-helper@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.1.1.tgz#d80e7577fae14cddde99d8946666d79973e7ba13" +image-size@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" + integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA== + +immutability-helper@^2.7.0: + version "2.9.1" + resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.9.1.tgz#71c423ba387e67b6c6ceba0650572f2a2a6727df" + integrity sha512-r/RmRG8xO06s/k+PIaif2r5rGc3j4Yhc01jSBfwPCXDLYZwp/yxralI37Df1mwmuzcCsen/E/ITKcTEvc1PQmQ== dependencies: invariant "^2.2.0" import-inspector@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-inspector/-/import-inspector-2.0.0.tgz#ce75fdb6a277d2800effe097e2295bc8690b2923" + integrity sha1-znX9tqJ30oAO/+CX4ilbyGkLKSM= import@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/import/-/import-0.0.6.tgz#d0eb79df86aa2677c6db61578a5212b3031e6042" + integrity sha1-0Ot534aqJnfG22FXilISswMeYEI= dependencies: optimist "0.3.x" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= dependencies: ansi-escapes "^1.1.0" ansi-regex "^2.0.0" @@ -2195,181 +2301,212 @@ inquirer@^0.12.0: through "^2.3.6" interpret@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + version "1.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" + integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== intl-format-cache@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.0.5.tgz#b484cefcb9353f374f25de389a3ceea1af18d7c9" + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316" + integrity sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY= intl-locales-supported@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/intl-locales-supported/-/intl-locales-supported-1.0.0.tgz#9a9d94dbf104a87818881952dcb782053f0aeefa" + integrity sha1-mp2U2/EEqHgYiBlS3LeCBT8K7vo= -intl-messageformat-parser@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.2.0.tgz#5906b7f953ab7470e0dc8549097b648b991892ff" +intl-messageformat-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075" + integrity sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU= -intl-messageformat@1.3.0, intl-messageformat@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-1.3.0.tgz#f7d926aded7a3ab19b2dc601efd54e99a4bd4eae" +intl-messageformat@^2.0.0, intl-messageformat@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc" + integrity sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw= dependencies: - intl-messageformat-parser "1.2.0" + intl-messageformat-parser "1.4.0" -intl-relativeformat@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-1.3.0.tgz#893dc7076fccd380cf091a2300c380fa57ace45b" +intl-relativeformat@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df" + integrity sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8= dependencies: - intl-messageformat "1.3.0" + intl-messageformat "^2.0.0" intl@^1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/intl/-/intl-1.2.5.tgz#82244a2190c4e419f8371f5aa34daa3420e2abde" + integrity sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94= -invariant@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" +invariant@2.2.4, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" -invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" +ipaddr.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= + +is-alphabetical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" + integrity sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg== + +is-alphanumerical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" + integrity sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg== dependencies: - loose-envify "^1.0.0" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -ip@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.4.tgz#de8247ffef940451832550fba284945e6e039bfb" +is-buffer@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -ipaddr.js@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== -is-buffer@^1.0.2: +is-callable@^1.1.3, is-callable@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" - -is-callable@^1.1.1, is-callable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= -is-finite@^1.0.0: +is-decimal@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" + integrity sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg== is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" +is-hexadecimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" + integrity sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A== -is-isodate@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/is-isodate/-/is-isodate-0.0.1.tgz#4fe2e937d50f3ba68c7b69b0218008b624aa5036" +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== -is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: - version "2.15.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" +is-my-json-valid@^2.10.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175" + integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q== dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" jsonpointer "^4.0.0" xtend "^4.0.0" -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" - dependencies: - path-is-inside "^1.0.1" +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" -is-property@^1.0.0: +is-property@^1.0.0, is-property@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= -is-regex@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.3.tgz#0d55182bddf9f2fde278220aec3a75642c908637" +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" is-resolvable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" - dependencies: - tryit "^1.0.1" + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= -is-symbol@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-url@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.2.tgz#498905a593bf47cc2d9e7f738372bbf7696c7f26" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-webpack-bundle@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-webpack-bundle/-/is-webpack-bundle-1.0.0.tgz#576a50bb7c53d1d6a5c1647939c93ae2cbb90eea" + integrity sha1-V2pQu3xT0dalwWR5Ock64su5Duo= -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +is-whitespace-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" + integrity sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ== + +is-word-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" + integrity sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA== isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isexe@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: +isomorphic-fetch@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= dependencies: node-fetch "^1.0.1" whatwg-fetch ">=0.10.0" @@ -2377,304 +2514,514 @@ isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -iterall@^1.0.0, iterall@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.1.tgz#f7f0af11e9a04ec6426260f5019d9fcca4d50214" - -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" +iterall@^1.1.0, iterall@^1.1.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" + integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== -join-component@~1.1.0: +join-component@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" + integrity sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU= js-base64@^2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" + version "2.5.1" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121" + integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw== -js-tokens@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^3.0.0: +js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= js-yaml@^3.5.1: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + version "3.12.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" + integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== dependencies: argparse "^1.0.7" - esprima "^2.6.0" + esprima "^4.0.0" jsbn@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" - -jsdom@^7.0.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e" - dependencies: - abab "^1.0.0" - acorn "^2.4.0" - acorn-globals "^1.0.4" - cssom ">= 0.3.0 < 0.4.0" - cssstyle ">= 0.2.29 < 0.3.0" - escodegen "^1.6.1" - nwmatcher ">= 1.3.7 < 2.0.0" - parse5 "^1.5.1" - request "^2.55.0" - sax "^1.1.4" - symbol-tree ">= 3.1.0 < 4.0.0" - tough-cookie "^2.2.0" - webidl-conversions "^2.0.0" - whatwg-url-compat "~0.6.5" - xml-name-validator ">= 2.0.1 < 3.0.0" - -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.0, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -json5@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= jsprim@^1.2.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: - extsprintf "1.0.2" + assert-plus "1.0.0" + extsprintf "1.3.0" json-schema "0.2.3" - verror "1.3.6" + verror "1.10.0" jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.5.tgz#9ba6297198d9f754594d62e59496ffb923778dd4" + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" + integrity sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE= + +juice@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-5.1.0.tgz#e7df05a8be7b3e082ccb026f6dda5036dd4e3068" + integrity sha512-SRfLv0y7xwAKnsd6HWS9aSF0+a9ozXEatKlXHiiTvozdZu0Vwz9dDk7NEpy/N2icFTnhYtk1Du3rPNtH7fFVHw== + dependencies: + cheerio "^0.22.0" + commander "^2.15.1" + cross-spawn "^6.0.5" + deep-extend "^0.6.0" + mensch "^0.3.3" + slick "^1.12.2" + web-resource-inliner "^4.2.1" + +keycode@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" + integrity sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ= + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: - acorn-jsx "^3.0.1" - object-assign "^4.1.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" -juice@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/juice/-/juice-1.11.0.tgz#4a7ff6bef3f3fa4b610f824aeb27ed8f3601ad73" - dependencies: - batch "0.5.3" - cheerio "0.19.0" - commander "2.9.0" - cross-spawn-async "^2.1.8" - cssom "0.3.0" - deep-extend "^0.4.0" - slick "1.12.2" - util-deprecate "^1.0.2" - web-resource-inliner "1.2.1" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" -keycode@^2.1.2: - version "2.1.8" - resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.8.tgz#94d2b7098215eff0e8f9a8931d5a59076c4532fb" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" -kind-of@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" +lodash-es@^4.2.1: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0" + integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q== + +lodash._basecallback@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz#b7b2bb43dc2160424a21ccf26c57e443772a8e27" + integrity sha1-t7K7Q9whYEJKIczybFfkQ3cqjic= dependencies: - is-buffer "^1.0.2" + lodash._baseisequal "^3.0.0" + lodash._bindcallback "^3.0.0" + lodash.isarray "^3.0.0" + lodash.pairs "^3.0.0" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" +lodash._baseeach@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz#cf8706572ca144e8d9d75227c990da982f932af3" + integrity sha1-z4cGVyyhROjZ11InyZDamC+TKvM= + dependencies: + lodash.keys "^3.0.0" -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" +lodash._basefind@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basefind/-/lodash._basefind-3.0.0.tgz#b2bba05cc645f972de2cf925fa2bf63a0f60c8ae" + integrity sha1-srugXMZF+XLeLPkl+iv2Og9gyK4= + +lodash._basefindindex@^3.0.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lodash._basefindindex/-/lodash._basefindindex-3.6.0.tgz#f083360a1b022418ed81bc899beb312e21e74a4f" + integrity sha1-8IM2ChsCJBjtgbyJm+sxLiHnSk8= + +lodash._baseisequal@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1" + integrity sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE= dependencies: - invert-kv "^1.0.0" + lodash.isarray "^3.0.0" + lodash.istypedarray "^3.0.0" + lodash.keys "^3.0.0" -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" +lodash._baseismatch@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lodash._baseismatch/-/lodash._baseismatch-3.1.3.tgz#0728fc48efa11699d3d5f2d73049f2ab13c40fd5" + integrity sha1-Byj8SO+hFpnT1fLXMEnyqxPED9U= dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + lodash._baseisequal "^3.0.0" + +lodash._basematches@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._basematches/-/lodash._basematches-3.2.0.tgz#f47e03f07ec20784ab0968d0cb6cb597e2101158" + integrity sha1-9H4D8H7CB4SrCWjQy2y1l+IQEVg= + dependencies: + lodash._baseismatch "^3.0.0" + lodash.pairs "^3.0.0" + +lodash._bindcallback@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._reinterpolate@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI= -lodash-es@^4.2.0, lodash-es@^4.2.1: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= -lodash.cond@^4.3.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.debounce@^4.0.8: +lodash.debounce@^4.0.6, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.find@4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= -lodash.memoize@4.1.2: +lodash.escaperegexp@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - -lodash.pick@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= -lodash.pickby@^4.6.0: +lodash.every@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" + resolved "https://registry.yarnpkg.com/lodash.every/-/lodash.every-4.6.0.tgz#eb89984bebc4364279bb3aefbbd1ca19bfa6c6a7" + integrity sha1-64mYS+vENkJ5uzrvu9HKGb+mxqc= -lodash@^3.10.1, lodash@^3.2.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - -lodash@^4.0.0, lodash@^4.1.0, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@~4.17.2: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4= -long@~3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" +lodash.find@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-3.2.1.tgz#046e319f3ace912ac6c9246c7f683c5ec07b36ad" + integrity sha1-BG4xnzrOkSrGySRsf2g8XsB7Nq0= + dependencies: + lodash._basecallback "^3.0.0" + lodash._baseeach "^3.0.0" + lodash._basefind "^3.0.0" + lodash._basefindindex "^3.0.0" + lodash.isarray "^3.0.0" + lodash.keys "^3.0.0" -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" +lodash.find@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" + integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.0.tgz#6b26248c42f6d4fa4b0d8542f78edfcde35642a8" +lodash.findwhere@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.findwhere/-/lodash.findwhere-3.1.0.tgz#7937d34f3eac818dec7fa94e8ca5e26fdba1cfc1" + integrity sha1-eTfTTz6sgY3sf6lOjKXib9uhz8E= dependencies: - js-tokens "^2.0.0" + lodash._basematches "^3.0.0" + lodash.find "^3.0.0" -loose-envify@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" - dependencies: - js-tokens "^3.0.0" +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= -lru-cache@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" - dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" +lodash.foreach@^4.3.0, lodash.foreach@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= -lru-cache@~2.6.5: - version "2.6.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= -lru-queue@0.1: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - dependencies: - es5-ext "~0.10.2" +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= -mailchimp@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mailchimp/-/mailchimp-1.2.0.tgz#cbabf992120e5f7225ca9484a4393844a58272b4" - dependencies: - qs "^6.2.0" - request "^2.72.0" +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= -make-error-cause@^1.0.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - dependencies: - make-error "^1.2.0" +lodash.isempty@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -make-error@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" +lodash.isobject@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" + integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= -marked@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.istypedarray@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62" + integrity sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I= -memoizee@~0.3.8: - version "0.3.10" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.3.10.tgz#4eca0d8aed39ec9d017f4c5c2f2f6432f42e5c8f" +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= dependencies: - d "~0.1.1" - es5-ext "~0.10.11" - es6-weak-map "~0.1.4" - event-emitter "~0.3.4" - lru-queue "0.1" - next-tick "~0.2.2" - timers-ext "0.1" + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= -message-box@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/message-box/-/message-box-0.0.2.tgz#38ee3de4bcddb0bcb883a7db2468cac81fd76f79" - dependencies: - deep-extend "^0.4.1" - handlebars "^4.0.5" +lodash.merge@^4.4.0, lodash.merge@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" + integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ== -metascraper@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-1.0.7.tgz#2343f9108f34e4d2b55b7b4f543c96dfc52e1478" - dependencies: - cheerio "^0.20.0" - chrono-node "^1.2.3" - is-isodate "0.0.1" - is-url "^1.2.1" - popsicle "^6.2.0" - to-title-case "^1.0.0" +lodash.mergewith@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ== -meteor-node-stubs@^0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/meteor-node-stubs/-/meteor-node-stubs-0.2.4.tgz#5b2644913d866eb5e499e8392cab20dbdf7471b0" +lodash.omit@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" + integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= + +lodash.pairs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9" + integrity sha1-u+CNV4bu6qCaFckevw3LfSvjJqk= dependencies: - assert "^1.4.1" - browserify-zlib "^0.1.4" + lodash.keys "^3.0.0" + +lodash.pick@^4.2.1, lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU= + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= + +lodash.template@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + integrity sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A= + dependencies: + lodash._reinterpolate "~3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + integrity sha1-K01OlbpEDZFf8IvImeRVNmZxMxY= + dependencies: + lodash._reinterpolate "~3.0.0" + +lodash.unescape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= + +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash.without@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" + integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= + +lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowlight@~1.9.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.9.2.tgz#0b9127e3cec2c3021b7795dd81005c709a42fdd1" + integrity sha512-Ek18ElVCf/wF/jEm1b92gTnigh94CtBNWiZ2ad+vTgW7cTmQxUY3I98BjHK68gZAJEWmybGBZgx9qv3QxLQB/Q== + dependencies: + fault "^1.0.2" + highlight.js "~9.12.0" + +mailchimp@^1.1.6: + version "1.2.1" + resolved "https://registry.yarnpkg.com/mailchimp/-/mailchimp-1.2.1.tgz#d1fc4ba4ca81d868228e2050be511c00b64b3cac" + integrity sha512-2qToqrSbsK8HXH+K5au4XsDO/m3XJD2Zf47va0Bnh7+QqzGXE7qQe7ajGvG1U8X5gc9u7hgU0unSBvFDS0RrGA== + dependencies: + qs "^6.2.0" + request "^2.72.0" + +markdown-escapes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" + integrity sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA== + +marked@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.5.2.tgz#3efdb27b1fd0ecec4f5aba362bddcd18120e5ba9" + integrity sha512-fdZvBa7/vSQIZCi4uuwo2N3q+7jJURpMVCcbaX0S1Mg65WZ5ilXvC67MviJAsdjqqgD+CEq4RKo5AYGgINkVAA== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdast-add-list-metadata@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf" + integrity sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA== + dependencies: + unist-util-visit-parents "1.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mensch@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.3.tgz#e200ff4dd823717f8e0563b32e3f5481fca262b2" + integrity sha1-4gD/TdgjcX+OBWOzLj9UgfyiYrI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +message-box@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/message-box/-/message-box-0.2.0.tgz#f93da217cb11f2a13cb501846fa713f21d6eabb7" + integrity sha512-SPLfVDEM2YcAgV2IB0B5vOGjvqXSSw7ZibEeXcff8HYpxyG1Uj+XjgnGUGyR1C0EQCvPI3MBx3p7opt2CIQ2hw== + dependencies: + lodash.merge "^4.6.0" + lodash.template "^4.4.0" + +meteor-node-stubs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/meteor-node-stubs/-/meteor-node-stubs-0.4.1.tgz#f755d8f127c1a1ae3d57dc71df197bb8c0e26767" + integrity sha512-UO2OStvLOKoApmOdIP5eCqoLaa/ritMXRg4ffJVdkNLEsczzPvTjgC0Mxk4cM4R8MZkwll90FYgjDf5qUTJdMA== + dependencies: + assert "^1.4.1" + browserify-zlib "^0.1.4" buffer "^4.9.1" console-browserify "^1.1.0" constants-browserify "^1.0.0" crypto-browserify "^3.11.0" domain-browser "^1.1.7" events "^1.1.1" - http-browserify "^1.7.0" https-browserify "0.0.1" os-browserify "^0.2.1" path-browserify "0.0.0" process "^0.11.9" punycode "^1.4.1" querystring-es3 "^0.2.1" - readable-stream "^2.2.1" + readable-stream "^2.3.6" stream-browserify "^2.0.1" - string_decoder "^0.10.31" + stream-http "^2.8.0" + string_decoder "^1.1.0" timers-browserify "^1.4.2" tty-browserify "0.0.0" url "^0.11.0" @@ -2684,214 +3031,363 @@ meteor-node-stubs@^0.2.3: methods@^1.1.1, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= miller-rabin@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== dependencies: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" +"mime-db@>= 1.36.0 < 2": + version "1.38.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" + integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== mime-db@~1.25.0: version "1.25.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" + integrity sha1-wY29fHOl2/b0SgJNwNFloeexw5I= + +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" + integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== -mime-types@2.1.13, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: +mime-types@2.1.13: version "2.1.13" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" + integrity sha1-4HqqnGxrmnyjASxpADrSWjnpKog= dependencies: mime-db "~1.25.0" -mime-types@~2.0.3: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.0.14.tgz#310e159db23e077f8bb22b748dabfa4957140aa6" +mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.19: + version "2.1.21" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" + integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== dependencies: - mime-db "~1.12.0" + mime-db "~1.37.0" -mime@1.3.4, mime@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -mime@~1.2.11, mime@~1.2.9: - version "1.2.11" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimer@*: - version "0.2.1" - resolved "https://registry.yarnpkg.com/mimer/-/mimer-0.2.1.tgz#c63c5a17fe86423f5161a85d55c3ed5189baaffc" +mimer@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mimer/-/mimer-0.3.2.tgz#0b83aabdf48eaacfd2e093ed4c0ed3d38eda8073" + integrity sha512-N6NcgDQAevhP/02DQ/epK6daLy4NKrIHyTlJcO6qBiYn98q+Y4a/knNsAATCe1xLS2F0nEmJp+QYli2s8vKwyQ== mingo@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/mingo/-/mingo-0.8.1.tgz#7e76fb604213a4ccb94fc5101355434301eeaf6a" + integrity sha1-fnb7YEITpMy5T8UQE1VDQwHur2o= -minimalistic-assert@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +minipass@^2.2.1, minipass@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -moment@^2.10.3: - version "2.18.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" +moment@^2.13.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== -moment@^2.13.0, moment@^2.8.2: - version "2.17.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" - -mongo-object@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/mongo-object/-/mongo-object-0.0.1.tgz#8cad470e5925f76b5d79b0988c5918f54904e170" +mongo-object@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/mongo-object/-/mongo-object-0.1.3.tgz#56b2a46d3ae4f0bdb1935241928fb444034b92e5" + integrity sha512-m3vs+a1JkvRXELJMe2ieMmBe03NUy6bctGjWicRhReYj8brDi0ojKHLKLmXWr/RupNaFP8Q7/x8xG8GpFtp9wg== dependencies: - lodash "^4.5.1" + lodash.foreach "^4.5.0" + lodash.isempty "^4.4.0" + lodash.isobject "^3.0.2" + lodash.without "^4.4.0" -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= -nan@2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.3.5.tgz#822a0dc266290ce4cd3a12282ca3e7e364668a08" +nan@2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" + integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +needle@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e" + integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA== + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= -netmask@~1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= -next-tick@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@^1.0.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" +node-fetch@1.7.3, node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== dependencies: encoding "^0.1.11" is-stream "^1.0.1" -node-uuid@~1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" +node-pre-gyp@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -nth-check@~1.0.0, nth-check@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.3.0.tgz#7f01e8e44408341379ca98cfd756e7b29bd2626c" + integrity sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -"nwmatcher@>= 1.3.7 < 2.0.0": - version "1.4.1" - resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f" - -oauth-sign@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.3.0.tgz#cb540f93bb2b22a7d5941691a288d60e8ea9386e" - -oauth-sign@~0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== + +object.assign@^4.0.4, object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" -object-keys@^1.0.8: - version "1.0.11" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.11.0" + function-bind "^1.1.1" + has "^1.0.1" -on-finished@^2.3.0, on-finished@~2.3.0: +object.values@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - -optics-agent@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/optics-agent/-/optics-agent-1.0.5.tgz#f8bcc7187e0741fea3492ca81fd84b442e93a35f" - dependencies: - graphql-tools "^0.8.4" - on-finished "^2.3.0" - protobufjs-no-cli "^5.0.1" - request "^2.74.0" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= optimist@0.3.x: version "0.3.7" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + integrity sha1-yQlBrVnkJzMokjB00s8ufLxuwNk= dependencies: wordwrap "~0.0.2" optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1, optionator@^0.8.2: +optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.4" @@ -2903,267 +3399,306 @@ optionator@^0.8.1, optionator@^0.8.2: os-browserify@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + integrity sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8= os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.1: +os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" -pac-proxy-agent@1: +p-try@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.0.0.tgz#dcd5b746581367430a236e88eacfd4e5b8d068a5" - dependencies: - agent-base "2" - debug "2" - extend "3" - get-uri "1" - http-proxy-agent "1" - https-proxy-agent "1" - pac-resolver "~1.2.1" - socks-proxy-agent "2" - stream-to-buffer "0.1.0" - -pac-resolver@~1.2.1: - version "1.2.6" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-1.2.6.tgz#ed03af0c5b5933505bdd3f07f75175466d5e7cfb" - dependencies: - co "~3.0.6" - degenerator "~1.0.0" - netmask "~1.0.4" - regenerator "~0.8.13" - thunkify "~2.1.1" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= parse-asn1@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + version "5.1.3" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.3.tgz#1600c6cc0727365d68b97f3aa78939e735a75204" + integrity sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" -parse5@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" +parse-entities@^1.1.0, parse-entities@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.0.tgz#9deac087661b2e36814153cb78d7e54a4c5fd6f4" + integrity sha512-XXtDdOPLSB0sHecbEapQi6/58U/ODj/KWfIXmmMCJF/eRn8laX6LZbOyioMoETOOJoWRW8/qTSl5VQkUIfKM5g== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" -parseurl@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= path-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= -path-exists@3.0.0: +path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - -path-exists@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" pbkdf2@^3.0.3: - version "3.0.9" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== dependencies: - create-hmac "^1.1.2" + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - dependencies: - find-up "^1.0.0" - -pkg-up@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= dependencies: - find-up "^1.0.0" + find-up "^2.1.0" pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" + integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= -popsicle@^6.2.0: - version "6.2.2" - resolved "https://registry.yarnpkg.com/popsicle/-/popsicle-6.2.2.tgz#e273c8bd48181f73a59b199e2a5e25b692b3e237" - dependencies: - any-promise "^1.3.0" - arrify "^1.0.0" - concat-stream "^1.4.7" - form-data "^0.2.0" - make-error-cause "^1.0.1" - throwback "^1.1.0" - tough-cookie "^2.0.0" - xtend "^4.0.0" +popper.js@^1.14.4, popper.js@^1.14.6: + version "1.14.7" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" + integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss@^5.2.8: - version "5.2.9" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.9.tgz#282a644f92d4b871ade2d3ce8bd0ea46f18317b6" +postcss@^5.2.16: + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" + integrity sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg== dependencies: chalk "^1.1.3" js-base64 "^2.1.9" source-map "^0.5.6" - supports-color "^3.1.2" + supports-color "^3.2.3" + +postcss@^7.0.5: + version "7.0.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.14.tgz#4527ed6b1ca0d82c53ce5ec1a2041c2346bbd6e5" + integrity sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -private@^0.1.6, private@~0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1" +prismjs@^1.8.4, prismjs@~1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9" + integrity sha512-Lf2JrFYx8FanHrjoV5oL8YHCclLQgbJcVZR+gikGGMqz6ub5QVWDTM6YIwm3BuPxM/LOV+rKns3LssXNLIf+DA== + optionalDependencies: + clipboard "^2.0.0" -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== process@^0.11.9, process@~0.11.0: - version "0.11.9" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= promise@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8: - version "15.5.10" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" +prop-types-extra@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.0.tgz#32609910ea2dcf190366bacd3490d5a6412a605f" + integrity sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg== + dependencies: + react-is "^16.3.2" + warning "^3.0.0" + +prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ== dependencies: - fbjs "^0.8.9" loose-envify "^1.3.1" + object-assign "^4.1.1" -protobufjs-no-cli@^5.0.1: +property-information@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/protobufjs-no-cli/-/protobufjs-no-cli-5.0.1.tgz#17a527da0bc49f1f974f512d852950be26acbb82" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.0.1.tgz#c3b09f4f5750b1634c0b24205adbf78f18bdf94f" + integrity sha512-nAtBDVeSwFM3Ot/YxT7s4NqZmqXI7lLzf46BThvotEtYf2uk2yH0ACYuWQkJ7gxKs49PPtKVY0UlDGkyN9aJlw== dependencies: - bytebuffer "~5" + xtend "^4.0.1" -proxy-addr@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" +proxy-addr@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== dependencies: - forwarded "~0.1.0" - ipaddr.js "1.1.1" + forwarded "~0.1.2" + ipaddr.js "1.8.0" -proxy-agent@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" - dependencies: - agent-base "2" - debug "2" - extend "3" - http-proxy-agent "1" - https-proxy-agent "1" - lru-cache "~2.6.5" - pac-proxy-agent "1" - socks-proxy-agent "2" - -pseudomap@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +psl@^1.1.24: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== public-encrypt@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" create-hash "^1.1.0" parse-asn1 "^5.0.0" randombytes "^2.0.1" + safe-buffer "^5.1.2" punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -q@^1.1.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" - -qs@6.2.0, qs@^6.1.0, qs@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@~0.6.0: - version "0.6.6" - resolved "https://registry.yarnpkg.com/qs/-/qs-0.6.6.tgz#6e015098ff51968b8a3c819001d5f2c89bc4b107" +qs@6.5.2, qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" +qs@^6.2.0, qs@^6.5.1: + version "6.6.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.6.0.tgz#a99c0f69a8d26bf7ef012f871cdabb0aee4424c2" + integrity sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA== -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" +qs@~6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.0.4.tgz#51019d84720c939b82737e84556a782338ecea7b" + integrity sha1-UQGdhHIMk5uCc36EVWp4Izjs6ns= query-string@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.2.3.tgz#9f27273d207a25a8ee4c7b8c74dcd45d556db822" + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= dependencies: object-assign "^4.1.0" strict-uri-encode "^1.0.0" @@ -3171,39 +3706,68 @@ query-string@^4.2.2: querystring-es3@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -randombytes@^2.0.0, randombytes@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= -raw-body@~2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" +raw-body@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== dependencies: - bytes "2.4.0" - iconv-lite "0.4.13" + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-addons-pure-render-mixin@^15.4.1: - version "15.4.2" - resolved "https://registry.yarnpkg.com/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.4.2.tgz#a8433c71c45e2368503721921dc47bdaf1fbabcd" + version "15.6.2" + resolved "https://registry.yarnpkg.com/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.2.tgz#6b83f40b6b36ee40735cbd6125eb3f13ce1cddc9" + integrity sha1-a4P0C2s27kBzXL1hJes/E84c3ck= dependencies: fbjs "^0.8.4" object-assign "^4.1.0" -react-apollo@^1.1.1: - version "1.4.15" - resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.15.tgz#3a213d11b847da7db03c4619ac640df734f9c4b5" +react-apollo@^1.4.15: + version "1.4.16" + resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.16.tgz#62a623458b67a174ff8ef25f64e7b42531518e19" + integrity sha512-/Htq9zI/u/PpIdoH/qBU9Zty09U23biH585H52GvEF0m/kK9te9QFCnYfjKpB30DS6YleIk1uKNcqeK3HEfU6w== dependencies: apollo-client "^1.4.0" + enzyme-adapter-react-16 "^1.0.0" graphql-tag "^2.0.0" hoist-non-react-statics "^2.2.0" invariant "^2.2.1" @@ -3211,344 +3775,439 @@ react-apollo@^1.1.1: object-assign "^4.0.1" prop-types "^15.5.8" -react-bootstrap-datetimepicker@0.0.22: - version "0.0.22" - resolved "https://registry.yarnpkg.com/react-bootstrap-datetimepicker/-/react-bootstrap-datetimepicker-0.0.22.tgz#07e448d993157d049ad0876d0f9a3c9c5029d9c5" +react-bootstrap@^1.0.0-beta.5: + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-1.0.0-beta.5.tgz#cf2c45a78bb0603c086d3fee3b454e21c3b5de6e" + integrity sha512-Osm0OtTbYwfsT1rpu88ESWuAHZxfaHFNKFiW8w3w+6YY9/bLEPHbGRZA6W21fg5yvcuKN9hJKT857TTHgY7SoQ== dependencies: - babel-runtime "^5.6.18" - classnames "^2.1.2" - moment "^2.8.2" - -react-bootstrap@^0.30.7: - version "0.30.7" - resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-0.30.7.tgz#39da80088693ecb71e8e63b5bdc313571fd993d1" - dependencies: - babel-runtime "^6.11.6" - classnames "^2.2.5" - dom-helpers "^2.4.0" - invariant "^2.2.1" + "@babel/runtime" "^7.2.0" + "@react-bootstrap/react-popper" "1.2.1" + classnames "^2.2.6" + dom-helpers "^3.4.0" + invariant "^2.2.3" keycode "^2.1.2" - react-overlays "^0.6.10" + popper.js "^1.14.6" + prop-types "^15.6.2" + prop-types-extra "^1.1.0" + react-context-toolbox "^2.0.2" + react-overlays "^1.0.0" react-prop-types "^0.4.0" - uncontrollable "^4.0.1" - warning "^3.0.0" + react-transition-group "^2.5.1" + uncontrollable "^6.0.0" + warning "^4.0.1" + +react-context-toolbox@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz#35637287cb23f801e6ed802c2bb7a97e1f04e3fb" + integrity sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A== -react-cookie@^0.4.6: - version "0.4.9" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-0.4.9.tgz#21a0fcc9c6265aa86bba6d174122acb3851b8736" +react-cookie@^2.1.6: + version "2.2.0" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-2.2.0.tgz#0ad91a04db235aa35af97032c388fe902be3560c" + integrity sha512-W6e+ZyeEkgUhJV4D/p41QaApfFKsChW/OLvhDXLcBRcV2pmKOh88YhHkBz2QwZt20J5xaiqcN0NqGGK+58gn/g== dependencies: - cookie "^0.3.1" + hoist-non-react-statics "^2.3.1" + prop-types "^15.0.0" + universal-cookie "^2.2.0" -react-datetime@^2.3.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-2.8.3.tgz#0ef74b3505cfc517b44ac113bbd6cdbc50d3e308" +react-datetime@^2.11.1: + version "2.16.3" + resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-2.16.3.tgz#7f9ac7d4014a939c11c761d0c22d1fb506cb505e" + integrity sha512-amWfb5iGEiyqjLmqCLlPpu2oN415jK8wX1qoTq7qn6EYiU7qQgbNHglww014PT4O/3G5eo/3kbJu/M/IxxTyGw== dependencies: + create-react-class "^15.5.2" object-assign "^3.0.0" - react-onclickoutside "^4.1.0" + prop-types "^15.5.7" + react-onclickoutside "^6.5.0" -react-dom@^15.4.1: - version "15.4.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.4.2.tgz#015363f05b0a1fd52ae9efdd3a0060d90695208f" +react-dom@^16.4.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.1.tgz#ec860f98853d09d39bafd3a6f1e12389d283dbb4" + integrity sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg== dependencies: - fbjs "^0.8.1" loose-envify "^1.1.0" - object-assign "^4.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.1" -react-dropzone@^3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-3.12.2.tgz#7e66a37322a80cf26a205d749ecf8cad0d90aa6f" +react-dropzone@^3.13.4: + version "3.13.4" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-3.13.4.tgz#84da26815c40339691c49b4544c2ef7a16912ccc" + integrity sha1-hNomgVxAM5aRxJtFRMLvehaRLMw= dependencies: attr-accept "^1.0.3" + prop-types "^15.5.7" react-helmet@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-5.1.3.tgz#cd40626593a29eecf684b6d38d711f44c48188af" + version "5.2.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-5.2.0.tgz#a81811df21313a6d55c5f058c4aeba5d6f3d97a7" + integrity sha1-qBgR3yExOm1VxfBYxK66XW89l6c= dependencies: deep-equal "^1.0.1" object-assign "^4.1.1" prop-types "^15.5.4" react-side-effect "^1.1.0" +react-input-autosize@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8" + integrity sha512-3+K4CD13iE4lQQ2WlF8PuV5htfmTRLH6MDnfndHM6LuBRszuXnuyIfE7nhSKt8AzRBZ50bu0sAhkNMeS5pxQQA== + dependencies: + prop-types "^15.5.8" + react-intl@^2.1.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.2.2.tgz#266a38334671937bec92729eb4973b4360c07996" + version "2.8.0" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.8.0.tgz#20b0c1f01d1292427768aa8ec9e51ab7e36503ba" + integrity sha512-1cSasNkHxZOXYYhms9Q1tSEWF8AWZQNq3nPLB/j8mYV0ZTSt2DhGQXHfKrKQMu4cgj9J1Crqg7xFPICTBgzqtQ== dependencies: + hoist-non-react-statics "^2.5.5" intl-format-cache "^2.0.5" - intl-messageformat "^1.3.0" - intl-relativeformat "^1.3.0" + intl-messageformat "^2.1.0" + intl-relativeformat "^2.1.0" invariant "^2.1.1" +react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.1.tgz#a80141e246eb894824fb4f2901c0c50ef31d4cdb" + integrity sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA== + +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-loadable@^4.0.3: version "4.0.5" resolved "https://registry.yarnpkg.com/react-loadable/-/react-loadable-4.0.5.tgz#225eba40a0e67cc5698bcb5db6e783f0d3a9e729" + integrity sha1-Il66QKDmfMVpi8tdtueD8NOp5yk= dependencies: import-inspector "^2.0.0" is-webpack-bundle "^1.0.0" webpack-require-weak "^1.0.1" -react-onclickoutside@^4.1.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-4.9.0.tgz#2908c3136e244102bea5e5437a838a528d5d2995" - dependencies: - object-assign "^4.0.1" - -react-overlays@^0.6.10: - version "0.6.10" - resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.6.10.tgz#e7e52dad47f00a0fc784eb044428c3a9e874bfa3" - dependencies: - classnames "^2.2.5" - dom-helpers "^2.4.0" - react-prop-types "^0.4.0" - warning "^3.0.0" +react-markdown@^3.1.5: + version "3.6.0" + resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-3.6.0.tgz#29f6aaab5270c8ef0a5e234093a873ec3e01722b" + integrity sha512-TV0wQDHHPCEeKJHWXFfEAKJ8uSEsJ9LgrMERkXx05WV/3q6Ig+59KDNaTmjcoqlCpE/sH5PqqLMh4t0QWKrJ8Q== + dependencies: + mdast-add-list-metadata "1.0.1" + prop-types "^15.6.1" + remark-parse "^5.0.0" + unified "^6.1.5" + unist-util-visit "^1.3.0" + xtend "^4.0.1" + +react-onclickoutside@^6.5.0: + version "6.7.1" + resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93" + integrity sha512-p84kBqGaMoa7VYT0vZ/aOYRfJB+gw34yjpda1Z5KeLflg70HipZOT+MXQenEhdkPAABuE2Astq4zEPdMqUQxcg== + +react-overlays@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-1.1.1.tgz#81f8dddce11f8402cf86ece0f8c9780941495b64" + integrity sha512-acL+HwhD1I/AY1Hz7qWQvnBaXEZt5K+huZ2AySdBtJORKNSxnLrd6j2FL3KOEM3rQQWuzAubCYiinlrm2XxXFA== + dependencies: + classnames "^2.2.6" + dom-helpers "^3.4.0" + prop-types "^15.6.2" + prop-types-extra "^1.1.0" + react-context-toolbox "^2.0.2" + react-popper "^1.3.2" + uncontrollable "^6.0.0" + warning "^4.0.2" react-places-autocomplete@^5.0.0: - version "5.4.2" - resolved "https://registry.yarnpkg.com/react-places-autocomplete/-/react-places-autocomplete-5.4.2.tgz#c9ea44e2752a7f8eca2d511b40001c3f8433aa4a" + version "5.4.3" + resolved "https://registry.yarnpkg.com/react-places-autocomplete/-/react-places-autocomplete-5.4.3.tgz#321166247a9279b93ff21ee59112836058dda0ba" + integrity sha512-NXTYVLyYESsW5LgYlOP3+7Bs+mWeXK8KAqrht2X48+cPYFHmT/S8BNnIRUxpmZyS036s1wuItuwpCJ53nYR/zQ== dependencies: lodash.debounce "^4.0.8" prop-types "^15.5.8" - react "^15.3.0" -react-prop-types@^0.4.0: - version "0.4.0" +react-popper@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.3.tgz#2c6cef7515a991256b4f0536cd4bdcb58a7b6af6" + integrity sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w== + dependencies: + "@babel/runtime" "^7.1.2" + create-react-context "<=0.2.2" + popper.js "^1.14.4" + prop-types "^15.6.1" + typed-styles "^0.0.7" + warning "^4.0.2" + +react-prop-types@^0.4.0: + version "0.4.0" resolved "https://registry.yarnpkg.com/react-prop-types/-/react-prop-types-0.4.0.tgz#f99b0bfb4006929c9af2051e7c1414a5c75b93d0" + integrity sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A= dependencies: warning "^3.0.0" -react-redux@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.2.tgz#3d9878f5f71c6fafcd45de1fbb162ea31f389814" +react-redux@^5.0.6: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" + integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== dependencies: - hoist-non-react-statics "^1.0.3" - invariant "^2.0.0" - lodash "^4.2.0" - lodash-es "^4.2.0" + "@babel/runtime" "^7.1.2" + hoist-non-react-statics "^3.1.0" + invariant "^2.2.4" loose-envify "^1.1.0" + prop-types "^15.6.1" + react-is "^16.6.0" + react-lifecycles-compat "^3.0.0" react-router-bootstrap@^0.23.1: - version "0.23.1" - resolved "https://registry.yarnpkg.com/react-router-bootstrap/-/react-router-bootstrap-0.23.1.tgz#a1e0b82f49d25a6083c72202ad16aaa64bba0e0c" + version "0.23.3" + resolved "https://registry.yarnpkg.com/react-router-bootstrap/-/react-router-bootstrap-0.23.3.tgz#970c35c53c04c61fb6b110d4ff651a7e8a73b2ba" + integrity sha1-lww1xTwExh+2sRDU/2Uafopzsro= + dependencies: + prop-types "^15.5.8" -react-router-scroll@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/react-router-scroll/-/react-router-scroll-0.4.1.tgz#8cafd348d34e54b1127ec0fb7f8323d0d4c489e0" +react-router-scroll@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/react-router-scroll/-/react-router-scroll-0.4.4.tgz#4d7b71c75b45ff296e4adca1e029a86e898a155d" + integrity sha512-FR+kyNmRrNqhRbMHDFSgFPVrOy923AdJZE0Qqefub5u56+5d7EENLy4DOrCZVr2fTHsJjWJDE0X1vU063t4bOA== dependencies: - scroll-behavior "^0.9.1" + prop-types "^15.6.0" + scroll-behavior "^0.9.5" warning "^3.0.0" -react-router@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.0.0.tgz#3f313e4dbaf57048c48dd0a8c3cac24d93667dff" +react-router@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.1.tgz#b9a3279962bdfbe684c8bd0482b81ef288f0f244" + integrity sha512-SXkhC0nr3G0ltzVU07IN8jYl0bB6FsrDIqlLC9dK3SITXqyTJyM7yhXlUqs89w3Nqi5OkXsfRUeHX+P874HQrg== dependencies: + create-react-class "^15.5.1" history "^3.0.0" - hoist-non-react-statics "^1.2.0" + hoist-non-react-statics "^2.3.1" invariant "^2.2.1" loose-envify "^1.2.0" + prop-types "^15.5.6" warning "^3.0.0" +react-select@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-1.3.0.tgz#1828ad5bf7f3e42a835c7e2d8cb13b5c20714876" + integrity sha512-g/QAU1HZrzSfxkwMAo/wzi6/ezdWye302RGZevsATec07hI/iSxcpB1hejFIp7V63DJ8mwuign6KmB3VjdlinQ== + dependencies: + classnames "^2.2.4" + prop-types "^15.5.8" + react-input-autosize "^2.1.2" + react-side-effect@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.3.tgz#512c25abe0dec172834c4001ec5c51e04d41bc5c" + version "1.1.5" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.5.tgz#f26059e50ed9c626d91d661b9f3c8bb38cd0ff2d" + integrity sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw== dependencies: exenv "^1.2.1" shallowequal "^1.0.1" -"react@>=0.14.7 || ^15.0.0-rc.2", react@^15.4.1: - version "15.4.2" - resolved "https://registry.yarnpkg.com/react/-/react-15.4.2.tgz#41f7991b26185392ba9bae96c8889e7e018397ef" +react-stripe-checkout@^2.4.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/react-stripe-checkout/-/react-stripe-checkout-2.6.3.tgz#3173a870b04e5a3c321a06d24cd53c6030111c45" + integrity sha1-MXOocLBOWjwyGgbSTNU8YDARHEU= + +react-syntax-highlighter@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-6.1.2.tgz#a6fd152400139dedf50dcad5e05fc07e054b4b94" + integrity sha512-ahNwcZ0FhUd8U5TQYcmAqC/pec6Q308mUAATKMcLFmNYkvGhN9wfmoqxzjACcccGb2e85d5ZnGpOiCIIzGO3yA== dependencies: - fbjs "^0.8.4" - loose-envify "^1.1.0" - object-assign "^4.1.0" + babel-runtime "^6.18.0" + highlight.js "~9.12.0" + lowlight "~1.9.1" + prismjs "^1.8.4" + refractor "^2.0.0" -react@^15.3.0: - version "15.6.1" - resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" +react-test-renderer@^16.0.0-0: + version "16.8.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.1.tgz#72845ad9269be526126e97853311982f781767be" + integrity sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.8.1" + scheduler "^0.13.1" + +react-transition-group@^2.5.1: + version "2.5.3" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.3.tgz#26de363cab19e5c88ae5dbae105c706cf953bb92" + integrity sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg== + dependencies: + dom-helpers "^3.3.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +"react@>=0.14.7 || ^15.0.0-rc.2", react@^16.4.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.1.tgz#ae11831f6cb2a05d58603a976afc8a558e852c4a" + integrity sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ== dependencies: - create-react-class "^15.6.0" - fbjs "^0.8.9" loose-envify "^1.1.0" - object-assign "^4.1.0" - prop-types "^15.5.10" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.1" -readable-stream@1.1, readable-stream@1.1.x: - version "1.1.13" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" -readable-stream@2, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.2.1, readable-stream@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== dependencies: - buffer-shims "^1.0.0" core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.6: + version "3.1.1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" + integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readline2@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" mute-stream "0.0.5" -recast@0.10.33: - version "0.10.33" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" - dependencies: - ast-types "0.8.12" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" - -recast@^0.11.17: - version "0.11.20" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.20.tgz#2cb9bec269c03b36d0598118a936cd0a293ca3f3" - dependencies: - ast-types "0.9.4" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" -recompose@^0.21.2: - version "0.21.2" - resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.21.2.tgz#ff3fbdb2397b1c77c47d451be2a63b9295d44681" +recompose@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.26.0.tgz#9babff039cb72ba5bd17366d55d7232fbdfb2d30" + integrity sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog== dependencies: change-emitter "^0.1.2" fbjs "^0.8.1" - hoist-non-react-statics "^1.0.0" + hoist-non-react-statics "^2.3.1" symbol-observable "^1.0.4" redux@^3.4.0, redux@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" + version "3.7.2" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== dependencies: lodash "^4.2.1" lodash-es "^4.2.1" loose-envify "^1.1.0" - symbol-observable "^1.0.2" + symbol-observable "^1.0.3" -regenerator-runtime@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" +refractor@^2.0.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.6.2.tgz#8e0877ab8864165275aafeea5d9c8eebe871552f" + integrity sha512-AMNEGkhaXfhoa0/0mW0bHdfizDJnuHDK29/D5oQaKICf6DALQ+kDEHW/36oDHCdfva4XrZ+cdMhRvPsTI4OIjA== + dependencies: + hastscript "^5.0.0" + parse-entities "^1.1.2" + prismjs "~1.15.0" -regenerator-runtime@^0.9.5, regenerator-runtime@~0.9.5: - version "0.9.6" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029" +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator@~0.8.13: - version "0.8.46" - resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.46.tgz#154c327686361ed52cad69b2545efc53a3d07696" - dependencies: - commoner "~0.10.3" - defs "~1.1.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - recast "0.10.33" - regenerator-runtime "~0.9.5" - through "~2.3.8" +regenerator-runtime@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" + integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== -regexp-quote@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/regexp-quote/-/regexp-quote-0.0.0.tgz#1e0f4650c862dcbfed54fd42b148e9bb1721fcf2" +remark-parse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" + integrity sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remove-trailing-slash@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.0.tgz#1498e5df0984c27e49b76ebf06887ca2d01150d2" + integrity sha1-FJjl3wmEwn5Jt26/Boh8otARUNI= -repeat-string@^1.5.2: +repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= -request@2.34.0: - version "2.34.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.34.0.tgz#b5d8b9526add4a2d4629f4d417124573996445ae" +request@^2.72.0, request@^2.78.0, request@^2.81.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: - forever-agent "~0.5.0" - json-stringify-safe "~5.0.0" - mime "~1.2.9" - node-uuid "~1.4.0" - qs "~0.6.0" - optionalDependencies: - aws-sign2 "~0.5.0" - form-data "~0.1.0" - hawk "~1.0.0" - http-signature "~0.10.0" - oauth-sign "~0.3.0" - tough-cookie ">=0.12.0" - tunnel-agent "~0.3.0" - -request@^2.49.0, request@^2.72.0, request@^2.74.0: - version "2.79.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.11.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~2.0.6" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - qs "~6.3.0" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" - uuid "^3.0.0" - -request@^2.55.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" + aws-sign2 "~0.7.0" + aws4 "^1.8.0" caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" + combined-stream "~1.0.6" + extend "~3.0.2" forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" - uuid "^3.0.0" + uuid "^3.3.2" require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= dependencies: caller-path "^0.1.0" resolve-from "^1.0.0" @@ -3556,37 +4215,42 @@ require-uncached@^1.0.2: resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= -resolve@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.5.0, resolve@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + dependencies: + path-parse "^1.0.6" restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= dependencies: exit-hook "^1.0.0" onetime "^1.0.0" -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" +rimraf@^2.6.1, rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== dependencies: - align-text "^0.1.1" + glob "^7.1.3" -rimraf@^2.2.8: - version "2.5.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== dependencies: - glob "^7.0.5" - -ripemd160@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" + hash-base "^3.0.0" + inherits "^2.0.1" rss@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/rss/-/rss-1.2.2.tgz#50a1698876138133a74f9a05d2bdc8db8d27a921" + integrity sha1-UKFpiHYTgTOnT5oF0r3I240nqSE= dependencies: mime-types "2.1.13" xml "1.0.1" @@ -3594,350 +4258,474 @@ rss@^1.2.1: run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= dependencies: once "^1.3.0" rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= -safe-buffer@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -sanitize-html@^1.11.4: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.13.0.tgz#4ee17cbec516bfe32f2ce6686a569d7e6b4f3631" - dependencies: - htmlparser2 "^3.9.0" - regexp-quote "0.0.0" - xtend "^4.0.0" +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -sax@^1.1.4: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sanitize-html@^1.16.3: + version "1.20.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.20.0.tgz#9a602beb1c9faf960fb31f9890f61911cc4d9156" + integrity sha512-BpxXkBoAG+uKCHjoXFmox6kCSYpnulABoGcZ/R3QyY9ndXbIM5S94eOr1IqnzTG8TnbmXaxWoDDzKC5eJv7fEQ== + dependencies: + chalk "^2.4.1" + htmlparser2 "^3.10.0" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.mergewith "^4.6.1" + postcss "^7.0.5" + srcset "^1.0.0" + xtend "^4.0.1" + +sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scroll-behavior@^0.9.1: - version "0.9.2" - resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.2.tgz#5c04648b1bd8bb14306410a97de3948c72ee1618" +scheduler@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.1.tgz#1a217df1bfaabaf4f1b92a9127d5d732d85a9591" + integrity sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A== dependencies: - dom-helpers "^3.0.0" - invariant "^2.2.1" + loose-envify "^1.1.0" + object-assign "^4.1.1" -semver@~5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" +scroll-behavior@^0.9.5: + version "0.9.10" + resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.10.tgz#c8953adeeb3586060b903328d860aa8346d62861" + integrity sha512-JVJQkBkqMLEM4ATtbHTKare97zhz/qlla9mNttFYY/bcpyOb4BuBGEQ/N9AQWXvshzf6zo9jP60TlphnJ4YPoQ== + dependencies: + dom-helpers "^3.2.1" + invariant "^2.2.2" + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -send@0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== dependencies: - debug "~2.2.0" - depd "~1.1.0" + debug "2.6.9" + depd "~1.1.2" destroy "~1.0.4" - encodeurl "~1.0.1" + encodeurl "~1.0.2" escape-html "~1.0.3" - etag "~1.7.0" - fresh "0.3.0" - http-errors "~1.5.0" - mime "1.3.4" - ms "0.7.1" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" on-finished "~2.3.0" range-parser "~1.2.0" - statuses "~1.3.0" + statuses "~1.4.0" -sendy-api@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/sendy-api/-/sendy-api-0.1.0.tgz#62107fb95972d63060c870204183388247d57810" - dependencies: - request "2.34.0" - -serve-static@~1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== dependencies: - encodeurl "~1.0.1" + encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.14.1" + parseurl "~1.3.2" + send "0.16.2" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -setprototypeof@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -sha.js@^2.3.6: - version "2.4.8" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== dependencies: inherits "^2.0.1" + safe-buffer "^5.0.1" shallowequal@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f" + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shelljs@^0.7.5: - version "0.7.6" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" + version "0.7.8" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= dependencies: glob "^7.0.0" interpret "^1.0.0" rechoir "^0.6.2" -simpl-schema@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/simpl-schema/-/simpl-schema-0.2.3.tgz#c1e5728aef88a7afaea7db953a443f1918763adb" - dependencies: - clone "1.0.2" - deep-extend "0.4.1" - message-box "0.0.2" - mongo-object "0.0.1" - underscore "1.8.3" - -simple-fmt@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" - -simple-is@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simpl-schema@^1.4.2: + version "1.5.5" + resolved "https://registry.yarnpkg.com/simpl-schema/-/simpl-schema-1.5.5.tgz#0bcba6f2445547e6edb79e066b50c6d83b9b490b" + integrity sha512-LEAKeqXS9VDpH7sVXmsIJsED7fdMj/WAP0roZw0u0IzOgQV7n3x7jcf722cl7WM6U5ANwI4wwv0dujLuwv266A== + dependencies: + clone "^2.1.1" + extend "^3.0.1" + lodash.every "^4.6.0" + lodash.find "^4.6.0" + lodash.findwhere "^3.1.0" + lodash.includes "^4.3.0" + lodash.isempty "^4.4.0" + lodash.isobject "^3.0.2" + lodash.omit "^4.5.0" + lodash.pick "^4.4.0" + lodash.union "^4.6.0" + lodash.uniq "^4.5.0" + message-box "^0.2.0" + mongo-object "^0.1.3" slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slick@1.12.2: +slick@^1.12.2: version "1.12.2" resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + integrity sha1-vQSN23TefRymkV+qSldXCzVQwtc= -smart-buffer@^1.0.13: - version "1.1.15" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" - -sntp@0.2.x: - version "0.2.4" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-0.2.4.tgz#fb885f18b0f3aad189f824862536bceeec750900" - dependencies: - hoek "0.9.x" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" +source-map-support@^0.5.1: + version "0.5.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" + integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ== dependencies: - hoek "2.x.x" + buffer-from "^1.0.0" + source-map "^0.6.0" -socks-proxy-agent@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.0.0.tgz#c674842d70410fb28ae1e92e6135a927854bc275" - dependencies: - agent-base "2" - extend "3" - socks "~1.1.5" +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -socks@~1.1.5: - version "1.1.10" - resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" - dependencies: - ip "^1.1.4" - smart-buffer "^1.0.13" +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map-support@^0.4.2: - version "0.4.8" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.8.tgz#4871918d8a3af07289182e974e32844327b2e98b" +space-separated-tokens@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz#e95ab9d19ae841e200808cd96bc7bd0adbbb3412" + integrity sha512-G3jprCEw+xFEs0ORweLmblJ3XLymGGr6hxZYTYZjIlvDti9vOBUjRQa1Rzjt012aRrocKstHwdNi+F7HguPsEA== dependencies: - source-map "^0.5.3" + trim "0.0.1" -source-map@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== dependencies: - amdefine ">=0.0.4" + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== dependencies: - amdefine ">=0.0.4" + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" + integrity sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g== speakingurl@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-9.0.0.tgz#a99494041627f31d6d14c38b571ffa3a2ec95bda" + integrity sha1-qZSUBBYn8x1tFMOLVx/6Oi7JW9o= + +sprintf-js@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== -sprintf-js@^1.0.3, sprintf-js@~1.0.2: +sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +srcset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef" + integrity sha1-pWad4StC87HV6D7QPHEEb8SPQe8= + dependencies: + array-uniq "^1.0.2" + number-is-nan "^1.0.0" sshpk@^1.7.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.1.tgz#30e1a5d329244974a1af61511339d595af6638b0" + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" + getpass "^0.1.1" jsbn "~0.1.0" + safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stable@~0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" +state-toggle@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" + integrity sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og== -"statuses@>= 1.3.1 < 2", statuses@~1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== dependencies: inherits "~2.0.1" readable-stream "^2.0.2" -stream-to-buffer@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9" +stream-http@^2.8.0: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== dependencies: - stream-to "~0.2.0" + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" -stream-to@~0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d" +stream-length@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-length/-/stream-length-1.0.2.tgz#8277f3cbee49a4daabcfdb4e2f4a9b5e9f2c9f00" + integrity sha1-gnfzy+5JpNqrz9tOL0qbXp8snwA= + dependencies: + bluebird "^2.6.2" + +stream-line-wrapper@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/stream-line-wrapper/-/stream-line-wrapper-0.1.1.tgz#3e2be1d368c6356f90aeef64786683f3eee3eea7" + integrity sha1-Pivh02jGNW+Qru9keGaD8+7j7qc= + dependencies: + async "~0.2.10" strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" +"string-width@^1.0.2 || 2", string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" - -string_decoder@^0.10.31, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -stringmap@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" + strip-ansi "^4.0.0" -stringset@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" +string_decoder@^1.1.0, string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" -stringstream@~0.0.4: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +stripe@^4.23.1: + version "4.25.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-4.25.0.tgz#16af99c255e4fe22adbaf629f392af0715370760" + integrity sha512-sSRPSQ4BTSbdcevVSrtIJzlOCTIAXm8T38DE4zPL6ysYpIWGfIBdo2XnhouLK12/6cuLvaEInlfCZQgoEVzXpQ== + dependencies: + bluebird "^2.10.2" + lodash.isplainobject "^4.0.6" + object-assign "^4.1.0" + qs "~6.0.4" styled-components@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.1.2.tgz#bb419978e1287c5d0d88fa9106b2dd75f66a324c" + version "2.4.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.4.1.tgz#663bd0485d4b6ab46f946210dc03d2398d1ade74" + integrity sha1-ZjvQSF1LarRvlGIQ3APSOY0a3nQ= dependencies: buffer "^5.0.3" css-to-react-native "^2.0.3" fbjs "^0.8.9" hoist-non-react-statics "^1.2.0" - is-function "^1.0.1" is-plain-object "^2.0.1" prop-types "^15.5.4" - stylis "^3.2.1" + stylis "^3.4.0" supports-color "^3.2.3" -stylis@^3.2.1: - version "3.2.13" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.2.13.tgz#1a5d2ff5ab09f362d6d8065186d526740e9f4f24" - -superagent-proxy@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/superagent-proxy/-/superagent-proxy-1.0.2.tgz#92d3660578f618ed43a82cf8cac799fe2938ba2d" - dependencies: - debug "2" - proxy-agent "2" +stylis@^3.4.0: + version "3.5.4" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" + integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== superagent-retry@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/superagent-retry/-/superagent-retry-0.6.0.tgz#e49b35ca96c0e3b1d0e3f49605136df0e0a028b7" + integrity sha1-5Js1ypbA47HQ4/SWBRNt8OCgKLc= -superagent@^3.0.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.4.1.tgz#4bd12741224d0ece6d9f757f1c3becbe7f24c115" +superagent@^3.5.0: + version "3.8.3" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" + integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== dependencies: component-emitter "^1.2.0" - cookiejar "^2.0.6" - debug "^2.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" extend "^3.0.0" - form-data "^2.1.1" - formidable "^1.0.17" + form-data "^2.3.1" + formidable "^1.2.0" methods "^1.1.1" - mime "^1.3.4" - qs "^6.1.0" - readable-stream "^2.0.5" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.3.5" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" - dependencies: - has-flag "^1.0.0" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= dependencies: has-flag "^1.0.0" -symbol-observable@^1.0.2, symbol-observable@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" +supports-color@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s= + dependencies: + has-flag "^2.0.0" -"symbol-tree@>= 3.1.0 < 4.0.0": - version "3.2.2" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== table@^3.7.8: version "3.8.3" resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= dependencies: ajv "^4.7.0" ajv-keywords "^1.0.0" @@ -3946,356 +4734,469 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" -templayed@*: - version "0.2.3" - resolved "https://registry.yarnpkg.com/templayed/-/templayed-0.2.3.tgz#4706df625bc6aecd86b7c9f6b0fb548b95cdf769" +tar@^4: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.4" + minizlib "^1.1.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -through@^2.3.6, through@~2.3.8: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -throwback@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/throwback/-/throwback-1.1.1.tgz#f007e7c17604a6d16d7a07c41aa0e8fedc6184a4" - dependencies: - any-promise "^1.3.0" - -thunkify@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= timers-browserify@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= dependencies: process "~0.11.0" -timers-ext@0.1: - version "0.1.0" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.0.tgz#00345a2ca93089d1251322054389d263e27b77e2" - dependencies: - es5-ext "~0.10.2" - next-tick "~0.2.2" - -title-case-minors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/title-case-minors/-/title-case-minors-1.0.0.tgz#51f17037c294747a1d1cda424b5004c86d8eb115" - -to-capital-case@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-capital-case/-/to-capital-case-1.0.0.tgz#a57c5014fd5a37217cf05099ff8a421bbf9c9b7f" - dependencies: - to-space-case "^1.0.0" - -to-fast-properties@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" - -to-no-case@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" - -to-sentence-case@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-sentence-case/-/to-sentence-case-1.0.0.tgz#c483bf3647737e5c738ef7006fe360d5f99c572e" - dependencies: - to-no-case "^1.0.0" +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== -to-space-case@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17" - dependencies: - to-no-case "^1.0.0" +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= -to-title-case@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-title-case/-/to-title-case-1.0.0.tgz#aca88f89d6064de50108a97cea0db44827e80061" - dependencies: - escape-regexp-component "^1.0.2" - title-case-minors "^1.0.0" - to-capital-case "^1.0.0" - to-sentence-case "^1.0.0" +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= -tough-cookie@>=0.12.0, tough-cookie@^2.0.0, tough-cookie@^2.2.0, tough-cookie@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: + psl "^1.1.24" punycode "^1.4.1" -tr46@~0.0.1: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - tracker-component@^1.3.14: version "1.3.21" resolved "https://registry.yarnpkg.com/tracker-component/-/tracker-component-1.3.21.tgz#4585dfccf9fce4ef0eee31babc9e4d190ea0ceaa" + integrity sha1-RYXfzPn85O8O7jG6vJ5NGQ6gzqo= dependencies: babel-runtime ">=0.14.7 || ^15.0.0-rc.2" react ">=0.14.7 || ^15.0.0-rc.2" -tryit@^1.0.1: +trim-trailing-lines@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" + integrity sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + +trough@^1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" + integrity sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw== -tryor@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" +tslib@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" -tunnel-agent@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.3.0.tgz#ad681b68f5321ad2827c4cfb1b7d5df2cfe942ee" - -tunnel-agent@~0.4.1: - version "0.4.3" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" -type-is@~1.6.13: - version "1.6.14" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" +type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== dependencies: media-typer "0.3.0" - mime-types "~2.1.13" + mime-types "~2.1.18" -typed-graphql@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-graphql/-/typed-graphql-1.0.2.tgz#4c0f788775d552df4d4ec3d73f25469252f40fb8" +typed-styles@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.5.tgz#a60df245d482a9b1adf9c06c078d0f06085ed1cf" + integrity sha512-ht+rEe5UsdEBAa3gr64+QjUOqjOLJfWLvl5HZR5Ev9uo/OnD3p43wPeFSB1hNFc13GXQF/JU1Bn0YHLUqBRIlw== + +typed-styles@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" + integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -ua-parser-js@^0.7.9: - version "0.7.12" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" +ua-parser-js@^0.7.18: + version "0.7.19" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b" + integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ== -uglify-js@^2.4.1, uglify-js@^2.6: - version "2.7.5" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" +uglify-js@^3.1.4: + version "3.4.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" + integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== dependencies: - async "~0.2.6" - source-map "~0.5.1" - uglify-to-browserify "~1.0.0" - yargs "~3.10.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + commander "~2.17.1" + source-map "~0.6.1" -uncontrollable@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.0.3.tgz#06ec76cb9e02914756085d9cea0354fc746b09b4" +uncontrollable@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-6.0.0.tgz#d39a3d5334802a862a0ef4a6e4ba0d19b8ddec4d" + integrity sha512-gmy2ESW40LGbijSbW5piBGiPv55IgyDbjQcMr7LkDR5icpw/06UgMqULAGDBAcFn2a9d/SRPgcb3oo8hdEUfIw== dependencies: - invariant "^2.1.0" + invariant "^2.2.4" underscore.string@^3.2.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" + version "3.3.5" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" + integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== dependencies: sprintf-js "^1.0.3" util-deprecate "^1.0.2" -underscore@1.8.3, underscore@^1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +underscore@^1.8.3: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +unherit@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" + integrity sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g== + dependencies: + inherits "^2.0.1" + xtend "^4.0.1" + +unified@^6.1.5: + version "6.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" + integrity sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^2.0.0" + x-is-string "^0.1.0" + +unist-util-is@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" + integrity sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw== + +unist-util-remove-position@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" + integrity sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q== + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== + +unist-util-visit-parents@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz#f6e3afee8bdbf961c0e6f028ea3c0480028c3d06" + integrity sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q== + +unist-util-visit-parents@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" + integrity sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA== + dependencies: + unist-util-is "^2.1.2" + +unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.0.tgz#1cb763647186dc26f5e1df5db6bd1e48b3cc2fb1" + integrity sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw== + dependencies: + unist-util-visit-parents "^2.0.0" + +universal-cookie-express@^2.1.5: + version "2.2.0" + resolved "https://registry.yarnpkg.com/universal-cookie-express/-/universal-cookie-express-2.2.0.tgz#83284b5fa06ce2f31d049df8e7b19e6a04c0fe79" + integrity sha512-wk+YPjuV6rMRWLRCIc9W+o0GPX+pB/KwLfpu+CwATMf/3fyLhJzTmDJiuZivwmObO4h1TvVYlz7frj7F4XLKGA== + dependencies: + universal-cookie "^2.2.0" + +universal-cookie@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-2.2.0.tgz#328dc3883cf0ebd5b4904902033ad4dfdb907ed2" + integrity sha512-weEuvQvnMs74WGwqKSbeapCRv/7ATHDCqrfZtj579r3fQCxcwtoXYMoaZ8YOCQga/ctQTYy20xMOH1u7cAAU6w== + dependencies: + cookie "^0.3.1" + object-assign "^4.1.0" unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= dependencies: punycode "1.3.2" querystring "0.2.0" -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= dependencies: os-homedir "^1.0.0" -util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@0.10.3, util@^0.10.3: +util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= dependencies: inherits "2.0.1" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== + dependencies: + inherits "2.0.3" -uuid@^3.0.0, uuid@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -vary@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" +uuid@^3.1.0, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +valid-data-url@^0.1.4: + version "0.1.6" + resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-0.1.6.tgz#d8dffab9b0ba1a0239580f71377386e75df340a7" + integrity sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" +vfile-location@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.4.tgz#2a5e7297dd0d9e2da4381464d04acc6b834d3e55" + integrity sha512-KRL5uXQPoUKu+NGvQVL4XLORw45W62v4U4gxJ3vRlDfI9QsT4ZN1PNXn/zQpKUulqGDpYuT0XDfp5q9O87/y/w== + +vfile-message@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1" + integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" + integrity sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w== dependencies: - extsprintf "1.0.2" + is-buffer "^1.1.4" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= dependencies: indexof "0.0.1" warning@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" + integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= dependencies: loose-envify "^1.0.0" -web-resource-inliner@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-1.2.1.tgz#c02332ad985ed00da4c2310c0699fb2e5ba9675f" - dependencies: - async "^0.9.0" - clean-css "1.1.7" - cli-color "^0.3.2" - datauri "~0.2.0" - lodash "^3.10.1" - request "^2.49.0" - uglify-js "^2.4.1" - xtend "^4.0.0" +warning@^4.0.1, warning@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607" + integrity sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug== + dependencies: + loose-envify "^1.0.0" -webidl-conversions@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" +web-resource-inliner@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-4.2.1.tgz#a3ec33d85794675cb526cfb93d2115741869f8be" + integrity sha512-fOWnBQHVX8zHvEbECDTxtYL0FXIIZZ5H3LWoez8mGopYJK7inEru1kVMDzM1lVdeJBNEqUnNP5FBGxvzuMcwwQ== + dependencies: + async "^2.1.2" + chalk "^1.1.3" + datauri "^1.0.4" + htmlparser2 "^3.9.2" + lodash.unescape "^4.0.1" + request "^2.78.0" + valid-data-url "^0.1.4" + xtend "^4.0.0" webpack-require-weak@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/webpack-require-weak/-/webpack-require-weak-1.0.1.tgz#a6a8e60871bebbe5b085a915ab0af633a412433f" + integrity sha1-pqjmCHG+u+WwhakVqwr2M6QSQz8= dependencies: is-webpack-bundle "^1.0.0" -whatwg-fetch@>=0.10.0, whatwg-fetch@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.1.tgz#078b9461bbe91cea73cbce8bb122a05f9e92b772" - -whatwg-url-compat@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" - dependencies: - tr46 "~0.0.1" +whatwg-fetch@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" + integrity sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ= -which@^1.2.8: - version "1.2.12" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" - dependencies: - isexe "^1.1.1" +whatwg-fetch@>=0.10.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +whatwg-fetch@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== -window-size@^0.1.2: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= dependencies: mkdirp "^0.5.1" -"xml-name-validator@>= 2.0.1 < 3.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= xml@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - -xtend@^4.0.0: +xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= -y18n@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" +zen-observable-ts@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.4.4.tgz#c244c71eaebef79a985ccf9895bc90307a6e9712" + integrity sha512-SNVY1sWWhoe7FwFmHpD9ERi+7Mhhj3+JdS0BGy2UxLIg7cY+3zQbyZauQCI6DN6YK4uoKNaIm3S7Qkqi1Lr+Fw== -yargs@~3.27.0: - version "3.27.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" +zen-observable-ts@^0.8.15: + version "0.8.15" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.15.tgz#6cf7df6aa619076e4af2f707ccf8a6290d26699b" + integrity sha512-sXKPWiw6JszNEkRv5dQ+lQCttyjHM2Iks74QU5NP8mMPS/NrzTlHDr780gf/wOBqmHkPO6NCLMlsa+fAQ8VE8w== dependencies: - camelcase "^1.2.1" - cliui "^2.1.0" - decamelize "^1.0.0" - os-locale "^1.4.0" - window-size "^0.1.2" - y18n "^3.2.0" + zen-observable "^0.8.0" -zen-observable-ts@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.4.0.tgz#a74bc9fe59747948a577bd513d438e70fcfae7e2" +zen-observable@^0.8.0: + version "0.8.13" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.13.tgz#a9f1b9dbdfd2d60a08761ceac6a861427d44ae2e" + integrity sha512-fa+6aDUVvavYsefZw0zaZ/v3ckEtMgCFi30sn91SEZea4y/6jQp05E3omjkX91zV6RVdn15fqnFZ6RKjRGbp2g==