diff --git a/.gitignore b/.gitignore
index 79518f7..a8b5a49 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,9 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+
+# Intellij
+.idea
+
+# PocketBase
+/pb_data
diff --git a/package.json b/package.json
index c387dcd..f0a2212 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "niko",
+ "name": "nikk",
"version": "0.0.1",
"private": true,
"scripts": {
@@ -20,21 +20,24 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^8.56.7",
+ "bulma": "^1.0.1",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
+ "sass": "^1.77.5",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0-alpha.20",
"vite": "^5.0.3",
- "vitest": "^1.2.0",
- "bulma": "^1.0.1",
- "sass": "^1.77.5"
+ "vitest": "^1.2.0"
},
- "type": "module"
+ "type": "module",
+ "dependencies": {
+ "pocketbase": "^0.21.3"
+ }
}
diff --git a/pb_migrations/1718834233_created_profiles.js b/pb_migrations/1718834233_created_profiles.js
new file mode 100644
index 0000000..b3b40e3
--- /dev/null
+++ b/pb_migrations/1718834233_created_profiles.js
@@ -0,0 +1,60 @@
+///
+migrate(
+ (db) => {
+ const collection = new Collection({
+ id: 'etdl54k56vwybrn',
+ created: '2024-06-19 21:57:13.774Z',
+ updated: '2024-06-19 21:57:13.774Z',
+ name: 'profiles',
+ type: 'base',
+ system: false,
+ schema: [
+ {
+ system: false,
+ id: 'vodgbchi',
+ name: 'user',
+ type: 'relation',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ collectionId: '_pb_users_auth_',
+ cascadeDelete: true,
+ minSelect: null,
+ maxSelect: 1,
+ displayFields: null
+ }
+ },
+ {
+ system: false,
+ id: 'ykt3zolr',
+ name: 'name',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ }
+ ],
+ indexes: ['CREATE INDEX `idx_VTowXf1` ON `profiles` (`name`)'],
+ listRule: null,
+ viewRule: null,
+ createRule: null,
+ updateRule: null,
+ deleteRule: null,
+ options: {}
+ });
+
+ return Dao(db).saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ return dao.deleteCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718834453_created_characters.js b/pb_migrations/1718834453_created_characters.js
new file mode 100644
index 0000000..fb76f37
--- /dev/null
+++ b/pb_migrations/1718834453_created_characters.js
@@ -0,0 +1,81 @@
+///
+migrate(
+ (db) => {
+ const collection = new Collection({
+ id: 'i7mafqbep52hivh',
+ created: '2024-06-19 22:00:53.918Z',
+ updated: '2024-06-19 22:00:53.918Z',
+ name: 'characters',
+ type: 'base',
+ system: false,
+ schema: [
+ {
+ system: false,
+ id: 'mn4unycx',
+ name: 'name',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ },
+ {
+ system: false,
+ id: '47sgeg6w',
+ name: 'displayName',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ },
+ {
+ system: false,
+ id: 'urnacitl',
+ name: 'element',
+ type: 'select',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ maxSelect: 1,
+ values: ['pyro', 'hydro', 'anemo', 'electro', 'dendro', 'cryo', 'geo']
+ }
+ },
+ {
+ system: false,
+ id: 'mhmjqqet',
+ name: 'visible',
+ type: 'bool',
+ required: false,
+ presentable: false,
+ unique: false,
+ options: {}
+ }
+ ],
+ indexes: ['CREATE UNIQUE INDEX `idx_Fd6ZMqS` ON `characters` (`name`)'],
+ listRule: 'visible = true',
+ viewRule: 'visible = true',
+ createRule: null,
+ updateRule: null,
+ deleteRule: null,
+ options: {}
+ });
+
+ return Dao(db).saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh');
+
+ return dao.deleteCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718834489_updated_profiles.js b/pb_migrations/1718834489_updated_profiles.js
new file mode 100644
index 0000000..326d677
--- /dev/null
+++ b/pb_migrations/1718834489_updated_profiles.js
@@ -0,0 +1,27 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.listRule = '@request.auth.id = user.id';
+ collection.viewRule = '@request.auth.id = user.id';
+ collection.createRule = '@request.auth.id = user.id';
+ collection.updateRule = '@request.auth.id = user.id';
+ collection.deleteRule = '@request.auth.id = user.id';
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.listRule = null;
+ collection.viewRule = null;
+ collection.createRule = null;
+ collection.updateRule = null;
+ collection.deleteRule = null;
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718834637_created_votes.js b/pb_migrations/1718834637_created_votes.js
new file mode 100644
index 0000000..262107c
--- /dev/null
+++ b/pb_migrations/1718834637_created_votes.js
@@ -0,0 +1,76 @@
+///
+migrate(
+ (db) => {
+ const collection = new Collection({
+ id: '2q5l1u64zseldm9',
+ created: '2024-06-19 22:03:57.192Z',
+ updated: '2024-06-19 22:03:57.192Z',
+ name: 'votes',
+ type: 'base',
+ system: false,
+ schema: [
+ {
+ system: false,
+ id: 'reelx3nj',
+ name: 'profile',
+ type: 'relation',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ collectionId: 'etdl54k56vwybrn',
+ cascadeDelete: true,
+ minSelect: null,
+ maxSelect: 1,
+ displayFields: null
+ }
+ },
+ {
+ system: false,
+ id: 'oryeao7u',
+ name: 'character',
+ type: 'relation',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ collectionId: 'i7mafqbep52hivh',
+ cascadeDelete: true,
+ minSelect: null,
+ maxSelect: 1,
+ displayFields: null
+ }
+ },
+ {
+ system: false,
+ id: 'sllown1l',
+ name: 'nick',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ }
+ ],
+ indexes: ['CREATE UNIQUE INDEX `idx_TdQeCRY` ON `votes` (\n `profile`,\n `character`\n)'],
+ listRule: '@request.auth.id != ""',
+ viewRule: '@request.auth.id != ""',
+ createRule: '@request.auth.id = profile.user.id',
+ updateRule: '@request.auth.id = profile.user.id',
+ deleteRule: '@request.auth.id = profile.user.id',
+ options: {}
+ });
+
+ return Dao(db).saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9');
+
+ return dao.deleteCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718834662_updated_profiles.js b/pb_migrations/1718834662_updated_profiles.js
new file mode 100644
index 0000000..6d7f268
--- /dev/null
+++ b/pb_migrations/1718834662_updated_profiles.js
@@ -0,0 +1,21 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.listRule = '@request.auth.id != ""';
+ collection.viewRule = '@request.auth.id != ""';
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.listRule = '@request.auth.id = user.id';
+ collection.viewRule = '@request.auth.id = user.id';
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718834688_updated_characters.js b/pb_migrations/1718834688_updated_characters.js
new file mode 100644
index 0000000..3ede090
--- /dev/null
+++ b/pb_migrations/1718834688_updated_characters.js
@@ -0,0 +1,21 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh');
+
+ collection.listRule = '@request.auth.id != "" && visible = true';
+ collection.viewRule = '@request.auth.id != "" && visible = true';
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('i7mafqbep52hivh');
+
+ collection.listRule = 'visible = true';
+ collection.viewRule = 'visible = true';
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718838136_updated_votes.js b/pb_migrations/1718838136_updated_votes.js
new file mode 100644
index 0000000..c8b4c42
--- /dev/null
+++ b/pb_migrations/1718838136_updated_votes.js
@@ -0,0 +1,25 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9');
+
+ collection.name = 'nicks';
+ collection.indexes = [
+ 'CREATE UNIQUE INDEX `idx_TdQeCRY` ON `nicks` (\n `profile`,\n `character`\n)'
+ ];
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9');
+
+ collection.name = 'votes';
+ collection.indexes = [
+ 'CREATE UNIQUE INDEX `idx_TdQeCRY` ON `votes` (\n `profile`,\n `character`\n)'
+ ];
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pb_migrations/1718839919_updated_nicks.js b/pb_migrations/1718839919_updated_nicks.js
new file mode 100644
index 0000000..80850dd
--- /dev/null
+++ b/pb_migrations/1718839919_updated_nicks.js
@@ -0,0 +1,51 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9');
+
+ // update
+ collection.schema.addField(
+ new SchemaField({
+ system: false,
+ id: 'sllown1l',
+ name: 'name',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ })
+ );
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('2q5l1u64zseldm9');
+
+ // update
+ collection.schema.addField(
+ new SchemaField({
+ system: false,
+ id: 'sllown1l',
+ name: 'nick',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ })
+ );
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pb_migrations/1719252581_updated_profiles.js b/pb_migrations/1719252581_updated_profiles.js
new file mode 100644
index 0000000..ecc124e
--- /dev/null
+++ b/pb_migrations/1719252581_updated_profiles.js
@@ -0,0 +1,55 @@
+///
+migrate(
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.indexes = ['CREATE UNIQUE INDEX `idx_VTowXf1` ON `profiles` (`name`)'];
+
+ // update
+ collection.schema.addField(
+ new SchemaField({
+ system: false,
+ id: 'ykt3zolr',
+ name: 'name',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: 2,
+ max: null,
+ pattern: ''
+ }
+ })
+ );
+
+ return dao.saveCollection(collection);
+ },
+ (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId('etdl54k56vwybrn');
+
+ collection.indexes = ['CREATE INDEX `idx_VTowXf1` ON `profiles` (`name`)'];
+
+ // update
+ collection.schema.addField(
+ new SchemaField({
+ system: false,
+ id: 'ykt3zolr',
+ name: 'name',
+ type: 'text',
+ required: true,
+ presentable: false,
+ unique: false,
+ options: {
+ min: null,
+ max: null,
+ pattern: ''
+ }
+ })
+ );
+
+ return dao.saveCollection(collection);
+ }
+);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f39a356..30912e2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,6 +7,10 @@ settings:
importers:
.:
+ dependencies:
+ pocketbase:
+ specifier: ^0.21.3
+ version: 0.21.3
devDependencies:
'@playwright/test':
specifier: ^1.28.1
@@ -1095,6 +1099,9 @@ packages:
engines: {node: '>=16'}
hasBin: true
+ pocketbase@0.21.3:
+ resolution: {integrity: sha512-bsQRZ1mj4dhPJ4P5iSrDqWQtBX2NtuBkf6IOZwTv27zZOw6zrISgo963i5JD/99qKKD5aMSMDPGk1BE9ZyI7Cg==}
+
postcss-load-config@3.1.4:
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
engines: {node: '>= 10'}
@@ -2469,6 +2476,8 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
+ pocketbase@0.21.3: {}
+
postcss-load-config@3.1.4(postcss@8.4.38):
dependencies:
lilconfig: 2.1.0
diff --git a/src/app.html b/src/app.html
index 77a5ff5..9df6a79 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,9 +1,36 @@
-
+
+
+ nikk - nicknames for your favorite characters
+
+
+
+
+
+
+
+
+
+
+
+
+
%sveltekit.head%
diff --git a/src/app.scss b/src/app.scss
index bd5f8df..df81920 100644
--- a/src/app.scss
+++ b/src/app.scss
@@ -1,15 +1,15 @@
/* Override global Sass variables from the /utilities folder */
-@use "bulma/sass/utilities" with (
-$link: $pink,
-);;
-@use "bulma/sass/base" with (
-$body-overflow-y: auto
+@use 'bulma/sass/utilities' with (
+ $link: $pink
+);
+@use 'bulma/sass/base' with (
+ $body-overflow-y: auto
);
/* Import the components you need */
-@use "bulma/sass/elements";
-@use "bulma/sass/form";
-@use "bulma/sass/components";
-@use "bulma/sass/grid";
-@use "bulma/sass/helpers";
-@use "bulma/sass/layout";
-@use "bulma/sass/themes";
\ No newline at end of file
+@use 'bulma/sass/elements';
+@use 'bulma/sass/form';
+@use 'bulma/sass/components';
+@use 'bulma/sass/grid';
+@use 'bulma/sass/helpers';
+@use 'bulma/sass/layout';
+@use 'bulma/sass/themes';
diff --git a/src/lib/components/ui/navbar.svelte b/src/lib/components/ui/navbar.svelte
new file mode 100644
index 0000000..41248a8
--- /dev/null
+++ b/src/lib/components/ui/navbar.svelte
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+ Edit username
+
+
+
+
+
+
+
+
diff --git a/src/lib/index.ts b/src/lib/index.ts
deleted file mode 100644
index 856f2b6..0000000
--- a/src/lib/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/lib/pocketbase.ts b/src/lib/pocketbase.ts
new file mode 100644
index 0000000..7005037
--- /dev/null
+++ b/src/lib/pocketbase.ts
@@ -0,0 +1,54 @@
+import PocketBase, { type AuthModel, type RecordModel } from 'pocketbase';
+import { env } from '$env/dynamic/public';
+import { writable, readonly } from 'svelte/store';
+
+export const pb = new PocketBase(env.PUBLIC_POCKETBASE_URL);
+
+const _user = writable(pb.authStore.model);
+
+pb.authStore.onChange(() => {
+ _user.set(pb.authStore.model);
+});
+
+export const user = readonly(_user);
+
+export const getAllStuff = async () => {
+ const profiles = await pb.collection('profiles').getFullList({
+ sort: '+created',
+ fields: 'id, name, user'
+ });
+ const characters = await pb.collection('characters').getFullList({
+ sort: '+created',
+ fields: 'id, name, displayName, element'
+ });
+ const nicks = await pb.collection('nicks').getFullList({
+ sort: '+created',
+ fields: 'id, profile, character, name'
+ });
+ return { profiles, characters, nicks };
+};
+
+export const saveNewNick = async (profileId: string, characterId: string, name: string) => {
+ let existingNick: RecordModel | null = null;
+ try {
+ existingNick = await pb.collection('nicks').getFirstListItem(
+ pb.filter('profile = {:profile} && character = {:character}', {
+ profile: profileId,
+ character: characterId
+ })
+ );
+ } catch (e) {}
+ if (existingNick && name.trim() === '') {
+ await pb.collection('nicks').delete(existingNick.id);
+ return;
+ }
+ if (existingNick) {
+ await pb.collection('nicks').update(existingNick.id, { name });
+ } else {
+ await pb.collection('nicks').create({
+ profile: profileId,
+ character: characterId,
+ name
+ });
+ }
+};
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index d23c24f..9fb1615 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1 +1,27 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
new file mode 100644
index 0000000..3900880
--- /dev/null
+++ b/src/routes/+layout.ts
@@ -0,0 +1,27 @@
+import type { LayoutLoad } from './$types';
+import { pb } from '$lib/pocketbase';
+import type { RecordModel } from 'pocketbase';
+
+export const load: LayoutLoad = async ({ setHeaders }) => {
+ pb.authStore.loadFromCookie(document.cookie);
+
+ try {
+ pb.authStore.isValid && (await pb.collection('users').authRefresh());
+ } catch (e) {
+ console.log('authRefresh error', e);
+ // do nothing
+ }
+
+ const user = pb.authStore.model;
+ let profile: RecordModel | null = null;
+ try {
+ profile =
+ user &&
+ (await pb
+ .collection('profiles')
+ .getFirstListItem(pb.filter('user = {:user}', { user: user.id })));
+ } catch (e) {}
+ return { user, profile };
+};
+
+export const ssr = false;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 5982b0a..85c7906 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,2 +1,201 @@
-Welcome to SvelteKit
-Visit kit.svelte.dev to read the documentation
+
+
+
+
+
+{#if !profile}
+
+

+
You must be logged in to view this page
+
+{:else}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#each $characters as character (character.id)}
+
+ {/each}
+
+{/if}
+
+
diff --git a/src/routes/+page.ts b/src/routes/+page.ts
new file mode 100644
index 0000000..0bd84ac
--- /dev/null
+++ b/src/routes/+page.ts
@@ -0,0 +1,6 @@
+import type { PageLoad } from './$types';
+import { getAllStuff } from '$lib/pocketbase';
+
+export const load: PageLoad = async () => {
+ return getAllStuff();
+};
diff --git a/src/routes/auth/callback/+page.svelte b/src/routes/auth/callback/+page.svelte
new file mode 100644
index 0000000..08d1f7c
--- /dev/null
+++ b/src/routes/auth/callback/+page.svelte
@@ -0,0 +1,43 @@
+
diff --git a/src/routes/auth/login/+page.svelte b/src/routes/auth/login/+page.svelte
new file mode 100644
index 0000000..c2ecf30
--- /dev/null
+++ b/src/routes/auth/login/+page.svelte
@@ -0,0 +1,17 @@
+
diff --git a/src/routes/auth/logout/+page.svelte b/src/routes/auth/logout/+page.svelte
new file mode 100644
index 0000000..f225e52
--- /dev/null
+++ b/src/routes/auth/logout/+page.svelte
@@ -0,0 +1,15 @@
+
diff --git a/src/variables.scss b/src/variables.scss
index a63d8cb..3469193 100644
--- a/src/variables.scss
+++ b/src/variables.scss
@@ -1,2 +1,2 @@
/* Set your brand colors */
-$pink: pink;
\ No newline at end of file
+$pink: pink;
diff --git a/static/assets/images/fail.png b/static/assets/images/fail.png
new file mode 100644
index 0000000..cf2b4e5
Binary files /dev/null and b/static/assets/images/fail.png differ
diff --git a/static/favicon.png b/static/favicon.png
index 825b9e6..ad3b5d1 100644
Binary files a/static/favicon.png and b/static/favicon.png differ
diff --git a/vite.config.ts b/vite.config.ts
index dc1598f..2a8dca3 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,18 +1,18 @@
-import { sveltekit } from "@sveltejs/kit/vite";
-import { defineConfig } from "vitest/config";
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vitest/config';
export default defineConfig({
- plugins: [sveltekit()],
+ plugins: [sveltekit()],
- test: {
- include: ["src/**/*.{test,spec}.{js,ts}"]
- },
+ test: {
+ include: ['src/**/*.{test,spec}.{js,ts}']
+ },
- css: {
- preprocessorOptions: {
- scss: {
- additionalData: "@use \"src/variables.scss\" as *;"
- }
- }
- }
-});
\ No newline at end of file
+ css: {
+ preprocessorOptions: {
+ scss: {
+ additionalData: '@use "src/variables.scss" as *;'
+ }
+ }
+ }
+});