Merge remote-tracking branch 'origin/master' into storybook

This commit is contained in:
Robert Schäfer 2019-08-01 00:08:15 +02:00
commit 34f86c95a0
52 changed files with 18014 additions and 877 deletions

View File

@ -28,6 +28,7 @@
* [HTTPS](deployment/digital-ocean/https/README.md)
* [Human Connection](deployment/human-connection/README.md)
* [Mailserver](deployment/human-connection/mailserver/README.md)
* [Maintenance](deployment/human-connection/maintenance/README.md)
* [Volumes](deployment/volumes/README.md)
* [Neo4J Offline-Backups](deployment/volumes/neo4j-offline-backup/README.md)
* [Volume Snapshots](deployment/volumes/volume-snapshots/README.md)

View File

@ -1,4 +1,4 @@
FROM node:12.6-alpine as base
FROM node:12.7-alpine as base
LABEL Description="Backend of the Social Network Human-Connection.org" Vendor="Human Connection gGmbH" Version="0.0.1" Maintainer="Human Connection gGmbH (developer@human-connection.org)"
EXPOSE 4000

View File

@ -9,6 +9,7 @@
"dev": "nodemon --exec babel-node src/ -e js,gql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,gql",
"lint": "eslint src --config .eslintrc.js",
"jest": "jest --forceExit --detectOpenHandles --runInBand",
"test": "run-s test:jest test:cucumber",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null",
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null",
@ -47,13 +48,13 @@
"apollo-client": "~2.6.3",
"apollo-link-context": "~1.0.18",
"apollo-link-http": "~1.5.15",
"apollo-server": "~2.7.0",
"apollo-server-express": "^2.6.9",
"apollo-server": "~2.8.0",
"apollo-server-express": "^2.7.2",
"bcryptjs": "~2.4.3",
"cheerio": "~1.0.0-rc.3",
"cors": "~2.8.5",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-beta.1",
"date-fns": "2.0.0-beta.3",
"debug": "~4.1.1",
"dotenv": "~8.0.0",
"express": "^4.17.1",
@ -61,14 +62,30 @@
"graphql": "~14.4.2",
"graphql-custom-directives": "~0.2.14",
"graphql-iso-date": "~3.6.1",
"graphql-middleware": "~3.0.2",
"graphql-middleware": "~3.0.3",
"graphql-shield": "~6.0.4",
"graphql-tag": "~2.10.1",
"helmet": "~3.19.0",
"helmet": "~3.20.0",
"jsonwebtoken": "~8.5.1",
"linkifyjs": "~2.1.8",
"lodash": "~4.17.14",
"merge-graphql-schemas": "^1.5.8",
"merge-graphql-schemas": "^1.6.1",
"metascraper": "^4.10.3",
"metascraper-audio": "^5.5.0",
"metascraper-author": "^5.6.3",
"metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.6.3",
"metascraper-description": "^5.5.0",
"metascraper-image": "^5.6.3",
"metascraper-lang": "^5.6.3",
"metascraper-lang-detector": "^4.8.5",
"metascraper-logo": "^5.5.0",
"metascraper-publisher": "^5.6.3",
"metascraper-soundcloud": "^5.5.3",
"metascraper-title": "^5.6.3",
"metascraper-url": "^5.5.0",
"metascraper-video": "^5.6.3",
"metascraper-youtube": "^4.8.5",
"neo4j-driver": "~1.7.4",
"neo4j-graphql-js": "^2.6.3",
"neode": "^0.2.16",
@ -83,23 +100,23 @@
"wait-on": "~3.3.0"
},
"devDependencies": {
"@babel/cli": "~7.5.0",
"@babel/core": "~7.5.4",
"@babel/cli": "~7.5.5",
"@babel/core": "~7.5.5",
"@babel/node": "~7.5.5",
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
"@babel/preset-env": "~7.5.5",
"@babel/register": "~7.5.5",
"apollo-server-testing": "~2.7.0",
"apollo-server-testing": "~2.8.0",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.2",
"babel-jest": "~24.8.0",
"chai": "~4.2.0",
"cucumber": "~5.1.0",
"eslint": "~6.0.1",
"eslint": "~6.1.0",
"eslint-config-prettier": "~6.0.0",
"eslint-config-standard": "~12.0.0",
"eslint-plugin-import": "~2.18.0",
"eslint-plugin-jest": "~22.11.1",
"eslint-plugin-import": "~2.18.2",
"eslint-plugin-jest": "~22.14.0",
"eslint-plugin-node": "~9.1.0",
"eslint-plugin-prettier": "~3.1.0",
"eslint-plugin-promise": "~4.2.1",
@ -110,4 +127,4 @@
"prettier": "~1.18.2",
"supertest": "~4.0.2"
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -52,10 +52,12 @@ export default schema => {
if (CONFIG.DISABLED_MIDDLEWARES) {
const disabledMiddlewares = CONFIG.DISABLED_MIDDLEWARES.split(',')
order = order.filter(key => {
if (disabledMiddlewares.includes(key)) {
/* eslint-disable-next-line no-console */
console.log(`Warning: Disabled "${disabledMiddlewares}" middleware.`)
}
return !disabledMiddlewares.includes(key)
})
/* eslint-disable-next-line no-console */
console.log(`Warning: "${disabledMiddlewares}" middlewares have been disabled.`)
}
const appliedMiddlewares = order.map(key => middlewares[key])

View File

@ -136,6 +136,7 @@ const permissions = shield(
Query: {
'*': deny,
findPosts: allow,
embed: allow,
Category: allow,
Tag: allow,
Report: isModerator,
@ -162,6 +163,7 @@ const permissions = shield(
DeletePost: isAuthor,
report: isAuthenticated,
CreateSocialMedia: isAuthenticated,
UpdateSocialMedia: isAuthenticated,
DeleteSocialMedia: isAuthenticated,
// AddBadgeRewarded: isAdmin,
// RemoveBadgeRewarded: isAdmin,

View File

@ -0,0 +1,9 @@
export const undefinedToNull = list => {
const resolvers = {}
list.forEach(key => {
resolvers[key] = async (parent, params, context, resolveInfo) => {
return typeof parent[key] === 'undefined' ? null : parent[key]
}
})
return resolvers
}

View File

@ -0,0 +1,29 @@
import scrape from './embeds/scraper.js'
import { undefinedToNull } from '../helpers'
export default {
Query: {
embed: async (object, { url }, context, resolveInfo) => {
return scrape(url)
},
},
Embed: {
...undefinedToNull([
'type',
'title',
'author',
'publisher',
'date',
'description',
'url',
'image',
'audio',
'video',
'lang',
'html',
]),
sources: async (parent, params, context, resolveInfo) => {
return typeof parent.sources === 'undefined' ? [] : parent.sources
},
},
}

View File

@ -0,0 +1,216 @@
import fetch from 'node-fetch'
import fs from 'fs'
import path from 'path'
import { createTestClient } from 'apollo-server-testing'
import createServer from '../../server'
import { gql } from '../../jest/helpers'
jest.mock('node-fetch')
const { Response } = jest.requireActual('node-fetch')
afterEach(() => {
fetch.mockRestore()
})
let variables = {}
const HumanConnectionOrg = fs.readFileSync(
path.join(__dirname, '../../jest/snapshots/embeds/HumanConnectionOrg.html'),
'utf8',
)
const pr960 = fs.readFileSync(
path.join(__dirname, '../../jest/snapshots/embeds/pr960.html'),
'utf8',
)
const babyLovesCat = fs.readFileSync(
path.join(__dirname, '../../jest/snapshots/embeds/babyLovesCat.html'),
'utf8',
)
const babyLovesCatEmbedResponse = new Response(
JSON.stringify({
height: 270,
provider_name: 'YouTube',
title: 'Baby Loves Cat',
type: 'video',
width: 480,
thumbnail_height: 360,
provider_url: 'https://www.youtube.com/',
thumbnail_width: 480,
html:
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
thumbnail_url: 'https://i.ytimg.com/vi/qkdXAtO40Fo/hqdefault.jpg',
version: '1.0',
author_name: 'Merkley Family',
author_url: 'https://www.youtube.com/channel/UC5P8yei950tif7UmdPpkJLQ',
}),
)
describe('Query', () => {
describe('embed', () => {
let embedAction
beforeEach(() => {
embedAction = async variables => {
const { server } = createServer({
context: () => {},
})
const { query } = createTestClient(server)
const embed = gql`
query($url: String!) {
embed(url: $url) {
type
title
author
publisher
date
description
url
image
audio
video
lang
sources
html
}
}
`
return query({ query: embed, variables })
}
})
describe('given a video link', () => {
beforeEach(() => {
fetch
.mockReturnValueOnce(Promise.resolve(new Response('')))
.mockReturnValueOnce(Promise.resolve(JSON.stringify({})))
variables = { url: 'https://www.w3schools.com/html/mov_bbb.mp4' }
})
it('shows some default data', async () => {
const expected = expect.objectContaining({
data: {
embed: {
audio: null,
author: null,
date: null,
description: null,
html: null,
image: null,
lang: null,
publisher: null,
sources: ['resource'],
title: null,
type: 'link',
url: 'https://www.w3schools.com/html/mov_bbb.mp4',
video: null,
},
},
})
await expect(embedAction(variables)).resolves.toEqual(expected)
})
})
describe('given a Facebook link', () => {
beforeEach(() => {
fetch
.mockReturnValueOnce(Promise.resolve(new Response(HumanConnectionOrg)))
.mockReturnValueOnce(Promise.resolve('invalid json'))
variables = { url: 'https://www.facebook.com/HumanConnectionOrg/' }
})
it('does not crash if embed provider returns invalid JSON', async () => {
const expected = expect.objectContaining({
data: {
embed: {
audio: null,
author: null,
date: expect.any(String),
description:
'Human Connection, Weilheim an der Teck. Gefällt 24.407 Mal. An upcoming non-profit social network focused on local and global positive change. Twitter accounts : @hc_world (EN), @hc_deutschland (GE),...',
html: null,
image:
'https://scontent.ftxl3-1.fna.fbcdn.net/v/t1.0-1/c5.0.200.200a/p200x200/12108307_997373093648222_70057205881020137_n.jpg?_nc_cat=110&_nc_oc=AQnPPYQlR0dU556gOfl4xkXr7IPZdRIAUfQeXl3fpUv4DAsFN8T4PfgOjPwuq85GPKGZ5S5E5mWQ8IVV1UiRBAIZ&_nc_ht=scontent.ftxl3-1.fna&oh=90309adddaab38839782f16e7d4b7bcf&oe=5DEEDFE5',
lang: 'de',
publisher: 'Facebook',
sources: ['resource'],
title: 'Human Connection',
type: 'link',
url: 'https://www.facebook.com/HumanConnectionOrg/',
video: null,
},
},
})
await expect(embedAction(variables)).resolves.toEqual(expected)
})
})
describe('given a Github link', () => {
beforeEach(() => {
fetch
.mockReturnValueOnce(Promise.resolve(new Response(pr960)))
.mockReturnValueOnce(Promise.resolve(JSON.stringify({})))
variables = { url: 'https://github.com/Human-Connection/Human-Connection/pull/960' }
})
it('returns meta data even if no embed html can be retrieved', async () => {
const expected = expect.objectContaining({
data: {
embed: {
type: 'link',
title:
'Editor embeds merge in nitro embed by mattwr18 · Pull Request #960 · Human-Connection/Human-Connection',
author: 'Human-Connection',
publisher: 'GitHub',
date: expect.any(String),
description: '🍰 Pullrequest Issues fixes #256',
url: 'https://github.com/Human-Connection/Human-Connection/pull/960',
image:
'https://repository-images.githubusercontent.com/112590397/52c9a000-7e11-11e9-899d-aaa55f3a3d72',
audio: null,
video: null,
lang: 'en',
sources: ['resource'],
html: null,
},
},
})
await expect(embedAction(variables)).resolves.toEqual(expected)
})
})
describe('given a youtube link', () => {
beforeEach(() => {
fetch
.mockReturnValueOnce(Promise.resolve(new Response(babyLovesCat)))
.mockReturnValueOnce(Promise.resolve(babyLovesCatEmbedResponse))
variables = { url: 'https://www.youtube.com/watch?v=qkdXAtO40Fo&t=18s' }
})
it('returns meta data plus youtube iframe html', async () => {
const expected = expect.objectContaining({
data: {
embed: {
type: 'video',
title: 'Baby Loves Cat',
author: 'Merkley Family',
publisher: 'YouTube',
date: expect.any(String),
description:
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al...',
url: 'https://www.youtube.com/watch?v=qkdXAtO40Fo',
image: 'https://i.ytimg.com/vi/qkdXAtO40Fo/maxresdefault.jpg',
audio: null,
video: null,
lang: 'de',
sources: ['resource', 'oembed'],
html:
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
},
},
})
await expect(embedAction(variables)).resolves.toEqual(expected)
})
})
})
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
import Metascraper from 'metascraper'
import fetch from 'node-fetch'
import fs from 'fs'
import path from 'path'
import { ApolloError } from 'apollo-server'
import isEmpty from 'lodash/isEmpty'
import isArray from 'lodash/isArray'
import mergeWith from 'lodash/mergeWith'
const error = require('debug')('embed:error')
const metascraper = Metascraper([
require('metascraper-author')(),
require('metascraper-date')(),
require('metascraper-description')(),
require('metascraper-image')(),
require('metascraper-lang')(),
require('metascraper-lang-detector')(),
require('metascraper-logo')(),
// require('metascraper-clearbit-logo')(),
require('metascraper-publisher')(),
require('metascraper-title')(),
require('metascraper-url')(),
require('metascraper-audio')(),
require('metascraper-soundcloud')(),
require('metascraper-video')(),
require('metascraper-youtube')(),
// require('./rules/metascraper-embed')()
])
let oEmbedProvidersFile = fs.readFileSync(path.join(__dirname, './providers.json'), 'utf8')
// some providers allow a format parameter
// we need JSON
oEmbedProvidersFile = oEmbedProvidersFile.replace('{format}', 'json')
const oEmbedProviders = JSON.parse(oEmbedProvidersFile)
const fetchEmbed = async url => {
const provider = oEmbedProviders.find(provider => {
return provider.provider_url.includes(url.hostname)
})
if (!provider) return {}
const {
endpoints: [endpoint],
} = provider
const endpointUrl = new URL(endpoint.url)
endpointUrl.searchParams.append('url', url.href)
endpointUrl.searchParams.append('format', 'json')
let json
try {
const response = await fetch(endpointUrl)
json = await response.json()
} catch (err) {
error(`Error fetching embed data: ${err.message}`)
return {}
}
return {
type: json.type,
html: json.html,
author: json.author_name,
date: json.upload_date,
sources: ['oembed'],
}
}
const fetchResource = async url => {
const response = await fetch(url)
const html = await response.text()
const resource = await metascraper({ html, url: url.href })
return {
sources: ['resource'],
...resource,
}
}
export default async function scrape(url) {
url = new URL(url)
if (url.hostname === 'youtu.be') {
// replace youtu.be to get proper results
url.hostname = 'youtube.com'
}
const [meta, embed] = await Promise.all([fetchResource(url), fetchEmbed(url)])
const output = mergeWith(meta, embed, (objValue, srcValue) => {
if (isArray(objValue)) {
return objValue.concat(srcValue)
}
})
if (isEmpty(output)) {
throw new ApolloError('Not found', 'NOT_FOUND')
}
return {
type: 'link',
...output,
}
}

View File

@ -3,14 +3,11 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
export default {
Mutation: {
CreateSocialMedia: async (object, params, context, resolveInfo) => {
/**
* TODO?: Creates double Nodes!
*/
const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false)
const session = context.driver.session()
await session.run(
`MATCH (owner:User {id: $userId}), (socialMedia:SocialMedia {id: $socialMediaId})
MERGE (socialMedia)<-[:OWNED]-(owner)
MERGE (socialMedia)<-[:OWNED]-(owner)
RETURN owner`,
{
userId: context.user.id,
@ -26,5 +23,21 @@ export default {
return socialMedia
},
UpdateSocialMedia: async (object, params, context, resolveInfo) => {
const session = context.driver.session()
await session.run(
`MATCH (owner: User { id: $userId })-[:OWNED]->(socialMedia: SocialMedia { id: $socialMediaId })
SET socialMedia.url = $socialMediaUrl
RETURN owner`,
{
userId: context.user.id,
socialMediaId: params.id,
socialMediaUrl: params.url,
},
)
session.close()
return params
},
},
}

View File

@ -5,9 +5,26 @@ import { host, login, gql } from '../../jest/helpers'
const factory = Factory()
describe('SocialMedia', () => {
let client
let headers
const mutationC = gql`
let client, headers, variables, mutation
const ownerParams = {
email: 'owner@example.com',
password: '1234',
id: '1234',
name: 'Pippi Langstrumpf',
}
const userParams = {
email: 'someuser@example.com',
password: 'abcd',
id: 'abcd',
name: 'Kalle Blomqvist',
}
const url = 'https://twitter.com/pippi-langstrumpf'
const newUrl = 'https://twitter.com/bullerby'
const createSocialMediaMutation = gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
@ -15,7 +32,15 @@ describe('SocialMedia', () => {
}
}
`
const mutationD = gql`
const updateSocialMediaMutation = gql`
mutation($id: ID!, $url: String!) {
UpdateSocialMedia(id: $id, url: $url) {
id
url
}
}
`
const deleteSocialMediaMutation = gql`
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
id
@ -24,92 +49,139 @@ describe('SocialMedia', () => {
}
`
beforeEach(async () => {
await factory.create('User', {
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
name: 'Matilde Hermiston',
slug: 'matilde-hermiston',
role: 'user',
email: 'test@example.org',
password: '1234',
})
await factory.create('User', userParams)
await factory.create('User', ownerParams)
})
afterEach(async () => {
await factory.cleanDatabase()
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
const variables = {
url: 'http://nsosp.org',
}
await expect(client.request(mutationC, variables)).rejects.toThrow('Not Authorised')
describe('create social media', () => {
beforeEach(() => {
variables = { url }
mutation = createSocialMediaMutation
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
})
})
describe('authenticated', () => {
beforeEach(async () => {
headers = await login(userParams)
client = new GraphQLClient(host, { headers })
})
it('creates social media with correct URL', async () => {
await expect(client.request(mutation, variables)).resolves.toEqual(
expect.objectContaining({
CreateSocialMedia: {
id: expect.any(String),
url: url,
},
}),
)
})
it('rejects empty string', async () => {
variables = { url: '' }
await expect(client.request(mutation, variables)).rejects.toThrow(
'"url" is not allowed to be empty',
)
})
it('rejects invalid URLs', async () => {
variables = { url: 'not-a-url' }
await expect(client.request(createSocialMediaMutation, variables)).rejects.toThrow(
'"url" must be a valid uri',
)
})
})
})
describe('authenticated', () => {
describe('update social media', () => {
beforeEach(async () => {
headers = await login({
email: 'test@example.org',
password: '1234',
})
client = new GraphQLClient(host, {
headers,
})
})
headers = await login(ownerParams)
client = new GraphQLClient(host, { headers })
it('creates social media with correct URL', async () => {
const variables = {
url: 'http://nsosp.org',
}
await expect(client.request(mutationC, variables)).resolves.toEqual(
expect.objectContaining({
CreateSocialMedia: {
id: expect.any(String),
url: 'http://nsosp.org',
},
}),
)
})
it('deletes social media', async () => {
const creationVariables = {
url: 'http://nsosp.org',
}
const { CreateSocialMedia } = await client.request(mutationC, creationVariables)
const { CreateSocialMedia } = await client.request(createSocialMediaMutation, { url })
const { id } = CreateSocialMedia
const deletionVariables = {
id,
}
const expected = {
DeleteSocialMedia: {
id: id,
url: 'http://nsosp.org',
},
}
await expect(client.request(mutationD, deletionVariables)).resolves.toEqual(expected)
variables = { url: newUrl, id }
mutation = updateSocialMediaMutation
})
it('rejects empty string', async () => {
const variables = {
url: '',
}
await expect(client.request(mutationC, variables)).rejects.toThrow(
'"url" is not allowed to be empty',
)
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
})
})
it('validates URLs', async () => {
const variables = {
url: 'not-a-url',
}
describe('authenticated as other user', () => {
// TODO: make sure it throws an authorization error
})
await expect(client.request(mutationC, variables)).rejects.toThrow(
'"url" must be a valid uri',
)
describe('authenticated as owner', () => {
it('updates social media', async () => {
const expected = { UpdateSocialMedia: { ...variables } }
await expect(client.request(mutation, variables)).resolves.toEqual(
expect.objectContaining(expected),
)
})
describe('given a non-existent id', () => {
// TODO: make sure it throws an error
})
})
})
describe('delete social media', () => {
beforeEach(async () => {
headers = await login(ownerParams)
client = new GraphQLClient(host, { headers })
const { CreateSocialMedia } = await client.request(createSocialMediaMutation, { url })
const { id } = CreateSocialMedia
variables = { id }
mutation = deleteSocialMediaMutation
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
})
})
describe('authenticated as other user', () => {
// TODO: make sure it throws an authorization error
})
describe('authenticated as owner', () => {
beforeEach(async () => {
headers = await login(ownerParams)
client = new GraphQLClient(host, { headers })
})
it('deletes social media', async () => {
const expected = {
DeleteSocialMedia: {
id: variables.id,
url: url,
},
}
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
})
})
})
})

View File

@ -2,6 +2,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js'
import fileUpload from './fileUpload'
import { neode } from '../../bootstrap/neo4j'
import { UserInputError } from 'apollo-server'
import { undefinedToNull } from '../helpers'
const instance = neode()
@ -36,16 +37,6 @@ const count = obj => {
return resolvers
}
const undefinedToNull = list => {
const resolvers = {}
list.forEach(key => {
resolvers[key] = async (parent, params, context, resolveInfo) => {
return typeof parent[key] === 'undefined' ? null : parent[key]
}
})
return resolvers
}
export const hasMany = obj => {
const resolvers = {}
for (const [key, connection] of Object.entries(obj)) {

View File

@ -0,0 +1,19 @@
type Embed {
type: String
title: String
author: String
publisher: String
date: String
description: String
url: String
image: String
audio: String
video: String
lang: String
html: String
sources: [String]
}
type Query {
embed(url: String!): Embed
}

File diff suppressed because it is too large Load Diff

View File

@ -1,73 +1,70 @@
import { When, Then } from 'cypress-cucumber-preprocessor/steps'
When('I search for {string}', value => {
cy.get('#nav-search')
import { When, Then } from "cypress-cucumber-preprocessor/steps";
When("I search for {string}", value => {
cy.get("#nav-search")
.focus()
.type(value)
})
.type(value);
});
Then('I should have one post in the select dropdown', () => {
cy.get('.ds-select-dropdown').should($li => {
expect($li).to.have.length(1)
})
})
Then("I should have one post in the select dropdown", () => {
cy.get(".input .ds-select-dropdown").should($li => {
expect($li).to.have.length(1);
});
});
Then('I should see the following posts in the select dropdown:', table => {
Then("I should see the following posts in the select dropdown:", table => {
table.hashes().forEach(({ title }) => {
cy.get('.ds-select-dropdown').should('contain', title)
})
})
cy.get(".ds-select-dropdown").should("contain", title);
});
});
When('I type {string} and press Enter', value => {
cy.get('#nav-search')
When("I type {string} and press Enter", value => {
cy.get("#nav-search")
.focus()
.type(value)
.type('{enter}', { force: true })
})
.type("{enter}", { force: true });
});
When('I type {string} and press escape', value => {
cy.get('#nav-search')
When("I type {string} and press escape", value => {
cy.get("#nav-search")
.focus()
.type(value)
.type('{esc}')
})
.type("{esc}");
});
Then('the search field should clear', () => {
cy.get('#nav-search').should('have.text', '')
})
Then("the search field should clear", () => {
cy.get("#nav-search").should("have.text", "");
});
When('I select an entry', () => {
cy.get('.ds-select-dropdown ul li')
When("I select an entry", () => {
cy.get(".input .ds-select-dropdown ul li")
.first()
.trigger('click')
})
.trigger("click");
});
Then("I should be on the post's page", () => {
cy.location('pathname').should(
'contain',
'/post/'
)
cy.location('pathname').should(
'eq',
'/post/p1/101-essays-that-will-change-the-way-you-think'
)
})
cy.location("pathname").should("contain", "/post/");
cy.location("pathname").should(
"eq",
"/post/p1/101-essays-that-will-change-the-way-you-think"
);
});
Then(
'I should see posts with the searched-for term in the select dropdown',
"I should see posts with the searched-for term in the select dropdown",
() => {
cy.get('.ds-select-dropdown').should(
'contain',
'101 Essays that will change the way you think'
)
cy.get(".ds-select-dropdown").should(
"contain",
"101 Essays that will change the way you think"
);
}
)
);
Then(
'I should not see posts without the searched-for term in the select dropdown',
"I should not see posts without the searched-for term in the select dropdown",
() => {
cy.get('.ds-select-dropdown').should(
'not.contain',
'No searched for content'
)
cy.get(".ds-select-dropdown").should(
"not.contain",
"No searched for content"
);
}
)
);

View File

@ -79,7 +79,7 @@ Then('I should be on the {string} page', page => {
})
When('I add a social media link', () => {
cy.get("input[name='social-media']")
cy.get('input#addSocialMedia')
.type('https://freeradical.zone/peter-pan')
.get('button')
.contains('Add link')
@ -98,7 +98,7 @@ Then('the new social media link shows up on the page', () => {
Given('I have added a social media link', () => {
cy.openPage('/settings/my-social-media')
.get("input[name='social-media']")
.get('input#addSocialMedia')
.type('https://freeradical.zone/peter-pan')
.get('button')
.contains('Add link')
@ -121,3 +121,34 @@ Then('it gets deleted successfully', () => {
cy.get('.iziToast-message')
.should('contain', 'Deleted social media')
})
When('I start editing a social media link', () => {
cy.get("a[name='edit']")
.click()
})
Then('I can cancel editing', () => {
cy.get('button#cancel')
.click()
.get('input#editSocialMedia')
.should('have.length', 0)
})
When('I edit and save the link', () => {
cy.get('input#editSocialMedia')
.clear()
.type('https://freeradical.zone/tinkerbell')
.get('button')
.contains('Save')
.click()
})
Then('the new url is displayed', () => {
cy.get("a[href='https://freeradical.zone/tinkerbell']")
.should('have.length', 1)
})
Then('the old url is not displayed', () => {
cy.get("a[href='https://freeradical.zone/peter-pan']")
.should('have.length', 0)
})

View File

@ -15,7 +15,7 @@ Feature: List Social Media Accounts
Then it gets saved successfully
And the new social media link shows up on the page
Scenario: Other user's viewing my Social Media
Scenario: Other users viewing my Social Media
Given I have added a social media link
When people visit my profile page
Then they should be able to see my social media links
@ -27,3 +27,16 @@ Feature: List Social Media Accounts
Given I have added a social media link
When I delete a social media link
Then it gets deleted successfully
Scenario: Editing Social Media
Given I am on the "settings" page
And I click on the "Social media" link
Then I should be on the "/settings/my-social-media" page
Given I have added a social media link
When I start editing a social media link
Then I can cancel editing
When I start editing a social media link
And I edit and save the link
Then it gets saved successfully
And the new url is displayed
But the old url is not displayed

View File

@ -0,0 +1,3 @@
FROM nginx:alpine
COPY ./onourjourney.svg /usr/share/nginx/html/
COPY ./maintenance.html /usr/share/nginx/html/index.html

View File

@ -0,0 +1,43 @@
# Maintenance mode
> Despite our best efforts, systems sometimes require downtime for a variety of reasons.
Quote from [here](https://www.nrmitchi.com/2017/11/easy-maintenance-mode-in-kubernetes/)
We use our maintenance mode for manual database backup and restore. Also we
bring the database into maintenance mode for manual database migrations.
## Deploy the service
We prepared sample configuration, so you can simply run:
```sh
# in folder deployment/
kubectl apply -f human-connection/maintenance
```
This will fire up a maintenance service.
## Bring application into maintenance mode
Now if you want to have a controlled downtime and you want to bring your
application into maintenance mode, you can edit your global ingress server.
E.g. in file `deployment/digital-ocean/https/ingress.yaml` change the following:
```yaml
...
- host: nitro-staging.human-connection.org
http:
paths:
- path: /
backend:
# serviceName: nitro-web
serviceName: maintenance
# servicePort: 3000
servicePort: 80
```
Then run ` kubectl apply -f deployment/digital-ocean/https/ingress.yaml`. If you
want to deactivate the maintenance server, just undo the edit and apply the
configuration again.

View File

@ -0,0 +1,27 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: maintenance
namespace: human-connection
spec:
selector:
matchLabels:
human-connection.org/selector: deployment-human-connection-maintenance
template:
metadata:
labels:
human-connection.org/commit: COMMIT
human-connection.org/selector: deployment-human-connection-maintenance
name: maintenance
spec:
containers:
- name: web
env:
- name: HOST
value: 0.0.0.0
image: humanconnection/maintenance:latest
ports:
- containerPort: 80
imagePullPolicy: Always
restartPolicy: Always
terminationGracePeriodSeconds: 30

View File

@ -0,0 +1,61 @@
<head>
<style>
.body {
background-color: #f5f4f6;
}
.left-header,
.right-header {
font-family: "LatoWeb", sans-serif;
line-height: 1.3;
}
@media only screen and (min-width: 960px) {
.outer-div {
margin: 3em;
vertical-align: middle;
text-align: center;
}
.left-header {
float: left;
width: 25%;
margin-right: 1em;
}
.image {
width: 35em;
}
.right-header {
float: right;
width: 25%;
margin: 12em 1em 5em 2em;
}
.email-link {
margin-left: -5em;
}
}
@media only screen and (max-width: 960px) {
.outer-div {
margin: 3em;
vertical-align: middle;
text-align: center;
}
.image {
margin: 2em 0em;
}
}
</style>
</head>
<body class="body">
<div class="outer-div">
<h1 class="left-header">
At the moment we are doing some scheduled maintenance, please try again
later.
</h1>
<img src="./onourjourney.svg" alt="Maintenance mode image" class="image" />
<h1 class="right-header">
Any Questions or concerns, send an email to <br />
<a href="mailto:info@human-connection.org" class="email-link"
>info@human-connection.org</a
>
</h1>
</div>
</body>

View File

@ -0,0 +1,360 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Back" viewBox="0 0 600 570">
<defs>
<style>
.cls-1,.cls-12{fill:#fff;}.cls-2,.cls-4{fill:#dfeff4;}.cls-3{fill:#fddb00;}.cls-12,.cls-13,.cls-14,.cls-15,.cls-16,.cls-18,.cls-19,.cls-20,.cls-21,.cls-23,.cls-24,.cls-26,.cls-27,.cls-28,.cls-29,.cls-30,.cls-32,.cls-34,.cls-35,.cls-36,.cls-37,.cls-38,.cls-39,.cls-4,.cls-40,.cls-41,.cls-42,.cls-44,.cls-45,.cls-46,.cls-47,.cls-48,.cls-50,.cls-51,.cls-52,.cls-53,.cls-55,.cls-56,.cls-57,.cls-58,.cls-59,.cls-64,.cls-67,.cls-68,.cls-9,.cls-96,.cls-97{fill-rule:evenodd;}.cls-5{mask:url(#mask);}.cls-6,.cls-7{fill:#9dca00;}.cls-6{opacity:0.5;}.cls-14,.cls-8{fill:#471e04;}.cls-9{fill:#70a30a;}.cls-10,.cls-16{fill:#f5b800;}.cls-11,.cls-21{fill:#ff9;}.cls-13{fill:#5b8408;}.cls-15{fill:#fc0;}.cls-17,.cls-48{fill:#f1502d;}.cls-18{fill:#f6aa32;}.cls-19{fill:#cc3;}.cls-20,.cls-63{fill:#f5e68a;}.cls-22,.cls-58{fill:#9cc;}.cls-23,.cls-62{fill:#a44624;}.cls-24,.cls-43{fill:#333;}.cls-25,.cls-45{fill:#d4a15e;}.cls-26{fill:#c4d621;}.cls-27{fill:#4e2111;}.cls-28{fill:#78331a;}.cls-30{fill:#058e7f;}.cls-31{fill:#fcda9a;}.cls-32{fill:#07b09d;}.cls-33,.cls-40{fill:#d63d3d;}.cls-34{fill:#3b696a;}.cls-35{fill:#a6d0d1;}.cls-36{fill:#d48439;}.cls-37{fill:#9e5a29;}.cls-38{fill:#e44b4b;}.cls-39{fill:#b0642e;}.cls-41{fill:#f66;}.cls-42,.cls-61{fill:#f2d291;}.cls-44{fill:#fcc;}.cls-46{fill:#edba68;}.cls-47{fill:#366;}.cls-49,.cls-50{fill:#1f0f05;}.cls-51{fill:#804921;}.cls-52,.cls-65{fill:#ff6;}.cls-53{fill:#f69883;}.cls-54,.cls-55{fill:#ff1f1f;}.cls-56{fill:#aac905;}.cls-57{fill:#f5e97d;}.cls-59,.cls-60{fill:#bf3611;}.cls-64{fill:#eccf7c;}.cls-66{fill:#ff441f;}.cls-67{fill:#88b02c;}.cls-68{fill:#500d00;}.cls-69{fill:#216194;}.cls-70{fill:#017f9f;}.cls-71{fill:#76b629;}.cls-72{fill:#a53589;}.cls-73{fill:#024f9d;}.cls-74{fill:#50568c;}.cls-75{fill:#c6d200;}.cls-76{fill:#0369b2;}.cls-77{fill:#007d75;}.cls-78{fill:#008d6d;}.cls-79{fill:#008051;}.cls-80{fill:#46942b;}.cls-81{fill:#d5146e;}.cls-82{fill:#15a338;}.cls-83{fill:#0080c7;}.cls-84{fill:#0089d0;}.cls-85{fill:#e9bb00;}.cls-86{fill:#007632;}.cls-87{fill:#c51553;}.cls-88{fill:#593872;}.cls-89{fill:#f19000;}.cls-90{fill:#f9b800;}.cls-91{fill:#e43311;}.cls-92{fill:#b7c200;}.cls-93{fill:#e64e10;}.cls-94{fill:#079de1;}.cls-95{fill:#93bf1f;}.cls-96{fill:#8fdbf3;}.cls-97{fill:#c20212;}.cls-98{fill:#152e56;}
</style>
<mask id="mask" width="1087.71" height="713.35" x="-161.4" y="91.4" maskUnits="userSpaceOnUse">
<circle cx="301.72" cy="330.39" r="238.99" class="cls-1"/>
</mask>
</defs>
<circle cx="300" cy="332.01" r="238.99" class="cls-2"/>
<circle cx="438.86" cy="295.31" r="80.84" class="cls-3"/>
<path d="M538.13 168.86a24.22 24.22 0 0 0-19.13 9.39 19.58 19.58 0 0 0-19 16.1 11.59 11.59 0 1 0-1.77 23c28.22 0 62.52-1.53 93.54-1.53a8.94 8.94 0 1 0-1.8-17.7 18.13 18.13 0 0 0-29.2-13.77 24.27 24.27 0 0 0-22.64-15.49zM82.63 147.25a30.27 30.27 0 0 1 24 11.73 24.46 24.46 0 0 1 23.7 20.12 14.48 14.48 0 1 1 2.21 28.79c-35.26 0-78.12-1.92-116.88-1.92a11.17 11.17 0 1 1 2.25-22.11 22.66 22.66 0 0 1 36.48-17.21 30.33 30.33 0 0 1 28.24-19.4zM89.91 50.71a10.35 10.35 0 0 0-8.19 4 8.36 8.36 0 0 0-8.1 6.88 5 5 0 0 0-.75-.06 5 5 0 1 0 0 9.9c12.06 0 26.71-.65 40-.65a3.82 3.82 0 1 0 0-7.64 3.87 3.87 0 0 0-.77.08 7.75 7.75 0 0 0-12.47-5.88 10.37 10.37 0 0 0-9.72-6.63z" class="cls-4"/>
<g class="cls-5">
<ellipse cx="189.6" cy="640.52" class="cls-6" rx="350.99" ry="164.22"/>
<ellipse cx="254.61" cy="625.53" class="cls-7" rx="349.38" ry="127.57"/>
<ellipse cx="575.32" cy="640.52" class="cls-6" rx="350.99" ry="164.22"/>
<ellipse cx="370.48" cy="648.66" class="cls-7" rx="272.66" ry="127.57"/>
</g>
<path d="M152.94 477.67h4.13v25.81h-4.13z" class="cls-8"/>
<path d="M135.69 472a5.9 5.9 0 0 1 .8-2.44 6.45 6.45 0 0 1-2.81-5.92 5.8 5.8 0 0 1 3-4.56 6.22 6.22 0 0 1-.37-2.81 5.81 5.81 0 0 1 5.14-5.24 6.2 6.2 0 0 1-.18-2.22 5.85 5.85 0 0 1 6.74-5.22 6.39 6.39 0 0 1 2.22.75c2.55-3.23 9.66-4.25 12.46.19a6 6 0 0 1 1.52-.51 6.48 6.48 0 0 1 7.41 4.92 6.14 6.14 0 0 1 0 2.6 6.55 6.55 0 0 1 4.54 4.78 6 6 0 0 1-1.33 5.35 6.45 6.45 0 0 1 1.26 2.58 5.89 5.89 0 0 1-3.1 6.69 6.43 6.43 0 0 1 .89 2.08 5.86 5.86 0 0 1-4.6 7.19 6.15 6.15 0 0 1-2.31 0c-3.64 7-11.92 3.56-12.59.39a5.87 5.87 0 0 1-6.2 3.26 6.54 6.54 0 0 1-5.38-4.86 6.18 6.18 0 0 1-1.63 0 6.49 6.49 0 0 1-5.48-7z" class="cls-9"/>
<circle cx="152.62" cy="452.42" r="2.69" class="cls-10"/>
<circle cx="163.65" cy="456.62" r="2.69" class="cls-10"/>
<circle cx="147.37" cy="471.06" r="2.69" class="cls-10"/>
<circle cx="159.98" cy="472.64" r="2.69" class="cls-10"/>
<circle cx="154.46" cy="463.45" r="2.69" class="cls-10"/>
<circle cx="167.46" cy="466.07" r="2.69" class="cls-10"/>
<circle cx="144.88" cy="460.16" r="2.69" class="cls-10"/>
<path d="M396.3 510.23h3.08v19.27h-3.08z" class="cls-8"/>
<path d="M383.42 506a4.4 4.4 0 0 1 .6-1.82 4.82 4.82 0 0 1-2.1-4.42 4.33 4.33 0 0 1 2.24-3.41 4.64 4.64 0 0 1-.28-2.1 4.34 4.34 0 0 1 3.84-3.91 4.63 4.63 0 0 1-.14-1.66 4.37 4.37 0 0 1 5-3.89 4.77 4.77 0 0 1 1.65.56c1.9-2.41 7.21-3.17 9.31.14a4.44 4.44 0 0 1 1.14-.38 4.84 4.84 0 0 1 5.53 3.67 4.58 4.58 0 0 1 0 1.94 4.89 4.89 0 0 1 3.39 3.57 4.45 4.45 0 0 1-1 4 4.82 4.82 0 0 1 .94 1.93 4.4 4.4 0 0 1-2.31 5 4.8 4.8 0 0 1 .66 1.55 4.38 4.38 0 0 1-3.43 5.37 4.59 4.59 0 0 1-1.72 0c-2.72 5.24-8.9 2.66-9.4.29a4.38 4.38 0 0 1-4.63 2.43 4.88 4.88 0 0 1-4-3.63 4.62 4.62 0 0 1-1.22 0 4.85 4.85 0 0 1-4.07-5.23z" class="cls-9"/>
<circle cx="396.06" cy="491.37" r="2.01" class="cls-11"/>
<circle cx="404.3" cy="494.51" r="2.01" class="cls-11"/>
<circle cx="392.14" cy="505.3" r="2.01" class="cls-11"/>
<circle cx="401.56" cy="506.47" r="2.01" class="cls-11"/>
<circle cx="397.44" cy="499.61" r="2.01" class="cls-11"/>
<circle cx="407.15" cy="501.57" r="2.01" class="cls-11"/>
<circle cx="390.28" cy="497.16" r="2.01" class="cls-11"/>
<path d="M446.95 488.05h1.66v10.4h-1.66z" class="cls-8"/>
<path d="M440 485.76a2.38 2.38 0 0 1 .32-1 2.6 2.6 0 0 1-1.13-2.38 2.34 2.34 0 0 1 1.21-1.84 2.51 2.51 0 0 1-.15-1.13 2.34 2.34 0 0 1 2.07-2.11 2.5 2.5 0 0 1-.07-.9 2.36 2.36 0 0 1 2.72-2.1 2.58 2.58 0 0 1 .89.3c1-1.3 3.89-1.71 5 .08a2.4 2.4 0 0 1 .61-.2 2.61 2.61 0 0 1 3 2 2.47 2.47 0 0 1 0 1 2.64 2.64 0 0 1 1.83 1.93 2.4 2.4 0 0 1-.53 2.16 2.6 2.6 0 0 1 .51 1 2.37 2.37 0 0 1-1.25 2.7 2.59 2.59 0 0 1 .36.84 2.36 2.36 0 0 1-1.85 2.9 2.48 2.48 0 0 1-.93 0c-1.47 2.83-4.8 1.43-5.08.16a2.37 2.37 0 0 1-2.5 1.31 2.64 2.64 0 0 1-2.17-2 2.49 2.49 0 0 1-.66 0 2.62 2.62 0 0 1-2.2-2.72z" class="cls-9"/>
<circle cx="446.83" cy="477.87" r="1.09" class="cls-11"/>
<circle cx="451.27" cy="479.56" r="1.09" class="cls-11"/>
<circle cx="444.71" cy="485.39" r="1.09" class="cls-11"/>
<circle cx="449.79" cy="486.02" r="1.09" class="cls-11"/>
<circle cx="447.57" cy="482.32" r="1.09" class="cls-11"/>
<circle cx="452.81" cy="483.38" r="1.09" class="cls-11"/>
<circle cx="443.7" cy="480.99" r="1.09" class="cls-11"/>
<path d="M193.42 486.85h2.94v18.37h-2.94z" class="cls-8"/>
<path d="M181.15 482.81a4.2 4.2 0 0 1 .57-1.74 4.59 4.59 0 0 1-2-4.21 4.13 4.13 0 0 1 2.14-3.25 4.43 4.43 0 0 1-.27-2 4.14 4.14 0 0 1 3.66-3.73 4.41 4.41 0 0 1-.13-1.58 4.16 4.16 0 0 1 4.8-3.71 4.55 4.55 0 0 1 1.58.53c1.81-2.3 6.88-3 8.87.13a4.23 4.23 0 0 1 1.08-.36 4.61 4.61 0 0 1 5.27 3.5 4.37 4.37 0 0 1 0 1.85 4.66 4.66 0 0 1 3.23 3.4 4.24 4.24 0 0 1-.94 3.81 4.59 4.59 0 0 1 .9 1.84 4.19 4.19 0 0 1-2.21 4.77 4.58 4.58 0 0 1 .63 1.48 4.17 4.17 0 0 1-3.27 5.12 4.38 4.38 0 0 1-1.64 0c-2.59 5-8.48 2.53-9 .28a4.18 4.18 0 0 1-4.41 2.32 4.66 4.66 0 0 1-3.83-3.46 4.4 4.4 0 0 1-1.16 0 4.62 4.62 0 0 1-3.87-4.99z" class="cls-9"/>
<circle cx="193.2" cy="468.87" r="1.92" class="cls-10"/>
<circle cx="201.05" cy="471.86" r="1.92" class="cls-10"/>
<circle cx="189.46" cy="482.14" r="1.92" class="cls-10"/>
<circle cx="198.43" cy="483.27" r="1.92" class="cls-10"/>
<circle cx="194.51" cy="476.72" r="1.92" class="cls-10"/>
<circle cx="203.76" cy="478.59" r="1.92" class="cls-10"/>
<circle cx="187.68" cy="474.38" r="1.92" class="cls-10"/>
<path d="M532.92 91.83a17.85 17.85 0 0 0-14.13 6.92 14.43 14.43 0 0 0-14 11.87 8.67 8.67 0 0 0-1.3-.1 8.54 8.54 0 1 0 0 17.08c20.8 0 46.08-1.13 68.95-1.13a6.59 6.59 0 1 0 0-13.18 6.68 6.68 0 0 0-1.32.13 13.36 13.36 0 0 0-21.52-10.15 17.89 17.89 0 0 0-16.68-11.44z" class="cls-4"/>
<path d="M449.81 370.21a17.85 17.85 0 0 0-14.13 6.92 14.43 14.43 0 0 0-14 11.87 8.67 8.67 0 0 0-1.3-.1 8.54 8.54 0 1 0 0 17.08c20.8 0 46.08-1.13 68.95-1.13a6.59 6.59 0 1 0 0-13.18 6.68 6.68 0 0 0-1.32.13 13.36 13.36 0 0 0-21.52-10.15 17.89 17.89 0 0 0-16.68-11.44zM153 369.27a15.23 15.23 0 0 0-12.05 5.9A12.31 12.31 0 0 0 129 385.3a7.4 7.4 0 0 0-1.11-.08 7.29 7.29 0 1 0 0 14.57c17.74 0 39.31-1 58.82-1a5.62 5.62 0 1 0 0-11.24 5.7 5.7 0 0 0-1.13.11 11.4 11.4 0 0 0-18.34-8.66 15.26 15.26 0 0 0-14.24-9.73zM215.67 220.63a23.91 23.91 0 0 0-18.92 9.26A19.32 19.32 0 0 0 178 245.78a11.61 11.61 0 0 0-1.74-.13 11.44 11.44 0 1 0 0 22.87c27.85 0 61.7-1.51 92.32-1.51a8.82 8.82 0 1 0 0-17.65 8.94 8.94 0 0 0-1.77.18A17.9 17.9 0 0 0 238 235.95a24 24 0 0 0-22.33-15.32z" class="cls-12"/>
<path d="M196.6 523.67h8.37l.04-8.41-8.41 8.41z" class="cls-13"/>
<path d="M199.32 529.07H205a.81.81 0 1 1 0 1.61h-5.65v2.37h-1.61v-12.59a.81.81 0 1 1 1.61 0v5.38H205a.81.81 0 0 1 0 1.61h-5.65v1.61z" class="cls-14"/>
<path d="M204.97 533.1v-19.36l19.36-19.36 25.81 25.82v12.9h-45.17z" class="cls-15"/>
<path d="M250.14 520.2v12.9h35.85v-19.36h-23.64l6.04 6.46h-18.25z" class="cls-16"/>
<path d="M270 494.38h-45.67l25.81 25.82h18.25l-6.04-6.46h27.01L270 494.38z" class="cls-13"/>
<path d="M250.34 485.34h10.67v17.28h-10.67z" class="cls-10"/>
<path d="M247.38 480.93h16.6v4.71h-16.6z" class="cls-10"/>
<path d="M210.84 514h5.77v13.22h-5.77zM221.14 514h5.77v13.22h-5.77z" class="cls-1"/>
<path d="M233.13 517.09h5.77v16.01h-5.77z" class="cls-17"/>
<path d="M256.24 523.79h5.77v5.17h-5.77zM272.49 518.03h9.61v10.14h-9.61z" class="cls-1"/>
<path d="M272.49 522.58a.81.81 0 0 1 0-1.61h9.61a.81.81 0 0 1 0 1.61h-9.61z" class="cls-18"/>
<path d="M223.61 493.63a1.07 1.07 0 0 1 1.52 0L251 519.54a1.07 1.07 0 0 1-1.52 1.52l-25.1-25.16-28.28 28.28a1.07 1.07 0 1 1-1.52-1.52z" class="cls-19"/>
<path d="M250.14 521.27a1.07 1.07 0 0 1 0-2.15h18.25a1.07 1.07 0 0 1 0 2.15h-18.25zM262.35 514.82a1.07 1.07 0 0 1 0-2.15h27a1.07 1.07 0 0 1 0 2.15h-27z" class="cls-19"/>
<path d="M356.8 421.12c-4.28 4.22-9.41 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-20"/>
<path d="M356.8 421.12c-4.28 4.22-9.41 9.69-10.45 15a9.75 9.75 0 0 0 .73 6.08 11.44 11.44 0 0 0 5.61 2.35c7.17.6 11-4.13 11.17-9.11.2-4.44-2.23-9.59-4.85-14.11l-.81-.1z" class="cls-21"/>
<path d="M239.15 407.15c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-20"/>
<path d="M239.15 407.15c-4.28 4.22-9.4 9.69-10.45 15a9.76 9.76 0 0 0 .73 6.08 11.44 11.44 0 0 0 5.61 2.35c7.17.6 11-4.13 11.17-9.11.2-4.44-2.23-9.59-4.85-14.11l-.81-.1z" class="cls-21"/>
<path d="M332.887 408.086l44.848-153.565 3.388.99-44.847 153.565zM269.147 242.175l3.526-.168 7.648 159.797-3.525.168zM356.93 408.03l59.335-148.57 3.278 1.31-59.334 148.57zM217.413 236.783l3.494-.505 22.9 158.333-3.493.506z" class="cls-22"/>
<path d="M314.19 383.78a12 12 0 0 0 4.1 3c2.34-5.06 5.21-14.71 8-23.87l-5.25-.62a150.41 150.41 0 0 1-6.85 21.49z" class="cls-23"/>
<path d="M322.53 385.92l22.27 2.64 3.2-27.26a5 5 0 0 0-4.35-5.53l-12.4-1.47a5 5 0 0 0-5.53 4.35z" class="cls-16"/>
<path d="M320 411.45l8.78 1a19 19 0 0 1-.15 6.56l-15.36-1.82c.42-3.49 6.52-3.58 6.73-5.74z" class="cls-24"/>
<path d="M313.073 417.213l.152-1.28 15.78 1.873-.153 1.28z" class="cls-25"/>
<path d="M341.25 414l-8.78-1a19 19 0 0 0-1.39 6.42l15.36 1.82c.47-3.6-5.44-5.11-5.19-7.24z" class="cls-24"/>
<path d="M330.913 419.318l.152-1.28 15.78 1.872-.153 1.28z" class="cls-25"/>
<path d="M345.19 385.57l-22.33-2.65-3.38 28.51 10.07 1.19 2.12-24.41 3.72.44-3.65 24.23 10.07 1.19z" class="cls-13"/>
<path d="M326.46 383.6l1.13 1.25-4.54 4.37-1.13-1.25 4.54-4.37zM341.52 385.39l-1.39.95 3.39 5.31 1.39-.95-3.39-5.31z" class="cls-26"/>
<path d="M343.56 355.76v.34l-.08.2a6.28 6.28 0 0 1-12-1.43v-.55l2.88.34a3.42 3.42 0 0 0 1 1.72 3.42 3.42 0 0 0 4.09.49 3.42 3.42 0 0 0 1.34-1.45z" class="cls-12"/>
<path d="M333.27 351.79l9.07 1.08c0 .11 0 .23.06.34l-.3 2.56a4.85 4.85 0 0 1-9.28-1.1l.3-2.56z" class="cls-23"/>
<path d="M333.31 352.53l9 .34c0 .11 0 .23.06.34l-.3 2.56a4.85 4.85 0 0 1-3.91 3z" class="cls-27"/>
<path d="M320.14 363.94l5.38 1.68 3.44-11c-4.05-1.2-6.42 2.49-7.05 4.29z" class="cls-16"/>
<path d="M341.24 323.41c-3-.29-12.18-1.09-13.6 9.52s.39 14.77 1.64 16.53a11 11 0 0 0 5.42 4.54 4.44 4.44 0 0 0 2.73 1.57z" class="cls-23"/>
<path d="M341.24 323.41c3 .43 12.1 1.8 11 12.44s-3.84 14.27-5.46 15.69a11 11 0 0 1-6.32 3.09 4.44 4.44 0 0 1-3 .89z" class="cls-28"/>
<path d="M328.65 338.55a1.89 1.89 0 0 0-1.32-1.56c-.86-.29-2.5 0-2.79 2.46s1.53 4.87 2.88 4.73 1.27-4.95 1.23-5.63z" class="cls-23"/>
<path d="M328 339.3a1.47 1.47 0 0 0-.92-1c-.52-.18-1.32 0-1.5 1.51s.73 2.95 1.55 2.86a1 1 0 0 0 .57-.84 1.15 1.15 0 0 1-.53-1.4 1.26 1.26 0 0 1 .82-.88c.01-.15.01-.25.01-.25zM349.74 341.07a2.42 2.42 0 0 1 1.85-1.2c.9-.08 2.44.56 2.14 3s-2.63 4.38-3.9 3.93-.09-5.73-.09-5.73z" class="cls-28"/>
<path d="M350.42 342a1.47 1.47 0 0 1 1.13-.73c.55 0 1.29.31 1.11 1.82s-1.4 2.7-2.18 2.42a1 1 0 0 1-.36-1 1.15 1.15 0 0 0 .84-1.24 1.26 1.26 0 0 0-.59-1z" class="cls-27"/>
<path d="M343.81 338.4l1.85.22a.66.66 0 0 0 .73-.58.66.66 0 0 0-.58-.73l-1.85-.22a.66.66 0 0 0-.73.58.66.66 0 0 0 .58.73z" class="cls-29"/>
<path d="M343.89 346.63c-.24 2-2.83 3.42-5.77 3.07s-5.14-2.29-4.89-4.33z" class="cls-12"/>
<path d="M333.23 337.15l1.85.22a.66.66 0 0 0 .73-.58.66.66 0 0 0-.58-.73l-1.85-.22a.66.66 0 0 0-.73.58.66.66 0 0 0 .58.73z" class="cls-29"/>
<path d="M327.54 335.9c-1.74-11.29 6.39-15.55 14-14.65s14.47 6.94 10.14 17.51c.22-2.3.42-5.11-.49-6.73-1.51-2.75-5.69-4.92-10.3-5.29-4.56-.72-9.14.41-11.25 2.73-1.3 1.37-1.77 4.14-2.1 6.43z" class="cls-29"/>
<circle cx="344.9" cy="341.69" r="1.03" transform="rotate(-83.23 344.905 341.69)"/>
<path d="M346.31 341.73a.32.32 0 1 0 .45-.45 2.48 2.48 0 0 0-3.65-.37.32.32 0 0 0 .35.53 1.93 1.93 0 0 1 2.85.29z" class="cls-29"/>
<circle cx="333.4" cy="340.32" r="1.03" transform="rotate(-83.23 333.4 340.322)"/>
<path d="M332 340a.32.32 0 1 1-.33-.54 2.48 2.48 0 0 1 3.63.5.32.32 0 1 1-.47.43 1.93 1.93 0 0 0-2.84-.39z" class="cls-29"/>
<path d="M353.64 388.46a12 12 0 0 1-4.69 1.95c-1.09-5.47-1.62-15.52-2.21-25.09l5.25.62a150.41 150.41 0 0 0 1.65 22.52z" class="cls-23"/>
<path d="M352.5 367.78l-5.62.37-.76-11.53c4.22-.22 5.66 3.93 5.85 5.83z" class="cls-16"/>
<path d="M306.22 407.49l-5.71-.68a12.06 12.06 0 0 0-1.61 5.37l11.87 1.41c.39-2.97-3.84-4.91-4.55-6.1z" class="cls-30"/>
<path d="M298.71 412.18l.087-.724 12.214 1.45-.085.725z" class="cls-31"/>
<path d="M308.6 409.89a.43.43 0 0 0 .29-.81s-1.68-.64-4.53 1.61a.43.43 0 1 0 .53.68c2.47-1.95 3.7-1.48 3.71-1.48zM307.54 408.74a.43.43 0 0 0 .29-.81s-1.68-.64-4.53 1.61a.43.43 0 1 0 .53.68c2.48-1.95 3.7-1.48 3.71-1.48z" class="cls-32"/>
<path d="M290.69 405.65l5.71.68a12.06 12.06 0 0 1 .31 5.6l-11.87-1.41c.31-2.99 4.88-3.88 5.85-4.87z" class="cls-30"/>
<path d="M284.68 410.527l.086-.725 12.214 1.45-.086.725z" class="cls-8"/>
<path d="M287.81 407.43a.43.43 0 1 1-.1-.85s1.78-.22 4 2.63a.43.43 0 0 1-.68.53c-1.91-2.48-3.22-2.31-3.22-2.31zM289.11 406.55a.43.43 0 0 1-.09-.85s1.78-.23 4 2.63a.43.43 0 1 1-.68.53c-1.92-2.48-3.23-2.31-3.23-2.31z" class="cls-32"/>
<path d="M299.47 407.756l3.483-29.324 9.602 1.14-3.48 29.324zM287.693 406.35l3.48-29.325 9.603 1.14-3.48 29.324z" class="cls-33"/>
<path d="M312.26 399l-25.38-3 6.77-44a5.06 5.06 0 0 1 5.54-4.36l12.43 1.48a5.06 5.06 0 0 1 4.36 5.54z" class="cls-34"/>
<path d="M321.51 381.33a12 12 0 0 1-4.69 1.95c-1.41-7.09-1.88-21.87-2.77-33.33 4.19-.22 5.75 3.89 5.81 5.78a156.51 156.51 0 0 0 1.65 25.6zM282.27 376.68a12 12 0 0 0 4.1 3c3-6.56 6.95-20.82 10.5-31.75-4-1.19-6.5 2.44-7 4.26a156.47 156.47 0 0 1-7.6 24.49z" class="cls-35"/>
<path d="M300.47 342.12l11.05 1.31-1.18 10c-.87 7.31-11.92 6-11.05-1.31z" class="cls-36"/>
<path d="M309.58 355.74l-9.34-11.68.23-1.94 11.05 1.31-1.18 10a5.94 5.94 0 0 1-.76 2.31z" class="cls-37"/>
<path d="M320.12 323.59c-1.53 2.93-15.8 14-22.78 20-1.36 1.17-4.8 2.13-5.57 5.17-1.29 5.07 7.07 8.61 11.35 9.08 7.38.8 14.37 1.86 17.42-4.44.54-1.12.26-4.56-2.47-5.31 2.93-6.73 2.76-19.86 2.05-24.5z" class="cls-38"/>
<path d="M309.28 315.14c3 .42 12 1.79 10.93 12.37s-3.82 14.19-5.43 15.6a11 11 0 0 1-6.29 3.08 4 4 0 0 1-5.71-.68 11 11 0 0 1-5.39-4.46c-1.24-1.75-3-5.89-1.63-16.44s10.5-9.76 13.52-9.47z" class="cls-36"/>
<path d="M309.28 315.14c3 .42 12 1.79 10.93 12.37s-3.82 14.19-5.43 15.6a11 11 0 0 1-6.29 3.08 4.41 4.41 0 0 1-3 .88z" class="cls-39"/>
<path d="M317.4 339.38a11.66 11.66 0 0 1-8.91 6.8 4 4 0 0 1-5.71-.68 11.66 11.66 0 0 1-7.07-8.7c0 3.54 4.74 11.7 6.88 16.24 9.82 1.22 13.25-8.25 14.82-13.66z" class="cls-40"/>
<path d="M296.76 320.82c-2.3 15.88 1.36 32.08 21.32 27.3-4.51 8.89-12.34 7.84-14 7.48a12.33 12.33 0 0 1-9.61-11c-1.05-8.32-.78-16.28 2.29-23.78z" class="cls-41"/>
<path d="M296.52 332.37c1.95-7.11 6.09-8.85 11.72-8.44l.84-7.1c-9.08-1.03-13.46 8.84-12.56 15.54z" class="cls-12"/>
<path d="M295.7 336.81c1-12.27 5.88-16.18 12.86-15.67l.71-6c-3-.29-12.11-1.08-13.52 9.47-.05.39-1.92 7.39-.05 12.2z" class="cls-41"/>
<path d="M317.65 334.87c-.23-7.37-3.85-10-9.42-11l.84-7.1c8.92 1.12 11 12.06 8.76 18.42-.04.17-.14-.04-.18-.32z" class="cls-12"/>
<path d="M317.4 339.38c1.86-12.17-1.93-17.11-8.84-18.25l.71-6c3 .42 12 1.79 10.93 12.37-.04.44.14 7.66-2.8 11.88z" class="cls-38"/>
<path d="M309.72 339.69a3.41 3.41 0 0 1-6.62-.79z" class="cls-12"/>
<path d="M314.19 334.39a.32.32 0 0 0 .45-.45 2.49 2.49 0 0 0-3.67-.37.32.32 0 1 0 .36.53 2 2 0 0 1 2.86.29z" class="cls-29"/>
<circle cx="312.68" cy="334.19" r=".96" transform="rotate(-83.23 312.683 334.193)"/>
<path d="M314.38 333.24a.19.19 0 1 0-.32-.23l-.42.58a.19.19 0 1 0 .32.23z" class="cls-29"/>
<path d="M314.81 333.46a.19.19 0 1 0-.25-.29l-.65.56a.19.19 0 1 0 .26.29zM312.1 330.14a.46.46 0 1 0-.07.91 8.33 8.33 0 0 1 2.07.72.46.46 0 0 0 .41-.82 6.64 6.64 0 0 0-2.41-.81zM300 332.7a.32.32 0 0 1-.33-.55 2.49 2.49 0 0 1 3.65.5.32.32 0 0 1-.47.43 2 2 0 0 0-2.85-.39z" class="cls-29"/>
<circle cx="301.51" cy="332.86" r=".96" transform="rotate(-83.23 301.517 332.864)"/>
<path d="M300.08 331.54a.19.19 0 1 1 .36-.15l.27.66a.19.19 0 1 1-.36.15z" class="cls-29"/>
<path d="M299.61 331.66a.19.19 0 1 1 .32-.23l.5.7a.19.19 0 1 1-.32.23zM303 329.07a.46.46 0 1 1-.14.9 8.34 8.34 0 0 0-2.19.22.46.46 0 0 1-.21-.89 6.65 6.65 0 0 1 2.54-.23z" class="cls-29"/>
<path d="M258.49 354.506l3.575-30.118 23.892 2.836-3.575 30.118z"/>
<path d="M269.64 377.64l5.58.66-3.97 30.02-5.08-.79 1.44-12.76 2.03-17.13zM265.25 377.12l-5.58-.67-3.17 30.12 5.12.42 1.59-12.74 2.04-17.13z" class="cls-42"/>
<path d="M256.45 405.19l5.61.67a11.85 11.85 0 0 1 .31 5.51L250.69 410c.31-3 4.81-3.83 5.76-4.81z" class="cls-34"/>
<path d="M250.546 409.986l.085-.715 12.007 1.426-.085.715z" class="cls-43"/>
<path d="M271.72 407l-5.61-.67a11.85 11.85 0 0 0-1.59 5.28l11.67 1.39c.39-2.92-3.77-4.83-4.47-6z" class="cls-34"/>
<path d="M264.33 411.615l.086-.715 12.005 1.426-.084.715z" class="cls-43"/>
<path d="M256.39 376.72l22 2.61 2.84-23.91a4.92 4.92 0 0 0-4.29-5.45l-12.22-1.45a4.92 4.92 0 0 0-5.45 4.29z" class="cls-44"/>
<path d="M259 357.59s-3.76 14.65-5.65 21.58h-1.67l-2.27-2.6c2-7.5 5.22-20 5.23-20z" class="cls-42"/>
<path d="M253.89 357.56l5.25 1.64 3.36-10.77c-4-1.17-6.27 2.43-6.88 4.19z" class="cls-44"/>
<path d="M252.55 382.3l-.69-2.88a.92.92 0 0 1 .68-1.1.92.92 0 0 1 1.1.68l.69 2.88a.92.92 0 0 1-.68 1.1.92.92 0 0 1-1.1-.68z" class="cls-42"/>
<path d="M250.61 379.13l-.46 5.69a.91.91 0 0 0 .84 1 .93.93 0 0 0 1-.85l.46-5.69a.91.91 0 0 0-.84-1 .93.93 0 0 0-1 .85z" class="cls-42"/>
<path d="M249.65 377.65l-.46 5.69a.91.91 0 0 0 .84 1 .93.93 0 0 0 1-.85l.46-5.69a.91.91 0 0 0-.84-1 .93.93 0 0 0-1 .85z" class="cls-42"/>
<path d="M249.34 376.33l-.47 5.67a.91.91 0 0 0 .84 1 .93.93 0 0 0 1-.85l.46-5.69a.91.91 0 0 0-.84-1 .93.93 0 0 0-.99.87z" class="cls-42"/>
<path d="M279.06 350.4v.64a6.47 6.47 0 0 1-3.12 4.69 9.62 9.62 0 0 1-11.61-1.38 6.47 6.47 0 0 1-1.93-5.29c0-.15 0-.33.09-.54v-.08a12.09 12.09 0 0 1 3.36 0l2.52.3-.13 1.1-1.1-.13-1.42-.17c-.35 0-.7-.07-1-.09h-.09a4.32 4.32 0 0 0 1.33 3.39 7.44 7.44 0 0 0 8.83 1 4.32 4.32 0 0 0 2.08-3h-.09c-.34-.07-.69-.12-1-.16l-1.42-.17-1.1-.13.13-1.1 2.52.3a12.06 12.06 0 0 1 3.15.82z" class="cls-12"/>
<path d="M277.94 350a4.82 4.82 0 0 1 0 .89c-.38 3.23-3.93 5.47-7.92 5s-6.92-3.48-6.53-6.71a4.82 4.82 0 0 1 .19-.87 13.87 13.87 0 0 1 2.18.1l1.42.17.47-3.94.11-.27 7 .83v.29l-.47 3.94 1.42.17a13.87 13.87 0 0 1 2.13.4z" class="cls-42"/>
<path d="M275.3 354.8l-7.64-9.73v-.22h1.59l.11-.27 5.41.64v.29l-.47 3.94 1.42.17a13.87 13.87 0 0 1 2.14.42 4.82 4.82 0 0 1 0 .89 5.39 5.39 0 0 1-2.56 3.87z" class="cls-45"/>
<path d="M274.5 317.74c-2.89-.28-11.62-1-13 9.08s.37 14.09 1.56 15.77a10.54 10.54 0 0 0 5.17 4.28 3.81 3.81 0 0 0 5.48.65 10.54 10.54 0 0 0 6-3c1.55-1.35 4.16-4.81 5.21-15s-7.54-11.38-10.42-11.78z" class="cls-42"/>
<path d="M274.5 317.74c2.88.41 11.54 1.71 10.49 11.87s-3.66 13.61-5.21 15a10.54 10.54 0 0 1-6 2.95 4.23 4.23 0 0 1-2.88.85z" class="cls-46"/>
<path d="M262.49 332.17a1.8 1.8 0 0 0-1.26-1.49c-.82-.28-2.39 0-2.67 2.34s1.46 4.65 2.74 4.51 1.24-4.71 1.19-5.36z" class="cls-42"/>
<path d="M280.12 324.23c.87 2.49.33 6.23 4.55 9.19 4.3-10.68-2.83-16.71-10.27-17.43-7.87-.76-14.2 3.76-13.24 14.87 2.84-3.13 17.39-5.26 18.96-6.63z" class="cls-29"/>
<path d="M275.74 341.17a4 4 0 0 1-7.92-.94z" class="cls-12"/>
<path d="M276.66 379l-18.57-2.2-1.39 10.84 18.79 2.23z" class="cls-47"/>
<path d="M272.74 331.5a49.29 49.29 0 0 1-11.06-2.89l1.08-6.1c3.38-7.4 11.74-5.82 11.74-5.82s8.49.42 10.05 8.41l-.37 6.18c-.75.12-2.1.15-2.92.22-.48 0 .23-2.51-1.1-5.63.38 3.13-.2 5.79-.78 5.79-1.98.04-4.75.01-6.64-.16z" class="cls-29"/>
<path d="M266.7 361.4a8 8 0 0 0-2.17-2.13c-.62-.31-.7 3-.55 3.87-.08 1-.72 2.26.56 4.56 1.2 2.15 2.74 3.16 3.71 2.76.85.62 2.59 0 4.26-1.81s1.45-3.29 1.61-4.3c.35-.83 1.05-4.05.37-3.89a8 8 0 0 0-2.6 1.56 5.79 5.79 0 0 0-5.19-.62z" class="cls-12"/>
<path d="M267.47 367.78a.28.28 0 1 1-.53.16 2.34 2.34 0 0 0-1.87-1.46.28.28 0 1 1 .11-.54 2.91 2.91 0 0 1 2.29 1.84zM269.9 368.3a.28.28 0 0 1-.48-.28 2.91 2.91 0 0 1 2.66-1.26.28.28 0 1 1 0 .55 2.35 2.35 0 0 0-2.18.99zM268.3 370.14a2.81 2.81 0 0 1-.66-.72c0-.51 1.56-.33 1.46.17a2.8 2.8 0 0 1-.8.55z" class="cls-44"/>
<path d="M249.73 374.84l4.29 1.33a.89.89 0 0 1 .59 1.12.89.89 0 0 1-1.12.59l-4.29-1.33a.89.89 0 0 1-.59-1.12.89.89 0 0 1 1.12-.59z" class="cls-48"/>
<path d="M261.86 332.85a1.41 1.41 0 0 0-.88-.93c-.5-.17-1.26 0-1.43 1.44s.7 2.81 1.48 2.73a1 1 0 0 0 .54-.81 1.09 1.09 0 0 1-.5-1.34 1.19 1.19 0 0 1 .78-.84c.01-.15.01-.25.01-.25z" class="cls-46"/>
<path d="M278.11 334.62a1 1 0 0 1 .26.07l.23-.33a.19.19 0 0 1 .31.21l-.23.33.34-.2a.19.19 0 0 1 .19.32l-.32.19h.41a.19.19 0 0 1 0 .37h-.38a.92.92 0 0 1 0 .1 1 1 0 1 1-.81-1.06zM277.76 332.55a.5.5 0 1 1 .08-1 5.43 5.43 0 0 1 1.83.53.5.5 0 0 1-.45.89 4.31 4.31 0 0 0-1.46-.42zM267.06 333.31a1 1 0 0 0-.27 0l-.15-.38a.19.19 0 0 0-.35.13l.15.38-.28-.27a.19.19 0 0 0-.26.27l.27.26-.38-.14a.19.19 0 0 0-.13.35l.36.13a.92.92 0 0 0 0 .1 1 1 0 1 0 1.04-.83zM268 330.39a.5.5 0 1 1-.16 1 4.31 4.31 0 0 0-1.52.08.5.5 0 0 1-.23-1 5.43 5.43 0 0 1 1.91-.08z" class="cls-29"/>
<path d="M279.44 335.78a.32.32 0 1 0 .34-.55 2.52 2.52 0 0 0-3.7.46.32.32 0 0 0 .47.44 2 2 0 0 1 2.89-.36zM265.48 334.12a.32.32 0 1 1-.21-.61 2.52 2.52 0 0 1 3.49 1.32.32.32 0 0 1-.56.32 2 2 0 0 0-2.72-1z" class="cls-29"/>
<path d="M280 360.16a165.74 165.74 0 0 0 4.56 22.84 1.49 1.49 0 0 0 .49.87l1-1.19 2.43-2.49c-1.66-3.06-3.83-19.46-3.66-20.09z" class="cls-42"/>
<path d="M285.33 361.29l-5.48.36-.75-11.25c4.12-.21 5.53 3.83 5.71 5.69z" class="cls-44"/>
<path d="M281.7 326.71l4.24.5-1.7 30.15c-.13 2.35-4.42 5.09-6.75 4.81z" class="cls-29"/>
<path d="M336.33 406.29l2.11-17.75a5 5 0 0 1 5.48-4.31l12.29 1.46a4.94 4.94 0 0 1 4.31 5.48l-2.11 17.75z" class="cls-48"/>
<circle cx="340.18" cy="361.87" r="6.71" class="cls-49" transform="rotate(-83.23 340.185 361.876)"/>
<circle cx="365.07" cy="364.83" r="6.71" class="cls-49" transform="rotate(-83.23 365.073 364.83)"/>
<path d="M355.19 380.64l-9.11-1.08-.74 6.23 3.93 5.85 5.18-4.77.74-6.23z" class="cls-36"/>
<path d="M352.36 389.23l-6.47-8.08.19-1.59 9.11 1.08-.74 6.23-2.09 2.36z" class="cls-37"/>
<path d="M353.91 352.55c-2.94-.28-11.82-1.05-13.2 9.24s.38 14.34 1.59 16a10.73 10.73 0 0 0 5.26 4.35 3.88 3.88 0 0 0 5.58.66 10.72 10.72 0 0 0 6.14-3c1.58-1.38 4.23-4.9 5.3-15.22s-7.75-11.58-10.67-12.03z" class="cls-37"/>
<path d="M353.91 352.55c-2.94-.28-11.82-1.05-13.2 9.24s.38 14.34 1.59 16a10.73 10.73 0 0 0 5.26 4.35 4.31 4.31 0 0 0 2.65 1.53z" class="cls-36"/>
<path d="M341.88 367.27a2.35 2.35 0 0 0-1.47-1.55c-.83-.28-2.43 0-2.71 2.38s1.48 4.73 2.79 4.59 1.39-5.42 1.39-5.42z" class="cls-36"/>
<path d="M341 368a1.43 1.43 0 0 0-.9-.95c-.51-.17-1.28 0-1.46 1.47s.71 2.86 1.51 2.78a1 1 0 0 0 .55-.82 1.11 1.11 0 0 1-.51-1.36 1.21 1.21 0 0 1 .8-.85c.01-.21.01-.27.01-.27z" class="cls-37"/>
<path d="M347.34 364.89a.51.51 0 1 0 .16-1 5.48 5.48 0 0 0-1.94.09.51.51 0 1 0 .24 1 4.39 4.39 0 0 1 1.54-.09zM357.48 365.07a.51.51 0 1 0-.08 1 4.39 4.39 0 0 1 1.48.44.51.51 0 1 0 .46-.91 5.48 5.48 0 0 0-1.86-.53z" class="cls-50"/>
<path d="M346.81 357.63c-1.45 2.25-1.81 6.08-6.69 8-1.71-11.59 6.78-15.85 14.31-14.79 8 1.12 14.53 7.37 10.93 18.12-5.22-1.76-7.74-4.79-9.94-8.09.55 2.65 2.07 4.13 1.64 4-5.01-1.87-6.82-2.37-10.25-7.24z" class="cls-50"/>
<path d="M362.35 369.68a1.84 1.84 0 0 1 1.6-1.17c.87-.08 2.37.54 2.08 3s-2.55 4.25-3.79 3.81-.09-5.01.11-5.64z" class="cls-37"/>
<path d="M362.82 370.55a1.43 1.43 0 0 1 1.09-.71c.53 0 1.25.3 1.07 1.77s-1.36 2.62-2.11 2.35a1 1 0 0 1-.35-.93 1.11 1.11 0 0 0 .82-1.2 1.22 1.22 0 0 0-.57-1z" class="cls-51"/>
<path d="M353.63 375.59a2.45 2.45 0 1 1-4.86-.58z" class="cls-12"/>
<path d="M349.27 391.64l5.21-.88.56-5.21-.43-.05-5.34 6.14zM349.27 391.64l-4.87-2.08.68-5.19.42.05 3.77 7.22z" class="cls-52"/>
<path d="M359.11 370.24a.32.32 0 0 0 .46-.46 2.53 2.53 0 0 0-3.72-.37.32.32 0 1 0 .36.54 2 2 0 0 1 2.91.29z" class="cls-29"/>
<circle cx="357.58" cy="370.03" r=".97" transform="rotate(-83.23 357.584 370.03)"/>
<path d="M359.3 369.07a.2.2 0 1 0-.32-.23l-.43.59a.2.2 0 1 0 .32.23z" class="cls-29"/>
<path d="M359.74 369.3a.2.2 0 0 0-.26-.3l-.66.57a.2.2 0 1 0 .26.3zM344.71 368.53a.32.32 0 1 1-.34-.55 2.53 2.53 0 0 1 3.71.51.32.32 0 1 1-.48.44 2 2 0 0 0-2.89-.39z" class="cls-29"/>
<circle cx="346.24" cy="368.69" r=".97" transform="rotate(-83.23 346.247 368.69)"/>
<path d="M344.79 367.35a.2.2 0 1 1 .37-.15l.27.67a.2.2 0 1 1-.37.15z" class="cls-29"/>
<path d="M344.32 367.47a.2.2 0 0 1 .32-.23l.51.71a.2.2 0 0 1-.32.23z" class="cls-29"/>
<path d="M335.15 406.15c1.38-4.24 2.81-9 4.24-13.62l-5.33-.63c-1.23 5-2.48 9.35-3.94 13.65z" class="cls-36"/>
<path d="M333.12 393.59l5.46 1.7 3.49-11.2c-4.11-1.22-6.52 2.53-7.16 4.36z" class="cls-48"/>
<path d="M333.3 392.9l5.67 1.83-.61 1.81-5.67-1.83.61-1.81z" class="cls-52"/>
<path d="M271.3 398.57l1.54-13a5.08 5.08 0 0 1 5.63-4.44l12.63 1.5a5.08 5.08 0 0 1 4.44 5.63l-1.54 13z" class="cls-53"/>
<path d="M278.19 381.16l.61 1.06 3.89 7.27 1 1.84 1.38-1.55 5.48-6.16.77-.9h-.2l-12.63-1.5z" class="cls-12"/>
<path d="M290.21 376.23l-9.36-1.11-.76 6.41 3.88 7.27 5.48-6.16.76-6.41z" class="cls-23"/>
<path d="M287.3 385.06l-6.65-8.3.2-1.64 9.36 1.11-.76 6.41-2.15 2.42z" class="cls-27"/>
<path d="M289.11 349.37c3 .42 12 1.78 10.91 12.35s-3.81 14.16-5.42 15.57a11 11 0 0 1-6.28 3.07 4 4 0 0 1-5.7-.68 11 11 0 0 1-5.38-4.45c-1.24-1.75-3-5.88-1.63-16.41s10.49-9.75 13.5-9.45z" class="cls-23"/>
<path d="M289.11 349.37c3 .42 12 1.78 10.91 12.35s-3.81 14.16-5.42 15.57a11 11 0 0 1-6.28 3.07 4.4 4.4 0 0 1-3 .88z" class="cls-28"/>
<path d="M289.86 373.27a3.57 3.57 0 0 1-7.08-.84z" class="cls-12"/>
<path d="M292.56 364.16a.52.52 0 1 1 .08-1 5.66 5.66 0 0 1 1.91.56.52.52 0 1 1-.47.93 4.49 4.49 0 0 0-1.52-.49zM282.44 361.91a.52.52 0 1 1-.16 1 4.49 4.49 0 0 0-1.58.08.52.52 0 1 1-.24-1 5.66 5.66 0 0 1 1.98-.08z" class="cls-29"/>
<path d="M283.12 396a3 3 0 0 1 3.12-1.58 2.76 2.76 0 0 1 2.58 2.9 5.66 5.66 0 0 1-1.76 3.13l-8.81-1a5.66 5.66 0 0 1-1-3.46 2.76 2.76 0 0 1 3.19-2.22 3 3 0 0 1 2.68 2.23z" class="cls-12"/>
<path d="M294.3 367.68a.33.33 0 0 0 .46-.46c-.65-.65-1.74-1.73-3.76-.38a.33.33 0 1 0 .36.54 2 2 0 0 1 2.93.3z" class="cls-29"/>
<circle cx="292.75" cy="367.48" r=".98" transform="rotate(-83.23 292.76 367.48)"/>
<path d="M294.49 366.51a.2.2 0 1 0-.32-.23l-.43.6a.2.2 0 0 0 .32.23z" class="cls-29"/>
<path d="M294.93 366.74a.2.2 0 0 0-.26-.3l-.67.56a.2.2 0 1 0 .26.3zM279.77 366a.33.33 0 1 1-.34-.56c.78-.48 2.09-1.28 3.74.51a.33.33 0 1 1-.48.44 2 2 0 0 0-2.92-.4z" class="cls-29"/>
<circle cx="281.32" cy="366.12" r=".98" transform="rotate(-83.23 281.326 366.125)"/>
<path d="M279.85 364.77a.2.2 0 1 1 .37-.15l.28.68a.2.2 0 0 1-.37.15z" class="cls-29"/>
<path d="M279.37 364.89a.2.2 0 0 1 .32-.23l.51.72a.2.2 0 1 1-.32.23zM303.18 353.32a.93.93 0 1 1 .42 1.82 6.3 6.3 0 0 0-3.8 2.71.93.93 0 0 1-1.62-.93 8.27 8.27 0 0 1 5-3.6zM304.89 364.63a.93.93 0 1 1-1.13 1.49 4.08 4.08 0 0 0-3.92-.46.93.93 0 1 1-.72-1.72 6 6 0 0 1 5.77.69z" class="cls-29"/>
<ellipse cx="303.53" cy="364.93" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-83.23 303.537 364.93)"/>
<path d="M304.82 358.93a.93.93 0 0 1-.57 1.78 4.05 4.05 0 0 0-3.81 1 .94.94 0 1 1-1.29-1.35 6 6 0 0 1 5.67-1.43z" class="cls-29"/>
<ellipse cx="303.65" cy="359.69" class="cls-54" rx="1.18" ry="1.21" transform="rotate(-14.05 303.772 359.798)"/>
<ellipse cx="302.49" cy="354.41" class="cls-54" rx="1.18" ry="1.21" transform="rotate(-30.52 302.444 354.37)"/>
<path d="M299.34 348.84a.94.94 0 0 1 1.33 1.32c-.14.15-.34.32-.56.52a12 12 0 0 0-2.67 3.31.93.93 0 0 1-1.62-.93 13.69 13.69 0 0 1 3-3.77c.18-.15.35-.29.52-.45z" class="cls-29"/>
<ellipse cx="299.35" cy="349.99" class="cls-54" rx="1.18" ry="1.21" transform="rotate(-30.53 299.335 349.985)"/>
<path d="M294.61 345.85a.93.93 0 0 1 1.66.86c-.11.21-.24.43-.38.66a12 12 0 0 0-1.58 3.95.93.93 0 0 1-1.82-.41 13.69 13.69 0 0 1 1.8-4.49c.12-.2.23-.42.32-.57z" class="cls-29"/>
<ellipse cx="295.01" cy="346.94" class="cls-54" rx="1.18" ry="1.21" transform="rotate(-30.52 295.067 346.982)"/>
<path d="M273.66 351.58a.93.93 0 0 1 .84-1.67 8.27 8.27 0 0 1 4 4.66.93.93 0 0 1-1.79.52 6.3 6.3 0 0 0-3.05-3.51z" class="cls-29"/>
<path d="M299.58 366.46c4.24-10.54-2.63-16.24-10-17.2-8.66-1.12-16.21 3.3-14.43 14.61 0 0 1.3.58 1.44-.12.47-3.49.78-5.06 2-6.39 1.66-1.87 8.74-.84 14.9-2.11a5 5 0 0 1 3.2 2.37c1.63 3.07 2.57 4.51 2.13 8.76a4.36 4.36 0 0 0 .76.08z" class="cls-29"/>
<path d="M297.1 352.84a.79.79 0 0 1 0 1.11l-2.45 2.36a.79.79 0 0 1-1.11 0 .79.79 0 0 1 0-1.11l2.45-2.36a.79.79 0 0 1 1.11 0zM299 354.27a.79.79 0 0 1 0 1.11l-2.45 2.36a.79.79 0 0 1-1.11 0 .79.79 0 0 1 0-1.11l2.45-2.35a.79.79 0 0 1 1.11-.01z" class="cls-55"/>
<path d="M270.94 362.22a.93.93 0 0 1-.75-1.71 6 6 0 0 1 5.78.68.93.93 0 0 1-1.11 1.5 4.08 4.08 0 0 0-3.92-.47z" class="cls-29"/>
<ellipse cx="271.44" cy="361.12" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-83.23 271.442 361.12)"/>
<path d="M271.72 356.85a.93.93 0 0 1-.13-1.86 6 6 0 0 1 5.18 2.69.94.94 0 1 1-1.57 1 4.05 4.05 0 0 0-3.48-1.83z" class="cls-29"/>
<ellipse cx="272.55" cy="356" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-62.4 272.587 356.01)"/>
<ellipse cx="274.91" cy="351.14" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-45.95 274.927 351.15)"/>
<path d="M277.68 347.43a.94.94 0 0 1 1.6-1c.12.2.24.37.36.55a13.67 13.67 0 0 1 2.07 4.38.93.93 0 0 1-1.79.52 12 12 0 0 0-1.82-3.84c-.17-.22-.32-.44-.42-.61z" class="cls-29"/>
<ellipse cx="279" cy="347.58" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-45.93 279.006 347.586)"/>
<path d="M282.76 345.1a.93.93 0 1 1 1.81-.45c.05.2.11.4.18.63a13.69 13.69 0 0 1 .7 4.79.93.93 0 0 1-1.87 0 12 12 0 0 0-.61-4.21c-.08-.28-.15-.53-.21-.76z" class="cls-29"/>
<ellipse cx="283.93" cy="345.62" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-45.94 283.966 345.642)"/>
<path d="M288.19 344.47a.94.94 0 1 1 1.87 0v1.13c0 .86.12 2 .09 4a.93.93 0 0 1-1.87 0c0-1.83 0-3-.09-3.84.02-.52 0-.9 0-1.29z" class="cls-29"/>
<ellipse cx="289.12" cy="345.25" class="cls-54" rx="1.21" ry="1.18" transform="rotate(-45.93 289.126 345.25)"/>
<path d="M276.61 364.38a1.88 1.88 0 0 0-1.31-1.55c-.85-.29-2.48 0-2.77 2.44s1.52 4.84 2.85 4.7 1.28-4.91 1.23-5.59z" class="cls-23"/>
<path d="M275.93 365.13a1.46 1.46 0 0 0-.92-1c-.52-.17-1.31 0-1.49 1.5s.73 2.93 1.54 2.84a1 1 0 0 0 .57-.84 1.14 1.14 0 0 1-.52-1.39 1.24 1.24 0 0 1 .81-.87c.01-.14.01-.24.01-.24zM297.54 366.89a2.4 2.4 0 0 1 1.84-1.19c.89-.08 2.42.55 2.12 3s-2.61 4.35-3.87 3.9-.09-5.71-.09-5.71z" class="cls-28"/>
<path d="M298.22 367.78a1.46 1.46 0 0 1 1.12-.73c.54 0 1.28.31 1.1 1.81s-1.39 2.67-2.16 2.4a1 1 0 0 1-.35-.95 1.14 1.14 0 0 0 .83-1.23 1.24 1.24 0 0 0-.59-1z" class="cls-27"/>
<path d="M272.4 391.4c-.69 2.17-1.53 4.56-2.49 7l-5.16-.61c1.6-3.87 2.82-7.11 2.82-7.39z" class="cls-23"/>
<path d="M267.15 391l5.59 1.74 3.58-11.48c-4.21-1.25-6.68 2.59-7.34 4.47z" class="cls-53"/>
<path d="M294.55 394c.16 2.27.42 4.79.78 7.39l5.16.61c-.65-4.14-1.08-7.57-1-7.85z" class="cls-23"/>
<path d="M299.74 394.87l-5.84.39-.79-12c4.39-.23 5.89 4.08 6.09 6.06z" class="cls-53"/>
<path d="M247 378.52c-.32 0-4 .31-4.35.3-9.81-.32-14.89-7.43-17.46-15.91-.06-.27-.12-.56-.18-.87v-.16A4.43 4.43 0 0 0 226 360l.66-2.19a.94.94 0 0 0-.66-1.16 1 1 0 0 0-1.07.46l-.14-.08-.06-4.14a.94.94 0 0 0-1-.93.94.94 0 0 0-.93 1v1.36l-.5-2.32a1 1 0 0 0-1.13-.7.94.94 0 0 0-.7 1.13l1.07 4.54-1.44-4.28a.94.94 0 0 0-1.19-.59.94.94 0 0 0-.59 1.19l1.45 4.3-1.28-2.17a.94.94 0 0 0-1.29-.33.94.94 0 0 0-.33 1.29l1.22 2.07a.44.44 0 0 0 0 .54l1.41 3a2 2 0 0 0 1 1.26c2.26 10.76 8.25 20.26 22.25 21.83z" class="cls-42"/>
<path d="M245.23 377.81c-.32 0-3.94-.27-4.24-.28a17.89 17.89 0 0 1-5-.84l-1.47 7a28.71 28.71 0 0 0 6.36 1.42z" class="cls-56"/>
<path d="M259.86 355c1.06.46 4-5.77 5.06-3.27 5.59 5.39-1.21 19.27 5.27 19.09-2.74 7.69-15.85 10.28-20.88 4.8 0 0 6.36-18.54 10.55-20.62z" class="cls-57"/>
<path d="M251.22 354c-1.14.2-3.12-6.77-4.73-4.58-6.69 3.94-2.76 19.24-9 17.54.87 8.12 13 13.71 19.18 9.55-.03-.02-1.87-19.51-5.45-22.51z" class="cls-57"/>
<path d="M239.4 393.69l1.36-11.45a5.07 5.07 0 0 1 5.62-4.43l12.6 1.5a5.07 5.07 0 0 1 4.43 5.62L262 396.38z" class="cls-56"/>
<path d="M246.1 377.79l.61 1.05 3.88 7.25 1 1.83 1.38-1.55 5.47-6.14.77-.89h-.2l-12.6-1.5z" class="cls-12"/>
<path d="M258.09 372.88l-9.34-1.11-.76 6.39 3.88 7.26 5.46-6.15.76-6.39z" class="cls-42"/>
<path d="M255.18 381.69l-6.62-8.29.19-1.63 9.34 1.11-.76 6.39-2.15 2.42z" class="cls-45"/>
<path d="M256.68 344.85c3 .42 12 1.78 10.88 12.32s-3.8 14.13-5.41 15.53a10.94 10.94 0 0 1-6.26 3.06 4 4 0 0 1-5.69-.68 10.94 10.94 0 0 1-5.37-4.44c-1.23-1.74-3-5.87-1.62-16.37s10.47-9.71 13.47-9.42z" class="cls-42"/>
<path d="M256.68 344.85c3 .42 12 1.78 10.88 12.32s-3.8 14.13-5.41 15.53a10.94 10.94 0 0 1-6.26 3.06 4.39 4.39 0 0 1-3 .88z" class="cls-46"/>
<path d="M244.22 359.82a1.87 1.87 0 0 0-1.31-1.55c-.85-.29-2.48 0-2.77 2.43s1.51 4.82 2.85 4.68 1.27-4.88 1.23-5.56z" class="cls-42"/>
<path d="M243.54 360.57a1.45 1.45 0 0 0-.91-1c-.52-.17-1.31 0-1.49 1.5s.72 2.92 1.53 2.83a1 1 0 0 0 .57-.84 1.13 1.13 0 0 1-.52-1.39 1.24 1.24 0 0 1 .81-.87c.01-.13.01-.23.01-.23zM265.1 362.32a2.4 2.4 0 0 1 1.83-1.19c.89-.08 2.42.55 2.12 3s-2.6 4.34-3.86 3.89-.09-5.7-.09-5.7z" class="cls-46"/>
<path d="M265.77 363.21a1.46 1.46 0 0 1 1.11-.72c.54 0 1.27.3 1.09 1.8s-1.39 2.67-2.16 2.4a1 1 0 0 1-.35-.95 1.13 1.13 0 0 0 .83-1.23 1.24 1.24 0 0 0-.59-1z" class="cls-45"/>
<path d="M262.51 351.59c.9 2.58.35 6.47 4.72 9.54 4.46-11.09-2.93-17.34-10.66-18.09-8.17-.79-16.17 3.84-15.17 15.37 4.71.68 7.15-4.46 7.41-4.49 5.25.61 9.13 1.67 13.7-2.33z" class="cls-21"/>
<path d="M257.43 368.69c-.41 1.22-2 2-3.79 1.79s-3.16-1.35-3.27-2.63z" class="cls-12"/>
<path d="M260.13 359.6a.52.52 0 1 1 .08-1 5.64 5.64 0 0 1 1.9.55.52.52 0 0 1-.47.93 4.45 4.45 0 0 0-1.51-.48zM250 357.36a.52.52 0 0 1-.16 1 4.46 4.46 0 0 0-1.58.08.52.52 0 0 1-.24-1 5.64 5.64 0 0 1 1.98-.08z" class="cls-45"/>
<path d="M262 362.82a.33.33 0 0 0 .47-.47c-.65-.65-1.75-1.75-3.8-.38a.33.33 0 1 0 .37.55 2 2 0 0 1 3 .3z" class="cls-14"/>
<circle cx="260.43" cy="362.61" r=".99" class="cls-8" transform="rotate(-83.23 260.438 362.615)"/>
<path d="M262.19 361.63a.2.2 0 1 0-.33-.23l-.43.6a.2.2 0 0 0 .33.23z" class="cls-14"/>
<path d="M262.63 361.86a.2.2 0 1 0-.26-.3l-.67.58a.2.2 0 0 0 .26.3zM247.32 361.08a.33.33 0 0 1-.34-.56c.79-.48 2.12-1.29 3.78.52a.33.33 0 1 1-.49.45 2 2 0 0 0-2.95-.4z" class="cls-14"/>
<circle cx="248.88" cy="361.24" r=".99" class="cls-8" transform="rotate(-83.23 248.886 361.245)"/>
<path d="M247.4 359.87a.2.2 0 0 1 .37-.15l.28.69a.2.2 0 1 1-.37.15z" class="cls-14"/>
<path d="M246.91 360a.2.2 0 1 1 .33-.23l.52.73a.2.2 0 0 1-.33.23z" class="cls-14"/>
<path d="M246.78 349.88a.78.78 0 0 1 1.05.34l1.77 3.45a.78.78 0 0 1-.34 1.05.79.79 0 0 1-1.05-.34l-1.77-3.45a.79.79 0 0 1 .34-1.05z" class="cls-48"/>
<path d="M263.59 390.63c.13 1.86.33 3.89.59 6l5.12.61a63.72 63.72 0 0 1-.79-6.44z" class="cls-42"/>
<path d="M268.78 391.47l-5.84.39-.79-12c4.39-.23 5.89 4.08 6.09 6.06z" class="cls-56"/>
<path d="M326.77 401l1.5-12.61a5 5 0 0 0-4.4-5.58l-12.52-1.49a5 5 0 0 0-5.58 4.4l-1.5 12.61z" class="cls-58"/>
<path d="M323.75 382.77v.35l-.08.2a6.35 6.35 0 0 1-12.16-1.44v-.56l2.9.34a3.45 3.45 0 0 0 1 1.74 3.45 3.45 0 0 0 4.13.49 3.45 3.45 0 0 0 1.36-1.47z" class="cls-12"/>
<path d="M313.36 378.76l9.16 1.09c0 .11 0 .23.06.34l-.31 2.58a4.9 4.9 0 0 1-9.37-1.11l.31-2.58c.05-.08.09-.21.15-.32z" class="cls-42"/>
<path d="M313.39 379.51l9.13.34c0 .11 0 .23.06.34l-.31 2.58a4.9 4.9 0 0 1-4 3z" class="cls-45"/>
<path d="M321.38 350.33c-3-.29-12.16-1.08-13.58 9.51s.39 14.75 1.64 16.51a11 11 0 0 0 5.41 4.48 4.43 4.43 0 0 0 2.72 1.57z" class="cls-42"/>
<path d="M321.38 350.33c3 .42 12.08 1.79 11 12.42s-3.83 14.25-5.45 15.66a11 11 0 0 1-6.31 3.09 4.43 4.43 0 0 1-3 .89z" class="cls-46"/>
<path d="M328.92 356.45c-.42-1-1-1.93-4-1.13s-8 3-13.34 1.88c-2.24 3.14-2.41 9.39-2.41 9.39l-2.83-2.36s.34-7.09 1.74-9.22c-.89-.26-1.54-1.26-1.2-3.44 1.12.29 1.69 1.27 3.35.24s5.47-4.72 11.5-3.85 8.3 4.44 9 5.82c3.78 4.08 2.91 8.4 1.92 13.55l-3.32 1.67c.45-2.15.73-9.93-.41-12.55z" class="cls-59"/>
<path d="M308.81 365.43a1.89 1.89 0 0 0-1.32-1.56c-.85-.29-2.5 0-2.79 2.45s1.53 4.86 2.87 4.72 1.28-4.93 1.24-5.61z" class="cls-42"/>
<path d="M308.13 366.19a1.47 1.47 0 0 0-.92-1c-.52-.18-1.32 0-1.5 1.51s.73 2.94 1.55 2.86a1 1 0 0 0 .57-.84 1.14 1.14 0 0 1-.53-1.4 1.25 1.25 0 0 1 .82-.88c.01-.15.01-.25.01-.25zM329.86 368a2.41 2.41 0 0 1 1.85-1.2c.9-.08 2.44.56 2.14 3s-2.62 4.37-3.9 3.92-.09-5.72-.09-5.72z" class="cls-46"/>
<path d="M330.55 368.85a1.47 1.47 0 0 1 1.12-.73c.55 0 1.28.31 1.1 1.82s-1.4 2.69-2.17 2.42a1 1 0 0 1-.36-1 1.14 1.14 0 0 0 .84-1.24 1.25 1.25 0 0 0-.59-1z" class="cls-45"/>
<rect width="1.32" height="4.18" x="313.49" y="360.4" class="cls-60" rx=".66" ry=".66" transform="rotate(-83.23 314.154 362.488)"/>
<rect width="1.32" height="4.18" x="324.9" y="361.75" class="cls-60" rx=".66" ry=".66" transform="rotate(-83.23 325.56 363.84)"/>
<path d="M321.2 373.17a2.52 2.52 0 1 1-5-.59z" class="cls-12"/>
<circle cx="325.58" cy="367.79" r="1.11" class="cls-8" transform="rotate(-83.23 325.58 367.785)"/>
<path d="M327.09 367.83a.34.34 0 0 0 .48-.48c-.68-.67-1.81-1.81-3.92-.39a.34.34 0 1 0 .38.57 2.08 2.08 0 0 1 3.06.31z" class="cls-14"/>
<circle cx="313.21" cy="366.32" r="1.11" class="cls-8" transform="rotate(-83.23 313.208 366.32)"/>
<path d="M311.72 366a.34.34 0 0 1-.36-.58c.81-.5 2.19-1.33 3.91.54a.34.34 0 1 1-.5.46 2.08 2.08 0 0 0-3-.42z" class="cls-14"/>
<path d="M303.59 398.22c.8-2.59 1.61-5.25 2.42-7.88l-5.43-.64c-.69 2.79-1.39 5.4-2.12 7.91z" class="cls-46"/>
<path d="M299.62 391.42l5.56 1.73 3.56-11.41c-4.19-1.24-6.64 2.58-7.29 4.44z" class="cls-58"/>
<path d="M299.582 391.507l.487-1.386 5.64 1.984-.487 1.387z" class="cls-1"/>
<path d="M327.5 401.06c-.17-2.71-.34-5.49-.51-8.23l5.43.64c0 2.87.08 5.57.21 8.19z" class="cls-46"/>
<path d="M332.95 395.38l-5.81.38-.79-11.93c4.36-.23 5.86 4.06 6.05 6z" class="cls-58"/>
<path d="M326.872 394.63l5.95-.607.148 1.463-5.95.607z" class="cls-1"/>
<path d="M240.28 390.81l122.07 14.49-13.55 56.4c-1.35 5.67-5.85 14.57-13.9 13.62l-84.33-10c-8-1-10.34-10.66-10.33-16.49z" class="cls-15"/>
<path d="M349.57 458.48l-.77 3.22c-1.35 5.67-5.85 14.57-13.9 13.62l-84.33-10c-8-1-10.34-10.66-10.33-16.49v-3.31c.9 5.48 3.76 11.74 10.27 12.48l86.16 10.23c6.52.77 10.75-4.63 12.9-9.75zM240.28 390.81l122.07 14.49-3.54 14.7-118.54-14.07.01-15.12z" class="cls-16"/>
<path d="M241.83 387.66l119.75 14.21a6.63 6.63 0 0 1 5.78 7.34A6.63 6.63 0 0 1 360 415l-119.73-14.22a6.63 6.63 0 0 1-5.78-7.34 6.63 6.63 0 0 1 7.34-5.78z" class="cls-20"/>
<path d="M241.83 387.66l119.75 14.21a6.63 6.63 0 0 1 5.78 7.34 6.56 6.56 0 0 1-.27 1.25 6.88 6.88 0 0 1-4.94 1.32L239 397.15a6.88 6.88 0 0 1-4.49-2.44 6.59 6.59 0 0 1 0-1.28 6.63 6.63 0 0 1 7.32-5.77z" class="cls-21"/>
<rect width="4.95" height="2.12" x="303.99" y="394.68" class="cls-61" rx="1.06" ry="1.06" transform="rotate(-83.23 306.474 395.745)"/>
<rect width="4.95" height="2.12" x="306.1" y="394.93" class="cls-61" rx="1.06" ry="1.06" transform="rotate(-83.23 308.58 395.994)"/>
<path d="M311 393.79a1.06 1.06 0 0 1 .93 1.18l-.33 2.81a1.06 1.06 0 0 1-1.18.93 1.06 1.06 0 0 1-.93-1.18l.33-2.81a1.06 1.06 0 0 1 1.18-.93z" class="cls-42"/>
<path d="M313.08 394a1.06 1.06 0 0 0-1.18.93l-.33 2.81a1.06 1.06 0 0 0 .93 1.18 1.06 1.06 0 0 0 1.18-.93l.33-2.81a1.06 1.06 0 0 0-.93-1.18zM321.85 395.08a1.06 1.06 0 0 1 .93 1.18l-.33 2.81a1.06 1.06 0 0 1-1.18.93 1.06 1.06 0 0 1-.93-1.18l.33-2.81a1.06 1.06 0 0 1 1.18-.93zM324 395.33a1.06 1.06 0 0 0-1.18.93l-.33 2.81a1.06 1.06 0 0 0 .93 1.18 1.06 1.06 0 0 0 1.18-.93l.33-2.81a1.06 1.06 0 0 0-.93-1.18z" class="cls-42"/>
<rect width="4.95" height="2.12" x="323.29" y="396.97" class="cls-61" rx="1.06" ry="1.06" transform="rotate(-83.23 325.77 398.033)"/>
<path d="M319.74 394.83a1.06 1.06 0 0 0-1.18.93l-.33 2.81a1.06 1.06 0 0 0 .93 1.18 1.06 1.06 0 0 0 1.18-.93l.33-2.81a1.06 1.06 0 0 0-.93-1.18z" class="cls-42"/>
<path d="M272.38 389.21a1.06 1.06 0 0 1 .93 1.18l-.31 2.8a1.06 1.06 0 0 1-1.18.93 1.06 1.06 0 0 1-.93-1.18l.33-2.81a1.06 1.06 0 0 1 1.16-.92z" class="cls-23"/>
<rect width="4.95" height="2.12" x="271.72" y="390.85" class="cls-62" rx="1.06" ry="1.06" transform="rotate(-83.23 274.19 391.91)"/>
<rect width="4.95" height="2.12" x="273.82" y="391.1" class="cls-62" rx="1.06" ry="1.06" transform="rotate(-83.23 276.302 392.165)"/>
<rect width="4.95" height="2.12" x="275.93" y="391.35" class="cls-62" rx="1.06" ry="1.06" transform="rotate(-83.23 278.407 392.413)"/>
<rect width="4.95" height="2.12" x="281.54" y="392.02" class="cls-62" rx="1.06" ry="1.06" transform="rotate(-83.23 284.02 393.08)"/>
<path d="M286.41 390.87a1.06 1.06 0 0 0-1.18.93l-.33 2.81a1.06 1.06 0 0 0 .93 1.18 1.06 1.06 0 0 0 1.18-.93l.33-2.81a1.06 1.06 0 0 0-.93-1.18z" class="cls-23"/>
<rect width="4.95" height="2.12" x="285.75" y="392.52" class="cls-62" rx="1.06" ry="1.06" transform="rotate(-83.23 288.23 393.576)"/>
<path d="M290.62 391.37a1.06 1.06 0 0 0-1.18.93l-.33 2.81a1.06 1.06 0 0 0 .93 1.18 1.06 1.06 0 0 0 1.18-.93l.33-2.81a1.06 1.06 0 0 0-.93-1.18z" class="cls-23"/>
<path d="M266.86 409.48s14.79 12.28 30.84 3.68l.83 1.55c-17.09 9.16-32.74-3.83-32.78-3.87zM301.23 413.56s14.79 12.28 30.84 3.68l.83 1.55c-17.09 9.16-32.74-3.83-32.79-3.87zM335.76 417.65s16.1 12.57 23.06 2.3l1.46 1c-8 11.84-25.55-1.85-25.6-1.89z" class="cls-21"/>
<path d="M264.27 412.88c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-16"/>
<path d="M263.6 410.06c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-20"/>
<path d="M263.6 410.06c-4.28 4.22-9.4 9.69-10.45 15a9.76 9.76 0 0 0 .73 6.08 11.44 11.44 0 0 0 5.61 2.35c7.17.6 11-4.13 11.17-9.11.2-4.44-2.23-9.59-4.85-14.11l-.81-.1z" class="cls-21"/>
<path d="M298.68 417c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17zM333.34 421.07c-4.28 4.22-9.41 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-16"/>
<path d="M332.35 418.21c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-20"/>
<path d="M332.35 418.21c-4.28 4.22-9.4 9.69-10.45 15a9.75 9.75 0 0 0 .73 6.08 11.44 11.44 0 0 0 5.61 2.35c7.17.6 11-4.13 11.17-9.11.2-4.44-2.23-9.59-4.85-14.11l-.81-.1z" class="cls-21"/>
<path d="M298 414.13c-4.28 4.22-9.4 9.69-10.45 15-1 4.89 1.62 10.37 8.73 11.47 7.17.6 11-4.13 11.17-9.11.24-5.45-3.46-12-6.63-17.07l-1.41-.17z" class="cls-20"/>
<path d="M298 414.13c-4.28 4.22-9.4 9.69-10.45 15a9.75 9.75 0 0 0 .73 6.08 11.44 11.44 0 0 0 5.61 2.35c7.17.6 11-4.13 11.17-9.11.2-4.44-2.23-9.59-4.85-14.11l-.81-.1zM263.57 409.08c-.05 0-18.86 8.36-23.22-3.25l-1.39.7c5 13.39 25.27 4.18 25.33 4.15z" class="cls-21"/>
<circle cx="299.56" cy="412.87" r="2.18" class="cls-63" transform="rotate(-83.23 299.563 412.878)"/>
<circle cx="265.19" cy="408.79" r="2.18" class="cls-63" transform="rotate(-83.23 265.19 408.8)"/>
<circle cx="333.93" cy="416.95" r="2.18" class="cls-63" transform="rotate(-83.23 333.935 416.956)"/>
<path d="M299.81 410.71a2.18 2.18 0 0 1 1.91 2.42v.09l-4.33-.51v-.09a2.18 2.18 0 0 1 2.42-1.91zM265.44 406.63a2.18 2.18 0 0 1 1.91 2.42v.09l-4.33-.51v-.09a2.18 2.18 0 0 1 2.42-1.91zM334.18 414.79a2.18 2.18 0 0 1 1.91 2.42v.09l-4.33-.51v-.09a2.18 2.18 0 0 1 2.42-1.91z" class="cls-64"/>
<path d="M359.32 417.89a2.18 2.18 0 0 1-1 4.15zM240.27 408a2.18 2.18 0 0 1 0-4.27V408z" class="cls-20"/>
<path d="M369.24 397.41l2.07 1.24c.59.35.93-.12 1.83-.68.69-.43 1.27-.29 2.63-.31a4.64 4.64 0 0 0 1.76-.67.89.89 0 0 0 .69-.8.78.78 0 0 0 .54-.87 1 1 0 0 1-.32-1.2.78.78 0 0 0-1-.45l-.14.06a.73.73 0 0 0-.8-.32 10.73 10.73 0 0 1-2.6.51.77.77 0 0 0-.13-.71.77.77 0 0 0-1.09-.11l-1.07.94a2.06 2.06 0 0 0-1.33.5 4.51 4.51 0 0 0-1.04 2.87z" class="cls-36"/>
<path d="M364 395.85c.1 1.88.85 6.82.86 6.82s1.51-2.23 4.88-6.84l2.36 2.78c-5.41 9-7.48 9.71-9.78 8-.88-.94-1.29-3.33-1.77-5.64a25.48 25.48 0 0 0-.91-3.54z" class="cls-36"/>
<path d="M364.57 397.44l-5.52.37-.75-11.32c4.14-.21 5.56 3.85 5.74 5.72z" class="cls-48"/>
<path d="M359.32 397.504l5.62-.49.165 1.903-5.62.49z" class="cls-65"/>
<circle cx="375.66" cy="391.02" r="4.54" class="cls-66" transform="rotate(-83.23 375.666 391.018)"/>
<path d="M373.7 386a2.64 2.64 0 1 0-2.32-.68 2.63 2.63 0 0 0 2.32.68z" class="cls-67"/>
<path d="M374.06 387.82a.23.23 0 1 1-.46.09 7.62 7.62 0 0 1 .67-3.77.23.23 0 1 1 .44.16 7.18 7.18 0 0 0-.65 3.52z" class="cls-68"/>
<circle cx="332.39" cy="150.75" r="145.02" class="cls-2" transform="rotate(-83.23 332.39 150.748)"/>
<path d="M405.16 72.93A203.65 203.65 0 0 0 377.3 62.8c.41 4.76.62 9.81.83 14.87 12.07-1.81 18.57-.56 27-4.75" class="cls-69"/>
<path d="M425.64 127.47a30.49 30.49 0 0 1 3.59 1.24c-.18-2.62-.17-5.38-.53-7.86a11.19 11.19 0 0 1-1 2 24 24 0 0 0-2 4.63" class="cls-70"/>
<path d="M310 254.66a25.25 25.25 0 0 1-.23-4.9 1.45 1.45 0 0 0-.07-.82 143.69 143.69 0 0 1-14.1-3c3.31 28.15 10.57 46.38 20.65 47.58a8 8 0 0 0 3.94-.34c-1.13-4.19-2.64-9.24-3.82-13-3.8-13.11-6.09-21.18-6.38-25.6" class="cls-71"/>
<path d="M268.12 55.36a200.3 200.3 0 0 1 45.05-6.18c7.06-19.78 15.52-35 24.35-42.4l-.52-.06c-26.12 2.58-50.68 20.77-68.92 48.64" class="cls-72"/>
<path d="M371 55.56C367.72 27.25 360.45 9.18 350.21 8S328.66 22.61 319 49.39a219.09 219.09 0 0 1 52 6.18" class="cls-73"/>
<path d="M339.25 92.05c7.6-6.57 10.54-5.4 20.9-9.21a129.19 129.19 0 0 1 12.65-4c-.24-6.2-.67-12.09-1.15-17.67a224 224 0 0 0-54.59-6.48c-2.22 6.39-4.16 13.14-6.13 20.21 10.73 2.57 20.94 6.87 28.32 17.16" class="cls-74"/>
<path d="M293.63 209.69c.17 10.9.55 21.33 1.23 30.67 4.73 1.21 9.49 2.1 14.24 3.15a31.34 31.34 0 0 0-2.33-7.74c-1.7-3.45-7.57-15.51-13.14-26.07" class="cls-75"/>
<path d="M363.1 10.14C369 18 373.47 32.15 376 52.25a24.12 24.12 0 0 1 .41 4.76 199.77 199.77 0 0 1 33.89 12.3 34.31 34.31 0 0 0 4.65-5c-11.25-27-29.36-46.67-51.85-54.21" class="cls-76"/>
<path d="M434.69 130.49c19.78 7.06 35 15.52 42.4 24.35l.06-.48c-2-21.5-14.52-41.82-34.6-58.65-2.79 4.38-5.66 9.39-9 15.65.54 6.4 1.08 12.79 1.14 19.13" class="cls-70"/>
<path d="M475.89 167.69c1.2-10.09-14.65-21.54-41.43-31.22a219.09 219.09 0 0 1-6.18 52c28.31-3.3 46.38-10.57 47.6-20.81" class="cls-77"/>
<path d="M394.74 175.75l-9.68 9.08c-.83 1.52-1.86 3.35-2.91 5.34a368.06 368.06 0 0 0 40.74-1.33 224 224 0 0 0 6.48-54.59c-1.7-.69-3.75-1.26-5.77-2-.06.48-.26.78-.31 1.26-4.09 11.2-8.67 23.81-28.55 42.23" class="cls-78"/>
<path d="M427.13 194.2a202.69 202.69 0 0 1-16.55 42.35c31.54-11.35 55-31 63.26-55.64v-.16c-7.86 6-22 10.53-42.15 13.18a26.83 26.83 0 0 0-4.58.27" class="cls-79"/>
<path d="M373 275.12A123.43 123.43 0 0 0 398.26 246c-5.87 1.58-11.84 2.65-18.15 3.85a54.87 54.87 0 0 1-3.77 14c-1.06 3.45-2.28 6.87-3.29 11.3" class="cls-80"/>
<path d="M214.35 81.61C227.3 71 243.51 63 260.2 57.83c12.37-20.77 29.47-39 50.13-48.92A145 145 0 0 0 206 83.38a69.68 69.68 0 0 1 8.33-1.77" class="cls-81"/>
<path d="M363.77 224.67l-.22.46c8.12 4 16.35 11.36 16.87 17.91-.06.48.07.82 0 1.3a168 168 0 0 0 22.2-5.16A191.78 191.78 0 0 0 421 194.94a410.77 410.77 0 0 1-42.18 1.16l-2.94 5.66c-5.18 9.45-10.62 19.68-12.14 22.91" class="cls-82"/>
<path d="M445.38 91c12.56 10.42 23.28 22.73 29.74 36.65a142.73 142.73 0 0 0-19.3-52.13c-1.21 2-2.75 4.06-4.3 6.15-1.64 2.89-3.8 6-6.14 9.34" class="cls-83"/>
<path d="M388.94 18.08c12.56 10.42 22.59 24.43 29.94 40.41A77.24 77.24 0 0 0 425 40a138.12 138.12 0 0 0-36-22" class="cls-84"/>
<path d="M240.31 224c-20.77-12.37-39-29.47-48.92-50.13a144.52 144.52 0 0 0 86.34 109.6c-17.79-14.29-30.1-36.37-37.42-59.48" class="cls-85"/>
<path d="M406.48 243.7c-9.28 15.29-21.21 29.63-34.93 39.85-.41 2.06-.64 4-1.22 6.19a144.16 144.16 0 0 0 95.76-83.28c-14.61 17.75-36.37 30.1-59.62 37.24" class="cls-86"/>
<path d="M255.76 65.09a127 127 0 0 0-29.68 14.82A212.18 212.18 0 0 0 251 75.08c1.54-3.39 3.24-6.76 4.76-10" class="cls-87"/>
<path d="M273.64 69c17.82 3.09 31.71 5.06 31.71 5.06 1.76-6.61 3.68-13.2 5.84-19.11a189.68 189.68 0 0 0-47.32 7.53c-1.86 3.35-3.72 6.7-5.6 10.21 8.48-3.05 7-4.36 15.37-3.69" class="cls-88"/>
<path d="M269.73 169c-6.85-5.2-12.47-11.22-17.8-16.89a132.84 132.84 0 0 0-13.08-12.92 263.93 263.93 0 0 0-1.67 27.72 379.55 379.55 0 0 0 40.74 11 15.6 15.6 0 0 0-2.62-3.88 34.76 34.76 0 0 0-5.57-5" class="cls-89"/>
<path d="M237.84 216.06a200.3 200.3 0 0 1-6.17-45.06c-19.16-6.82-33.8-14.73-41.54-23.44l-.88.55c3.28 25.71 21.19 49.92 48.59 67.94" class="cls-90"/>
<path d="M281.36 184.53A426.21 426.21 0 0 1 237.44 173a189.68 189.68 0 0 0 7.56 47.31 191.78 191.78 0 0 0 44.25 18.4 327.53 327.53 0 0 1-.95-39.89c-2-3.65-3.85-8.57-6.91-14.29" class="cls-3"/>
<path d="M211.14 134.64l-.18.14-15.78 9.82c6.32 6.92 19.28 14 36.7 20.43a201 201 0 0 1 2-29.47 28.08 28.08 0 0 0-10.9-3.89 15.51 15.51 0 0 0-11.88 3" class="cls-91"/>
<path d="M289.81 244.63a202.69 202.69 0 0 1-42.35-16.55c11.35 31.54 31 55 55.64 63.26h.16c-6-7.86-10.53-22-13.18-42.15a16.72 16.72 0 0 0-.27-4.58" class="cls-92"/>
<path d="M223.71 125.9c13.45 1.6 24 12.75 32.75 22.23 5.35 5.51 10.65 11.49 17 16.47a28.42 28.42 0 0 1 6.4 6.28 26.83 26.83 0 0 1 4.38 6.85c1.3 2.75 2.37 4.66 3.17 4.76 1.6.19 5.33-1.15 6.14-3.82 1.93-5.29-2.75-11-6.45-15.38-5-6.11-12.6-16.92-14.47-21.69-15-38.14 30.95-62.4 53.95-38.73 2.85-2.1 5.79-5 8-6.85-11.84-16.5-31.79-15.3-51.47-19.1-2.86-.5-10.79-2.09-13.11-1.72a28.57 28.57 0 0 0-6.6 2.3c-19.45 7.92-44.85 7.18-61 13.7a144.06 144.06 0 0 0-11.87 42.59c-.29 2.4-.57 4.8-.71 7.38L208 130a21 21 0 0 1 15.75-4.14" class="cls-93"/>
<path d="M374.24 84.51c-8 2-14.58 5.25-19.24 6.16-8.85 2-10.25 4.3-17.31 10.45a93 93 0 0 1-7.78 6.71 3.59 3.59 0 0 0 .55 2.18c1.86 3.47 5.2 2.73 8.4.35 33.68-25.22 54.89 2.62 56.14 18 .62 7.05 0 17.86-10.39 34.16 3.83 1.92 6.43 4.66 6.39 9 25.39-23.77 23.77-36.14 31.61-51.61C437.2 90.2 446 80.69 452.87 70.31a142.92 142.92 0 0 0-22.42-25.88C419.61 88 393.11 80.1 374.24 84.51" class="cls-94"/>
<path d="M367.08 274.74c2.36-13 8.36-18.49 7.46-31.42-.26-3.28-7.33-10.77-16-14.08l-2.82-.82 2.86-6.32c1.79-4.17 9.68-18.66 16.52-31.16.81-1.36 9.38-16.09 9.38-18.85-.2-2.46-2.73-4.38-4.65-4.61s-4.26 3.07-6.76 6.34c-3.43 4.3-5.73 8.57-8.66 12.77-3.13 4.5-6.62 9.28-10.31 14.36q-5.56 7.86-12.85 18a9.13 9.13 0 0 1-9.11 4.28c-3.2-.38-6.77-1.78-7.64-2.69s-21.65-31-26.91-37.29a12.8 12.8 0 0 1-8.51 4.67c7 13.81 20.46 39.6 23.07 44.95 3.91 8.09 3.18 12.88 3.81 21.23.26 6 7.51 27 10.81 40.24A143.35 143.35 0 0 0 364 291.1c1-5.56 2.13-11.11 3.08-16.36" class="cls-95"/>
<path d="M163 44.29a25.89 25.89 0 0 0-20.48 10 20.92 20.92 0 0 0-20.27 17.21 12.57 12.57 0 0 0-1.89-.14 12.38 12.38 0 1 0 0 24.77c30.16 0 66.81-1.64 100-1.64a9.55 9.55 0 1 0 0-19.11 9.68 9.68 0 0 0-1.92.19 19.38 19.38 0 0 0-31.2-14.72A25.94 25.94 0 0 0 163 44.29z" class="cls-4"/>
<path d="M167.72 295.05c2.43.86 4.29 2.11 7 1.43s5.42-1.17 6.39-.5 8.28 8 12.53 20.36-1.93 2.83-4 1.35-7.11-10.44-9.28-11.65-8.44.11-14.24-2c-1.7-.63-4.15.45-5.16 3.15s-1.93 6.53-1.93 6.53-5.38 1-10.92-5.66c1.81-1.85 6.19-4.06 7.18-5.23.41-1.12-.83-2.46-2-3.37a64.77 64.77 0 0 1-7.81-6.77c-2.3-2.63-4.87-1.72-7.8-1.58s-8.72 1.25-11.19 1.08-6.23.22-.57-2.3 19.5-7.48 24.5-6.46c3.25.67 2.67 1.71 4.19 3.69s4.68 6.38 6.9 4.7a17.2 17.2 0 0 1 4.86-3c1.49-.52 2.92-.15 2.4 1.56s-1.05 4.67-1.05 4.67z" class="cls-96"/>
<path d="M171.42 288.28a4 4 0 0 0-3.2.9c-.66.75.57.91.82.78a26 26 0 0 1 2.38-1.68z" class="cls-97"/>
<ellipse cx="166.11" cy="289.09" class="cls-98" rx=".48" ry=".38" transform="rotate(-34.42 166.098 289.075)"/>
<path d="M118.86 326c1.88.3 3.4 1 5.29.11s3.78-1.57 4.57-1.21 7.07 4.68 11.8 13.12-1 2.31-2.69 1.5-6.55-6.63-8.28-7.23-6.11 1.2-10.61.42c-1.31-.23-3 .87-3.33 3s-.54 5-.54 5-3.78 1.42-8.68-2.66c1.07-1.58 4-3.77 4.52-4.75.15-.87-.93-1.67-1.87-2.19a47.81 47.81 0 0 1-6.57-3.88c-2-1.61-3.77-.61-5.87-.11s-6.17 2.06-8 2.27-4.5 1-.72-1.6 13.17-8 16.93-7.94c2.45.05 2.17.89 3.54 2.12s4.25 4 5.64 2.5a12.69 12.69 0 0 1 3.13-2.81c1-.57 2.1-.49 1.95.81s-.21 3.53-.21 3.53z" class="cls-96"/>
<path d="M120.65 320.64a2.92 2.92 0 0 0-2.21 1.08c-.38.63.53.59.7.46a19.22 19.22 0 0 1 1.51-1.54z" class="cls-97"/>
<ellipse cx="116.9" cy="321.94" class="cls-98" rx=".35" ry=".28" transform="rotate(-44.77 116.913 321.942)"/>
<path d="M117.91 237.8c1.29-.47 2.69-1.4 3.91-4.27s2-2.28 4.38-3.45a25.09 25.09 0 0 1 8.88-1.92c3-.18-2.8 3.39-4 4s-3.8 1.4-4.09 2.34a18.45 18.45 0 0 1-2.63 4.5c-.76.76-3.16 2.63-2.34 3.68s3.91 2.1 4.27 2.22-1 3.8-4.5 4.33a23.51 23.51 0 0 1-1.76-4c-.29-.65-1.87-1-3.39.47s-4.32 2.1-5.49 2.4-3 3.27-4.55 4.38-3.45 3-4.33 3.27.53-4 1.11-5a63.46 63.46 0 0 1 5.37-7c.88-.7 2.87-.64 4.62-1.75s1.75-1.23 1.69-1.81-1.87-2.16-2.1-2.75.24-.93 1-.87a11.35 11.35 0 0 1 3.95 1.23z" class="cls-96"/>
<path d="M111.55 236.06a15.21 15.21 0 0 1 1.52 1.25c.22.25.6-.38.55-.58a2.87 2.87 0 0 0-2.07-.67z" class="cls-97"/>
<ellipse cx="114.62" cy="236.83" class="cls-98" rx=".27" ry=".21"/>
</svg>

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: maintenance
namespace: human-connection
labels:
human-connection.org/selector: deployment-human-connection-maintenance
spec:
ports:
- name: web
port: 80
targetPort: 80
selector:
human-connection.org/selector: deployment-human-connection-maintenance

View File

@ -1 +1,2 @@
.ssh/
ssh/

View File

@ -9,4 +9,4 @@
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storage: 5Gi

View File

@ -23,11 +23,7 @@ So, all we have to do is edit the kubernetes deployment of our Neo4J database
and set a custom `command` every time we have to carry out tasks like backup,
restore, seed etc.
{% hint style="info" %}
TODO: implement maintenance mode
{% endhint %}
First bring the application into maintenance mode to ensure there are no
First bring the application into [maintenance mode](https://github.com/Human-Connection/Human-Connection/blob/master/deployment/human-connection/maintenance/README.md) to ensure there are no
database connections left and nobody can access the application.
Run the following:

View File

@ -42,6 +42,14 @@ services:
context: neo4j
networks:
- hc-network
maintenance:
image: humanconnection/maintenance:latest
build:
context: deployment/human-connection/maintenance
networks:
- hc-network
ports:
- 80:80
networks:
hc-network:

View File

@ -23,14 +23,14 @@
"codecov": "^3.5.0",
"cross-env": "^5.2.0",
"cypress": "^3.4.0",
"cypress-cucumber-preprocessor": "^1.12.0",
"cypress-cucumber-preprocessor": "^1.13.0",
"cypress-file-upload": "^3.3.2",
"cypress-plugin-retries": "^1.2.2",
"dotenv": "^8.0.0",
"faker": "Marak/faker.js#master",
"graphql-request": "^1.8.2",
"neo4j-driver": "^1.7.5",
"neode": "^0.2.16",
"neode": "^0.2.18",
"npm-run-all": "^4.1.5",
"slug": "^1.1.0"
}

View File

@ -4,7 +4,9 @@ docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t huma
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-web:latest $TRAVIS_BUILD_DIR/webapp
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT -t humanconnection/neo4j:latest $TRAVIS_BUILD_DIR/neo4j
docker build -t humanconnection/maintenance-worker:latest $TRAVIS_BUILD_DIR/deployment/legacy-migration/maintenance-worker
docker build -t humanconnection/maintenance:latest $TRAVIS_BUILD_DIR/deployment/human-connection/maintenance
docker push humanconnection/nitro-backend:latest
docker push humanconnection/nitro-web:latest
docker push humanconnection/neo4j:latest
docker push humanconnection/maintenance-worker:latest
docker push humanconnection/maintenance-worker:latest
docker push humanconnection/maintenance:latest

View File

@ -1,4 +1,4 @@
FROM node:12.6-alpine as base
FROM node:12.7-alpine as base
LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)"
EXPOSE 3000

View File

@ -14,6 +14,8 @@ localVue.use(Styleguide)
localVue.use(Filters)
config.stubs['no-ssr'] = '<span><slot /></span>'
config.stubs['nuxt-link'] = '<span><slot /></span>'
config.stubs['v-popover'] = '<span><slot /></span>'
describe('ContributionForm.vue', () => {
let wrapper
@ -24,9 +26,22 @@ describe('ContributionForm.vue', () => {
let mocks
let propsData
const postTitle = 'this is a title for a post'
const postTitleTooShort = 'xx'
let postTitleTooLong = ''
for (let i = 0; i < 65; i++) {
postTitleTooLong += 'x'
}
const postContent = 'this is a post'
const postContentTooShort = 'xx'
let postContentTooLong = ''
for (let i = 0; i < 2001; i++) {
postContentTooLong += 'x'
}
const imageUpload = {
file: { filename: 'avataar.svg', previewElement: '' },
file: {
filename: 'avataar.svg',
previewElement: '',
},
url: 'someUrlToImage',
}
const image = '/uploads/1562010976466-avataaars'
@ -34,22 +49,17 @@ describe('ContributionForm.vue', () => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockResolvedValueOnce({
data: {
CreatePost: {
title: postTitle,
slug: 'this-is-a-title-for-a-post',
content: postContent,
contentExcerpt: postContent,
language: 'en',
},
mutate: jest.fn().mockResolvedValueOnce({
data: {
CreatePost: {
title: postTitle,
slug: 'this-is-a-title-for-a-post',
content: postContent,
contentExcerpt: postContent,
language: 'en',
},
})
.mockRejectedValue({
message: 'Not Authorised!',
}),
},
}),
},
$toast: {
error: jest.fn(),
@ -71,6 +81,13 @@ describe('ContributionForm.vue', () => {
'editor/placeholder': () => {
return 'some cool placeholder'
},
'auth/user': () => {
return {
id: '4711',
name: 'You yourself',
slug: 'you-yourself',
}
},
}
const store = new Vuex.Store({
getters,
@ -106,16 +123,53 @@ describe('ContributionForm.vue', () => {
})
describe('invalid form submission', () => {
it('title required for form submission', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
await wrapper.find('form').trigger('submit')
it('title and content should not be empty ', async () => {
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('content required for form submission', async () => {
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
it('title should not be empty', async () => {
await wrapper.vm.updateEditorContent(postContent)
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('title should not be too long', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitleTooLong)
await wrapper.vm.updateEditorContent(postContent)
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('title should not be too short', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitleTooShort)
await wrapper.vm.updateEditorContent(postContent)
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('content should not be empty', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
await wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('content should not be too short', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
await wrapper.vm.updateEditorContent(postContentTooShort)
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('content should not be too long', async () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
await wrapper.vm.updateEditorContent(postContentTooLong)
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
})
@ -136,15 +190,16 @@ describe('ContributionForm.vue', () => {
}
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
await wrapper.vm.updateEditorContent(postContent)
})
it('with title and content', () => {
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
it("sends a fallback language based on a user's locale", () => {
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
@ -152,7 +207,7 @@ describe('ContributionForm.vue', () => {
expectedParams.variables.language = 'de'
deutschOption = wrapper.findAll('li').at(0)
deutschOption.trigger('click')
await wrapper.find('form').trigger('submit')
wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
@ -160,22 +215,26 @@ describe('ContributionForm.vue', () => {
const categoryIds = ['cat12', 'cat15', 'cat37']
expectedParams.variables.categoryIds = categoryIds
wrapper.find(CategoriesSelect).vm.$emit('updateCategories', categoryIds)
await wrapper.find('form').trigger('submit')
await wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
it('supports adding a teaser image', async () => {
expectedParams.variables.imageUpload = imageUpload
wrapper.find(TeaserImage).vm.$emit('addTeaserImage', imageUpload)
await wrapper.find('form').trigger('submit')
await wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
it("pushes the user to the post's page", async () => {
wrapper.find('.submit-button-for-test').trigger('click')
await mocks.$apollo.mutate
expect(mocks.$router.push).toHaveBeenCalledTimes(1)
})
it('shows a success toaster', () => {
it('shows a success toaster', async () => {
wrapper.find('.submit-button-for-test').trigger('click')
await mocks.$apollo.mutate
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
})
@ -191,18 +250,19 @@ describe('ContributionForm.vue', () => {
describe('handles errors', () => {
beforeEach(async () => {
jest.useFakeTimers()
mocks.$apollo.mutate = jest.fn().mockRejectedValueOnce({
message: 'Not Authorised!',
})
wrapper = Wrapper()
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
// second submission causes mutation to reject
await wrapper.find('form').trigger('submit')
await wrapper.vm.updateEditorContent(postContent)
})
it('shows an error toaster when apollo mutation rejects', async () => {
await wrapper.find('form').trigger('submit')
await wrapper.find('.submit-button-for-test').trigger('click')
await mocks.$apollo.mutate
expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!')
})
})
})
@ -217,7 +277,12 @@ describe('ContributionForm.vue', () => {
content: 'auf Deutsch geschrieben',
language: 'de',
image,
categories: [{ id: 'cat12', name: 'Democracy & Politics' }],
categories: [
{
id: 'cat12',
name: 'Democracy & Politics',
},
],
},
}
wrapper = Wrapper()
@ -255,7 +320,7 @@ describe('ContributionForm.vue', () => {
postTitleInput = wrapper.find('.ds-input')
postTitleInput.setValue(postTitle)
wrapper.vm.updateEditorContent(postContent)
await wrapper.find('form').trigger('submit')
await wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
@ -266,7 +331,7 @@ describe('ContributionForm.vue', () => {
wrapper.vm.updateEditorContent(postContent)
expectedParams.variables.categoryIds = categoryIds
wrapper.find(CategoriesSelect).vm.$emit('updateCategories', categoryIds)
await wrapper.find('form').trigger('submit')
await wrapper.find('.submit-button-for-test').trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
})
})

View File

@ -1,5 +1,5 @@
<template>
<ds-form ref="contributionForm" v-model="form" :schema="formSchema" @submit="submit">
<ds-form ref="contributionForm" v-model="form" :schema="formSchema">
<template slot-scope="{ errors }">
<ds-card>
<hc-teaser-image :contribution="contribution" @addTeaserImage="addTeaserImage">
@ -9,7 +9,11 @@
:src="contribution.image | proxyApiUrl"
/>
</hc-teaser-image>
<ds-space />
<hc-user :user="currentUser" :trunc="35" />
<ds-space />
<ds-input model="title" class="post-title" placeholder="Title" name="title" autofocus />
<small class="smallTag">{{ form.title.length }}/{{ formSchema.title.max }}</small>
<no-ssr>
<hc-editor
:users="users"
@ -17,6 +21,7 @@
:value="form.content"
@input="updateEditorContent"
/>
<small class="smallTag">{{ form.contentLength }}/{{ contentMax }}</small>
</no-ssr>
<ds-space margin-bottom="xxx-large" />
<hc-categories-select
@ -41,18 +46,20 @@
<div slot="footer" style="text-align: right">
<ds-button
class="cancel-button"
:disabled="loading || disabled"
:disabled="loading"
ghost
@click.prevent="$router.back()"
>
{{ $t('actions.cancel') }}
</ds-button>
<ds-button
class="submit-button-for-test"
type="submit"
icon="check"
:loading="loading"
:disabled="disabled || errors"
:disabled="disabledByContent || errors"
primary
@click.prevent="submit"
>
{{ $t('actions.save') }}
</ds-button>
@ -65,18 +72,21 @@
<script>
import gql from 'graphql-tag'
import HcEditor from '~/components/Editor/Editor'
import orderBy from 'lodash/orderBy'
import { mapGetters } from 'vuex'
import HcEditor from '~/components/Editor/Editor'
import locales from '~/locales'
import PostMutations from '~/graphql/PostMutations.js'
import HcCategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
import HcTeaserImage from '~/components/TeaserImage/TeaserImage'
import HcUser from '~/components/User'
export default {
components: {
HcEditor,
HcCategoriesSelect,
HcTeaserImage,
HcUser,
},
props: {
contribution: { type: Object, default: () => {} },
@ -86,6 +96,7 @@ export default {
form: {
title: '',
content: '',
contentLength: 0,
teaserImage: null,
image: null,
language: null,
@ -94,13 +105,16 @@ export default {
},
formSchema: {
title: { required: true, min: 3, max: 64 },
content: { required: true, min: 3 },
content: [{ required: true }],
},
id: null,
loading: false,
disabled: false,
disabledByContent: true,
slug: null,
users: [],
contentMin: 3,
contentMax: 2000,
hashtags: [],
}
},
@ -113,8 +127,9 @@ export default {
}
this.id = contribution.id
this.slug = contribution.slug
this.form.content = contribution.content
this.form.title = contribution.title
this.form.content = contribution.content
this.manageContent(this.form.content)
this.form.image = contribution.image
this.form.categoryIds = this.categoryIds(contribution.categories)
},
@ -128,6 +143,9 @@ export default {
: locales.find(loc => this.$i18n.locale() === loc.code)
return locale.name
},
...mapGetters({
currentUser: 'auth/user',
}),
},
mounted() {
this.availableLocales()
@ -160,7 +178,7 @@ export default {
.then(res => {
this.loading = false
this.$toast.success(this.$t('contribution.success'))
this.disabled = true
this.disabledByContent = true
const result = res.data[this.id ? 'UpdatePost' : 'CreatePost']
this.$router.push({
@ -171,12 +189,21 @@ export default {
.catch(err => {
this.$toast.error(err.message)
this.loading = false
this.disabled = false
this.disabledByContent = false
})
},
updateEditorContent(value) {
// this.form.content = value
// TODO: Do smth????? what is happening
this.$refs.contributionForm.update('content', value)
this.manageContent(value)
},
manageContent(content) {
// filter HTML out of content value
const str = content.replace(/<\/?[^>]+(>|$)/gm, '')
// Set counter length of text
this.form.contentLength = str.length
// Enable save button if requirements are met
this.disabledByContent = !(this.contentMin <= str.length && str.length <= this.contentMax)
},
availableLocales() {
orderBy(locales, 'name').map(locale => {
@ -233,6 +260,11 @@ export default {
</script>
<style lang="scss">
.smallTag {
width: 100%;
position: relative;
left: 90%;
}
.post-title {
margin-top: $space-x-small;
margin-bottom: $space-xx-small;

View File

@ -79,8 +79,8 @@ export default i18n => {
export const filterPosts = i18n => {
const lang = i18n.locale().toUpperCase()
return gql`
query Post($filter: _PostFilter, $first: Int, $offset: Int) {
Post(filter: $filter, first: $first, offset: $offset) {
query Post($filter: _PostFilter, $first: Int, $offset: Int, $orderBy: [_PostOrdering]) {
Post(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
id
title
contentExcerpt

View File

@ -23,6 +23,12 @@
"bank": "Bankverbindung",
"germany": "Deutschland"
},
"sorting": {
"newest": "Neuste",
"oldest": "Älteste",
"popular": "Beliebt",
"commented": "meist Kommentiert"
},
"login": {
"copy": "Wenn Du bereits ein Konto bei Human Connection hast, melde Dich bitte hier an.",
"login": "Einloggen",
@ -168,7 +174,8 @@
},
"social-media": {
"name": "Soziale Medien",
"placeholder": "Füge eine Social-Media URL hinzu",
"placeholder": "Deine Social-Media URL",
"requireUnique": "Dieser Link existiert bereits",
"submit": "Link hinzufügen",
"successAdd": "Social-Media hinzugefügt. Profil aktualisiert!",
"successDelete": "Social-Media gelöscht. Profil aktualisiert!"
@ -286,6 +293,7 @@
"reportContent": "Melden",
"validations": {
"email": "muss eine gültige E-Mail Adresse sein",
"url": "muss eine gültige URL sein",
"verification-code": "muss genau 6 Buchstaben lang sein"
}
},

View File

@ -23,6 +23,12 @@
"bank": "bank account",
"germany": "Germany"
},
"sorting": {
"newest": "Newest",
"oldest": "Oldest",
"popular": "Popular",
"commented": "most Commented"
},
"login": {
"copy": "If you already have a human-connection account, login here.",
"login": "Login",
@ -169,7 +175,8 @@
},
"social-media": {
"name": "Social media",
"placeholder": "Add social media url",
"placeholder": "Your social media url",
"requireUnique": "You added this url already",
"submit": "Add link",
"successAdd": "Added social media. Updated user profile!",
"successDelete": "Deleted social media. Updated user profile!"
@ -288,6 +295,7 @@
"reportContent": "Report",
"validations": {
"email": "must be a valid email address",
"url": "must be a valid URL",
"verification-code": "must be 6 characters long"
}
},

View File

@ -58,9 +58,9 @@
"accounting": "~0.4.1",
"apollo-cache-inmemory": "~1.5.1",
"apollo-client": "~2.6.3",
"cookie-universal-nuxt": "~2.0.16",
"cookie-universal-nuxt": "~2.0.17",
"cross-env": "~5.2.0",
"date-fns": "2.0.0-beta.2",
"date-fns": "2.0.0-beta.3",
"express": "~4.17.1",
"graphql": "~14.4.2",
"isemail": "^3.2.0",
@ -77,7 +77,7 @@
"vue-count-to": "~1.0.13",
"vue-izitoast": "1.1.2",
"vue-sweetalert-icons": "~3.2.0",
"vuex-i18n": "~1.11.0",
"vuex-i18n": "~1.13.0",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
@ -88,7 +88,7 @@
"@storybook/vue": "~5.1.9",
"@babel/preset-env": "~7.5.5",
"@vue/cli-shared-utils": "~3.9.0",
"@vue/eslint-config-prettier": "~4.0.1",
"@vue/eslint-config-prettier": "~5.0.0",
"@vue/server-test-utils": "~1.0.0-beta.29",
"@vue/test-utils": "~1.0.0-beta.29",
"babel-core": "~7.0.0-bridge.0",
@ -101,13 +101,14 @@
"eslint-config-prettier": "~6.0.0",
"eslint-config-standard": "~12.0.0",
"eslint-loader": "~2.2.1",
"eslint-plugin-import": "~2.18.0",
"eslint-plugin-jest": "~22.11.1",
"eslint-plugin-import": "~2.18.2",
"eslint-plugin-jest": "~22.14.0",
"eslint-plugin-node": "~9.1.0",
"eslint-plugin-prettier": "~3.1.0",
"eslint-plugin-promise": "~4.2.1",
"eslint-plugin-standard": "~4.0.0",
"eslint-plugin-vue": "~5.2.3",
"flush-promises": "^1.0.2",
"fuse.js": "^3.4.5",
"jest": "~24.8.0",
"node-sass": "~4.12.0",

140
webapp/pages/index.spec.js Normal file
View File

@ -0,0 +1,140 @@
import { config, shallowMount, mount, createLocalVue } from '@vue/test-utils'
import PostIndex from './index.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
import Filters from '~/plugins/vue-filters'
import VTooltip from 'v-tooltip'
import FilterMenu from '~/components/FilterMenu/FilterMenu'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
localVue.use(Filters)
localVue.use(VTooltip)
config.stubs['no-ssr'] = '<span><slot /></span>'
config.stubs['router-link'] = '<span><slot /></span>'
config.stubs['nuxt-link'] = '<span><slot /></span>'
describe('PostIndex', () => {
let wrapper
let Wrapper
let store
let mocks
beforeEach(() => {
store = new Vuex.Store({
getters: {
'posts/posts': () => {
return [
{
id: 'p23',
name: 'It is a post',
author: {
id: 'u1',
},
},
]
},
'auth/user': () => {
return { id: 'u23' }
},
},
})
mocks = {
$t: key => key,
$filters: {
truncate: a => a,
removeLinks: jest.fn(),
},
// If you are mocking router, than don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
$router: {
history: {
push: jest.fn(),
},
push: jest.fn(),
},
$toast: {
success: jest.fn(),
error: jest.fn(),
},
$apollo: {
mutate: jest.fn().mockResolvedValue(),
queries: {
Post: {
refetch: jest.fn(),
fetchMore: jest.fn().mockResolvedValue([
{
id: 'p23',
name: 'It is a post',
author: {
id: 'u1',
},
},
]),
},
},
},
$route: {
query: {},
},
}
})
describe('shallowMount', () => {
Wrapper = () => {
return shallowMount(PostIndex, {
store,
mocks,
localVue,
})
}
beforeEach(() => {
wrapper = Wrapper()
})
it('refetches Posts when changeFilterBubble is emitted', () => {
wrapper.find(FilterMenu).vm.$emit('changeFilterBubble')
expect(mocks.$apollo.queries.Post.refetch).toHaveBeenCalledTimes(1)
})
it('clears the search when the filter menu emits clearSearch', () => {
wrapper.find(FilterMenu).vm.$emit('clearSearch')
expect(wrapper.vm.hashtag).toBeNull()
})
it('calls the changeFilterBubble if there are hasthags in the route query', () => {
mocks.$route.query.hashtag = { id: 'hashtag' }
wrapper = Wrapper()
expect(mocks.$apollo.queries.Post.refetch).toHaveBeenCalledTimes(1)
})
describe('mount', () => {
beforeEach(() => {
wrapper = mount(PostIndex, {
store,
mocks,
localVue,
})
})
it('sets the post in the store when there are posts', () => {
wrapper
.findAll('li')
.at(0)
.trigger('click')
expect(wrapper.vm.sorting).toEqual('createdAt_desc')
})
it('loads more posts when a user clicks on the load more button', () => {
wrapper
.findAll('button')
.at(2)
.trigger('click')
expect(mocks.$apollo.queries.Post.fetchMore).toHaveBeenCalledTimes(1)
})
})
})
})

View File

@ -9,6 +9,17 @@
@clearSearch="clearSearch"
/>
</ds-flex-item>
<ds-flex-item>
<div class="sorting-dropdown">
<ds-select
v-model="selected"
:options="sortingOptions"
size="large"
v-bind:icon-right="sortingIcon"
@input="toggleOnlySorting"
></ds-select>
</div>
</ds-flex-item>
<hc-post-card
v-for="(post, index) in posts"
:key="post.id"
@ -53,6 +64,36 @@ export default {
pageSize: 12,
filter: {},
hashtag,
placeholder: this.$t('sorting.newest'),
selected: this.$t('sorting.newest'),
sortingIcon: 'sort-amount-desc',
sorting: 'createdAt_desc',
sortingOptions: [
{
label: this.$t('sorting.newest'),
value: 'Newest',
icons: 'sort-amount-desc',
order: 'createdAt_desc',
},
{
label: this.$t('sorting.oldest'),
value: 'Oldest',
icons: 'sort-amount-asc',
order: 'createdAt_asc',
},
{
label: this.$t('sorting.popular'),
value: 'Popular',
icons: 'fire',
order: 'shoutedCount_desc',
},
{
label: this.$t('sorting.commented'),
value: 'Commented',
icons: 'comment',
order: 'commentsCount_desc',
},
],
}
},
mounted() {
@ -89,7 +130,12 @@ export default {
}
}
this.filter = filter
this.$apollo.queries.Post.refresh()
this.$apollo.queries.Post.refetch()
},
toggleOnlySorting(x) {
this.sortingIcon = x.icons
this.sorting = x.order
this.$apollo.queries.Post.refetch()
},
clearSearch() {
this.$router.push({ path: '/' })
@ -144,6 +190,7 @@ export default {
filter: this.filter,
first: this.pageSize,
offset: 0,
orderBy: this.sorting,
}
},
fetchPolicy: 'cache-and-network',
@ -161,4 +208,11 @@ export default {
transform: translate(-120%, -120%);
box-shadow: $box-shadow-x-large;
}
.sorting-dropdown {
width: 250px;
position: relative;
float: right;
padding: 0 18px;
}
</style>

View File

@ -1,4 +1,5 @@
import { mount, createLocalVue } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import MySocialMedia from './my-social-media.vue'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
@ -12,23 +13,17 @@ localVue.use(Filters)
describe('my-social-media.vue', () => {
let wrapper
let store
let mocks
let getters
let input
let submitBtn
const socialMediaUrl = 'https://freeradical.zone/@mattwr18'
const newSocialMediaUrl = 'https://twitter.com/mattwr18'
const faviconUrl = 'https://freeradical.zone/favicon.ico'
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({
data: { CreateSocialMeda: { id: 's1', url: socialMediaUrl } },
}),
mutate: jest.fn(),
},
$toast: {
error: jest.fn(),
@ -43,79 +38,161 @@ describe('my-social-media.vue', () => {
})
describe('mount', () => {
let form, input, submitButton
const Wrapper = () => {
store = new Vuex.Store({
const store = new Vuex.Store({
getters,
})
return mount(MySocialMedia, { store, mocks, localVue })
}
it('renders', () => {
wrapper = Wrapper()
expect(wrapper.contains('div')).toBe(true)
})
describe('given currentUser has a social media account linked', () => {
describe('adding social media link', () => {
beforeEach(() => {
getters = {
'auth/user': () => {
return {
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}
},
}
})
it("displays a link to the currentUser's social media", () => {
wrapper = Wrapper()
const socialMediaLink = wrapper.find('a').attributes().href
expect(socialMediaLink).toBe(socialMediaUrl)
form = wrapper.find('form')
input = wrapper.find('input#addSocialMedia')
submitButton = wrapper.find('button')
})
beforeEach(() => {
mocks = {
$t: jest.fn(),
$apollo: {
mutate: jest
.fn()
.mockRejectedValue({ message: 'Ouch!' })
.mockResolvedValueOnce({
data: { DeleteSocialMeda: { id: 's1', url: socialMediaUrl } },
}),
},
$toast: {
error: jest.fn(),
success: jest.fn(),
},
}
getters = {
'auth/user': () => {
return {
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}
},
}
it('requires the link to be a valid url', () => {
input.setValue('some value')
form.trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
it('displays a trash sympol after a social media and allows the user to delete it', () => {
wrapper = Wrapper()
const deleteSelector = wrapper.find({ name: 'delete' })
expect(deleteSelector).toEqual({ selector: 'Component' })
const icon = wrapper.find({ name: 'trash' })
icon.trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
it('displays an error message when not saved successfully', async () => {
mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' })
input.setValue(newSocialMediaUrl)
form.trigger('submit')
await flushPromises()
expect(mocks.$toast.error).toHaveBeenCalledTimes(1)
})
describe('success', () => {
beforeEach(() => {
mocks.$apollo.mutate.mockResolvedValue({
data: { CreateSocialMedia: { id: 's2', url: newSocialMediaUrl } },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
})
it('sends the new url to the backend', () => {
const expected = expect.objectContaining({
variables: { url: newSocialMediaUrl },
})
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
it('clears the form', async () => {
await flushPromises()
expect(input.value).toBe(undefined)
expect(submitButton.vm.$attrs.disabled).toBe(true)
})
})
})
describe('currentUser does not have a social media account linked', () => {
it('allows a user to add a social media link', () => {
describe('given existing social media links', () => {
beforeEach(() => {
getters = {
'auth/user': () => ({
socialMedia: [{ id: 's1', url: socialMediaUrl }],
}),
}
wrapper = Wrapper()
input = wrapper.find({ name: 'social-media' })
input.element.value = socialMediaUrl
input.trigger('input')
submitBtn = wrapper.find('.ds-button')
submitBtn.trigger('click')
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
form = wrapper.find('form')
})
describe('for each link it', () => {
it('displays the favicon', () => {
expect(wrapper.find(`img[src="${faviconUrl}"]`).exists()).toBe(true)
})
it('displays the url', () => {
expect(wrapper.find(`a[href="${socialMediaUrl}"]`).exists()).toBe(true)
})
it('displays the edit button', () => {
expect(wrapper.find('a[name="edit"]').exists()).toBe(true)
})
it('displays the delete button', () => {
expect(wrapper.find('a[name="delete"]').exists()).toBe(true)
})
})
it('does not accept a duplicate url', () => {
input = wrapper.find('input#addSocialMedia')
input.setValue(socialMediaUrl)
form.trigger('submit')
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
describe('editing social media link', () => {
beforeEach(() => {
const editButton = wrapper.find('a[name="edit"]')
editButton.trigger('click')
input = wrapper.find('input#editSocialMedia')
})
it('disables adding new links while editing', () => {
const addInput = wrapper.find('input#addSocialMedia')
expect(addInput.exists()).toBe(false)
})
it('sends the new url to the backend', () => {
const expected = expect.objectContaining({
variables: { id: 's1', url: newSocialMediaUrl },
})
input.setValue(newSocialMediaUrl)
form.trigger('submit')
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('allows the user to cancel editing', () => {
const cancelButton = wrapper.find('button#cancel')
cancelButton.trigger('click')
expect(wrapper.find('input#editSocialMedia').exists()).toBe(false)
})
})
describe('deleting social media link', () => {
beforeEach(() => {
const deleteButton = wrapper.find('a[name="delete"]')
deleteButton.trigger('click')
})
it('sends the link id to the backend', () => {
const expected = expect.objectContaining({
variables: { id: 's1' },
})
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})
it('displays a success message', async () => {
await flushPromises()
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
})
})
})
})

View File

@ -1,49 +1,90 @@
<template>
<ds-card :header="$t('settings.social-media.name')">
<ds-space v-if="socialMediaLinks" margin-top="base" margin="x-small">
<ds-list>
<ds-list-item v-for="link in socialMediaLinks" :key="link.id">
<a :href="link.url" target="_blank">
<img :src="link.favicon | proxyApiUrl" alt="Social Media link" width="16" height="16" />
{{ link.url }}
</a>
&nbsp;&nbsp;
<span class="layout-leave-active">|</span>
&nbsp;&nbsp;
<ds-icon name="edit" class="layout-leave-active" />
<a name="delete" @click="handleDeleteSocialMedia(link)">
<ds-icon name="trash" />
</a>
</ds-list-item>
</ds-list>
</ds-space>
<ds-space margin-top="base">
<div>
<ds-input
v-model="value"
:placeholder="$t('settings.social-media.placeholder')"
name="social-media"
:schema="{ type: 'url' }"
/>
</div>
<ds-space margin-top="base">
<div>
<ds-button primary @click="handleAddSocialMedia">
{{ $t('settings.social-media.submit') }}
</ds-button>
</div>
<ds-form
v-model="formData"
:schema="formSchema"
@input="handleInput"
@input-valid="handleInputValid"
@submit="handleSubmitSocialMedia"
>
<ds-card :header="$t('settings.social-media.name')">
<ds-space v-if="socialMediaLinks" margin-top="base" margin="x-small">
<ds-list>
<ds-list-item v-for="link in socialMediaLinks" :key="link.id" class="list-item--high">
<ds-input
v-if="editingLink.id === link.id"
id="editSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<template v-else>
<a :href="link.url" target="_blank">
<img :src="link.favicon" alt="Link:" height="16" width="16" />
{{ link.url }}
</a>
<span class="divider">|</span>
<a name="edit" @click="handleEditSocialMedia(link)">
<ds-icon
:aria-label="$t('actions.edit')"
class="icon-button"
name="edit"
:title="$t('actions.edit')"
/>
</a>
<a name="delete" @click="handleDeleteSocialMedia(link)">
<ds-icon
:aria-label="$t('actions.delete')"
class="icon-button"
name="trash"
:title="$t('actions.delete')"
/>
</a>
</template>
</ds-list-item>
</ds-list>
</ds-space>
</ds-space>
</ds-card>
<ds-space margin-top="base">
<ds-input
v-if="!editingLink.id"
id="addSocialMedia"
model="socialMediaUrl"
type="text"
:placeholder="$t('settings.social-media.placeholder')"
/>
<ds-space margin-top="base">
<ds-button primary :disabled="disabled">
{{ editingLink.id ? $t('actions.save') : $t('settings.social-media.submit') }}
</ds-button>
<ds-button v-if="editingLink.id" id="cancel" ghost @click="handleCancel()">
{{ $t('actions.cancel') }}
</ds-button>
</ds-space>
</ds-space>
</ds-card>
</ds-form>
</template>
<script>
import unionBy from 'lodash/unionBy'
import gql from 'graphql-tag'
import { mapGetters, mapMutations } from 'vuex'
export default {
data() {
return {
value: '',
formData: {
socialMediaUrl: '',
},
formSchema: {
socialMediaUrl: {
type: 'url',
message: this.$t('common.validations.url'),
},
},
disabled: true,
editingLink: {},
}
},
computed: {
@ -51,11 +92,10 @@ export default {
currentUser: 'auth/user',
}),
socialMediaLinks() {
const domainRegex = /^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g
const { socialMedia = [] } = this.currentUser
return socialMedia.map(socialMedia => {
const { id, url } = socialMedia
const matches = url.match(/^(?:https?:\/\/)?(?:[^@\n])?(?:www\.)?([^:/\n?]+)/g)
const [domain] = matches || []
return socialMedia.map(({ id, url }) => {
const [domain] = url.match(domainRegex) || []
const favicon = domain ? `${domain}/favicon.ico` : null
return { id, url, favicon }
})
@ -65,39 +105,28 @@ export default {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
handleAddSocialMedia() {
this.$apollo
.mutate({
mutation: gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`,
variables: {
url: this.value,
},
update: (store, { data }) => {
const socialMedia = [...this.currentUser.socialMedia, data.CreateSocialMedia]
this.setCurrentUser({
...this.currentUser,
socialMedia,
})
},
})
.then(() => {
this.$toast.success(this.$t('settings.social-media.successAdd'))
this.value = ''
})
.catch(error => {
this.$toast.error(error.message)
})
handleCancel() {
this.editingLink = {}
this.formData.socialMediaUrl = ''
this.disabled = true
},
handleDeleteSocialMedia(link) {
this.$apollo
.mutate({
handleEditSocialMedia(link) {
this.editingLink = link
this.formData.socialMediaUrl = link.url
},
handleInput(data) {
this.disabled = true
},
handleInputValid(data) {
if (data.socialMediaUrl.length < 1) {
this.disabled = true
} else {
this.disabled = false
}
},
async handleDeleteSocialMedia(link) {
try {
await this.$apollo.mutate({
mutation: gql`
mutation($id: ID!) {
DeleteSocialMedia(id: $id) {
@ -119,19 +148,83 @@ export default {
})
},
})
.then(() => {
this.$toast.success(this.$t('settings.social-media.successDelete'))
})
.catch(error => {
this.$toast.error(error.message)
this.$toast.success(this.$t('settings.social-media.successDelete'))
} catch (err) {
this.$toast.error(err.message)
}
},
async handleSubmitSocialMedia() {
const isEditing = !!this.editingLink.id
const url = this.formData.socialMediaUrl
const duplicateUrl = this.socialMediaLinks.find(link => link.url === url)
if (duplicateUrl && duplicateUrl.id !== this.editingLink.id) {
return this.$toast.error(this.$t('settings.social-media.requireUnique'))
}
let mutation = gql`
mutation($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`
const variables = { url }
let successMessage = this.$t('settings.social-media.successAdd')
if (isEditing) {
mutation = gql`
mutation($id: ID!, $url: String!) {
UpdateSocialMedia(id: $id, url: $url) {
id
url
}
}
`
variables.id = this.editingLink.id
successMessage = this.$t('settings.data.success')
}
try {
await this.$apollo.mutate({
mutation,
variables,
update: (store, { data }) => {
const newSocialMedia = isEditing ? data.UpdateSocialMedia : data.CreateSocialMedia
this.setCurrentUser({
...this.currentUser,
socialMedia: unionBy([newSocialMedia], this.currentUser.socialMedia, 'id'),
})
},
})
this.$toast.success(successMessage)
this.formData.socialMediaUrl = ''
this.disabled = true
this.editingLink = {}
} catch (err) {
this.$toast.error(err.message)
}
},
},
}
</script>
<style lang="scss">
.layout-leave-active {
.divider {
opacity: 0.4;
padding: 0 $space-small;
}
.icon-button {
cursor: pointer;
}
.list-item--high {
.ds-list-item-prefix {
align-self: center;
}
}
</style>

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>bold</title>
<path d="M16 7h-9v18h11c2.8 0 5-2.2 5-5 0-2.2-1.4-4-3.3-4.7 0.8-0.9 1.3-2 1.3-3.3 0-2.8-2.2-5-5-5zM9 15v-6h7c1.7 0 3 1.3 3 3s-1.3 3-3 3h-7zM9 23v-6h9c1.7 0 3 1.3 3 3s-1.3 3-3 3h-9zM16 5v0c3.9 0 7 3.1 7 7 0 0.9-0.2 1.8-0.5 2.6 1.5 1.3 2.5 3.3 2.5 5.4 0 3.9-3.1 7-7 7h-13v-22h11zM11 11v0 2h5c0.6 0 1-0.4 1-1s-0.4-1-1-1h-5zM11 19v0 2h7c0.6 0 1-0.4 1-1s-0.4-1-1-1h-7z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 531 B

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>italic</title>
<path d="M11.75 5h10.031l-0.094 1.063-0.188 3-0.063 0.938h-2l-0.875 12h2l-0.063 1.063-0.188 3-0.063 0.938h-10.031l0.094-1.063 0.188-3 0.063-0.938h2l0.875-12h-2l0.063-1.063 0.188-3zM13.625 7l-0.063 1h2l-0.063 1.063-1 14-0.063 0.938h-2l-0.063 1h6l0.063-1h-2l0.063-1.063 1-14 0.063-0.938h2l0.063-1h-6z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 468 B

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>list-ol</title>
<path d="M5.969 3h2.031v7h-2v-4.531c-0.444 0.255-0.913 0.531-1.594 0.531v-2c0.494 0 1.25-0.656 1.25-0.656zM11 6h17v2h-17v-2zM6.5 12c1.383 0 2.5 1.117 2.5 2.5 0 0.481-0.248 1.090-0.75 1.5l0.031 0.031-0.125 0.094-0.875 0.875h1.719v2h-5v-1.625l0.313-0.281 2.688-2.594c0-0.217-0.283-0.5-0.5-0.5s-0.5 0.283-0.5 0.5v0.5h-2v-0.5c0-1.383 1.117-2.5 2.5-2.5zM11 15h17v2h-17v-2zM4 21h4v1.469l-0.125 0.25-0.406 0.688c0.853 0.398 1.531 1.089 1.531 2.094 0 1.383-1.117 2.5-2.5 2.5h-2.5v-2h2.5c0.217 0 0.5-0.283 0.5-0.5s-0.283-0.5-0.5-0.5h-1.5v-1.375l0.125-0.219 0.25-0.406h-1.375v-2zM11 24h17v2h-17v-2z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 759 B

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>list-ul</title>
<path d="M4 5h6v6h-6v-6zM6 7v2h2v-2h-2zM12 7h15v2h-15v-2zM4 13h6v6h-6v-6zM6 15v2h2v-2h-2zM12 15h15v2h-15v-2zM4 21h6v6h-6v-6zM6 23v2h2v-2h-2zM12 23h15v2h-15v-2z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 330 B

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>paragraph</title>
<path d="M12 5h12v2h-2v20h-2v-20h-2v20h-2v-10h-4c-3.302 0-6-2.698-6-6s2.698-6 6-6zM12 7c-2.22 0-4 1.78-4 4s1.78 4 4 4h4v-8h-4z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 299 B

View File

@ -1,5 +0,0 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>quote-right</title>
<path d="M4 8h10v10c0 3.302-2.698 6-6 6v-2c2.22 0 4-1.78 4-4h-8v-10zM18 8h10v10c0 3.302-2.698 6-6 6v-2c2.22 0 4-1.78 4-4h-8v-10zM6 10v6h6v-6h-6zM20 10v6h6v-6h-6z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 336 B

View File

@ -729,7 +729,7 @@
dependencies:
regenerator-runtime "^0.12.0"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.3":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.3", "@babel/runtime@^7.5.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
@ -1443,16 +1443,16 @@
warning "^3.0.0"
"@storybook/addon-a11y@^5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-5.1.9.tgz#3feea3f49680f6c311cefd1838b82721d59f397e"
integrity sha512-5u90lEpJtO1W8unwNy5fKTKQG7Sbe3IZJpiC6rf1MdGk0avSwoxDyblt0NImPDBHTh4LeUcuCn57D7AVNLUplg==
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-5.1.10.tgz#0bf37a8e2827cdaa199b293b14e71e8434246591"
integrity sha512-YiRj/8IQ5zq/I+x+aRyfS5PP9nTfuTU7O90+WtNomqCJPMBOrR3BYsEcl510jOy2iwhQwh76MFT5s1tKpMclAA==
dependencies:
"@storybook/addons" "5.1.9"
"@storybook/api" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/components" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/addons" "5.1.10"
"@storybook/api" "5.1.10"
"@storybook/client-logger" "5.1.10"
"@storybook/components" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/theming" "5.1.10"
axe-core "^3.2.2"
common-tags "^1.8.0"
core-js "^3.0.1"
@ -1466,15 +1466,15 @@
util-deprecate "^1.0.2"
"@storybook/addon-actions@^5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.1.9.tgz#a515b62b109cb886ccd75ef2f5b12f8c27b43dd3"
integrity sha512-h/csHPotBESyEUYlML3yyF2jUlDChB+u3TUNC3Ztzh/x7HzLqy88SL0INSIdY0dCBGx4TK5Gh+rMI7z28Hfdyw==
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.1.10.tgz#8ed4272a6afc68f4a30372da2eeff414f0fe6ecd"
integrity sha512-njl2AHBGi27NvisOB8LFnWH/3RcyJT/CW7tl1cvV2j5FH2oBjq5MsjxKyJIcKwC677k1Wr8G8fw/zSEHrPpmgA==
dependencies:
"@storybook/addons" "5.1.9"
"@storybook/api" "5.1.9"
"@storybook/components" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/addons" "5.1.10"
"@storybook/api" "5.1.10"
"@storybook/components" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/theming" "5.1.10"
core-js "^3.0.1"
fast-deep-equal "^2.0.1"
global "^4.3.2"
@ -1485,28 +1485,28 @@
react-inspector "^3.0.2"
uuid "^3.3.2"
"@storybook/addons@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.1.9.tgz#ecf218d08508b97ca5e6e0f1ed361081385bd3ff"
integrity sha512-1bavbcS/NiE65DwyKj8c0DmWmz9VekOinB+has2Pqt2bOffZoZwVnbmepcz9hH3GUyvp5fQBYbxTEmTDvF2lLA==
"@storybook/addons@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.1.10.tgz#2d8d8ca20b6d9b4652744f5fc00ead483f705435"
integrity sha512-M9b2PCp9RZxDC6wL7vVt2SCKCGXrrEAOsdpMvU569yB1zoUPEiiqElVDwb91O2eAGPnmd2yjImp90kOpKUW0EA==
dependencies:
"@storybook/api" "5.1.9"
"@storybook/channels" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/api" "5.1.10"
"@storybook/channels" "5.1.10"
"@storybook/client-logger" "5.1.10"
core-js "^3.0.1"
global "^4.3.2"
util-deprecate "^1.0.2"
"@storybook/api@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.1.9.tgz#eec5b2f775392ce0803930104c6ce14fa4931e8b"
integrity sha512-d1HhpOkW+706/WJ9lP5nCqOrp/icvbm0o+6jFFOGJ35AW5O9D8vDBxzvgMEO45jjN4I+rtbcNHQCxshSbPvP9w==
"@storybook/api@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.1.10.tgz#5eeb5d9a7c268e5c89bd40c9a80293a7c72343b8"
integrity sha512-YeZe/71zLMmgT95IMAEZOc9AwL6Y23mWvkZMwFbkokxS9+bU/qmVlQ0B9c3JBzO3OSs7sXaRqyP1o3QkQgVsiw==
dependencies:
"@storybook/channels" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/router" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/channels" "5.1.10"
"@storybook/client-logger" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/router" "5.1.10"
"@storybook/theming" "5.1.10"
core-js "^3.0.1"
fast-deep-equal "^2.0.1"
global "^4.3.2"
@ -1520,33 +1520,33 @@
telejson "^2.2.1"
util-deprecate "^1.0.2"
"@storybook/channel-postmessage@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.1.9.tgz#bd710ca74d7998a234c6b1f38009020d7c34bbc0"
integrity sha512-H71PsnDKW81eflOS48Lv9yK4O8AcoqXL6ohsWvLdrHWIBsH4zpjOIhdWHtmAaT3hyfMy+l49DQ+uCHLECEt55g==
"@storybook/channel-postmessage@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-5.1.10.tgz#e0a58461d56ef20a87d8bc4df1067e7afc76950e"
integrity sha512-kQZIwltN2cWDXluhCfdModFDK1LHV9ZhNQ1b/uD9vn1c65rQ9u7r4lRajCfS0X1dmAWqz48cBcEurAubNgmswg==
dependencies:
"@storybook/channels" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/channels" "5.1.10"
"@storybook/client-logger" "5.1.10"
core-js "^3.0.1"
global "^4.3.2"
telejson "^2.2.1"
"@storybook/channels@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.1.9.tgz#003cfca0b9f1ba6cf47ce68304aedd71bdb55e74"
integrity sha512-R6i7859FsXgY9XFFErVe7gS37wGYpQEEWsO1LzUW7YptGuFTUa8yLgKkNkgfy7Zs61Xm+GiBq8PvS/CWxjotPw==
"@storybook/channels@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.1.10.tgz#04fd35c05032c675f7816ea1ca873c1a0415c6d9"
integrity sha512-w7n/bV1BLu51KI1eLc75lN9H1ssBc3PZMXk88GkMiKyBVRzPlJA5ixnzH86qwYGReE0dhRpsgHXZ5XmoKaVmPA==
dependencies:
core-js "^3.0.1"
"@storybook/client-api@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.1.9.tgz#b598efe4ab07bffaeb4cb9e30ed9c21add739df1"
integrity sha512-J5HDtOS7x5YRpF/CMiHdxywV5NIh1i/03Xh2RhG15lmPy87VStIGpLzhF71uCRPLEJinYelcjuXRNAJgRzUOlg==
"@storybook/client-api@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-5.1.10.tgz#a10f028f2d33d044e5c3b3daea5d8375323e6a66"
integrity sha512-v2PqiNUhwDlVDLYL94f6LFjdYMToTpuwWh9aeqzt/4PAJUnIcA+2P8+qXiYdJTqQy/u7P72HFMlc9Ru4tl3QFg==
dependencies:
"@storybook/addons" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/router" "5.1.9"
"@storybook/addons" "5.1.10"
"@storybook/client-logger" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/router" "5.1.10"
common-tags "^1.8.0"
core-js "^3.0.1"
eventemitter3 "^3.1.0"
@ -1556,20 +1556,20 @@
memoizerific "^1.11.3"
qs "^6.6.0"
"@storybook/client-logger@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.1.9.tgz#87e2f7578416269adeccd407584010bc353f14d3"
integrity sha512-1+Otcn0EFgWNviDPNCR5LtUViADlboz9fmpZc7UY7bgaY5FVNIUO01E4T43tO7fduiRZoEvdltwTuQRm260Vjw==
"@storybook/client-logger@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.1.10.tgz#f83a8717924dd222e0a6df82ae74701f27e0bb35"
integrity sha512-vB1NoFWRTgcERwodhbgoDwI00eqU8++nXI7GhMS1CY8haZaSp3gyKfHRWyfH+M+YjQuGBRUcvIk4gK6OtSrDOw==
dependencies:
core-js "^3.0.1"
"@storybook/components@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.1.9.tgz#2a5258780fff07172d103287759946dbb4b13e2d"
integrity sha512-F4xcRlifSAfqkuFWtCKRvQDahXyfWBWV2Wa+kYy4YGwEfm3kKtIHVlgdgARL22g9BdYpRFEOJ+42juOu5YvIeQ==
"@storybook/components@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.1.10.tgz#4b6436f0b5bb2483fb231bee263d173a9ed7d241"
integrity sha512-QUQeeQp1xNWiL4VlxFAea0kqn2zvBfmfPlUddOFO9lBhT6pVy0xYPjXjbTVWjVcYzZpyUNWw5GplqrR5jhlaCA==
dependencies:
"@storybook/client-logger" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/client-logger" "5.1.10"
"@storybook/theming" "5.1.10"
core-js "^3.0.1"
global "^4.3.2"
markdown-to-jsx "^6.9.1"
@ -1587,32 +1587,32 @@
recompose "^0.30.0"
simplebar-react "^1.0.0-alpha.6"
"@storybook/core-events@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.1.9.tgz#441a6297e2ccfa743e15d1db1f4ac445b91f40d8"
integrity sha512-jHe2uyoLj9i6fntHtOj5azfGdLOb75LF0e1xXE8U2SX7Zp3uwbLAcfJ+dPStdc/q+f/wBiip3tH1dIjaNuUiMw==
"@storybook/core-events@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.1.10.tgz#5aed88c572036b6bd6dfff28976ee96e6e175d7a"
integrity sha512-Lvu/rNcgS+XCkQKSGdNpUSWjpFF9AOSHPXsvkwHbRwJYdMDn3FznlXfDUiubOWtsziXHB6vl3wkKDlH+ckb32Q==
dependencies:
core-js "^3.0.1"
"@storybook/core@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.1.9.tgz#8b30507676531fd41ac333b7c71b1c0db6b8da35"
integrity sha512-P3aavCnl3Cl3WMXVERjQqnqV1Z8tN0tyOTqqiGb1fMxITSE8uZNvp33Dl0K3jr1PBl9trW+2t7eHH4h0sguLlQ==
"@storybook/core@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/core/-/core-5.1.10.tgz#53d23d07716aa2721e1572d44a7f05967d7da39e"
integrity sha512-zkNjufOFrLpFpmr73F/gaJh0W0vWqXIo5zrKvQt1LqmMeCU/v8MstHi4XidlK43UpeogfaXl5tjNCQDO/bd0Dw==
dependencies:
"@babel/plugin-proposal-class-properties" "^7.3.3"
"@babel/plugin-proposal-object-rest-spread" "^7.3.2"
"@babel/plugin-syntax-dynamic-import" "^7.2.0"
"@babel/plugin-transform-react-constant-elements" "^7.2.0"
"@babel/preset-env" "^7.4.5"
"@storybook/addons" "5.1.9"
"@storybook/channel-postmessage" "5.1.9"
"@storybook/client-api" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/node-logger" "5.1.9"
"@storybook/router" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/ui" "5.1.9"
"@storybook/addons" "5.1.10"
"@storybook/channel-postmessage" "5.1.10"
"@storybook/client-api" "5.1.10"
"@storybook/client-logger" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/node-logger" "5.1.10"
"@storybook/router" "5.1.10"
"@storybook/theming" "5.1.10"
"@storybook/ui" "5.1.10"
airbnb-js-shims "^1 || ^2"
autoprefixer "^9.4.9"
babel-plugin-add-react-displayname "^0.0.5"
@ -1666,10 +1666,10 @@
webpack-dev-middleware "^3.7.0"
webpack-hot-middleware "^2.25.0"
"@storybook/node-logger@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-5.1.9.tgz#4aacf0096811fde1639fc9d1d2d521f7220dd4be"
integrity sha512-rcSuI5n53hDMHW83gl5TR0Yn885/i2XY0AzX1DsbTeGOl3x5LhrCSZsZWetKGcx7zsO4n7o5mQszLuN1JlyE8A==
"@storybook/node-logger@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-5.1.10.tgz#92c80b46177687cd8fda1f93a055c22711984154"
integrity sha512-Z4UKh7QBOboQhUF5S/dKOx3OWWCNZGwYu8HZa/O+P68+XnQDhuZCYwqWG49xFhZd0Jb0W9gdUL2mWJw5POG9PA==
dependencies:
chalk "^2.4.2"
core-js "^3.0.1"
@ -1677,10 +1677,10 @@
pretty-hrtime "^1.0.3"
regenerator-runtime "^0.12.1"
"@storybook/router@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.1.9.tgz#8cd97bea4f2acf8ec5f6694d06fb0633dde33417"
integrity sha512-eAmeerE/OTIwCV7WBnb1BPINVN1GTSMsUXLNWpqSISuyWJ+NZAJlObFkvXoc57QSQlv0cvXlm1FMkmRt8ku1Hw==
"@storybook/router@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.1.10.tgz#d3cffd3f1105eb665882f389746ccabbb98c3c16"
integrity sha512-BdG6/essPZFHCP2ewCG0gYFQfmuuTSHXAB5fd/rwxLSYj1IzNznC5OxkvnSaTr4rgoxxaW/z1hbN1NuA0ivlFA==
dependencies:
"@reach/router" "^1.2.1"
core-js "^3.0.1"
@ -1688,14 +1688,14 @@
memoizerific "^1.11.3"
qs "^6.6.0"
"@storybook/theming@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.1.9.tgz#c425f5867fae0db79e01112853b1808332a5f1a2"
integrity sha512-4jIFJwTWVf9tsv27noLoFHlKC2Jl9DHV3q+rxGPU8bTNbufCu4oby82SboO5GAKuS3eu1cxL1YY9pYad9WxfHg==
"@storybook/theming@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.1.10.tgz#f9bd519cdf9cccf730656e3f5fd56a339dd07c9f"
integrity sha512-5cN1lmdVUwAR8U3T49Lfb8JW5RBvxBSPGZpUmbLGz1zi0tWBJgYXoGtw4RbTBjV9kCQOXkHGH12AsdDxHh931w==
dependencies:
"@emotion/core" "^10.0.9"
"@emotion/styled" "^10.0.7"
"@storybook/client-logger" "5.1.9"
"@storybook/client-logger" "5.1.10"
common-tags "^1.8.0"
core-js "^3.0.1"
deep-object-diff "^1.1.0"
@ -1706,19 +1706,19 @@
prop-types "^15.7.2"
resolve-from "^5.0.0"
"@storybook/ui@5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.1.9.tgz#406667469e6dbdf320086647d8d80776bb051a51"
integrity sha512-guzKv4VYM+06BzMXeO3QqlX0IwUHyeS6lwdPCL8Oy2V4Gi2IYHHiD6Hr1NgnBO18j9luxE38f4Ii7gEIzXMFbQ==
"@storybook/ui@5.1.10":
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-5.1.10.tgz#4262b1b09efa43d125d694452ae879b89071edd1"
integrity sha512-ezkoVtzoKh93z2wzkqVIqyrIzTkj8tizgAkoPa7mUAbLCxu6LErHITODQoyEiJWI4Epy3yU9GYXFWwT71hdwsA==
dependencies:
"@storybook/addons" "5.1.9"
"@storybook/api" "5.1.9"
"@storybook/channels" "5.1.9"
"@storybook/client-logger" "5.1.9"
"@storybook/components" "5.1.9"
"@storybook/core-events" "5.1.9"
"@storybook/router" "5.1.9"
"@storybook/theming" "5.1.9"
"@storybook/addons" "5.1.10"
"@storybook/api" "5.1.10"
"@storybook/channels" "5.1.10"
"@storybook/client-logger" "5.1.10"
"@storybook/components" "5.1.10"
"@storybook/core-events" "5.1.10"
"@storybook/router" "5.1.10"
"@storybook/theming" "5.1.10"
copy-to-clipboard "^3.0.8"
core-js "^3.0.1"
core-js-pure "^3.0.1"
@ -1747,11 +1747,11 @@
util-deprecate "^1.0.2"
"@storybook/vue@~5.1.9":
version "5.1.9"
resolved "https://registry.yarnpkg.com/@storybook/vue/-/vue-5.1.9.tgz#ff37730e3d9575b389dddc6870a07c34c7a53e47"
integrity sha512-ssBQOHdArHFn/FXVsXqi+FiiSvnulux6CIjmaZTjWMgktDa1Hp7TWFN3rlYCM+WxO2KBtydK5AyRWd8+r9YelQ==
version "5.1.10"
resolved "https://registry.yarnpkg.com/@storybook/vue/-/vue-5.1.10.tgz#37916c93faf2eca21497b359748109727ccf3216"
integrity sha512-UeRbQ5bOWUTx5oBMfPf+ZtP5E5X74nFFhrkg0yNakohW6pLuTVoci/G8hDJ4wsjT7PgNjoE1/dggf4JKCU9tjA==
dependencies:
"@storybook/core" "5.1.9"
"@storybook/core" "5.1.10"
common-tags "^1.8.0"
core-js "^3.0.1"
global "^4.3.2"
@ -1866,6 +1866,11 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"
"@types/json-schema@^7.0.3":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
"@types/long@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef"
@ -1937,6 +1942,23 @@
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==
"@typescript-eslint/experimental-utils@^1.13.0":
version "1.13.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e"
integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "1.13.0"
eslint-scope "^4.0.0"
"@typescript-eslint/typescript-estree@1.13.0":
version "1.13.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e"
integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==
dependencies:
lodash.unescape "4.0.1"
semver "5.5.0"
"@vue/babel-helper-vue-jsx-merge-props@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040"
@ -2049,14 +2071,12 @@
source-map "~0.6.1"
vue-template-es2015-compiler "^1.9.0"
"@vue/eslint-config-prettier@~4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@vue/eslint-config-prettier/-/eslint-config-prettier-4.0.1.tgz#a036d0d2193c5c836542b35a3a7c35c4e1c68c97"
integrity sha512-rJEDXPb61Hfgg8GllO3XXFP98bcIxdNNHSrNcxP/vBSukOolgOwQyZJ5f5z/c7ViPyh5/IDlC4qBnhx/0n+I4g==
"@vue/eslint-config-prettier@~5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@vue/eslint-config-prettier/-/eslint-config-prettier-5.0.0.tgz#ce66c8c4d9e01ad1d4e1a12140ab685bd5ef345a"
integrity sha512-OXcH+XWevp3DIdC3BBornC1q6/MNYfca/3HY66awV6aGm+dtkR/hpfBb6fX7nsVjcox13kgG+eSUtUfJ3uxZ8A==
dependencies:
eslint-config-prettier "^3.3.0"
eslint-plugin-prettier "^3.0.0"
prettier "^1.15.2"
eslint-config-prettier "^6.0.0"
"@vue/server-test-utils@~1.0.0-beta.29":
version "1.0.0-beta.29"
@ -2292,6 +2312,11 @@ acorn@^6.0.1, acorn@^6.0.2, acorn@^6.0.5, acorn@^6.0.7:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f"
integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==
acorn@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.1.tgz#3ed8422d6dec09e6121cc7a843ca86a330a86b51"
integrity sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==
address@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
@ -2895,9 +2920,9 @@ aws4@^1.8.0:
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
axe-core@^3.2.2:
version "3.3.0"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.3.0.tgz#3b32d7e54390d89ff4891b20394d33ad7a192776"
integrity sha512-54XaTd2VB7A6iBnXMUG2LnBOI7aRbnrVxC5Tz+rVUwYl9MX/cIJc/Ll32YUoFIE/e9UKWMZoQenQu9dFrQyZCg==
version "3.3.1"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.3.1.tgz#3d1fa78cca8ead1b78c350581501e4e37b97b826"
integrity sha512-gw1T0JptHPF4AdLLqE8yQq3Z7YvsYkpFmFWd84r6hnq/QoKRr8icYHFumhE7wYl5TVIHgVlchMyJsAYh0CfwCQ==
axios-retry@^3.1.2:
version "3.1.2"
@ -3419,7 +3444,7 @@ bluebird@^3.1.1, bluebird@^3.5.1, bluebird@^3.5.3:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714"
integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==
bluebird@^3.3.5:
bluebird@^3.3.5, bluebird@^3.5.5:
version "3.5.5"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
@ -3694,6 +3719,27 @@ cacache@^11.0.2, cacache@^11.3.2:
unique-filename "^1.1.1"
y18n "^4.0.0"
cacache@^12.0.2:
version "12.0.2"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.2.tgz#8db03205e36089a3df6954c66ce92541441ac46c"
integrity sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg==
dependencies:
bluebird "^3.5.5"
chownr "^1.1.1"
figgy-pudding "^3.5.1"
glob "^7.1.4"
graceful-fs "^4.1.15"
infer-owner "^1.0.3"
lru-cache "^5.1.1"
mississippi "^3.0.0"
mkdirp "^0.5.1"
move-concurrently "^1.0.1"
promise-inflight "^1.0.1"
rimraf "^2.6.3"
ssri "^6.0.1"
unique-filename "^1.1.1"
y18n "^4.0.0"
cache-base@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
@ -3807,9 +3853,9 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000957, can
integrity sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==
caniuse-lite@^1.0.30000955:
version "1.0.30000985"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz#0eb40f6c8a8c219155cbe43c4975c0efb4a0f77f"
integrity sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w==
version "1.0.30000988"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000988.tgz#742f35ec1b8b75b9628d705d7652eea1fef983db"
integrity sha512-lPj3T8poYrRc/bniW5SQPND3GRtSrQdUM/R4mCYTbZxyi3jQiggLvZH4+BYUuX0t4TXjU+vMM7KFDQg+rSzZUQ==
caniuse-lite@^1.0.30000973:
version "1.0.30000973"
@ -4172,7 +4218,7 @@ commander@2.17.x:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
commander@^2.18.0, commander@^2.19.0, commander@~2.20.0:
commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
@ -4330,10 +4376,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie-universal-nuxt@~2.0.16:
version "2.0.16"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.16.tgz#8d528098c973162b352199240e40da0e5429b13f"
integrity sha512-wRK2zw8w+a5xPehb5kLbgOic/4mbjl2exUCxWZwGuttcwsFgOymiwDrCOzmQslqrDevPDL2SsBbH6wtOm7dB9g==
cookie-universal-nuxt@~2.0.17:
version "2.0.17"
resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.17.tgz#efa066cade8bc28ab81046c35b6557e3e4ec29fb"
integrity sha512-kJTLOJFOJBiWHd8ehLnheTNyFJbc4zqdZ9YinDSZmWgBMKOrNPd+3hTCsSVGCmybJdpmEJkDenSbRg/xFouqTQ==
dependencies:
"@types/cookie" "^0.3.1"
cookie-universal "^2.0.16"
@ -4425,12 +4471,12 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
corejs-upgrade-webpack-plugin@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/corejs-upgrade-webpack-plugin/-/corejs-upgrade-webpack-plugin-2.1.0.tgz#6afa44672486353ae639c297548c0686b64fb325"
integrity sha512-gc+S4t8VT9YFSgOPrhZlD6kDoGZtUq71QwXxS2neGNPhli0veKhbzzilODIpy73TjXGUrCHCpevK8vBnzUPuhw==
version "2.2.0"
resolved "https://registry.yarnpkg.com/corejs-upgrade-webpack-plugin/-/corejs-upgrade-webpack-plugin-2.2.0.tgz#503293bf1fdcb104918eb40d0294e4776ad6923a"
integrity sha512-J0QMp9GNoiw91Kj/dkIQFZeiCXgXoja/Wlht1SPybxerBWh4NCmb0pOgCv61lrlQZETwvVVfAFAA3IqoEO9aqQ==
dependencies:
resolve-from "^5.0.0"
webpack "^4.33.0"
webpack "^4.38.0"
cors@^2.8.4:
version "2.8.5"
@ -4840,10 +4886,10 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-fns@2.0.0-beta.2:
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-beta.2.tgz#ccd556df832ef761baa88c600f53d2e829245999"
integrity sha512-4cicZF707RNerr3/Q3CcdLo+3OHMCfrRXE7h5iFgn7AMvX07sqKLxSf8Yp+WJW5bvKr2cy9/PkctXLv4iFtOaA==
date-fns@2.0.0-beta.3:
version "2.0.0-beta.3"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-beta.3.tgz#2e28f5af945930f774ddd778e184d68227101d55"
integrity sha512-z5O262BvHPhwUvA1weXH+AZodygnZUcORERw8hjwBUrRPGrAo2e/rjXfC8Ykf1OGJZGDuLnK/WXbEZBIc0exGQ==
date-now@^0.1.4:
version "0.1.4"
@ -5239,9 +5285,9 @@ ejs@^2.6.1:
integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
electron-to-chromium@^1.3.122:
version "1.3.199"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.199.tgz#f9a62a74cda77854310a2abffde8b75591ea09a1"
integrity sha512-gachlDdHSK47s0N2e58GH9HMC6Z4ip0SfmYUa5iEbE50AKaOUXysaJnXMfKj0xB245jWbYcyFSH+th3rqsF8hA==
version "1.3.208"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.208.tgz#6df90b89e8b9af139db3c8d7a6b935db0801e61c"
integrity sha512-ljgZXaKSfRg32jEl1V8zRJaT3u653jxfZRztKQWM/I1kE+ifBCQKRY+jOPzb4JS48a4czvT/LQfjdiDq5qjU4g==
electron-to-chromium@^1.3.133:
version "1.3.137"
@ -5409,14 +5455,7 @@ escodegen@^1.9.1:
optionalDependencies:
source-map "~0.6.1"
eslint-config-prettier@^3.3.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz#8ca3ffac4bd6eeef623a0651f9d754900e3ec217"
integrity sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==
dependencies:
get-stdin "^6.0.0"
eslint-config-prettier@~6.0.0:
eslint-config-prettier@^6.0.0, eslint-config-prettier@~6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz#f429a53bde9fc7660e6353910fd996d6284d3c25"
integrity sha512-vDrcCFE3+2ixNT5H83g28bO/uYAwibJxerXPj+E7op4qzBCsAV36QfvdAyVOoNxKAH2Os/e01T/2x++V0LPukA==
@ -5463,10 +5502,10 @@ eslint-plugin-es@^1.4.0:
eslint-utils "^1.3.0"
regexpp "^2.0.1"
eslint-plugin-import@~2.18.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz#7a5ba8d32622fb35eb9c8db195c2090bd18a3678"
integrity sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==
eslint-plugin-import@~2.18.2:
version "2.18.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6"
integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==
dependencies:
array-includes "^3.0.3"
contains-path "^0.1.0"
@ -5475,15 +5514,17 @@ eslint-plugin-import@~2.18.0:
eslint-import-resolver-node "^0.3.2"
eslint-module-utils "^2.4.0"
has "^1.0.3"
lodash "^4.17.11"
minimatch "^3.0.4"
object.values "^1.1.0"
read-pkg-up "^2.0.0"
resolve "^1.11.0"
eslint-plugin-jest@~22.11.1:
version "22.11.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.11.1.tgz#04b586e2fddd07e55900a381255d6b3d9242ae87"
integrity sha512-kPF1Nmr5xMLz6DT7qEttz0TTeyx1x6SozIkNO9y4F2yxuWjHMp/e70fo742pR3y0MewgXQQMIIXeSKLB66iO7Q==
eslint-plugin-jest@~22.14.0:
version "22.14.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.14.0.tgz#f9b09837f665cfe360b55c08866904255294cc16"
integrity sha512-Xtc9ZTtxdYFC7vu0PHxDeQ9lOMQ8gjwMmSQq/ni83TdflgL3eVh/qg3t99I7gcDxpeXfcp+lHu9C0vN3QAhATw==
dependencies:
"@typescript-eslint/experimental-utils" "^1.13.0"
eslint-plugin-node@~9.1.0:
version "9.1.0"
@ -5497,7 +5538,7 @@ eslint-plugin-node@~9.1.0:
resolve "^1.10.1"
semver "^6.1.0"
eslint-plugin-prettier@^3.0.0, eslint-plugin-prettier@~3.1.0:
eslint-plugin-prettier@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz#8695188f95daa93b0dc54b249347ca3b79c4686d"
integrity sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==
@ -6109,6 +6150,11 @@ flatten@^1.0.2:
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
flush-promises@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-promises/-/flush-promises-1.0.2.tgz#4948fd58f15281fed79cbafc86293d5bb09b2ced"
integrity sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA==
flush-write-stream@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@ -6296,19 +6342,25 @@ function-bind@^1.0.2, function-bind@^1.1.1:
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
function.prototype.name@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327"
integrity sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==
version "1.1.1"
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.1.tgz#6d252350803085abc2ad423d4fe3be2f9cbda392"
integrity sha512-e1NzkiJuw6xqVH7YSdiW/qDHebcmMhPNe6w+4ZYYEg0VA+LaLzx37RimbPLuonHhYGFGPx1ME2nSi74JiaCr/Q==
dependencies:
define-properties "^1.1.2"
define-properties "^1.1.3"
function-bind "^1.1.1"
is-callable "^1.1.3"
functions-have-names "^1.1.1"
is-callable "^1.1.4"
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
functions-have-names@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.1.1.tgz#79d35927f07b8e7103d819fed475b64ccf7225ea"
integrity sha512-U0kNHUoxwPNPWOJaMG7Z00d4a/qZVrFtzWJRaK8V9goaVOCXBSQSJpt3MYGNtkScKEBKovxLjnNdC9MlXwo5Pw==
fuse.js@^3.4.4, fuse.js@^3.4.5:
version "3.4.5"
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.5.tgz#8954fb43f9729bd5dbcb8c08f251db552595a7a6"
@ -7076,6 +7128,11 @@ indexof@0.0.1:
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=
infer-owner@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -7254,7 +7311,7 @@ is-buffer@^2.0.2:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
is-callable@^1.1.3, is-callable@^1.1.4:
is-callable@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
@ -8282,11 +8339,11 @@ launch-editor@^2.2.1:
shell-quote "^1.6.1"
lazy-universal-dotenv@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.0.tgz#e71f07f89d8de6bbf491478e4503df3c96729b8d"
integrity sha512-Mbf5AeGOs74lE5BdQXHFJ7Rt383jxnWKNfW2EWL0Pibnhea5JRStRIiUpdTenyMxCGuCjlMpYQhhay1XZBSSQA==
version "3.0.1"
resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38"
integrity sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==
dependencies:
"@babel/runtime" "^7.0.0"
"@babel/runtime" "^7.5.0"
app-root-dir "^1.0.2"
core-js "^3.0.4"
dotenv "^8.0.0"
@ -8432,11 +8489,6 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
@ -8517,6 +8569,11 @@ lodash.throttle@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
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.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@ -8760,9 +8817,9 @@ merge-stream@^1.0.1:
readable-stream "^2.0.1"
merge2@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5"
integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==
version "1.2.4"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3"
integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==
methods@~1.1.2:
version "1.1.2"
@ -9156,9 +9213,9 @@ node-pre-gyp@^0.12.0:
tar "^4"
node-releases@^1.1.13:
version "1.1.25"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.25.tgz#0c2d7dbc7fed30fbe02a9ee3007b8c90bf0133d3"
integrity sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==
version "1.1.26"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.26.tgz#f30563edc5c7dc20cf524cc8652ffa7be0762937"
integrity sha512-fZPsuhhUHMTlfkhDLGtfY80DSJTjOcx+qD1j5pqPkuhUHVS7xHZIg9EE4DHK8O3f0zTxXHX5VIkDG8pu98/wfQ==
dependencies:
semver "^5.3.0"
@ -10632,7 +10689,7 @@ prettier@1.16.3:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d"
integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==
prettier@^1.15.2, prettier@~1.18.2:
prettier@~1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
@ -10670,20 +10727,13 @@ pretty-time@^1.1.0:
resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e"
integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==
prismjs@^1.8.4:
prismjs@^1.8.4, prismjs@~1.17.0:
version "1.17.1"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be"
integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==
optionalDependencies:
clipboard "^2.0.0"
prismjs@~1.16.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.16.0.tgz#406eb2c8aacb0f5f0f1167930cb83835d10a4308"
integrity sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==
optionalDependencies:
clipboard "^2.0.0"
private@^0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@ -11083,9 +11133,9 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
strip-json-comments "~2.0.1"
react-clientside-effect@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.1.tgz#feb81abe9531061d4987941c15a00f2b3d0b6071"
integrity sha512-foSwZatJak6r+F4OqJ8a+MOWcBi3jwa7/RPdJIDZI1Ck0dn/FJZkkFu7YK+SiZxsCZIrotolxHSobcnBHgIjfw==
version "1.2.2"
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz#6212fb0e07b204e714581dd51992603d1accc837"
integrity sha512-nRmoyxeok5PBO6ytPvSjKp9xwXg9xagoTK1mMjwnQxqM9Hd7MNPl+LS1bOSOe+CV2+4fnEquc7H/S8QD3q697A==
dependencies:
"@babel/runtime" "^7.0.0"
@ -11423,13 +11473,13 @@ redux@^4.0.1:
symbol-observable "^1.2.0"
refractor@^2.4.1:
version "2.9.0"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.9.0.tgz#0a381aadb51513e4e6ec1ed410b5104dd65e2489"
integrity sha512-lCnCYvXpqd8hC7ksuvo516rz5q4NwzBbq0X5qjH5pxRfcQKiQxKZ8JctrSQmrR/7pcV2TRrs9TT+Whmq/wtluQ==
version "2.10.0"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.0.tgz#4cc7efc0028a87924a9b31d82d129dec831a287b"
integrity sha512-maW2ClIkm9IYruuFYGTqKzj+m31heq92wlheW4h7bOstP+gf8bocmMec+j7ljLcaB1CAID85LMB3moye31jH1g==
dependencies:
hastscript "^5.0.0"
parse-entities "^1.1.2"
prismjs "~1.16.0"
prismjs "~1.17.0"
regenerate-unicode-properties@^8.0.2:
version "8.0.2"
@ -11880,6 +11930,11 @@ semver-diff@^2.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
semver@5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
semver@^6.0.0, semver@^6.1.0, semver@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
@ -12186,6 +12241,14 @@ source-map-support@^0.5.6, source-map-support@~0.5.10:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@~0.5.12:
version "0.5.13"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@ -12336,9 +12399,9 @@ stealthy-require@^1.1.1:
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
store2@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/store2/-/store2-2.7.1.tgz#22070b7dc04748a792fc6912a58ab99d3a21d788"
integrity sha512-zzzP5ZY6QWumnAFV6kBRbS44pUMcpZBNER5DWUe1HETlaKXqLcCQxbNu6IHaKr1pUsjuhUGBdOy8sWKmMkL6pQ==
version "2.8.0"
resolved "https://registry.yarnpkg.com/store2/-/store2-2.8.0.tgz#032d5dcbd185a5d74049d67a1765ff1e75faa04b"
integrity sha512-FBJpcOEZQLZBIGL4Yp7W5RgZ0ejaURmcfUjIpyOb64BpI8z/iJXw7zd/NTBeq304dVMxuWVDZEUUCGn7llaVrA==
stream-browserify@^2.0.1:
version "2.0.2"
@ -12673,16 +12736,16 @@ tar@^4:
yallist "^3.0.2"
telejson@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/telejson/-/telejson-2.2.1.tgz#d9ee7e7eba0c81d9378257342fde7142a03787e2"
integrity sha512-JtFAnITek+Z9t+uQjVl4Fxur9Z3Bi3flytBLc3KZVXmMUHLXdtAxiP0g8IBkHvKn1kQIYZC57IG0jjGH1s64HQ==
version "2.2.2"
resolved "https://registry.yarnpkg.com/telejson/-/telejson-2.2.2.tgz#d61d721d21849a6e4070d547aab302a9bd22c720"
integrity sha512-YyNwnKY0ilabOwYgC/J754En1xOe5PBIUIw+C9e0+5HjVVcnQE5/gdu2yET2pmSbp5bxIDqYNjvndj2PUkIiYA==
dependencies:
global "^4.3.2"
is-function "^1.0.1"
is-regex "^1.0.4"
is-symbol "^1.0.2"
isobject "^3.0.1"
lodash.get "^4.4.2"
lodash "^4.17.11"
memoizerific "^1.11.3"
term-size@^1.2.0:
@ -12711,7 +12774,22 @@ terser-webpack-plugin@^1.1.0:
webpack-sources "^1.1.0"
worker-farm "^1.5.2"
terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.3.0:
terser-webpack-plugin@^1.2.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz#61b18e40eaee5be97e771cdbb10ed1280888c2b4"
integrity sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==
dependencies:
cacache "^12.0.2"
find-cache-dir "^2.1.0"
is-wsl "^1.1.0"
schema-utils "^1.0.0"
serialize-javascript "^1.7.0"
source-map "^0.6.1"
terser "^4.1.2"
webpack-sources "^1.4.0"
worker-farm "^1.7.0"
terser-webpack-plugin@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz#69aa22426299f4b5b3775cbed8cb2c5d419aa1d4"
integrity sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==
@ -12745,6 +12823,15 @@ terser@^4.0.0:
source-map "~0.6.1"
source-map-support "~0.5.10"
terser@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.2.tgz#b2656c8a506f7ce805a3f300a2ff48db022fa391"
integrity sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
source-map-support "~0.5.12"
test-exclude@^5.2.3:
version "5.2.3"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0"
@ -13610,10 +13697,10 @@ vue@^2.6.10, vue@^2.6.6:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.10.tgz#a72b1a42a4d82a721ea438d1b6bf55e66195c637"
integrity sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==
vuex-i18n@~1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.11.0.tgz#e6cdc95080c445ab2c211cc6b64a907d217639d3"
integrity sha512-+Eme0C7FS3VFLIWpAwisohC3KcRDw+YcXFANssUZZq16P2C4z8V2VGbEtFHFw0DzkvZcdM2CAkUj6rdMl9wYmg==
vuex-i18n@~1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.13.0.tgz#161c22548e1d3038523f5af49e5730e17a0ca4f2"
integrity sha512-r9ZS8NFr8OAIvtlSnqrQLFXxh07QHhkdeGem8RyWFBKej9/yxPAneBEQaLCUM0BOKPpAzBX8t2cjQmtukWbWLg==
vuex@^3.1.1:
version "3.1.1"
@ -13734,6 +13821,14 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0, webpack-sources@^1.3.0:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack-sources@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.1.tgz#b91b2c5b1c4e890ff50d1d35b7fa3657040da1da"
integrity sha512-XSz38193PTo/1csJabKaV4b53uRVotlMgqJXm3s3eje0Bu6gQTxYDqpD38CmQfDBA+gN+QqaGjasuC8I/7eW3Q==
dependencies:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack@^4.33.0:
version "4.33.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.33.0.tgz#c30fc4307db432e5c5e3333aaa7c16a15a3b277e"
@ -13764,6 +13859,35 @@ webpack@^4.33.0:
watchpack "^1.5.0"
webpack-sources "^1.3.0"
webpack@^4.38.0:
version "4.38.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.38.0.tgz#6d77108404b08883c78f4e7e45a43c4e5c47c931"
integrity sha512-lbuFsVOq8PZY+1Ytz/mYOvYOo+d4IJ31hHk/7iyoeWtwN33V+5HYotSH+UIb9tq914ey0Hot7z6HugD+je3sWw==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
"@webassemblyjs/wasm-edit" "1.8.5"
"@webassemblyjs/wasm-parser" "1.8.5"
acorn "^6.2.0"
ajv "^6.1.0"
ajv-keywords "^3.1.0"
chrome-trace-event "^1.0.0"
enhanced-resolve "^4.1.0"
eslint-scope "^4.0.0"
json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
loader-utils "^1.1.0"
memory-fs "~0.4.1"
micromatch "^3.1.8"
mkdirp "~0.5.0"
neo-async "^2.5.0"
node-libs-browser "^2.0.0"
schema-utils "^1.0.0"
tapable "^1.1.0"
terser-webpack-plugin "^1.1.0"
watchpack "^1.5.0"
webpack-sources "^1.3.0"
webpackbar@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.2.0.tgz#bdaad103fad11a4e612500e72aaae98b08ba493f"

View File

@ -1810,10 +1810,10 @@ cucumber@^4.2.1:
util-arity "^1.0.2"
verror "^1.9.0"
cypress-cucumber-preprocessor@^1.12.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.12.0.tgz#092428ba267331e3d2cc6e1309c331d17632b8b1"
integrity sha512-uKrWbs51hGeHiLgcSZcjFvvVEW9UdStsLVpD1snuPuik9WE61kbZv7xumlPjRmkMF81zTUGnNLwZuAk3CV9dEw==
cypress-cucumber-preprocessor@^1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.13.0.tgz#efacd70ce21c7d0adc60e25af166f5fb2e990fb8"
integrity sha512-Y3B4El3oYqKUvEhfn7k7NrX/hMJvOCJIO+sgMbvvPXsUngzLWUdiS2LOAaSxpV4t2BCyFuvfzGH0j+C3tu4UvA==
dependencies:
"@cypress/browserify-preprocessor" "^1.1.2"
chai "^4.1.2"
@ -3569,10 +3569,10 @@ neo4j-driver@^1.6.3, neo4j-driver@^1.7.5:
text-encoding-utf-8 "^1.0.2"
uri-js "^4.2.2"
neode@^0.2.16:
version "0.2.16"
resolved "https://registry.yarnpkg.com/neode/-/neode-0.2.16.tgz#20532cc67604fd00cc88de841d422f5238ae5bd3"
integrity sha512-L9p55IDKGzAZsQgHdXrfd2xasDuB46RipcrPw6NP7ESgkmfJMaMWRZ1F3Kv+f4V4U1WnhZ1IILvwVFhYPnpXEg==
neode@^0.2.18:
version "0.2.18"
resolved "https://registry.yarnpkg.com/neode/-/neode-0.2.18.tgz#b7cc26e69df46dbfc3ea2e97334db8691f52d9e7"
integrity sha512-oUWYMao6tM8DdsqeyaVUmFKhwEbjqIfwW2KKL9HQE4ffWK531cnslWqzu2B/xoQj+d1Q3Ebhe3to1U5Z1Y+pfQ==
dependencies:
dotenv "^4.0.0"
joi "^13.7.0"