Merge branch 'profile-feature'

This commit is contained in:
a-ill 2023-08-01 15:58:04 +03:00
commit 035fee2997
127 changed files with 9576 additions and 680 deletions

View File

@ -1,8 +1,8 @@
# This file is machine-generated - editing it directly is not advised
julia_version = "1.9.0"
julia_version = "1.9.1"
manifest_format = "2.0"
project_hash = "829f3e210629ef04542eb44fc4cb5acdb74d23eb"
project_hash = "09d33216e2516631ede3cbab2af65d3f95eb0598"
[[deps.Adapt]]
deps = ["LinearAlgebra", "Requires"]
@ -62,9 +62,9 @@ version = "1.3.1"
[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83"
git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.1"
version = "0.7.2"
[[deps.CommonMark]]
deps = ["Crayons", "JSON", "PrecompileTools", "URIs"]
@ -74,9 +74,9 @@ version = "0.8.12"
[[deps.Compat]]
deps = ["UUIDs"]
git-tree-sha1 = "4e88377ae7ebeaf29a047aa1ee40826e0b708a5d"
git-tree-sha1 = "5ce999a19f4ca23ea484e92a1774a61b8ca4cf8e"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.7.0"
version = "4.8.0"
weakdeps = ["Dates", "LinearAlgebra"]
[deps.Compat.extensions]
@ -89,31 +89,36 @@ version = "1.0.2+0"
[[deps.ConcurrentUtilities]]
deps = ["Serialization", "Sockets"]
git-tree-sha1 = "96d823b94ba8d187a6d8f0826e731195a74b90e9"
git-tree-sha1 = "5372dbbf8f0bdb8c700db5367132925c0771ef7e"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.2.0"
version = "2.2.1"
[[deps.Crayons]]
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.1.1"
[[deps.DBInterface]]
git-tree-sha1 = "9b0dc525a052b9269ccc5f7f04d5b3639c65bca5"
uuid = "a10d1c49-ce27-4219-8d33-6db1a4562965"
version = "2.5.0"
[[deps.DataAPI]]
git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.15.0"
[[deps.DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SnoopPrecompile", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "aa51303df86f8626a962fccb878430cdb0a97eee"
deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "REPL", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "04c738083f29f86e62c8afc341f0967d8717bdb8"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.5.0"
version = "1.6.1"
[[deps.DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0"
git-tree-sha1 = "cf25ccb972fec4e4817764d01c82386ae94f77b4"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.13"
version = "0.18.14"
[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
@ -168,9 +173,9 @@ uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
version = "0.1.9"
[[deps.ExprTools]]
git-tree-sha1 = "c1d06d129da9f55715c6c212866f5b1bddc5fa00"
git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
version = "0.1.9"
version = "0.1.10"
[[deps.EzXML]]
deps = ["Printf", "XML2_jll"]
@ -193,12 +198,6 @@ version = "0.9.20"
[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
[[deps.Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"
[[deps.Future]]
deps = ["Random"]
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"
@ -257,9 +256,9 @@ version = "1.3.1"
[[deps.HTTP]]
deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"]
git-tree-sha1 = "2613d054b0e18a3dea99ca1594e9a3960e025da4"
git-tree-sha1 = "cb56ccdd481c0dd7f975ad2b3b62d9eda088f7e2"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "1.9.7"
version = "1.9.14"
[[deps.HttpCommon]]
deps = ["Dates", "Nullables", "Test", "URIParser"]
@ -339,6 +338,12 @@ git-tree-sha1 = "5b62d93f2582b09e469b3099d839c2d2ebf5066d"
uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
version = "1.13.1"
[[deps.JWTs]]
deps = ["Base64", "Downloads", "JSON", "MbedTLS", "Random"]
git-tree-sha1 = "a1f3ded6307ef85cc18dec93d9b993814eb4c1a0"
uuid = "d850fbd6-035d-5a70-a269-1ca2e636ac6c"
version = "0.2.2"
[[deps.JuliaFormatter]]
deps = ["CSTParser", "CommonMark", "DataStructures", "Glob", "Pkg", "PrecompileTools", "Tokenize"]
git-tree-sha1 = "60567b51bd9e1e19ae2fd8a54dcd6bc5994727f0"
@ -386,10 +391,10 @@ deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
[[deps.LibPQ]]
deps = ["CEnum", "Dates", "Decimals", "DocStringExtensions", "FileWatching", "Infinity", "Intervals", "IterTools", "LayerDicts", "LibPQ_jll", "Libdl", "Memento", "OffsetArrays", "SQLStrings", "Tables", "TimeZones", "UTCDateTimes"]
git-tree-sha1 = "114d9d239ab8e1251354ad6bb97ed38622133114"
deps = ["CEnum", "DBInterface", "Dates", "Decimals", "DocStringExtensions", "FileWatching", "Infinity", "Intervals", "IterTools", "LayerDicts", "LibPQ_jll", "Libdl", "Memento", "OffsetArrays", "SQLStrings", "Tables", "TimeZones", "UTCDateTimes"]
git-tree-sha1 = "d8967f68674aa9ad4b9b3df114e3842f269ac147"
uuid = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"
version = "1.15.1"
version = "1.16.0"
[[deps.LibPQ_jll]]
deps = ["Artifacts", "JLLWrappers", "Kerberos_krb5_jll", "Libdl", "OpenSSL_jll", "Pkg"]
@ -519,9 +524,9 @@ version = "1.0.0"
[[deps.OffsetArrays]]
deps = ["Adapt"]
git-tree-sha1 = "82d7c9e310fe55aa54996e6f7f94674e2a38fcb4"
git-tree-sha1 = "2ac17d29c523ce1cd38e27785a7d23024853a4bb"
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
version = "1.12.9"
version = "1.12.10"
[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
@ -552,9 +557,9 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.5+0"
[[deps.OrderedCollections]]
git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282"
git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.6.0"
version = "1.6.2"
[[deps.Parameters]]
deps = ["OrderedCollections", "UnPack"]
@ -564,9 +569,9 @@ version = "0.12.3"
[[deps.Parsers]]
deps = ["Dates", "PrecompileTools", "UUIDs"]
git-tree-sha1 = "4b2e829ee66d4218e0cef22c0a64ee37cf258c29"
git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.7.1"
version = "2.7.2"
[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
@ -608,10 +613,10 @@ uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.4.0"
[[deps.PrettyTables]]
deps = ["Crayons", "Formatting", "LaTeXStrings", "Markdown", "Reexport", "StringManipulation", "Tables"]
git-tree-sha1 = "213579618ec1f42dea7dd637a42785a608b1ea9c"
deps = ["Crayons", "LaTeXStrings", "Markdown", "Printf", "Reexport", "StringManipulation", "Tables"]
git-tree-sha1 = "ee094908d720185ddbdc58dbe0c1cbe35453ec7a"
uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
version = "2.2.4"
version = "2.2.7"
[[deps.Printf]]
deps = ["Unicode"]
@ -701,12 +706,6 @@ git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1"
uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7"
version = "1.1.0"
[[deps.SnoopPrecompile]]
deps = ["Preferences"]
git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c"
uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c"
version = "1.0.3"
[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
@ -739,9 +738,9 @@ version = "1.9.0"
[[deps.StringEncodings]]
deps = ["Libiconv_jll"]
git-tree-sha1 = "33c0da881af3248dafefb939a21694b97cfece76"
git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb"
uuid = "69024149-9ee7-55f6-a4c4-859efe599b68"
version = "0.3.6"
version = "0.3.7"
[[deps.StringManipulation]]
git-tree-sha1 = "46da2434b41f41ac3594ee9816ce5541c6096123"
@ -765,10 +764,10 @@ uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"
[[deps.TableShowUtils]]
deps = ["DataValues", "Dates", "JSON", "Markdown", "Test"]
git-tree-sha1 = "14c54e1e96431fb87f0d2f5983f090f1b9d06457"
deps = ["DataValues", "Dates", "JSON", "Markdown", "Unicode"]
git-tree-sha1 = "2a41a3dedda21ed1184a47caab56ed9304e9a038"
uuid = "5e66a065-1f0a-5976-b372-e0b8c017ca10"
version = "0.2.5"
version = "0.2.6"
[[deps.TableTraits]]
deps = ["IteratorInterfaceExtensions"]
@ -810,9 +809,9 @@ version = "1.0.1"
[[deps.TimeZones]]
deps = ["Dates", "Downloads", "InlineStrings", "LazyArtifacts", "Mocking", "Printf", "RecipesBase", "Scratch", "Unicode"]
git-tree-sha1 = "cdaa0c2a4449724aded839550eca7d7240bb6938"
git-tree-sha1 = "5b347464bdac31eccfdbe1504d9484c31645cafc"
uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53"
version = "1.10.0"
version = "1.11.0"
[[deps.Tokenize]]
git-tree-sha1 = "90538bf898832b6ebd900fa40f223e695970e3a5"
@ -880,7 +879,7 @@ version = "1.2.13+0"
[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "5.7.0+0"
version = "5.8.0+0"
[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]

View File

@ -4,6 +4,7 @@ authors = ["a-ill <a_ill@outlook.com>"]
version = "0.1.0"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
CSVFiles = "5d742f6a-9f54-50ce-8119-2520741973ca"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
@ -15,6 +16,7 @@ GenieSession = "03cc5b98-4f21-4eb6-99f2-22eced81f962"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
Inflector = "6d011eab-0732-4556-8808-e463c76bf3b6"
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
JWTs = "d850fbd6-035d-5a70-a269-1ca2e636ac6c"
LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"

View File

@ -25,7 +25,11 @@
<!--<loadscreen-component></loadscreen-component>-->
<div id="content">
<navbar-component></navbar-component>
<% if authenticated() %>
<navbar-logged></navbar-logged>
<% else %>
<navbar-not-logged></navbar-not-logged>
<% end %>
<%@yield%>

View File

@ -0,0 +1,193 @@
module AuthenticationController
using Genie, Genie.Requests, Genie.Renderer, Genie.Renderer.Json, Genie.Renderer.Html, GenieSession, SearchLight, GenieAuthentication, GenieAuthorisation
using Logging
using JSON3, Random, Base64, HTTP, Dates
using Server.Users, Server.EmailSupport, Server.TemplateEditor, Server.Cookies, Server.DatabaseSupport
import Server.TemplateEditor.generate_layout_html
import Server.DatabaseSupport.select_from_table
using JWTs
#---Helpers----------------------------------------------------------
const keyset = JWKSet("https://www.googleapis.com/oauth2/v3/certs")
refresh!(keyset)
current_user() = findone(Users.User, id = get_authentication())
function send_signup_confirmation_email(receiver,confirmation_code)
subject,message = ["Sign-up confirmation","Hello!\r\nYour confirmation code is "*confirmation_code*"\r\n"]
message = "Content-Type: text/html\r\n"*message
return send_email(receiver,subject,message)
end
function register_google()
jws = rawpayload()
jws_split = split(jws,".")
payload_encoded = jws_split[2]
rem = length(payload_encoded)%4
if rem!= 0
payload_encoded = payload_encoded* "="^(4-rem)
end
payload = String(base64decode(payload_encoded))
json = JSON3.read(payload)
sub = json[:sub]
email = json[:email]
user = findone(User, email = email)
if isnothing(user)
# ENABLE WHEN IN PRODUCTION
user = User(email = email,google_id = sub) |> save!
authenticate(user.id, GenieSession.session(params()))
assign_role(user, findone(Role, name = "free"))
save(user)
return true
return 0
else
jwt = JWT(payload="")
jwt.header = jws_split[1]
jwt.payload = jws_split[2]
jwt.signature = jws_split[3]
if validate!(jwt, keyset)
if user.google_id==""
user.google_id = sub
save(user)
authenticate(user.id, GenieSession.session(params()))
return 3
elseif user.google_id==sub
authenticate(user.id, GenieSession.session(params()))
return 3
else
return 0
end
else
return 0
end
end
end
function get_locale()
data = payload()
if :locale in keys(data)
return data[:locale]
else
return "en"
end
end
const auth_info = Dict(
"en" => Dict(
:title => "LibSoc - Login/Sign Up",
:description => ""
),
"ru" => Dict(
:title => "LibSoc - Логин/Регистрация",
:description => ""
)
)
#---Routing functions---------------------------------------------------
controller = "authentication"
const dict_layouts = Dict(
:auth => generate_layout_html("main",controller,"auth",libraries=["GoogleAuth"]),
:profile => generate_layout_html("main",controller,"profile",libraries=["Leaflet"]),
:email_confirmation => generate_layout_html("main",controller,"email_confirmation"),
)
function auth()
locale = get_locale()
set_cookies(params())
html(:authentication,:auth, layout = dict_layouts[:auth], context = @__MODULE__,
title = auth_info[locale][:title],
description = auth_info[locale][:description]
)
end
function profile()
set_cookies(params())
html(:authentication,:profile, layout = dict_layouts[:profile], context = @__MODULE__,
title = "Chiron | Profile",
description = ""
)
end
function email_confirmation()
set_cookies(params())
html(:authentication,:email_confirmation, layout = dict_layouts[:email_confirmation], context = @__MODULE__,
title = "Chiron | Email Confirmation",
description = ""
)
end
function confirm_email()
code = rawpayload()
user = current_user()
if code==user.confirmation_code
GenieAuthorisation.Relationship!(user, findone(Role, name = "unconfirmed")) |> delete
assign_role(user, findone(Role, name = "free"))
return true
else
return false
end
end
function register()
data = jsonpayload()
user = findone(User, email = data["email"])
if isnothing(user)
user = User(email = data["email"],
password = data["password"] |> Users.hash_password,
) |> save!
authenticate(user.id, GenieSession.session(params()))
assign_role(user, findone(Role, name = "free"))
confirmation_code = randstring('0':'9', 5)
user.confirmation_code = confirmation_code
save(user)
#send_signup_confirmation_email(data["email"],confirmation_code)
return true
else
return false
end
end
function login()
data = jsonpayload()
user = findone(User, email = data["email"])
if isnothing(user)
return 0
else
if (user.password==Users.hash_password(data["password"]))
authenticate(user.id, GenieSession.session(params()))
return 2
else
return 1
end
end
end
function logout()
deauthenticate(GenieSession.session(params()))
return
end
function change_user()
data = jsonpayload()
user = findone(Users.User, id = get_authentication())
for (field,value) in data
setfield!(user, Symbol(field), value)
end
save(user)
return JSON3.write(true)
end
function get_user()
try
user = findone(Users.User, id = get_authentication())
return JSON3.write(user)
catch ex
return JSON3.write(false)
end
end
end

View File

@ -0,0 +1,2 @@
<auth-component></auth-component>

View File

@ -0,0 +1 @@
<confirmation-component></confirmation-component>

View File

@ -0,0 +1,2 @@
<profile-component></profile-component>

View File

@ -1,6 +1,6 @@
module BasicController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor

View File

@ -1,6 +1,6 @@
module CommunesController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor

View File

@ -1,6 +1,6 @@
module CooperativesController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor

View File

@ -1,9 +1,10 @@
module GroupsController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication, DataFrames
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor
using SearchLight,SearchLightPostgreSQL, LibPQ, JSON3
using Server.DatabaseSupport, Server.TemplateEditor, Server.Users
import Server.DatabaseSupport: select_from_table, insert_into_table, delete_from_table, exist_in_table
controller = "groups"
dict_layouts = Dict(
@ -33,8 +34,61 @@ function get_locale()
end
end
#---Helpers-----------------------------------------------------------
function table_to_json(name,df)
ar = []
for df_row in eachrow(df)
dict = Dict()
for id in names(df_row)
dict[id] = df_row[id]
end
push!(ar,dict)
end
open("public/assets/"*name*".json", "w") do io
JSON3.write(io, ar)
end
end
function compile(name)
df = select_from_table([name => ["*"]])
table_to_json(name,df)
end
function move_requests(name)
df_requests = select_from_table(["$(name)_requests" => ["*"]], where_data=["verified" => true, "added" => false])
df = select_from_table([name => ["*"]])
latitudes = df.latitude
longitudes = df.longitude
for df_row in eachrow(df_requests)
ind_id_given = ismissing(df_row.id_given) ? nothing : findfirst(df_row.id_given.==df.id)
if (!isnothing(ind_id_given))
id = df[ind_id_given,:id]
row_found = df[ind_id_given,Not(:id)]
dict = Dict(zip(names(row_found),values(row_found)))
dict["members"] += 1
update_table(name,dict, where_data=["id" => id])
else
id = df_row.id
dict_update = Dict("added" => true)
update_table("$(name)_requests",dict_update, where_data=["id" => id])
df_row_to_add = df_row[Not(:id_given)]
df_row_to_add = df_row_to_add[Not(:verified)]
df_row_to_add = df_row_to_add[Not(:added)]
df_row_to_add = df_row_to_add[Not(:id)]
dict = Dict(zip(names(df_row_to_add),values(df_row_to_add)))
dict["members"] = 1
insert_into_table(name,dict)
end
end
end
#---Functions---------------------------------------------------------
current_user() = findone(Users.User, id = get_authentication())
function groups()
locale = get_locale()
html(:groups,:groups, layout = dict_layouts[:groups], context = @__MODULE__,
@ -52,8 +106,156 @@ function groups_add()
end
function groups_add_post()
data = jsonpayload()
insert_into_table("groups_requests",data)
data = copy(jsonpayload())
mode = data["mode"]
delete!(data,"mode")
user = current_user()
user_id = user.id
if mode==0 # Create
if user.verified
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
has_group = !isempty(existing_user_group_data)
delete!(data,"group_id")
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
if has_group
user_groups_id = existing_user_group_data[1,"id"]
prev_group_id = existing_user_group_data[1,"group_id"]
update_table("users_groups",Dict("group_id" => group_id), where_data=["id" => user_groups_id])
members = select_from_table(["groups" => ["members"]], where_data=["id" => prev_group_id])[1,1]
if (members==1)
delete_from_table("groups",["id" => prev_group_id])
else
update_table("groups",Dict("members" => members - 1), where_data=["id" => id])
end
else
dict_users_groups = Dict("user_id" => user.id, "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
end
compile("groups")
else
data["status"] = 0
data["user_id"] = user_id
insert_into_table("groups_requests",data)
end
elseif mode==1 # Join
data["user_id"] = user_id
if exist_in_table("users_groups",["group_id" => data["group_id"]])
if exist_in_table("groups_requests",["user_id" => user_id])
delete_from_table("groups_requests",["user_id" => user_id])
end
data["status"] = 0
insert_into_table("groups_requests",data)
else
group_id = data["group_id"]
members = select_from_table("groups" => ["members"], where_data = ["id" => group_id])[1,1]
dict = Dict("members" => members + 1)
update_table("groups",dict, where_data=["id" => group_id])
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
end
elseif mode==2 # Move
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
group_id = existing_user_group_data[1,"group_id"]
delete!(data,"group_id")
delete!(data,"members")
delete!(data,"contact")
update_table("groups",data, where_data=["id" => group_id])
compile("groups")
elseif mode==3 # Leave
existing_user_group_data = select_from_table(["users_groups" => ["*"]], where_data=["user_id" => user_id])
if size(existing_user_group_data,1)==0
if exist_in_table("groups_requests",["user_id" => user_id])
delete_from_table("groups_requests",["user_id" => user_id])
end
else
delete_from_table("users_groups",["user_id" => user_id])
end
end
return nothing
end
function get_user_groups()
local data_dicts
user_id = get_authentication()
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
data_dicts = []
if isnothing(group_id)
local data
data = select_from_table("groups_requests" => ["*"], where_data = ["user_id" => user_id,"status" => 0])
if size(data,1)==0
data = select_from_table("groups_requests" => ["*"], where_data = ["user_id" => user_id,"status" => 2])
if size(data,1)!=0
data = data[[end],:]
end
end
for row in eachrow(data)
dict = Dict(zip(names(row),values(row)))
if (!ismissing(row["group_id"]))
extra_data = select_from_table("groups" => ["*"], where_data = ["id" => row["group_id"]])
merge!(dict, Dict(zip(names(extra_data[1,:]),values(extra_data[1,:]))))
end
push!(data_dicts, dict)
end
else
group_data = select_from_table("groups" => ["*"], where_data = ["id" => group_id])
ns = names(group_data)
data_dicts = map(x -> Dict(zip(ns,values(x))),eachrow(group_data))
end
return JSON3.write(data_dicts)
end
function get_group_requests()
user_id = get_authentication()
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
data_dicts = []
if !isnothing(group_id)
user_ids = select_from_table("groups_requests" => ["user_id"], where_data = ["group_id" => group_id, "status" => 0])[:,1]
for user2_id in user_ids
email = select_from_table("users" => ["email"], where_data = ["id" => user2_id])[1,1]
push!(data_dicts,Dict("email" => email, "user_id" => user2_id))
end
end
return JSON3.write(data_dicts)
end
function approve_request()
data = copy(jsonpayload())
user_id = get_authentication()
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
members = select_from_table("groups" => ["members"], where_data = ["id" => group_id])[1,1]
dict = Dict("members" => members + 1)
update_table("groups",dict, where_data=["id" => group_id])
update_table("groups_requests",Dict("status" => 1), where_data=["group_id" => group_id, "user_id" => data["user_id"]])
dict_users_groups = Dict("user_id" => data["user_id"], "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
return nothing
end
function reject_request()
data = copy(jsonpayload())
user_id = get_authentication()
groups_ids = select_from_table("users_groups" => ["group_id"], where_data = ["user_id" => user_id])[:,1]
group_id = isempty(groups_ids) ? nothing : groups_ids[1]
update_table("groups_requests",Dict("status" => 2), where_data=["group_id" => group_id, "user_id" => data["user_id"]])
return nothing
end
function add_verified_groups()
groups_create_requests_verified = select_from_table("groups_requests" => ["*"], where_data = ["group_id" => nothing, "status" => 1])
data = Dict(zip(names(groups_create_requests_verified),groups_create_requests_verified[end,:]))
user_id = data["user_id"]
delete!(data,"group_id")
delete!(data,"user_id")
delete!(data,"id")
delete!(data,"status")
group_id = insert_into_table("groups",data, "RETURNING id")[1,1]
dict_users_groups = Dict("user_id" => user_id, "group_id" => group_id)
insert_into_table("users_groups",dict_users_groups)
delete_from_table("groups_requests",["user_id" => user_id])
end
end

View File

@ -1,6 +1,6 @@
module PartiesController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor

View File

@ -1,6 +1,6 @@
module PartnersController
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests, GenieAuthentication
using JSON3
using SearchLight
using Server.DatabaseSupport, Server.TemplateEditor

View File

@ -0,0 +1,29 @@
module Users
using SearchLight, SearchLight.Validation, Server.UsersValidator
using SHA
using Random
export User
Base.@kwdef mutable struct User <: AbstractModel
### FIELDS
id::DbId = DbId()
email::String = ""
password::String = ""
google_id::String = ""
confirmation_code::String = ""
verified::Bool = false
end
Validation.validator(u::Type{User}) = ModelValidator([
ValidationRule(:email, UsersValidator.not_empty),
ValidationRule(:email, UsersValidator.unique),
ValidationRule(:password, UsersValidator.not_empty)
])
function hash_password(password::String)
sha256(password) |> bytes2hex
end
end

View File

@ -0,0 +1,21 @@
module UsersValidator
using SearchLight, SearchLight.Validation, SearchLight.QueryBuilder
function not_empty(field::Symbol, m::T, args::Vararg{Any})::ValidationResult where {T<:AbstractModel}
isempty(getfield(m, field)) && return ValidationResult(invalid, :not_empty, "should not be empty")
ValidationResult(valid)
end
function unique(field::Symbol, m::T, args::Vararg{Any})::ValidationResult where {T<:AbstractModel}
ispersisted(m) && return ValidationResult(valid) # don't validate updates
if SearchLight.count(typeof(m), where("$field = ?", getfield(m, field))) > 0
return ValidationResult(invalid, :unique, "is already used")
end
ValidationResult(valid)
end
end

View File

@ -0,0 +1,180 @@
label {
font-size: 1.3rem;
font-family: var(--sans-serif);
}
.auth-pane {
position: relative;
padding: 3.4rem;
padding-top: 3.4rem;
padding-bottom: 3.4rem;
width: 30rem;
height: auto;
}
.auth-title {
position: relative;
top: 0.2rem;
margin-bottom: 2.7rem;
}
.auth-label {
display: inline-block;
margin-bottom: 0.3rem;
}
.authEmailInput, .authPasswordInput {
position: relative;
width: 100%;
border-radius: 0.34rem;
color: #353535;
height: 2.73rem;
padding-left: 0.34rem;
}
.authEmailInput {
margin-bottom: 0.682rem;
}
.auth-button {
margin-top: 1.365rem;
height: 3.412rem;
width: 100%;
font-family: var(--sans-serif,sans-serif);
font-size: 1.3rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.512rem;
filter: drop-shadow(0.068rem 0.136rem 0.068rem rgb(0 0 0 / 0.4));
}
.auth-button:active {
background-color: var(--darker-pink);
}
#email-msg,#password-msg {
display: inline;
color:red;
font-family: var(--sans-serif,sans-serif);
}
.auth-line {
margin-top: 1.5rem;
width: 100%;
height: 0.07rem;
border: 0;
border-radius: 0.1rem;
background: black;
}
.auth-methods-group {
display: grid;
grid-template-columns: auto ; /*auto auto*/
justify-content: center;
gap: 2.7rem;
margin-top: 2rem;
}
.auth-methods-group img {
height: auto;
width: 3.4rem;
}
.auth-methods-group> div {
position: relative;
border-radius: 6.8rem;
width: 3.4rem;
height: 3.4rem;
overflow: hidden;
}
#google-btn {
position: absolute;
top: -0.8rem;
left: -0.8rem;
}
#google-logo {
position: relative;
background: white;
pointer-events: none;
}
#google-btn-wrapper {
cursor: pointer;
}
#google-btn div {
position: absolute;
height: 5rem;
width: 5rem;
}
#google-btn iframe {
position: absolute;
height: 10rem;
width: 10rem;
}
.password-field {
position: relative;
}
.eye-icon {
display: block;
position: absolute;
cursor: pointer;
opacity: 0.25;
top: 2.6rem;
right: 0.8rem;
width: 1.7rem;
}
.eye-icon * {
pointer-events: none;
}
#forgot-password {
display: block;
position: relative;
margin-top: 0.5rem;
height: 2rem;
color:#5f5f5f;
margin-left: auto;
width: max-content;
font-family: var(--sans-serif);
font-size: 1.3rem;
}
#remember-me {
position: relative;
display: flex;
align-items: center;
margin-top: 1rem;
gap: 1rem;
}
#remember-me-checkbox {
min-height: 1.5rem;
min-width: 1.5rem;
flex: 0;
accent-color: var(--gray);
}
#remember-me label {
position: relative;
margin-top: -0.2rem;
color: #5f5f5f;
}
#content {
position: relative;
display: flex;
justify-content: space-between;
flex-direction: column;
height: 100%;
min-height: 100vh;
}

View File

@ -1,21 +1,7 @@
:root {
--light-blue:hsl(195, 67%, 95%);
--darker-pink:hsl(344, 60%, 47%);
--pink:hsl(344, 73%, 57%);
--dark-green:hsl(176, 63%, 25%);
--green:hsl(147, 33%, 60%);
--orange:hsl(30, 97%, 72%);
--light-orange: hsl(19, 76%, 72%);
--dark-brown:hsl(23, 47%, 20%);
--brown:hsl(23, 47%, 30%);
--light-brown: hsl(23, 47%, 50%);
--dark-pink:hsl(343, 39%, 16%);
--red:hsl(359, 72%, 61%);
--dark-blue:hsl(217, 25%, 16%);
--grey-blue:hsl(223, 13%, 22%);
--cream:hsl(34, 43%, 90%);
--dark-cream:hsl(33, 26%, 84%);
--red:#c52a28;
--gray: #5B6970;
--sans-serif: "OpenSans";
--serif: "Lora";
}
@ -48,12 +34,10 @@ body {
#content {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
display: grid;
grid-template-rows: max-content auto max-content;
height: 100%;
min-height: 100vh;
flex-grow: 1;
}
/*---Fonts---------------------------------------------------------*/
@ -475,9 +459,8 @@ input[type=number]::-webkit-outer-spin-button {
.pane {
background: white;
border: 0;
border: 0.1rem solid rgb(187, 187, 187);
border-radius: 0.635rem;
box-shadow: 0 0 0.314rem rgb(187, 187, 187);
}
.pane-container {

View File

@ -0,0 +1,268 @@
/* Header */
#navbar{
position: relative;
top: 0;
width: min(100%,116rem);
z-index: 1000000000;
height: 5.26rem;
padding-left: 0rem;
padding-right: 0rem;
}
#navbar * {
font-family: var(--sans-serif, sans-serif);
}
/* Logo */
#logo-container {
display: flex;
position: absolute;
margin-left: 1rem;
height: 100%;
max-height: 5.26rem;
color: black;
z-index: 1;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
}
#navbar-logo {
height: 3.5rem;
width: 3.5rem;
object-fit: contain;
border-radius: 10rem;
}
#navbar-logo-text {
position: relative;
word-wrap: normal;
height: 100%;
line-height: 400%;
font-size: 1.4rem;
color: #292222;
font-family: var(--sans-serif, sans-serif);
font-weight: 400;
padding-left: 1.2rem;
}
/* Nav menu */
#nav {
position: fixed;
width: 100%;
height: 100%;
background-color: white;
overflow: hidden;
z-index: 0;
}
#menu > li > a, .options-button {
display: block;
padding: 1.2rem;
padding-top: 1rem;
padding-bottom: 1rem;
color: black;
font-size: 1.4rem;
}
#menu > li > a:active{
}
#menu li {
list-style-type: none;
}
#nav{
max-height: 0;
/*transition: max-height .5s ease-out;*/
}
/* Menu Icon */
#hamb{
position: absolute;
cursor: pointer;
right: 0rem;
padding: 2.8rem 2rem;
z-index: 9999;
}/* Style label tag */
#hamb-line {
background: black;
display: block;
height: 2px;
position: relative;
width: 24px;
} /* Style span tag */
#hamb-line::before,
#hamb-line::after{
background: black;
content: '';
display: block;
height: 100%;
position: absolute;
transition: all .2s ease-out;
width: 100%;
}
#hamb-line::before{
top: 5px;
}
#hamb-line::after{
top: -5px;
}
#side-menu {
display: none;
} /* Hide checkbox */
/* Toggle menu icon */
#side-menu:checked ~ nav {
display: block;
max-height: 100%;
padding-top: 5.625rem;
}
#side-menu:checked ~ #logo-container {
position: fixed;
}
#side-menu:checked ~ #hamb {
position: fixed;
}
#side-menu:checked ~ #logo-container {
position: fixed;
}
#side-menu:checked ~ #hamb #hamb-line {
background: transparent;
}
#side-menu:checked ~ #hamb #hamb-line::before {
transform: rotate(-45deg);
top: 0;
}
#side-menu:checked ~ #hamb #hamb-line::after {
transform: rotate(45deg);
top: 0;
}
/* Options */
.options-dropdown {
position: absolute;
display: none;
top: 5.6rem;
right: 1.8rem;
border: #404040 solid 0.1rem;
background-color: white;
z-index: 10;
}
.options-dropdown button, .options-dropdown a {
display: block;
font-family: var(--sans-serif,sans-serif);
font-size: 1.2rem;
width: 100%;
padding: 1rem;
text-align: left;
}
.options-dropdown button:hover, .options-dropdown a:hover {
background-color: var(--red);
color: white;
}
.options-button {
width: 100%;
text-align: left;
}
/* Localization */
#locales {
position: relative;
}
#locales button {
width: 100%;
text-align: left;
height: 4rem;
}
#locales button:hover {
opacity: 0.5;
}
#locales-img {
position: relative;
top: 0rem;
height: 2rem;
margin-left: 1.2rem;
}
/*
#options-dropdown>:first-child {
padding-bottom: 0.5rem;
}
#options-dropdown>:nth-child(2) {
padding-top: 0.5rem;
}
*/
/* Responsiveness */
@media only screen and (min-width: 1200px) {
#navbar {
position: relative;
width: min(100%,116rem);
left: 50%;
transform: translateX(-50%);
padding-right: 4rem;
padding-left: 4rem;
}
#nav {
max-height: none;
top: 0;
position: relative;
float: right;
width: fit-content;
background-color: transparent;
overflow: visible;
}
#side-menu:checked ~ nav {
padding-top: 0;
}
#menu li {
float: left;
}
#menu > li > a:hover, .options-button:hover, #navbar-logo-text:hover {
color: rgb(127, 127, 127);
}
#menu > li > a, .options-button {
padding: 0.9rem;
padding-top: 1.9rem;
padding-bottom: 1.9rem;
}
#hamb {
display: none;
}
#locales {
position: relative;
margin-right: 1.8rem;
}
#locales-img {
top: 0.9rem;
}
}

View File

@ -0,0 +1,55 @@
/*
#notifications-div>button {
cursor: pointer;
margin-left: 0.341rem;
}
#notifications-div>:nth-child(1) {
width: 1.706rem;
height: 1.706rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>:nth-child(2) {
width: 2.047rem;
height: 2.047rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>:nth-child(3) {
width: 2.389rem;
height: 2.389rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>button>div {
cursor: pointer;
margin: auto;
background-color: white;
border-radius: 100%;
}
#notifications-div>:nth-child(1)>div {
width: 1.228rem;
height: 1.228rem;
}
#notifications-div>:nth-child(2)>div {
width: 1.57rem;
height: 1.57rem;
}
#notifications-div>:nth-child(3)>div {
width: 1.843rem;
height: 1.843rem;
}
*/

View File

@ -0,0 +1,163 @@
import {getData, sendData} from "/js/libraries/serverTools.js"
export function getUser(user,loaded,callbackOuter) {
let callback = function(response) {
Object.assign(user,JSON.parse(response))
if(callbackOuter!=undefined) {
callbackOuter()
}
loaded.update((val) => {
return val + 1
})
}
getData("/xx/get-user",callback)
}
export function changeUser(name,value,user) {
if (user[name]!=value && user[name]!=undefined) {
user[name] = value
let data = new Object();
data[name] = value
sendData("/xx/change-user",data)
}
}
export function changePasswordVisibility(button) {
let input = button.previousElementSibling
let type = input.type
if (type=="text") {
input.type = "password";
button.style.opacity = 0.25
}
else {
input.type = "text";
button.style.opacity = 1
}
}
export function checkEmail(email,msg) {
if (email.includes("@")) {
return true
}
else {
msg.innerHTML = "must contain '@'"
return false
}
}
export function checkPassword(password,msg) {
let passwordLength = password.length
if (passwordLength<8) {
msg.innerHTML = "must be 8 characters"
return false
}
let numNumbers = password.match(/\d/g)?.length || 0;
if (numNumbers<1) {
msg.innerHTML = "mush have digits"
return false
}
let numLetters = password.match(/\D/g)?.length || 0;
if (numLetters<2) {
msg.innerHTML = "must have letters"
return false
}
return true
}
export function redirectLogged() {
let callback = function(responseText) {
let response = JSON.parse(responseText)
if (response) {
window.location.href = "/";
}
}
getData("/xx/check-login",callback)
}
export function redirectNotLogged() {
let callback = function(responseText) {
let response = JSON.parse(responseText)
if (!response) {
window.location.href = "/";
}
}
getData("/xx/check-login",callback)
}
// Redirect to the landing page
export function toLandingPage(response) {
if (response!=0) {
window.location.href = "/";
}
}
// Redirect to the dashboard page
export function toDashboard() {
window.location.href = "/";
}
// Process log in
export function processLoginResponse(response,msgs,remember) {
if (response==0) {
msgs.email.innerHTML = "not found"
}
else if (response==1) {
msgs.password.innerHTML = "is wrong"
}
else {
if (remember) {
let date = new Date()
date.setMonth(date.getMonth()+1)
date = date.toUTCString()
document.cookie = "__genierememberme=; expires=" + date + "; path=/;SameSite=Lax";
}
toDashboard()
}
}
// Log in
export function login(msgs,inputs) {
msgs.email.innerHTML = ""
msgs.password.innerHTML = ""
let data = {email: inputs.email.value, password: inputs.password.value, remember: inputs.remember.checked}
sendData('/xx/login-post', data, (response) => processLoginResponse(response,msgs,inputs.remember.checked))
}
// Process sign in
function processSignupResponse(response,msgs) {
if (response) {
toDashboard()
}
else {
msgs.email.innerHTML = "already exists"
}
}
// Sign up
export function signup(msgs,inputs) {
msgs.email.innerHTML = ""
let email = inputs.email.value
let password = inputs.password.value
if (checkEmail(email,msgs.email)==false) {
return
}
if (checkPassword(password,msgs.password)==false) {
return
}
let data = {email: email, password: password}
sendData('/xx/signup-post', data, (response) => processSignupResponse(response,msgs))
}
export function confirmEmail(msg,code,callback) {
msg.innerHTML = ""
sendData('xx/confirm-email',code,callback)
}
// Log out
export function logout() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "/logout", false ); // false for synchronous request
xmlHttp.send( null );
window.location.href = "/";
}

View File

@ -10,7 +10,7 @@ export function translate(content, x) {
}
}
function addMarkersToLayer(g,layer,content,locale,addPinContent,markerColor) {
function addMarkersToLayer(g,layer,content,locale,addPinContent,markerColor,options) {
let {text,coordinates} = addPinContent(g,content,locale)
var markerIcon = new L.Icon({
iconUrl: 'https://www.libsoc.org/img/common/markers/marker-' + markerColor + '.png',
@ -21,57 +21,69 @@ function addMarkersToLayer(g,layer,content,locale,addPinContent,markerColor) {
shadowSize: [41, 41]
})
let marker = L.marker(coordinates, {icon: markerIcon})
marker.id = g.id
marker.members = g.members
marker.contact = g.contact
marker.addTo(layer).bindPopup(text)
if (options.pinCallback!=undefined) {
marker.on('click', (event) => options.pinCallback(marker,event))
}
}
export function addMarkersEntries(entries,entriesByCountry,map,content,locale,addPinContent,markerColor) {
export function addMarkersEntries(entries,entriesByCountry,map,content,locale,addPinContent,markerColor,options) {
let entriesMarkersLayer = L.layerGroup()
let entriesMarkersLayerOut = L.layerGroup()
let entriesMarkersLayerIn = L.layerGroup()
for (let g of entries) {
if (g.country!="Online" && g.country!="Worldwide") {
addMarkersToLayer(g,entriesMarkersLayerIn,content,locale,addPinContent,markerColor)
addMarkersToLayer(g,entriesMarkersLayerIn,content,locale,addPinContent,markerColor,options)
}
}
for (let gs of Object.values(entriesByCountry)) {
if (gs.length==1) {
let g = {...gs[0]}
g.country = [g.country]
if (g.country!="Online" && g.country!="Worldwide") {
addMarkersToLayer(g,entriesMarkersLayerOut,content,locale,addPinContent,markerColor)
if (options.enableCountryGrouping) {
for (let gs of Object.values(entriesByCountry)) {
if (gs.length==1) {
let g = {...gs[0]}
g.country = [g.country]
if (g.country!="Online" && g.country!="Worldwide") {
addMarkersToLayer(g,entriesMarkersLayerOut,content,locale,addPinContent,markerColor,options)
}
}
}
else {
if (gs[0].country!="Online" && gs[0].country!="Worldwide") {
let locationName = gs[0].country
let locationCoordinates = [0,0]
let members = 0
let contact = gs[0].contact
for (let g of gs) {
locationCoordinates[0] += g.latitude
locationCoordinates[1] += g.longitude
members += g.members
if (g.contact[0]!=gs[0].contact[0]) {
contact = contactGeneral
else {
if (gs[0].country!="Online" && gs[0].country!="Worldwide") {
let locationName = gs[0].country
let locationCoordinates = [0,0]
let members = 0
let contact = gs[0].contact
for (let g of gs) {
locationCoordinates[0] += g.latitude
locationCoordinates[1] += g.longitude
members += g.members
if (g.contact[0]!=gs[0].contact[0]) {
contact = contactGeneral
}
}
locationCoordinates[0] = locationCoordinates[0]/gs.length
locationCoordinates[1] = locationCoordinates[1]/gs.length
let gNew = {
country: locationName,
latitude: locationCoordinates[0],
longitude: locationCoordinates[1],
members: members,
contact: contact
}
addMarkersToLayer(gNew,entriesMarkersLayerOut,content,locale,addPinContent,markerColor,options)
}
locationCoordinates[0] = locationCoordinates[0]/gs.length
locationCoordinates[1] = locationCoordinates[1]/gs.length
let gNew = {
country: locationName,
latitude: locationCoordinates[0],
longitude: locationCoordinates[1],
members: members,
contact: contact
}
addMarkersToLayer(gNew,entriesMarkersLayerOut,content,locale,addPinContent,markerColor)
}
}
entriesMarkersLayerOut.addTo(entriesMarkersLayer)
map.on("zoomend", () => onZoomEnd(map,entriesMarkersLayer,entriesMarkersLayerOut,entriesMarkersLayerIn))
}
else {
entriesMarkersLayerIn.addTo(entriesMarkersLayer)
}
entriesMarkersLayerOut.addTo(entriesMarkersLayer)
entriesMarkersLayer.addTo(map)
map.on("zoomend", () => onZoomEnd(map,entriesMarkersLayer,entriesMarkersLayerOut,entriesMarkersLayerIn))
return entriesMarkersLayer
}

View File

@ -7,5 +7,7 @@
"communes": "Communes",
"cooperatives": "Cooperatives",
"parties": "Parties",
"partners": "Partners"
"partners": "Partners",
"login": "Login",
"profile": "Profile"
}

View File

@ -10,6 +10,7 @@ import watch from "rollup-plugin-watch";
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;

View File

@ -0,0 +1,129 @@
<svelte:options tag="auth-component" />
<script>
// Import statements
import { onMount, setContext,getContext } from 'svelte'
import { sendText } from "/js/libraries/serverTools.js"
import * as AuthTools from "/js/libraries/authTools.js"
import "/js/components/login-component.js"
import "/js/components/signup-component.js"
// Main code
AuthTools.redirectLogged()
let loginComponent
let signupComponent
let context = {
googleInit: false
}
setContext("auth",context)
function switchFocus(component) {
if (component==loginComponent) {
loginComponent.focused = true
signupComponent.focused = false
}
else {
loginComponent.focused = false
signupComponent.focused = true
}
}
function callbackGoogle(data) {
console.log(data)
sendText("/signup-google",data.credential,(response) => AuthTools.processLoginResponse(response,context.msgs,context.remember.checked))
}
function initGoogle() {
if (typeof google != 'undefined') {
google.accounts.id.initialize({
client_id: '93612176787-sr8qjqem4e3kok4msrnj8s1illt85a9g.apps.googleusercontent.com',
callback: callbackGoogle,
auto_select: true,
context: "signin"
})
context.googleInit = true
}
else {
setTimeout(initGoogle,100)
}
}
initGoogle()
</script>
<div id="auth-group">
<div id="auth-grid-group">
<login-component bind:this={loginComponent} on:click={() => switchFocus(loginComponent)} on:keydown={() => ""}></login-component>
<signup-component bind:this={signupComponent} on:click={() => switchFocus(signupComponent)} on:keydown={() => ""}></signup-component>
</div>
<div id="auth-or" class="pane">
<span>OR</span>
</div>
</div>
<style>
@import '/css/common.css';
@import '/css/auth.css';
span {
font-size: 1.4rem;
font-family: var(--sans-serif,sans-serif);
}
#auth-group {
margin: auto;
width: auto;
margin-bottom: 3rem;
}
#auth-grid-group {
display: grid;
grid-template-columns: 30rem 30rem;
justify-content: center;
gap: 1.37rem;
width: 100%;
}
#auth-or {
display: flex;
position: absolute;
margin: auto;
top: 40%;/*40%;*/
left: 50%;
transform: translate(-50%, -50%);
width: 5.4rem;
height: 5.4rem;
border-radius: 6.8rem;
background-color: white;
align-items:center;
justify-content:center;
font-family: var(--sans-serif,sans-serif);
font-weight: 500;
}
@media only screen and (max-width: 1200px) {
#auth-grid-group {
display: grid;
grid-template-columns: 30rem;
grid-template-rows: auto auto;
justify-content: center;
gap: 1.37rem;
width: 100%;
}
#auth-or {
top: 40rem;/*46.4rem;*/
}
#auth-group {
margin-top: 2rem;
margin-bottom: 3rem;
}
}
</style>

View File

@ -0,0 +1,131 @@
<svelte:options tag="confirmation-component" />
<script>
// Import statements
import { onMount } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Export statements
// Main code
let confirmationInputs = []
let confirmationMsg
let confirmationButton
function onlyNumberKey(ind,evt) {
// Only ASCII character in that range allowed
var value = evt.data
if (value in ["0","1","2","3","4","5","6","7","8","9"]) {
if (ind<4) {
confirmationInputs[ind+1].focus()
}
else {
AuthTools.confirmEmail(confirmationMsg,getCode(),callback)
}
}
else {
confirmationInputs[ind].value = ""
}
}
function getCode() {
let code = ""
for (let input of confirmationInputs) {
code += input.value
}
return parseInt(code)
}
function callback(response) {
if (response=="true") {
AuthTools.toDashboard()
}
else {
confirmationMsg.innerHTML = "Wrong code"
}
}
onMount(() => {
})
</script>
<div class="pane auth-pane">
<h2 class="auth-title title-highlight">CONFIRMATION CODE</h2>
<div id="confirmationInputs">
<input bind:this={confirmationInputs[0]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(0,evt)}><span class="dash">-</span>
<input bind:this={confirmationInputs[1]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(1,evt)}><span class="dash">-</span>
<input bind:this={confirmationInputs[2]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(2,evt)}><span class="dash">-</span>
<input bind:this={confirmationInputs[3]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(3,evt)}><span class="dash">-</span>
<input bind:this={confirmationInputs[4]} class="authConfirmationInput" type="text" maxlength="1" on:input={(evt) => onlyNumberKey(4,evt)}>
</div>
<span bind:this={confirmationMsg} id="confirmation-msg"></span>
<button bind:this={confirmationButton} class="auth-button" on:click="{() => AuthTools.confirmEmail(confirmationMsg,getCode(),callback)}">Confirm</button>
</div>
<style>
@import '/css/common.css';
.auth-pane {
position: relative;
padding: 3.4rem;
padding-top: 5.5rem;
padding-bottom: 5.5rem;
width: 33rem;
height: auto;
margin: auto;
}
.auth-title {
position: relative;
left: 0.7rem;
top: 0.2rem;
margin-bottom: 1.4rem;
}
.authConfirmationInput {
position: relative;
width: 3.16rem;
font-family: var(--serif,serif);
font-size: 3rem;
border-radius: 0.34rem;
margin-bottom: 0.7rem;
text-align: center;
padding-left: 0;
padding-bottom: 0.3 rem;
}
.dash {
display: block;
font-size: 3rem;
font-family: var(--serif,serif);
}
#confirmationInputs {
margin: auto;
display: grid;
justify-content: space-between;
grid-auto-flow: column;
}
.auth-button {
margin-top: 1.4rem;
height: 3.4rem;
width: 100%;
font-family: var(--sans-serif,sans-serif);
font-size: 1.6rem;
color: white;
background-color: var(--pink);
border-color: var(--pink);
border-radius: 0.5rem;
filter: drop-shadow(0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
}
#confirmation-msg {
display: inline;
color:red;
}
</style>

View File

@ -0,0 +1,110 @@
<svelte:options tag="login-component" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Export statements
export let focused = false
// Main code
let emailInput
let passwordInput
let inputs
let passwordVisibilityButton
let emailMsg
let passwordMsg
let msgs
let rememberMe
let googleButton
let parentProps = getContext("auth")
function renderGoogle() {
if (parentProps.googleInit) {
google.accounts.id.renderButton(googleButton,{
theme: 'outline',
size: 'large'
})
let iframe = googleButton.getElementsByTagName('iframe')[0]
iframe.style.height = "5rem"
iframe.style.width = "5rem"
}
else {
setTimeout(renderGoogle,100)
}
}
onMount(() => {
rememberMe.checked = true
inputs = {email: emailInput, password: passwordInput, remember: rememberMe}
msgs = {email: emailMsg, password: passwordMsg}
parentProps.msgs = msgs
parentProps.remember = rememberMe
parentProps.loginGoogle = googleButton
document.addEventListener("keypress", function(event) {
if (event.code == "Enter") {
if (focused) {
AuthTools.login(msgs,inputs)
}
}
})
renderGoogle()
})
</script>
<div id="login-group"class="pane auth-pane">
<h2 class="auth-title">LOG IN</h2>
<label class="auth-label" for="emailInput">Email&nbsp;</label><span bind:this={emailMsg} id="email-msg"></span>
<input bind:this={emailInput} id="emailInput" class="authEmailInput" type="email">
<div class="password-field">
<label class="auth-label" for="passwordInput">Password&nbsp;</label><span bind:this={passwordMsg} id="password-msg"></span>
<input bind:this={passwordInput} id="passwordInput" class="authPasswordInput" type="password">
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye icon"></object>
</button>
</div>
<div id="remember-me">
<input bind:this={rememberMe} type="checkbox" id="remember-me-checkbox"><label id="remember-me-label" for="passwordInput">remember me</label>
</div>
<button class="auth-button" on:click="{() => AuthTools.login(msgs,inputs)}">Log in</button>
<a id="forgot-password" href="forgot-password">Forgot password?</a>
<!--
<hr class="auth-line">
<div class="auth-methods-group">
<div id="google-btn-wrapper">
<div bind:this={googleButton} id="google-btn"></div>
<img src="/img/auth/google_icon.svg" id="google-logo" alt="google icon">
</div>
<button on:click={openGoogleWindow}>
<img src="img/auth/google_icon.svg" id="navbar-logo" alt="google icon">
</button>
<button onclick="">
<img src="img/auth/facebook_icon.svg" id="navbar-logo" alt="facebook icon">
</button>
<button onclick="">
<img src="img/auth/linkedin_icon.svg" id="navbar-logo" alt="linkedin icon">
</button>
</div>
-->
</div>
<style>
@import '/css/common.css';
@import '/css/auth.css';
</style>

View File

@ -0,0 +1,243 @@
<svelte:options tag="signup-component" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Export statements
export let focused = false
// Main code
let signupGroup
let emailInput
let passwordInput
let passwordVisibilityButton
let inputs
let googleButton
let emailMsg
let passwordMsg
let msgs
let rememberMe
let dialog
let signUp
let signUpField
let parentProps = getContext("auth")
function removeMsg(msg) {
if (msg.innerHTML!="") {
msg.innerHTML = ""
}
}
function showDialog() {
dialog.style.display = "block"
}
function hide() {
if (dialog!=null) {
dialog.style.display = "none";
}
}
function sendEmail() {
let email = signUpField.value
if (email.includes("@")) {
sendText("/get-email",email)
signUpField.value = ""
signUpField.placeholder = "Subscribed!"
signUpField.style.setProperty("--c", "hsl(147, 33%, 60%)")
}
else {
signUpField.value = ""
signUpField.placeholder = "must contain '@'"
signUpField.style.setProperty("--c", "hsl(0, 100%, 60%)")
}
}
function clearField() {
signUpField.placeholder = ""
}
function renderGoogle() {
if (parentProps.googleInit) {
google.accounts.id.renderButton(googleButton,{
theme: 'outline',
size: 'large'
})
}
else {
setTimeout(renderGoogle,100)
}
}
onMount(() => {
rememberMe.checked = true
inputs = {email: emailInput, password: passwordInput}
msgs = {email: emailMsg, password: passwordMsg}
document.addEventListener("keypress", function(event) {
if (event.code == "Enter") {
if (focused) {
AuthTools.signup(msgs,inputs,toLandingPage)
}
}
})
//renderGoogle()
})
</script>
<div id="signup-group" class="pane auth-pane" bind:this={signupGroup}>
<h2 class="auth-title">SIGN UP</h2>
<label class="auth-label" for="emailInput">Email&nbsp;</label><span bind:this={emailMsg} id="email-msg" on:change={() => removeMsg(emailMsg)}></span>
<input bind:this={emailInput} id="emailInput" class="authEmailInput" type="email">
<div class="password-field">
<label class="auth-label" for="emailInput">Password&nbsp;</label><span bind:this={passwordMsg} id="password-msg"></span>
<input bind:this={passwordInput} id="passwordInput" class="authPasswordInput" type="password" on:change={() => removeMsg(passwordMsg)}>
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye-icon"></object>
</button>
</div>
<div id="remember-me">
<input bind:this={rememberMe} type="checkbox" id="remember-me-checkbox"><label id="remember-me-label" for="passwordInput">remember me</label>
</div>
<button class="auth-button" on:click="{showDialog}">Sign up</button> <!--() => AuthTools.signup(msgs,inputs,AuthTools.toLandingPage)-->
<p id="forgot-password"></p>
<!--
<hr class="auth-line">
<div class="auth-methods-group">
<button on:click="{showDialog}">
<img src="/img/auth/google_icon.svg" id="navbar-logo" alt="google icon">
</button>
<button onclick="">
<img src="img/auth/facebook_icon.svg" id="navbar-logo" alt="facebook icon">
</button>
<button onclick="">
<img src="img/auth/linkedin_icon.svg" id="navbar-logo" alt="linkedin icon">
</button>
</div>
-->
</div>
<div bind:this={dialog} id="dialog">
<button id="shadow" on:click={hide}></button>
<div id="wrapper" class="pane">
<h2>Registration is closed</h2>
<p>We are still in the process of opening.</p>
<p>Sign up for updates to know when it becomes available:</p>
<div id="newsletter-container">
<input bind:this={signUpField} on:click={clearField} id="newsletterEmailInput" type="text">
<button bind:this={signUp} on:click={sendEmail} id="newsletterEmailButton">sign up</button>
</div>
<button id="no-button" on:click={hide}>No thanks</button>
</div>
</div>
<style>
@import '/css/common.css';
@import '/css/auth.css';
#dialog {
display: none;
}
#wrapper p {
text-align: justify;
}
#wrapper h2 {
text-align: center;
margin-bottom: 1rem;
}
#shadow {
position: fixed;
cursor: default;
top: 50%; right: 50%;
transform: translate(50%,-50%);
width: 100vw;
height: 100vh;
background:rgb(0, 0, 0, 0.2);
z-index: 999999;
}
#newsletter-container {
position: relative;
height: 3rem;
width: 100%;
margin-top: 1rem;
display: flex;
flex-direction: row;
}
#newsletterEmailInput {
height: 2.5rem;
border-radius: 0.2rem 0 0 0.2rem;
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
flex-grow: 1;
}
#newsletterEmailInput::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: var(--c,gray);
opacity: 1; /* Firefox */
}
#newsletterEmailButton {
width: 6.8rem;
height: 2.5rem;
background: var(--pink);
color: #ffffff;
font-family: var(--sans-serif,sans-serif);
font-size: 1.4rem;
border-radius: 0 0.2rem 0.2rem 0;
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
}
#newsletterEmailButton:active {
background: var(--darker-pink);
}
#wrapper {
top: 50%; right: 50%;
transform: translate(50%,-50%);
position: fixed;
max-width: 36rem;
width: 90vw;
padding: 2rem 4rem;
z-index: 1999999;
}
#wrapper * {
font-family: var(--sans-serif);
}
#no-button {
position: relative;
left: 50%;
transform: translateX(-50%);
width: 13rem;
height: 3rem;
margin-top: 2rem;
margin-bottom: 0.5rem;
background: #ffffff;
border: 0.2rem solid var(--pink);
font-family: var(--sans-serif,sans-serif);
font-size: 1.4rem;
border-radius: 0.5rem;
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
}
#no-button:active {
background: hsl(343, 23%, 82%);
}
</style>

View File

@ -43,7 +43,10 @@
function mapCallback(createMap,content,locale) {
let map = createMap([22, 0],2)
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCommunePinContent,"red")
let options = {
enableCountryGrouping: true,
}
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCommunePinContent,"red",options)
}
function getCountry(x) {

View File

@ -9,6 +9,7 @@
// Export statements
export let callback = null
export let colors = null
export let map = null
// Main code
let mapContainer

View File

@ -71,17 +71,17 @@
<div bind:this={root} id="root" class="pane-centering">
<div class="pane-container">
<div id="sidebars-left" class="sidebar">
<div bind:this={sidebarLeft} id="sidebar-left" class="pane">
<div bind:this={sidebarLeft} id="sidebar-left">
<slot name="sidebar-left"></slot>
</div>
<div bind:this={sidebarLeft2} id="sidebar-left2" class="pane">
<div bind:this={sidebarLeft2} id="sidebar-left2">
<slot name="sidebar-left2"></slot>
</div>
</div>
<div bind:this={sidebarRight} id="sidebar-right" class="pane sidebar">
<slot name="sidebar-right"></slot>
</div>
<div bind:this={mainPane} id="main-pane" class="pane">
<div bind:this={mainPane} id="main-pane">
<slot name="main" id="main-slot"></slot>
</div>
</div>
@ -95,7 +95,10 @@
}
#root {
position: relative;
margin-top: auto;
min-height: var(--min-height,auto);
height: 100%;
}
#main-pane {
@ -105,10 +108,8 @@
padding-top: var(--padding-top,0rem);
padding-bottom: var(--padding-bottom,0rem);
text-align: justify;
background: var(--background,white);
box-shadow: var(--box-shadow,0 0 0.314rem rgb(187, 187, 187));
margin: auto;
height: min-content;
height: 100%;
max-width: var(--width-main,66rem);
width: var(--width-main,66rem);
z-index: 1;
@ -124,13 +125,14 @@
flex-direction: column;
gap: 1rem;
margin-left: calc(-1*var(--width-left,22.5rem) - 1rem - 4rem);
width: calc(var(--width-left,22.5rem) + 4rem);
width: max-content;
max-width: 30rem;
}
#sidebar-left,#sidebar-left2 {
position: relative;
background-color: white;
padding: 2rem 2rem;
padding: 0rem 0rem;
}
#sidebar-left {
@ -149,16 +151,16 @@
padding: 2rem 2rem;
}
@media only screen and (max-width: 1880px) {
@media only screen and (max-width: 1340px) {
#main-pane {
max-width: initial;
width: 100%;
max-width: var(--width-main,66rem);
padding-left: var(--padding-left-mobile,1.8rem);
padding-right: var(--padding-right-mobile,1.8rem);
padding-top: var(--padding-top-mobile,1.8rem);
padding-bottom: var(--padding-bottom-mobile,1.8rem);
padding-left: var(--padding-left-mobile,0rem);
padding-right: var(--padding-right-mobile,0rem);
padding-top: var(--padding-top-mobile,0rem);
padding-bottom: var(--padding-bottom-mobile,0rem);
}
#sidebars-left, #sidebar-right {

View File

@ -43,7 +43,10 @@
function mapCallback(createMap,content,locale) {
let map = createMap([22, 0],2)
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCoopPinContent,"blue")
let options = {
enableCountryGrouping: true,
}
addMarkersEntries(entries,entriesByCountry,map,content,locale,addCoopPinContent,"blue",options)
}
function getCountry(x) {

View File

@ -32,8 +32,8 @@
<button on:click={() => {location.href='#'}} id="footer-up" aria-label="go up">
<svg xmlns="http://www.w3.org/2000/svg" width="42.545" height="72.601" viewBox="0 0 42.545 72.601">
<g id="Group_268" data-name="Group 268" transform="translate(-6.177 -2.399)">
<rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="#cb1816"/>
<path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="#DD1C1A"/>
<rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="var(--red)"/>
<path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="var(--red)"/>
</g>
</svg>
</button>
@ -54,8 +54,8 @@ footer {
bottom: 0;
width: 100%;
height: auto;
background: #5B6970;/*var(--dark-green);*/
border-top: #cb1816 solid 0.5rem;
background: var(--gray);
border-top: var(--red) solid 0.5rem;
}
footer p, footer a {

View File

@ -2,19 +2,26 @@
<script>
// Import statements
import { onMount } from 'svelte'
import { onMount, getContext } from 'svelte'
import { writable } from 'svelte/store';
import { loadLocaleContent, getData, sendData } from "/js/libraries/serverTools.js"
import { addMarkersEntries, translate } from "/js/libraries/mapTools.js"
// Import components
import "/js/components/map-component.js"
// Export statements
export let map = null
// Main code
let loaded = writable(0)
let content = writable({})
let entries
let entriesByCountry
let userData
let buttonsGroupMember
let buttonsNotGroupMember
let callback = (response) => {
entries = JSON.parse(response)
@ -41,11 +48,32 @@
let confirmationMsg
let addressInput
let contactInput
let addressVec
let userPinLat = 0
let membersInput
let addressVec = ["","",""]
let userPinData = {
}
let userPinLng = 0
let userPin = createPin(0,0)
userPin.setOpacity(0)
let modeButtons = []
let context = getContext("profile-component")
let closeGroupsAdd = context.closeGroupsAdd
let maps = context.maps
let onLoadedGroups = context.onLoadedGroups
let userGroups = context.userGroups
let user = context.user
let has_group = userGroups.length!=0
let mode = has_group ? 2 : 0
let pendingGroup
if (has_group) {
pendingGroup= userGroups[0].status!=undefined
if (pendingGroup) {
mode = 3
}
}
let locale = loadLocaleContent(content,"groups-component",loaded)
loadLocaleContent(content,"countries",loaded)
@ -105,23 +133,25 @@
response = JSON.parse(response)
// Extract the address information from the response
let address = response.address
let city = address.city || address.town || address.village || address.hamlet
let state = address.state
let country = address.country
let fullAddress = country
if (state!=undefined) {
fullAddress += ", " + state
if (address!=undefined) {
let city = address.city || address.town || address.village || address.hamlet
let state = address.state
let country = address.country
let fullAddress = country
if (state!=undefined) {
fullAddress += ", " + state
}
else {
state = ""
}
if (city!=undefined) {
fullAddress += ", " + city
}
else {
city = ""
}
addressVec = [country,state,city]
}
else {
state = ""
}
if (city!=undefined) {
fullAddress += ", " + city
}
else {
city = ""
}
addressVec = [country,state,city]
}
getData(url,callback)
}
@ -153,85 +183,180 @@
return {text,coordinates}
}
function mapCallback(createMap,content,locale) {
let map = createMap([22, 0],2)
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green")
map = createMap([22, 0],2)
maps["groupsAdd"] = map
let options = {
enableCountryGrouping: false,
pinCallback: pinCallback
}
addMarkersEntries(entries,entriesByCountry,map,content,locale,addGroupPinContent,"green",options)
userPin.addTo(map)
map.on('click', function(event) {
let lat = event.latlng.lat;
let lng = event.latlng.lng;
userPinLat = lat
userPinLng = lng
updatePin(userPin,lat,lng)
userPin.setOpacity(1)
reverseGeocodeLocal(lat, lng)
reverseGeocode(lat, lng)
if (mode==0) {
let lat = event.latlng.lat;
let lng = event.latlng.lng;
userPinData["latitude"] = lat
userPinData["longitude"] = lng
userPinData["id"] = null
updatePin(userPin,lat,lng)
userPin.setOpacity(1)
reverseGeocodeLocal(lat, lng)
reverseGeocode(lat, lng)
}
})
}
function updateConfirmationMsg(response) {
if (response!==false) {
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
if (mode==0 && !user.verified) {
confirmationMsg.innerHTML = "You have been added to our database! Now go to our Discord to verify yourself."
}
else {
confirmationMsg.innerHTML = "Success!"
}
confirmationMsg.style.color = "green"
if (mode==0 || mode==1) {
userGroups[0] = {}
}
userGroups[0].country = addressVec[0]=="" ? null : addressVec[0]
userGroups[0].state = addressVec[1]=="" ? null : addressVec[1]
userGroups[0].town = addressVec[2]=="" ? null : addressVec[2]
userGroups[0].members = userPinData["members"]
onLoadedGroups()
}
else {
confirmationMsg.innerHTML = "Something went wrong."
confirmationMsg.style.color = "red"
}
}
function submitLocation() {
if (addressVec!=undefined) {
let data = {
if (addressVec[0]!="" || mode==3) {
let membersVal, contactVal
if (mode==0) { // Create
membersVal = membersInput.value
contactVal = contactInput.value
}
else if (mode==1) { // Join
contactVal = contactInput.value
}
else if (mode==2 || mode==3) { // Move
membersVal = ""
contactVal = ""
}
else if (mode==3) { // Leave
membersVal = ""
contactVal = ""
addressVec = [null,null,null]
userPinData["latitude"] = null
userPinData["longitude"] = null
}
userData = {
country: addressVec[0],
state: addressVec[1],
town: addressVec[2],
latitude: userPinLat,
longitude: userPinLng,
contact: contactInput.value
latitude: userPinData["latitude"],
longitude: userPinData["longitude"],
contact: contactVal=="" ? null : contactVal,
members: membersVal=="" ? null : parseInt(membersVal),
group_id: userPinData["id"],
mode: mode
}
if (data.state=="") {
data.state = null
if (userData.state=="") {
userData.state = null
}
if (data.town=="") {
data.town = null
}
if (data.contact=="") {
data.contact = null
if (userData.town=="") {
userData.town = null
}
let url = "/" + locale + "/groups-add-post/"
sendData(url,data,updateConfirmationMsg)
sendData(url,userData,updateConfirmationMsg)
}
}
function resizeInput(el) {
el.nextElementSibling.innerHTML = el.value
}
function pinCallback(marker,event) {
if (mode==1) {
let lat = event.latlng.lat;
let lng = event.latlng.lng;
userPinData["latitude"] = lat
userPinData["longitude"] = lng
userPinData["id"] = marker.id
userPinData["members"] = marker.members
updatePin(userPin,lat,lng)
userPin.setOpacity(1)
reverseGeocodeLocal(lat, lng)
reverseGeocode(lat, lng)
}
}
function chooseButton(index) {
for (let b of modeButtons) {
if (b!=undefined) {
b.style.background = "rgba(197, 43, 40, 0.319)"
b.style.color = "black"
}
}
modeButtons[index].style.background = "rgb(197, 43, 40)"
modeButtons[index].style.color = "white"
mode = index
}
function getAddress(g) {
if (g!=undefined) {
let location = [g.country,g.state,g.town].filter(x => x!=null)
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
}
else {
return "Create or join group"
}
}
function onLoaded() {
if ($loaded==3) {
chooseButton(mode)
if (mode==2 || mode==3) {
addressInput.value = getAddress(userGroups[0])
}
}
else {
let f = () => onLoaded()
setTimeout(f, 100)
}
}
onMount(() => {
onLoaded()
})
</script>
{#key $loaded}
{#if $loaded==3}
<div id="container">
<button class="close-button" on:click={closeGroupsAdd}></button>
<!--<img src="img/crowd.png" id="crowd" alt="crowd">-->
<div id="text-container">
<h1>Add a Group</h1>
<img id="groups-img" src="/img/common/groups.svg" alt="groups">
<p class="description">If there are no groups in your town with whom you can organize then do the following:</p>
<ol>
<li>Click on the map to show us where you are located;</li>
<li>Add a way to contact you or leave blank for a pin to point to our discord;</li>
<li>Press "Submit" to add yourself to our map;</li>
<li>Verify yourself by having a chat with us at our Discord server to show on the map;</li>
</ol>
{#if !has_group}
<div bind:this={buttonsNotGroupMember} id="button-line">
<button bind:this={modeButtons[0]} on:click={() => chooseButton(0)}>Create</button>
<button bind:this={modeButtons[1]} on:click={() => chooseButton(1)}>Join</button>
</div>
{:else if has_group && !pendingGroup}
<div bind:this={buttonsGroupMember} id="button-line">
<button bind:this={modeButtons[2]} on:click={() => chooseButton(2)} style={"display: " + (pendingGroup ? "none" : "initial")}>Move</button>
<button bind:this={modeButtons[3]} on:click={() => chooseButton(3)}>Leave</button>
</div>
{:else}
<div bind:this={buttonsGroupMember} id="button-line">
<button bind:this={modeButtons[3]} on:click={() => chooseButton(3)}>Leave</button>
</div>
{/if}
<div id="address-input-wrapper" class="input-label-wrapper">
<label for="address-input">Location: </label>
<div class="input-wrapper">
@ -239,16 +364,30 @@
<div class="ghost-input"></div>
</div>
</div>
<div class="input-label-wrapper">
<label for="contact-input">Contact: </label>
<div class="input-wrapper">
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
<div class="ghost-input"></div>
</div>
</div>
{#key mode}
{#if mode==0}
<div id="members-input-wrapper" class="input-label-wrapper">
<label for="members-input">Members: </label>
<div class="input-wrapper">
<input bind:this={membersInput} id="members-input" type="text" value={1}>
</div>
</div>
{/if}
{#if mode==0 || mode==1}
<div class="input-label-wrapper">
<label for="contact-input">Contact: </label>
<div class="input-wrapper">
<input bind:this={contactInput} on:input={() => resizeInput(contactInput)} id="contact-input" type="text">
<div class="ghost-input"></div>
</div>
</div>
{/if}
{/key}
<button id="submit-button" on:click={submitLocation}>Submit</button>
<p id="confirmation-msg" bind:this={confirmationMsg}></p>
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
{#if !(has_group && pendingGroup)}
<map-component id="map" callback={(createMap) => mapCallback(createMap,$content,locale)}></map-component>
{/if}
</div>
</div>
{/if}
@ -257,13 +396,75 @@
<style>
@import '/css/common.css';
#button-line {
position: relative;
width: fit-content;
margin: auto;
margin-top: 1.5rem;
}
#button-line button{
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
padding: 1rem 0;
width: 7rem;
}
#button-line :first-child {
border-top-left-radius: 1rem;
border-bottom-left-radius: 1rem;
margin-right: 0.1rem;
}
#button-line :last-child {
margin-left: 0.1rem;
border-top-right-radius: 1rem;
border-bottom-right-radius: 1rem;
}
.close-button {
position: absolute;
top: 2rem;
right: 0rem;
width: 2rem;
height: 2rem;
border: none;
cursor: pointer;
z-index: 2;
}
.close-button:hover {
opacity: 0.7;
}
.close-button::before,
.close-button::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 0.2rem;
height: 2rem;
background-color: #000;
border-radius: 1rem;
}
.close-button::before {
transform: translate(-50%, -50%) rotate(45deg);
}
.close-button::after {
transform: translate(-50%, -50%) rotate(-45deg);
}
#confirmation-msg {
margin-top: 0.5rem;
margin-bottom: 2rem;
}
ol li {
margin-left: 3rem;
margin-left: 1rem;
margin-bottom: 0.5rem;
}
@ -273,7 +474,7 @@
font-size: 1.15rem;
line-height: 160%;
color: #222222;
width: 5.5rem;
width: 6rem;
}
input, .ghost-input {
@ -296,6 +497,14 @@
margin-bottom: 1rem;
}
#members-input-wrapper {
margin-bottom: 1rem;
}
#members-input {
width: 5rem;
}
.ghost-input {
display: block;
visibility: hidden;
@ -339,42 +548,6 @@
color: white;
}
#add-prompt {
margin-bottom: 2rem;
}
#groups-img {
position: absolute;
width: 14rem;
left: 50%;
transform: translate(-50%);
z-index: 0;
opacity: 0.2;
}
#text-container>:nth-child(3) {
margin-top: 8rem;
}
.country-name {
margin-bottom: 0.5rem;
}
.country-block {
margin-bottom: 2rem;
}
.location-info {
margin-bottom: 0.75rem;
}
.location-info p {
margin-bottom: 0;
}
a {
color: #DD1C1A;
}
#map {
--height: 30rem;
@ -389,16 +562,6 @@
margin: auto;
}
h1 {
margin-bottom: 1rem;
font-size: 2.2rem;
text-align: center;
}
h3 {
margin-bottom: 1rem;
}
#container {
margin: auto;
max-width: 800px;

View File

@ -62,10 +62,13 @@
function mapCallback(createMap,content,locale) {
let map = createMap([22, 0],2)
let groupsMarkersLayer = addMarkersEntries(entries["groups"],entriesByCountry["groups"],map,content,locale,addGroupPinContent,"green")
let communesMarkersLayer = addMarkersEntries(entries["communes"],entriesByCountry["communes"],map,content,locale,addCommunePinContent,"red")
let coopsMarkersLayer = addMarkersEntries(entries["cooperatives"],entriesByCountry["cooperatives"],map,content,locale,addCoopPinContent,"blue")
let partiesMarkersLayer = addMarkersEntries(entries["parties"],entriesByCountry["parties"],map,content,locale,addPartyPinContent,"gold")
let options = {
enableCountryGrouping: true,
}
let groupsMarkersLayer = addMarkersEntries(entries["groups"],entriesByCountry["groups"],map,content,locale,addGroupPinContent,"green",options)
let communesMarkersLayer = addMarkersEntries(entries["communes"],entriesByCountry["communes"],map,content,locale,addCommunePinContent,"red",options)
let coopsMarkersLayer = addMarkersEntries(entries["cooperatives"],entriesByCountry["cooperatives"],map,content,locale,addCoopPinContent,"blue",options)
let partiesMarkersLayer = addMarkersEntries(entries["parties"],entriesByCountry["parties"],map,content,locale,addPartyPinContent,"gold",options)
let overlayMaps = {}
overlayMaps[content.groups] = groupsMarkersLayer
@ -163,7 +166,7 @@
font-family: var(--sans-serif,sans-serif);
width: 14rem;
line-height: 4rem;
background: #cb1816;
background: var(--red);
color: white;
text-align: center;
}

View File

@ -0,0 +1,151 @@
<svelte:options tag="navbar-logged" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import { writable } from 'svelte/store'
import { loadLocaleContent, locales } from "/js/libraries/serverTools.js"
// Main code
let hambInput
let navbar
let localesDropdown
let initiativesDropdown
let loaded = writable(0)
let content = writable({})
let logoText
let locale = loadLocaleContent(content,"navbar-component",loaded)
function changeNavbar() {
if (hambInput.checked) {
navbar.style.background = "white"
//navbar.style.boxShadow = "0 0 0.314rem rgb(187, 187, 187)"
}
else {
setTimeout(()=> {
navbar.style.position = "relative"
navbar.style.background = ""
navbar.style.boxShadow = ""
}
,510)
}
}
function showDropdown(dropdown) {
let state = dropdown.style.display
initiativesDropdown.style.display = "none"
localesDropdown.style.display = "none"
if (state=="block") {
dropdown.style.display = "none"
}
else {
dropdown.style.display = "block"
}
}
function changeLocale(lang) {
localStorage.setItem("locale",lang)
let locSplit = location.href.split("/")
let localesSymbols = Object.keys(locales)
locSplit = locSplit.filter(x => !localesSymbols.includes(x))
let loc = locSplit.slice(0,locSplit.length-1).join("/") + "/" + lang + "/" + locSplit[locSplit.length-1]
location.href = loc
}
function fixHeading() {
if (locale=="ru") {
let func = () => {
if (logoText==undefined) {
setTimeout(func,100)
}
else {
if (((window.innerWidth < 1700 && window.innerWidth > 1400) || window.innerWidth < 400) && logoText.style.lineHeight!="100%") {
logoText.style.lineHeight = "120%"
logoText.style.top = "1rem"
logoText.style.width = "16rem"
}
else if (((window.innerWidth > 1700) || (window.innerWidth > 400 && window.innerWidth < 1400)) && logoText.style.lineHeight!="400%") {
logoText.style.lineHeight = "400%"
logoText.style.top = "0rem"
logoText.style.width = "auto"
}
}
}
func()
addEventListener("resize", func)
}
}
function hide(dropdown) {
let callback = () => {
dropdown.style.display = "none"
}
setTimeout(callback,100)
}
onMount(() => {
fixHeading()
})
</script>
<!-- Navigation bar -->
{#key loaded}
{#if Object.keys($content).length!=0}
<header bind:this={navbar} id="navbar">
<!-- Hamburger icon -->
<input bind:this={hambInput} type="checkbox" id="side-menu" on:click={changeNavbar}>
<label id="hamb" for="side-menu"><span id="hamb-line"></span></label>
<!-- Logo -->
<a id=logo-container href={"/" + locale + "/"}>
<img src="/img/common/flag.png" id="navbar-logo" alt="logo">
<span bind:this={logoText} id="navbar-logo-text" >{@html $content.orgName}</span>
</a>
<!-- Menu -->
<nav id="nav">
<ul id="menu">
<li><a href={"/"+locale+"/join-us"}>{$content.joinUs}</a></li>
<li><a href={"/"+locale+"/manifesto"}>{$content.manifesto}</a></li>
<!-- Options dropdown -->
<!-- A list of links pointing to different pages of the website. Implemented as a div opened on :hover-->
<li id="options-container">
<button on:click={() => showDropdown(initiativesDropdown)} on:focusout={() => hide(initiativesDropdown)} class="options-button">{$content.initiatives}</button>
<div bind:this={initiativesDropdown} class="options-dropdown">
<a href={"/"+locale+"/groups"}>{$content.groups}</a>
<a href={"/"+locale+"/communes"}>{$content.communes}</a>
<a href={"/"+locale+"/cooperatives"}>{$content.cooperatives}</a>
<a href={"/"+locale+"/parties"}>{$content.parties}</a>
<a href={"/"+locale+"/partners"}>{$content.partners}</a>
</div>
</li>
<li><a href={"/"+locale+"/profile"}>{$content.profile}</a></li>
<li id="locales">
<button on:click={() => showDropdown(localesDropdown)} on:focusout={() => hide(localesDropdown)}>
<picture>
<source srcset="/img/common/globe.webp">
<source srcset="/img/common/globe.png">
<img id="locales-img" alt="globe">
</picture>
</button>
</li>
<div bind:this={localesDropdown} class="options-dropdown">
{#each Object.entries(locales) as [loc,name]}
<button on:click={() => changeLocale(loc)}>{name}</button>
{/each}
</div>
</ul>
</nav>
</header>
{/if}
{/key}
<style>
@import '/css/common.css';
@import '/css/navbar.css';
</style>

View File

@ -1,4 +1,4 @@
<svelte:options tag="navbar-component" />
<svelte:options tag="navbar-not-logged" />
<script>
@ -121,6 +121,7 @@
<a href={"/"+locale+"/partners"}>{$content.partners}</a>
</div>
</li>
<li><a href={"/"+locale+"/auth"}>{$content.login}</a></li>
<li id="locales">
<button on:click={() => showDropdown(localesDropdown)} on:focusout={() => hide(localesDropdown)}>
<picture>
@ -146,274 +147,5 @@
<style>
@import '/css/common.css';
/* Header */
#navbar{
position: relative;
top: 0;
width: min(100%,116rem);
z-index: 1000000000;
height: 5.26rem;
padding-left: 0rem;
padding-right: 0rem;
}
#navbar * {
font-family: var(--sans-serif, sans-serif);
}
/* Logo */
#logo-container {
display: flex;
position: absolute;
margin-left: 1rem;
height: 100%;
max-height: 5.26rem;
color: black;
z-index: 1;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
}
#navbar-logo {
height: 3.5rem;
width: 3.5rem;
object-fit: contain;
border-radius: 10rem;
}
#navbar-logo-text {
position: relative;
word-wrap: normal;
height: 100%;
line-height: 400%;
font-size: 1.4rem;
color: #292222;
font-family: var(--sans-serif, sans-serif);
font-weight: 400;
padding-left: 1.2rem;
}
/* Nav menu */
#nav {
position: fixed;
width: 100%;
height: 100%;
background-color: white;
overflow: hidden;
z-index: 0;
}
#menu > li > a, .options-button {
display: block;
padding: 1.2rem;
padding-top: 1rem;
padding-bottom: 1rem;
color: black;
font-size: 1.4rem;
}
#menu > li > a:active{
background-color: #f7aec0;
}
#menu li {
list-style-type: none;
}
#nav{
max-height: 0;
/*transition: max-height .5s ease-out;*/
}
/* Menu Icon */
#hamb{
position: absolute;
cursor: pointer;
right: 0rem;
padding: 2.8rem 2rem;
z-index: 9999;
}/* Style label tag */
#hamb-line {
background: black;
display: block;
height: 2px;
position: relative;
width: 24px;
} /* Style span tag */
#hamb-line::before,
#hamb-line::after{
background: black;
content: '';
display: block;
height: 100%;
position: absolute;
transition: all .2s ease-out;
width: 100%;
}
#hamb-line::before{
top: 5px;
}
#hamb-line::after{
top: -5px;
}
#side-menu {
display: none;
} /* Hide checkbox */
/* Toggle menu icon */
#side-menu:checked ~ nav {
display: block;
max-height: 100%;
padding-top: 5.625rem;
}
#side-menu:checked ~ #logo-container {
position: fixed;
}
#side-menu:checked ~ #hamb {
position: fixed;
}
#side-menu:checked ~ #logo-container {
position: fixed;
}
#side-menu:checked ~ #hamb #hamb-line {
background: transparent;
}
#side-menu:checked ~ #hamb #hamb-line::before {
transform: rotate(-45deg);
top: 0;
}
#side-menu:checked ~ #hamb #hamb-line::after {
transform: rotate(45deg);
top: 0;
}
/* Options */
.options-dropdown {
position: absolute;
display: none;
top: 5.6rem;
right: 1.8rem;
border: #404040 solid 0.1rem;
background-color: white;
z-index: 10;
}
.options-dropdown button, .options-dropdown a {
display: block;
font-family: var(--sans-serif,sans-serif);
font-size: 1.2rem;
width: 100%;
padding: 1rem;
text-align: left;
}
.options-dropdown button:hover, .options-dropdown a:hover {
background-color: rgb(187 53 52 / 96%);
color: white;
}
.options-button {
width: 100%;
text-align: left;
}
/* Localization */
#locales {
position: relative;
}
#locales button {
width: 100%;
text-align: left;
height: 4rem;
}
#locales button:hover {
opacity: 0.5;
}
#locales-img {
position: relative;
top: 0rem;
height: 2rem;
margin-left: 1.2rem;
}
/*
#options-dropdown>:first-child {
padding-bottom: 0.5rem;
}
#options-dropdown>:nth-child(2) {
padding-top: 0.5rem;
}
*/
/* Responsiveness */
@media only screen and (min-width: 1200px) {
#navbar {
position: relative;
width: min(100%,116rem);
left: 50%;
transform: translateX(-50%);
padding-right: 4rem;
padding-left: 4rem;
}
#nav {
max-height: none;
top: 0;
position: relative;
float: right;
width: fit-content;
background-color: transparent;
overflow: visible;
}
#side-menu:checked ~ nav {
padding-top: 0;
}
#menu li {
float: left;
}
#menu > li > a:hover, .options-button:hover, #navbar-logo-text:hover {
color: rgb(127, 127, 127);
}
#menu > li > a, .options-button {
padding: 0.9rem;
padding-top: 1.9rem;
padding-bottom: 1.9rem;
}
#hamb {
display: none;
}
#locales {
position: relative;
margin-right: 1.8rem;
}
#locales-img {
top: 0.9rem;
}
}
@import '/css/navbar.css';
</style>

View File

@ -0,0 +1,29 @@
<svelte:options tag="profile-communes" />
<script>
// Import statements
import { onMount } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Main code
onMount(() => {
})
</script>
<h3>Under development</h3>
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your commune to be added.</p>
<style>
@import '/css/common.css';
h3 {
text-align: center;
}
</style>

View File

@ -0,0 +1,284 @@
<svelte:options tag="profile-component" />
<script>
// Import libraries
import { onMount, afterUpdate, setContext } from 'svelte'
import { writable } from 'svelte/store'
import * as AuthTools from "/js/libraries/authTools.js"
import {svgFromObject} from "/js/libraries/miscTools.js"
//Import components
import "/js/components/pane-aligner.js"
import "/js/components/profile-general.js"
import "/js/components/profile-groups.js"
import "/js/components/profile-communes.js"
import "/js/components/profile-coops.js"
import "/js/components/profile-parties.js"
import "/js/components/groups-add-component.js"
// Main code
AuthTools.redirectNotLogged()
let root
let general
let groups
let communes
let coops
let parties
let panes
let groupsAdd
let generalButton
let groupsButton
let communesButton
let coopsButton
let partiesButton
let buttons
let currentPaneIndex = 0
let locationPopup
let maps = {}
let user = {}
let loaded = writable(0)
let reloadTriggerVal = writable(0)
AuthTools.getUser(user,loaded)
function changePane(pane,button) {
for (let p of panes) {
p.style.display = "none"
}
for (let b of buttons) {
styleField(b,400,"#636363")
}
pane.style.display = "initial"
styleField(button,500,"#c52a28")
}
function styleField(div,weight,color) {
let svgObject = div.querySelector("object")
if (svgObject==null) {
let f = () => styleField(div,weight,color)
setTimeout(f,100)
}
else {
let svgItem = svgFromObject(svgObject)
if (svgItem==null) {
let f = () => styleField(div,weight,color)
setTimeout(f,100)
}
else {
div.style.fontWeight = weight
svgItem.setAttribute("fill", color)
}
}
}
function fillFields() {
if (Object.keys(user).length!=0 && root!=undefined) {
for (let b of buttons) {
styleField(b,400,"#636363")
}
styleField(buttons[currentPaneIndex],500,"#c52a28")
}
else {
setTimeout(fillFields, 100)
}
}
function valid(el) {
return (el!=undefined) && (el!=null)
}
function init() {
panes = [general,groups,communes,coops,parties]
buttons = [generalButton,groupsButton,communesButton,coopsButton,partiesButton]
if ($loaded==1 && panes.every(x => valid(x)) && buttons.every(x => valid(x))) {
panes = [general,groups,communes,coops,parties]
buttons = [generalButton,groupsButton,communesButton,coopsButton,partiesButton]
fillFields()
general.style.display = "initial"
}
else {
let f = () => init()
setTimeout(f,100)
}
}
function reloadTrigger() {
reloadTriggerVal.update((val) => {
return val + 1
})
}
setContext("profile-component",{user,maps,reloadTrigger})
onMount(() => {
init()
})
</script>
<!--
<div bind:this={locationPopup} class="overlay" style="display: none">
<div id="location-overlay-content">
</div>
<button class="overlay-button" on:click={() => locationPopup.style.display = "none"}></button>
</div>
-->
<pane-aligner>
<div id="left-column" class="pane" slot="sidebar-left" bind:this={root}>
<button bind:this={generalButton} on:click={() => changePane(general,generalButton)}>
<object id="general-img" class="icons" type="image/svg+xml" data="/img/profile/icons/general.svg" title="general"></object>
<span>general</span>
</button>
<button bind:this={groupsButton} on:click={() => changePane(groups,groupsButton)}>
<object id="groups-img" class="icons" type="image/svg+xml" data="/img/common/groups.svg" title="groups"></object>
<span>groups</span>
</button>
<button bind:this={communesButton} on:click={() => changePane(communes,communesButton)}>
<object id="communes-img" class="icons" type="image/svg+xml" data="/img/common/communes.svg" title="communes"></object>
<span>communes</span>
</button>
<button bind:this={coopsButton} on:click={() => changePane(coops,coopsButton)}>
<object id="coops-img" class="icons" type="image/svg+xml" data="/img/common/coops.svg" title="coops"></object>
<span>cooperatives</span>
</button>
<button bind:this={partiesButton} on:click={() => changePane(parties,partiesButton)}>
<object id="parties-img" class="icons" type="image/svg+xml" data="/img/common/parties.svg" title="parties"></object>
<span>parties</span>
</button>
<button on:click={AuthTools.logout} id="logout-button">
<object id="logout-img" class="icons" type="image/svg+xml" data="/img/profile/icons/logout.svg" title=""></object>
<span>logout</span>
</button>
</div>
<div id="main-column" slot="main">
{#key $loaded}
{#if $loaded==1}
<profile-general bind:this={general} style="display: none;"></profile-general>
<profile-groups bind:this={groups} style="display: none;"></profile-groups>
<profile-communes bind:this={communes} style="display: none;"></profile-communes>
<profile-coops bind:this={coops} style="display: none;"></profile-coops>
<profile-parties bind:this={parties} style="display: none;"></profile-parties>
{/if}
{/key}
</div>
</pane-aligner>
<style>
@import '/css/common.css';
#general-img {
top: 0rem;
}
#groups-img {
top: 0.3rem;
}
#coops-img {
top: 0rem;
}
#parties-img {
top: 0rem;
}
#logout-img {
width: 1.5rem;
}
#logout-button {
padding-top: 1rem;
padding-left: 0.1rem;
}
#left-column {
position: relative;
display: flex;
flex-direction: column;
width: 15.2rem;
padding: 2rem;
border-radius: 0.64rem 0.64rem 0.64rem 0.64rem;
gap: 1rem;
}
.icons {
position: relative;
width: 1.8rem;
}
#left-column button span {
position: absolute;
padding-left: 3.4rem;
margin-top: 0rem;
font-family: var(--sans-serif,sans-serif);
}
#left-column button {
display: flex;
flex-direction: row;
}
#main-column {
padding: 1rem 2rem 1rem 2rem;
height: 100%;
width: 100%;
border-radius: 0 0.64rem 0.64rem 0;
flex-grow: 1;
flex-shrink: 1;
min-height: 20rem;
}
pane-aligner {
--width-main: 800px;
--width-left: 10.5rem;
}
@media only screen and (max-width: 1340px) {
#left-column {
position: relative;
margin-left: 0rem;
width: 100%;
border-radius: 0.64rem 0.64rem 0rem 0;
}
#main-column {
border-radius: 0.64rem;
padding: 3rem 0.5rem;
padding-bottom: 1.5rem;
border-radius: 0rem 0rem 0.64rem 0.64rem;
width: 100%;
}
#logout-button {
position: relative;
bottom: 0;
}
#left-column button {
margin-left: auto;
margin-right: auto;
width: 10rem;
}
#logout-button {
padding-top: 1rem;
margin-bottom: 0rem;
}
}
</style>

View File

@ -0,0 +1,29 @@
<svelte:options tag="profile-coops" />
<script>
// Import statements
import { onMount } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Main code
onMount(() => {
})
</script>
<h3>Under development</h3>
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your cooperative to be added.</p>
<style>
@import '/css/common.css';
h3 {
text-align: center;
}
</style>

View File

@ -0,0 +1,433 @@
<svelte:options tag="profile-general" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
//Import components
import "/js/components/select-component.js"
import "/js/components/switch-component.js"
//Export statements
// Main code
let emailInput
let section
let saveEmailButton
let changePasswordInputDiv
let changePasswordMsg
let savePasswordButton
let passwordInput
let changePasswordDiv
let passwordVisibilityButton
let emailMsg
let passwordDiv
let emailDiv
let emailInputDiv
let prevEmail
let context = getContext("profile-component")
let user = context.user
function showSaveButton(button) {
prevEmail = emailInput.value
button.style.display = "initial"
emailMsg.style.display = "inline"
let windowWidth = window.innerWidth
if (windowWidth<1100) {
emailInputDiv.style.marginTop = "1rem"
emailDiv.style.flexDirection = "column"
}
else {
//emailInput.style.width = "19rem"
}
}
function saveEmail() {
let email = emailInput.value
if (AuthTools.checkEmail(email,emailMsg)) {
if (email!=user.email) {
AuthTools.changeUser("email",email,user)
}
resetEmailField()
}
}
function resetEmailField() {
if (prevEmail!=undefined) {
emailInput.value = prevEmail
}
emailInput.style.width = "100%"
emailMsg.style.display = "none"
emailDiv.style.flexDirection = "row"
emailInputDiv.style.marginTop = "0rem"
saveEmailButton.style.display = "none"
emailMsg.innerHTML = ""
}
function launchChangePassword() {
let windowWidth = window.innerWidth
if (windowWidth<1100) {
changePasswordInputDiv.style.display = "flex";
}
else {
changePasswordInputDiv.style.display = "initial";
}
changePasswordDiv.style.display = "none";
passwordInput.focus()
}
function savePassword() {
let password = passwordInput.value
if (AuthTools.checkPassword(password,changePasswordMsg)) {
if (password!=user.password) {
AuthTools.changeUser("password",password,user)
}
changePasswordMsg.innerHTML = ""
resetPasswordField()
}
}
function resetPasswordField() {
changePasswordInputDiv.style.display = "none";
changePasswordDiv.style.display = "initial";
changePasswordMsg.innerHTML = ""
}
function fillFields() {
if (user!=null && Object.keys(user).length!=0 && section!=undefined) {
emailInput.value = user.email
}
else {
setTimeout(fillFields, 10)
}
}
function resizeInput(el) {
el.nextElementSibling.innerHTML = el.value
}
onMount(() => {
fillFields()
document.addEventListener("click", function(event) {
if (passwordDiv.focused) {
resetEmailField()
}
else if (emailDiv.focused) {
resetPasswordField()
}
else {
resetEmailField()
resetPasswordField()
}
})
})
</script>
<section bind:this={section} id="general-section">
<h2 class="title-highlight">General</h2>
<div bind:this={emailDiv} on:mouseenter={emailDiv.focused=true} on:mouseleave={emailDiv.focused=false}>
<div class="title-msg">
<span>Email:</span>
<span bind:this={emailMsg} id="signup-email-msg"></span>
</div>
<div bind:this={emailInputDiv} id="emailInputDiv">
<button bind:this={saveEmailButton} id="save-email" class="save-button" on:click={saveEmail}>save</button>
<div class="input-wrapper">
<input bind:this={emailInput} id="emailInput" class="text-input" type="text" on:click={() => showSaveButton(saveEmailButton)} on:input={() => resizeInput(emailInput)}>
<div class="ghost-input"></div>
</div>
</div>
</div>
<div bind:this={passwordDiv} on:mouseenter={passwordDiv.focused=true} on:mouseleave={passwordDiv.focused=false} id="change-password-line-wrapper">
<div id="change-password-line">
<div class="title-msg">
<span>Password:</span>
<span bind:this={changePasswordMsg} id="signup-password-msg"></span>
</div>
<div bind:this={changePasswordDiv} id="change-password-div">
<button id="change-password" on:click={launchChangePassword}>change
<object type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon"></object>
</button>
</div>
</div>
<div bind:this={changePasswordInputDiv} id="change-password-input-div">
<button bind:this={savePasswordButton} id="save-password" class="save-button" on:click={savePassword}>save</button>
<input bind:this={passwordInput} id="passwordInput" class="text-input" type="password">
<button bind:this={passwordVisibilityButton} class="eye-icon" on:click="{() => AuthTools.changePasswordVisibility(passwordVisibilityButton)}">
<object type="image/svg+xml" data="/img/auth/eye_icon.svg" title="eye icon"></object>
</button>
</div>
</div>
<div>
<div id="verifiedDiv">
<span>Verified:</span>
<span style="color: {user.verified ? "green" : "red"}">{user.verified}</span>
</div>
</div>
</section>
<style>
@import '/css/common.css';
#verifiedDiv {
display: inline;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 2rem;
width: 100%;
}
/*---General section-----------------------------------------------------------*/
.ghost-input {
display: block;
visibility: hidden;
height: 0;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.input-wrapper {
display: inline-block;
max-width: calc(100% - 10rem);
min-width: 0rem;
height: 2.5rem;
position: relative;
right: 0
}
span {
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
}
#general-section {
display: flex;
flex-direction: column;
}
#general-section h2 {
margin: auto;
margin-top: 0;
margin-bottom: 0;
}
#general-section >div {
height: 3.5rem;
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0.14rem solid;
border-color: #cdcdcd;
}
#general-section >div >:first-child {
font-family: var(--sans-serif,sans-serif);
}
/* add padding to every line to center the diving line*/
#general-section >div:last-child {
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0;
}
#general-section >div div,
#general-section >div input,
#general-section >div :not(:first-child) input {
font-weight: 500;
font-size: 1.15rem;
font-family: var(--sans-serif,sans-serif);
color: #292222;
border: 0;
}
#general-section >div>:last-child {
padding-right: 1.35rem;
}
.text-input {
position: relative;
width: 20.475rem;
direction: rtl;
border: 0;
outline: none;
bottom: 0.341rem;
}
/*---Email-------------------------------------------------------------------*/
#emailInput {
position: relative;
right: 0;
top: 0.1rem;
width: 100%;
}
#save-email {
display: none;
margin-top: 0.5rem;
}
#signup-email-msg,
#signup-password-msg {
position: relative;
display: inline-block;
color:red;
font-weight: 400;
white-space: nowrap;
}
#signup-email-msg {
display: none;
}
#general-section >div:nth-child(2) {
display: flex;
flex-direction: row;
}
#emailInputDiv {
display: flex;
flex-direction: row;
justify-content: right;
align-items: center;
height: 2rem;
width: 100%;
}
.title-msg {
display: flex;
gap: 0.5rem;
}
.title-msg * {
text-align: left;
}
/*---Change password-------------------------------------------------------------------*/
#change-password-line {
display: flex;
justify-content: space-between;
}
#change-password-div {
width: 9.3rem;
left: 0;
}
#change-password {
position: absolute;
cursor: pointer;
width: 8rem;
height: 2.73rem;
font-size: 1.15rem;
font-family: var(--sans-serif,sans-serif);
font-weight: 500;
text-align: right;
padding-right: 2rem;
margin-top: -0.55rem;
background-color: transparent;
}
#change-password > object {
pointer-events: none;
position: absolute;
width: 1.5rem;
right: 0.0rem;
}
#change-password-input-div {
display: none;
float: right;
position: relative;
margin-top: -1.7rem;
}
#passwordInput {
width: 15rem;
right: 0.65rem;
margin-left: 1.5rem;
}
.save-button {
position: relative;
bottom: 0.34rem;
margin-right: 0.6rem;
height: 2.73rem;
width: 4.778rem;
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.512rem;
}
#save-password {
bottom: 0.6rem
}
.eye-icon {
display: inline-block;
position: relative;
cursor: pointer;
opacity: 0.25;
height: 2.2rem;
width: 1.7rem;
}
.eye-icon * {
pointer-events: none;
}
@media only screen and (max-width: 1100px) {
#change-password-line-wrapper {
display: flex;
flex-direction: column;
height: auto;
min-height: 4rem;
}
#change-password-input-div {
margin-top: 1rem;
justify-content: flex-end;
}
#general-section >div:nth-child(2) {
height: auto;
min-height: 4rem;
position: relative;
}
#passwordInput {
width: 100%;
bottom: 0;
}
#emailInput {
width: 100%;
}
#save-password {
bottom: 0rem
}
}
</style>

View File

@ -0,0 +1,455 @@
<svelte:options tag="profile-groups" />
<script>
// Import statements
import { onMount, getContext } from 'svelte'
import { writable } from 'svelte/store'
import { getData, sendData } from "/js/libraries/serverTools.js"
//Import components
import "/js/components/select-component.js"
import "/js/components/switch-component.js"
//Export statements
// Main code
let section
let userGroups = []
let groupsRequests = []
let content = writable({})
let loaded = writable(0)
let keyRequests = 0
let numLoaded = 2
let mainPane
let groupsAdd
let membersInput
let saveMembersButton
let membersInputDiv
let contactInput
let saveContactButton
let contactInputDiv
let locale = "en"
let inputLocation
let inputContact
let inputMembers
let pencilMembers
let pencilContact
let pencilButtonMembers
let pencilButtonContact
let myGroupLocation
let myGroupStatus
let context = getContext("profile-component")
let maps = context.maps
function groups_callback(response) {
userGroups = JSON.parse(response)
context["userGroups"] = userGroups
loaded.update((val) => {
return val + 1
})
}
getData("/xx/get-user-groups",groups_callback)
function requests_callback(response) {
let parsed = JSON.parse(response)
groupsRequests.push(...parsed)
loaded.update((val) => {
return val + 1
})
}
getData("/xx/get-group-requests",requests_callback)
function getAddress(g) {
if (g!=undefined) {
let location = [g.country,g.state,g.town].filter(x => x!=null)
return location.map(x => locale=="en" ? x : translate($content,x)).join(", ")
}
else {
return "Create or join group"
}
}
function getContact(c) {
if (c==null) {
return "https://discord.gg/Qk8KUk787z"
}
else {
return c
}
}
function launchChangeLocation() {
showLocationOverlay()
}
function launchChangeMembers() {
}
function showSaveButton(button,input) {
if (!input.readOnly) {
button.style.display = "initial"
}
}
function resetMembersField() {
saveMembersButton.style.display = "none"
}
function resetContactField() {
saveContactButton.style.display = "none"
}
function saveMembers() {
let email = emailInput.value
if (AuthTools.checkEmail(email,emailMsg)) {
if (email!=user.email) {
AuthTools.changeUser("email",email,user)
}
resetMembersField()
}
}
function saveContact() {}
function updateUserGroup(newInfo) {
if (newInfo!=undefined) {
myGroupLocation.innerHTML = getAddress(newInfo)
}
}
function onLoadedGroups() {
let els = [saveMembersButton,saveContactButton,membersInputDiv,contactInputDiv]
if ($loaded==numLoaded && els.every(x => x!=undefined && x!=null)) {
document.addEventListener("click", function(event) {
let activeEl
let shadowRoot = this.activeElement.shadowRoot
if (shadowRoot!=null) {
activeEl = shadowRoot.activeElement
shadowRoot = activeEl.shadowRoot
if (shadowRoot!=null) {
activeEl = shadowRoot.activeElement
}
}
if (activeEl == membersInput || activeEl == saveMembersButton) {
resetContactField()
}
else if (activeEl == contactInput || activeEl == saveContactButton) {
resetMembersField()
}
else {
resetMembersField()
resetContactField()
}
})
context["updateUserGroup"] = updateUserGroup
inputLocation = getAddress(userGroups[0])
if (userGroups.length==0) {
inputContact = ""
inputMembers = ""
}
else {
let group = userGroups[0]
inputContact = getContact(group.contact)
inputMembers = group.members
let status = group.status
if (status!=undefined) {
if (status==0) {
myGroupStatus.innerHTML = "(pending)"
myGroupStatus.style.color = "#FFC90E"
}
else if (status==2) {
myGroupStatus.innerHTML = "(rejected)"
myGroupStatus.style.color = "#c52a28"
}
pencilMembers.style.display = "none"
pencilContact.style.display = "none"
pencilButtonContact.style.cursor = "default"
pencilButtonMembers.style.cursor = "default"
membersInput.readOnly = true
contactInput.readOnly = true
}
}
}
else {
let f = () => onLoadedGroups()
setTimeout(f, 100)
}
}
function focus(el) {
el.focus()
el.click()
}
function approveRequest(ind,user_id) {
sendData("/xx/group-approve-request",{user_id: user_id})
groupsRequests.splice(ind,1)
keyRequests = keyRequests + 1
}
function rejectRequest(ind,user_id) {
sendData("/xx/group-reject-request",{user_id: user_id})
groupsRequests.splice(ind,1)
keyRequests = keyRequests + 1
}
function launchGroupsAdd() {
groupsAdd.style.display = "block"
mainPane.style.display = "none"
if (maps["groupsAdd"]!=undefined) {
maps["groupsAdd"].invalidateSize()
}
}
function closeGroupsAdd() {
groupsAdd.style.display = "none"
mainPane.style.display = "block"
}
context["onLoadedGroups"] = onLoadedGroups
context["launchGroupsAdd"] = launchGroupsAdd
context["closeGroupsAdd"] = closeGroupsAdd
onMount(() => {
onLoadedGroups()
})
</script>
{#key $loaded}
{#if $loaded==numLoaded}
<div bind:this={mainPane}>
<h2>Groups</h2>
<div>
<h3 class="group-heading">My group</h3>
<span bind:this={myGroupStatus} class="status"></span>
</div>
<section bind:this={section} class="entries-section">
<div>
<div class="change-field-line">
<span>Location:</span>
<div class="change-field-div">
<button class="change-field-button" bind:this={myGroupLocation} on:click={launchGroupsAdd}>{inputLocation}
<object type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
</button>
</div>
</div>
</div>
<div>
<div class="change-field-line">
<span>Members:</span>
<div bind:this={membersInputDiv} class="change-field-div input-pencil">
<div class="save-button-wrapper">
<button bind:this={saveMembersButton} on:click={saveMembers} class="save-button" style="display: none">save</button>
</div>
<input bind:this={membersInput} id="membersInput" class="text-input" type="text" bind:value={inputMembers} on:click={() => showSaveButton(saveMembersButton,membersInput)}>
<button bind:this={pencilButtonMembers} class="text-input-pencil-button" on:click={() => {focus(membersInput)}}>
<object bind:this={pencilMembers} type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
</button>
</div>
</div>
</div>
<div>
<div class="change-field-line">
<span>Contact:</span>
<div bind:this={contactInputDiv} class="change-field-div input-pencil">
<div class="save-button-wrapper">
<button bind:this={saveContactButton} on:click={saveContact} class="save-button" style="display: none">save</button>
</div>
<input bind:this={contactInput} id="contactInput" class="text-input" type="text" bind:value={inputContact} on:click={() => showSaveButton(saveContactButton,contactInput)}>
<button bind:this={pencilButtonContact} class="text-input-pencil-button" on:click={focus(contactInput)}>
<object bind:this={pencilContact} type="image/svg+xml" data="/img/profile/icons/pencil.svg" title="pencil-icon" class="pencil"></object>
</button>
</div>
</div>
</div>
</section>
<h3>Requests</h3>
<section bind:this={section} class="entries-section">
{#key keyRequests}
{#each groupsRequests as req,ind}
<div>
<div class="change-field-line">
<span>{req.email}</span>
<div class="request-button-wrapper">
<button on:click={() => approveRequest(ind,req.user_id)} class="approve-button">approve</button>
<button on:click={() => rejectRequest(ind,req.user_id)} class="approve-button" style="display:visible">reject</button>
</div>
</div>
</div>
{/each}
{/key}
</section>
</div>
<!--Helper panes-->
<groups-add-component bind:this={groupsAdd} style="display: none;"></groups-add-component>
{/if}
{/key}
<style>
@import '/css/common.css';
.request-button-wrapper {
display: flex;
gap: 1rem;
}
.approve-button {
height: 2.7rem;
padding: 0rem 1rem;
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.5rem;
margin-top: -0.5rem;
}
.group-heading {
display: inline-block;
}
.status {
display: inline-block;
font-size: 1.15rem;
font-family: var(--sans-serif,sans-serif);
margin-left: 0.5rem;
}
input {
font-family: var(--sans-serif,sans-serif)
}
.text-input-pencil-button {
display: inline-block;
position: relative;
height: 2.7rem;
width: 2rem;
}
.text-input-pencil-button object {
top: 0rem;
}
.pencil {
pointer-events: none;
position: absolute;
width: 1.5rem;
right: 0.0rem;
}
.change-field-div input.text-input {
position: relative;
width: 20.475rem;
direction: rtl;
border: 0;
outline: none;
height: 2.7rem;
font-style: var(--sans-serif,sans-serif);
background: transparent;
margin-top: -0.5rem;
}
#membersInput {
width: 5rem;
}
#contactInput {
max-width: 18rem;
}
.save-button {
position: absolute;
right: 0;
top: -0.4rem;
margin-right: 0.6rem;
height: 2.7rem;
width: 5rem;
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.5rem;
}
.save-button-wrapper {
display: inline-block;
position: relative;
height: 2rem;
}
h2 {
text-align: center;
margin-bottom: 0.5rem;
}
.entries-section {
margin-bottom: 1rem;
}
.entries-section >div {
height: 3.5rem;
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0.14rem solid;
border-color: #cdcdcd;
}
/* add padding to every line to center the diving line*/
.entries-section >div:last-child {
padding-bottom: 0.75rem;
padding-top: 0.75rem;
border-bottom: 0;
}
/*---Change field-------------------------------------------------------------------*/
.change-field-line {
display: flex;
justify-content: space-between;
}
.change-field-div {
width: max-content;
position: relative;
display: flex;
}
.change-field-button {
position: relative;
cursor: pointer;
height: 2.7rem;
font-size: 1.15rem;
font-family: var(--sans-serif,sans-serif);
font-weight: 500;
text-align: right;
padding-right: 1.9rem;
margin-top: -0.55rem;
background-color: transparent;
width: 100%;
}
/*---General section-----------------------------------------------------------*/
h3 {
margin-bottom: 0.5rem;
}
span {
font-family: var(--sans-serif,sans-serif);
font-size: 1.15rem;
}
</style>

View File

@ -0,0 +1,29 @@
<svelte:options tag="profile-parties" />
<script>
// Import statements
import { onMount } from 'svelte'
import * as AuthTools from "/js/libraries/authTools.js"
// Main code
onMount(() => {
})
</script>
<h3>Under development</h3>
<p style=" position: relative; margin-top: 2rem;">Visit <a href="https://discord.gg/Qk8KUk787z" style="color: #c52a28;">https://discord.gg/Qk8KUk787z</a> and ask for your party to be added.</p>
<style>
@import '/css/common.css';
h3 {
text-align: center;
}
</style>

View File

@ -12,8 +12,8 @@ server {
add_header Pragma public;
add_header Cache-Control "public";
}
location ~* \.(?:css|js|json)$ {
expires 1d;
location ~* \.(?:css|js|json|txt)$ {
expires 1h;
add_header Pragma public;
add_header Cache-Control "public";
}
@ -22,6 +22,7 @@ server {
add_header Pragma public;
add_header Cache-Control "public";
}
location /js/ {}
location /css {}
location /img/ {}
@ -29,18 +30,29 @@ server {
location /fonts/ {}
location /favicon.ico {}
location /robots.txt {}
location /sitemap.txt {}
location ~ /loaderio-58f125137ee61345d68285d88016ce2a {}
location / {
proxy_pass http://127.0.0.1:8001/;
map $request_uri $language_redirect {
~^/(check-login|login-post|logout|signup-post|signup-google|change-user|get-user|confirm-email) ""; # Excluded URIs, no redirection
~^/([a-zA-Z]{2})/ ""; # Matches URIs that start with a two-letter language code and sets it to an empty string
default /en; # Redirects all other URIs to the /en prefix
}
rewrite https://www.libsoc.org/communities https://www.libsoc.org/en/communes permanent;
location / {
rewrite ^ $language_redirect$request_uri? permanent;
proxy_pass http://127.0.0.1:8001;
}
rewrite /en/communities /en/communes permanent;
rewrite /en/coops /en/cooperatives permanent;
rewrite /en/affiliates /en/partners permanent;
listen 443 http3;
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/libsoc.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/libsoc.org/privkey.pem; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/libsoc.org-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/libsoc.org-0001/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
@ -51,6 +63,7 @@ server {
resolver 1.1.1.1 1.0.0.1 valid=300s;
resolver_timeout 5s;
}
server {
@ -59,17 +72,20 @@ server {
server_name libsoc.org;
listen 443 ssl http2; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/libsoc.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/libsoc.org/privkey.pem; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/libsoc.org-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/libsoc.org-0001/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
return 301 https://www.libsoc.org$request_uri;
listen 80;
listen [::]:80;
return 301 https://www.libsoc.org$request_uri;
listen 80;
listen [::]:80;
server_name www.libsoc.org libsoc.org;
server_name www.libsoc.org libsoc.org;
}

View File

@ -8,12 +8,9 @@ function up()
primary_key()
column(:email, :string)
column(:password, :string, limit = 100)
column(:name, :string)
column(:profile_picture, :int)
column(:country, :int)
column(:newsletter, :bool)
column(:notifications, :int)
column(:confirmation_code, :string)
column(:google_id, :string)
column(:verified, :bool)
]
end

View File

@ -2,6 +2,9 @@ module CreateTableGroups
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
include("../../lib/DatabaseSupport.jl")
import .DatabaseSupport: add_foreign_key, add_index
function up()
create_table(:groups) do
[
@ -13,8 +16,12 @@ function up()
column(:latitude, :float)
column(:longitude, :float)
column(:members, :int)
column(:user_id, :int)
]
end
add_foreign_key(:groups,:user_id,:users,:id)
add_index(:groups, :user_id)
end
function down()

View File

@ -18,7 +18,6 @@ function up()
column(:name, :string)
column(:market, :string)
column(:website, :string)
column(:description, :string)
]
end
end

View File

@ -0,0 +1,41 @@
module CreateTableGroupsRequests
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
include("../../lib/DatabaseSupport.jl")
using .DatabaseSupport
import .DatabaseSupport: add_foreign_key, add_index, set_default
function up()
create_table(:groups_requests) do
[
primary_key()
column(:group_id, :integer)
column(:user_id, :integer)
column(:country, :string)
column(:state, :string)
column(:town, :string)
column(:contact, :string)
column(:latitude, :float)
column(:longitude, :float)
column(:longitude, :float)
column(:members,:integer)
column(:added, :bool)
column(:status,:Integer)
]
end
add_foreign_key(:groups_requests,:user_id,:users,:id)
add_foreign_key(:groups_requests,:group_id,:groups,:id)
add_index(:groups_requests, :user_id)
set_default("groups_requests","added",false)
end
function down()
drop_table(:groups_requests)
end
end

View File

@ -0,0 +1,24 @@
module CreateTableGroupsUsers
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
function up()
create_table(:groups_users) do
[
primary_key()
column(:user_id, :int)
column(:group_id, :int)
]
end
add_foreign_key(:groups_users,:user_id,:users,:id)
add_foreign_key(:groups_users,:group_id,:groups,:id)
add_index(:groups_users, :user_id)
add_index(:groups_users, :group_id)
end
function down()
drop_table(:groups_users)
end
end

View File

@ -0,0 +1,24 @@
module CreateTableCommunesUsers
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
function up()
create_table(:communes_users) do
[
primary_key()
column(:user_id, :int)
column(:commune_id, :int)
]
end
add_foreign_key(:communes_users,:user_id,:users,:id)
add_foreign_key(:communes_users,:commune_id,:communes,:id)
add_index(:communes_users, :user_id)
add_index(:communes_users, :commune_id)
end
function down()
drop_table(:communes_users)
end
end

View File

@ -0,0 +1,24 @@
module CreateTableCooperativesUsers
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
function up()
create_table(:cooperatives_users) do
[
primary_key()
column(:user_id, :int)
column(:cooperative_id, :int)
]
end
add_foreign_key(:cooperatives_users,:user_id,:users,:id)
add_foreign_key(:cooperatives_users,:cooperative_id,:cooperatives,:id)
add_index(:cooperatives_users, :user_id)
add_index(:cooperatives_users, :cooperative_id)
end
function down()
drop_table(:cooperatives_users)
end
end

View File

@ -0,0 +1,24 @@
module CreateTablePartiesUsers
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
function up()
create_table(:parties_users) do
[
primary_key()
column(:user_id, :int)
column(:party_id, :int)
]
end
add_foreign_key(:parties_users,:user_id,:users,:id)
add_foreign_key(:parties_users,:party_id,:parties,:id)
add_index(:parties_users, :user_id)
add_index(:parties_users, :party_id)
end
function down()
drop_table(:parties_users)
end
end

View File

@ -0,0 +1,27 @@
module CreateTableUsersGroups
import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table
include("../../lib/DatabaseSupport.jl")
import .DatabaseSupport: add_foreign_key, add_index
function up()
create_table(:users_groups) do
[
primary_key()
column(:user_id, :int)
column(:group_id, :int)
]
end
add_foreign_key(:users_groups,:user_id,:users,:id)
add_foreign_key(:users_groups,:group_id,:groups,:id)
add_index(:users_groups, :user_id)
add_index(:users_groups, :group_id)
end
function down()
drop_table(:users_groups)
end
end

27
Server/lib/Cookies.jl Normal file
View File

@ -0,0 +1,27 @@
module Cookies
using HTTP, Dates, Genie
export set_cookies, set_remember, remove_remember
function set_cookies(params)
request = params[:REQUEST]
response = params[:RESPONSE]
if !occursin("__genierememberme",string(request))
headers = Genie.Responses.getheaders(response)
headers["Set-Cookie"] = replace(headers["Set-Cookie"],"Max-Age=2592000;" => "")
Genie.Responses.setheaders!(response,headers)
end
end
#=
function set_remember(params)
#cookie_remember = HTTP.Cookies.Cookie("__genieremember", params[:SESSION].id, path="/", expires=now() + Dates.Month(1), httponly=true,samesite=HTTP.Cookies.SameSiteLaxMode)
#HTTP.Cookies.addcookie!(params[:RESPONSE],cookie_remember)
end
function remove_remember(params)
#cookie_remember = HTTP.Cookies.Cookie("__genieremember", params[:SESSION].#id, path="/", httponly=true,samesite=HTTP.Cookies.SameSiteLaxMode)
#HTTP.Cookies.addcookie!(params[:RESPONSE],cookie_remember)
end
=#
end

View File

@ -4,7 +4,7 @@ module DatabaseSupport
using SearchLight, SearchLightPostgreSQL, LibPQ
using DataFrames
export exist_in_table, insert_into_table, update_table, select_from_table, add_foreign_key, set_default
export exist_in_table, insert_into_table, update_table, select_from_table, delete_from_table, add_foreign_key, set_default
options = SearchLight.Configuration.read_db_connection_data("db/connection.yml")
conn = SearchLight.connect(options)
@ -19,15 +19,22 @@ function format(x)
end
end
function insert_into_table(table_name,dict_values)
function insert_into_table(table_name,dict_values,other="")
names_string = join(keys(dict_values),", ")
vals_raw = values(dict_values)
vals = map(x -> format(x),vals_raw)
vals_string = join(values(vals),", ")
query = "INSERT INTO $table_name ($names_string) VALUES ($vals_string)"
query = "INSERT INTO $table_name ($names_string) VALUES ($vals_string) " * other
return SearchLight.query(query)
end
function delete_from_table(table_name,where_data)
query = "DELETE FROM $table_name"
if !isnothing(where_data)
query *= where_query(where_data)
end
SearchLight.query(query)
return nothing
end
function update_table(table_name,dict_values; where_data=nothing)
@ -46,13 +53,21 @@ function update_table(table_name,dict_values; where_data=nothing)
end
function where_query(pair::Pair)
return " WHERE $(pair[1]) = $(pair[2])"
if isnothing(pair[2])
return " WHERE $(pair[1]) is null"
else
return " WHERE $(pair[1]) = $(pair[2])"
end
end
function where_query(data::Vector{<:Pair})
conds = String[]
for pair in data
push!(conds,"$(pair[1]) = $(pair[2])")
if isnothing(pair[2])
push!(conds, "$(pair[1]) is null")
else
push!(conds,"$(pair[1]) = $(pair[2])")
end
end
query = " WHERE "*join(conds," AND ")
return query

View File

@ -0,0 +1,53 @@
module EmailSupport
using Genie, Genie.Requests, Genie.Renderer.Json
using SMTPClient, JSON3, Random
export send_email
#--------------------------------------------------------------------
function send_email(receiver,subject,message)
url = "smtps://mail.privateemail.com:465"
rcpt = ["<"*receiver*">","<info@chrn.health>"]
from = "<no-reply@chrn.health>"
# Message body as RFC5322 within an IO
to = [receiver]
from = "no-reply@chrn.health"
replyto = "Chiron <info@chrn.health>"
body = get_body(to, from, subject, message; replyto)
resp = send(url, rcpt, from, body, opt)
return resp
end
# Load credentails
function load_credentials_inner(path)
credentials_text = open(joinpath(path,"credentials.json")) do f
read(f, String)
end
credentials_json = JSON3.read(credentials_text)
opt.username = credentials_json[:username]
opt.passwd = credentials_json[:password]
return
end
function load_credentials(path)
load_credentials_inner(path)
end
function load_credentials()
path = @__DIR__
load_credentials_inner(path)
end
opt = SendOptions(
isSSL = true,
username = "",
passwd = ""
)
#--------------------------------------------------------------------
load_credentials()
end

View File

@ -0,0 +1,4 @@
{
"username": "info@chrn.health",
"password": "Ur4tdgishg"
}

View File

@ -0,0 +1,8 @@
using Genie
import Server.AuthenticationController
import Server.Users
import SearchLight: findone
export current_user
current_user() = findone(Users.User, id = get_authentication())

View File

@ -1 +1 @@
[{"town":"Atlanta","contact":null,"latitude":33.7243396617476,"longitude":-84.39697265625,"id":9,"members":1,"country":"United States","state":"Georgia"},{"town":null,"contact":null,"latitude":39.98855476000615,"longitude":-105.2105712890625,"id":10,"members":1,"country":"United States","state":"Colorado"},{"town":null,"contact":null,"latitude":28.27955105276024,"longitude":-81.47460937500001,"id":11,"members":1,"country":"United States","state":"Florida"},{"town":"Dublin","contact":null,"latitude":40.13360099478965,"longitude":-83.10607910156251,"id":12,"members":1,"country":"United States","state":"Ohio"},{"town":"Toronto","contact":null,"latitude":43.68959002213805,"longitude":-79.36523437500001,"id":13,"members":1,"country":"Canada","state":"Ontario"},{"town":"Halifax","contact":null,"latitude":44.64996307546047,"longitude":-63.60809326171876,"id":14,"members":1,"country":"Canada","state":"Nova Scotia"},{"town":null,"contact":null,"latitude":53.353612430518126,"longitude":-8.085937500000002,"id":15,"members":1,"country":"Ireland","state":null},{"town":"Cham","contact":null,"latitude":47.18444711300418,"longitude":8.461189270019533,"id":16,"members":1,"country":"Switzerland","state":"Zug"},{"town":"Wiesbaden","contact":null,"latitude":50.085975903187155,"longitude":8.240432739257814,"id":17,"members":1,"country":"Germany","state":"Hesse"},{"town":"Copenhagen","contact":null,"latitude":55.68832070332783,"longitude":12.568359375000002,"id":18,"members":1,"country":"Denmark","state":"Capital Region of Denmark"},{"town":"Kolding","contact":null,"latitude":55.5095568556412,"longitude":9.486694335937502,"id":19,"members":1,"country":"Denmark","state":null},{"town":"Municipal Unit of Moschato","contact":null,"latitude":37.950275539773436,"longitude":23.673992156982425,"id":20,"members":1,"country":"Greece","state":"Attica"},{"town":"Varna","contact":null,"latitude":43.18381722560103,"longitude":27.905273437500004,"id":21,"members":1,"country":"Bulgaria","state":null},{"town":"Riga","contact":null,"latitude":56.966939887376796,"longitude":24.142456054687504,"id":22,"members":1,"country":"Latvia","state":"Vidzeme"},{"town":"Kohtla-Järve linn","contact":null,"latitude":59.40196127188141,"longitude":27.28042602539063,"id":23,"members":1,"country":"Estonia","state":null},{"town":"Tallinn","contact":null,"latitude":59.39656672058008,"longitude":24.72610473655427,"id":24,"members":1,"country":"Estonia","state":null},{"town":"Chiang Mai","contact":null,"latitude":18.796128352413316,"longitude":98.98753015423392,"id":25,"members":1,"country":"Thailand","state":null}]
[{"town":null,"contact":null,"latitude":52.16045455774706,"longitude":36.21093750000001,"id":39,"members":1,"country":"Russia","state":"Kursk Oblast"}]

180
Server/public/css/auth.css Normal file
View File

@ -0,0 +1,180 @@
label {
font-size: 1.3rem;
font-family: var(--sans-serif);
}
.auth-pane {
position: relative;
padding: 3.4rem;
padding-top: 3.4rem;
padding-bottom: 3.4rem;
width: 30rem;
height: auto;
}
.auth-title {
position: relative;
top: 0.2rem;
margin-bottom: 2.7rem;
}
.auth-label {
display: inline-block;
margin-bottom: 0.3rem;
}
.authEmailInput, .authPasswordInput {
position: relative;
width: 100%;
border-radius: 0.34rem;
color: #353535;
height: 2.73rem;
padding-left: 0.34rem;
}
.authEmailInput {
margin-bottom: 0.682rem;
}
.auth-button {
margin-top: 1.365rem;
height: 3.412rem;
width: 100%;
font-family: var(--sans-serif,sans-serif);
font-size: 1.3rem;
color: white;
background-color: var(--red);
border-color: var(--red);
border-radius: 0.512rem;
filter: drop-shadow(0.068rem 0.136rem 0.068rem rgb(0 0 0 / 0.4));
}
.auth-button:active {
background-color: var(--darker-pink);
}
#email-msg,#password-msg {
display: inline;
color:red;
font-family: var(--sans-serif,sans-serif);
}
.auth-line {
margin-top: 1.5rem;
width: 100%;
height: 0.07rem;
border: 0;
border-radius: 0.1rem;
background: black;
}
.auth-methods-group {
display: grid;
grid-template-columns: auto ; /*auto auto*/
justify-content: center;
gap: 2.7rem;
margin-top: 2rem;
}
.auth-methods-group img {
height: auto;
width: 3.4rem;
}
.auth-methods-group> div {
position: relative;
border-radius: 6.8rem;
width: 3.4rem;
height: 3.4rem;
overflow: hidden;
}
#google-btn {
position: absolute;
top: -0.8rem;
left: -0.8rem;
}
#google-logo {
position: relative;
background: white;
pointer-events: none;
}
#google-btn-wrapper {
cursor: pointer;
}
#google-btn div {
position: absolute;
height: 5rem;
width: 5rem;
}
#google-btn iframe {
position: absolute;
height: 10rem;
width: 10rem;
}
.password-field {
position: relative;
}
.eye-icon {
display: block;
position: absolute;
cursor: pointer;
opacity: 0.25;
top: 2.6rem;
right: 0.8rem;
width: 1.7rem;
}
.eye-icon * {
pointer-events: none;
}
#forgot-password {
display: block;
position: relative;
margin-top: 0.5rem;
height: 2rem;
color:#5f5f5f;
margin-left: auto;
width: max-content;
font-family: var(--sans-serif);
font-size: 1.3rem;
}
#remember-me {
position: relative;
display: flex;
align-items: center;
margin-top: 1rem;
gap: 1rem;
}
#remember-me-checkbox {
min-height: 1.5rem;
min-width: 1.5rem;
flex: 0;
accent-color: var(--gray);
}
#remember-me label {
position: relative;
margin-top: -0.2rem;
color: #5f5f5f;
}
#content {
position: relative;
display: flex;
justify-content: space-between;
flex-direction: column;
height: 100%;
min-height: 100vh;
}

View File

@ -1,21 +1,7 @@
:root {
--light-blue:hsl(195, 67%, 95%);
--darker-pink:hsl(344, 60%, 47%);
--pink:hsl(344, 73%, 57%);
--dark-green:hsl(176, 63%, 25%);
--green:hsl(147, 33%, 60%);
--orange:hsl(30, 97%, 72%);
--light-orange: hsl(19, 76%, 72%);
--dark-brown:hsl(23, 47%, 20%);
--brown:hsl(23, 47%, 30%);
--light-brown: hsl(23, 47%, 50%);
--dark-pink:hsl(343, 39%, 16%);
--red:hsl(359, 72%, 61%);
--dark-blue:hsl(217, 25%, 16%);
--grey-blue:hsl(223, 13%, 22%);
--cream:hsl(34, 43%, 90%);
--dark-cream:hsl(33, 26%, 84%);
--red:#c52a28;
--gray: #5B6970;
--sans-serif: "OpenSans";
--serif: "Lora";
}
@ -48,12 +34,10 @@ body {
#content {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
display: grid;
grid-template-rows: max-content auto max-content;
height: 100%;
min-height: 100vh;
flex-grow: 1;
}
/*---Fonts---------------------------------------------------------*/
@ -475,9 +459,8 @@ input[type=number]::-webkit-outer-spin-button {
.pane {
background: white;
border: 0;
border: 0.1rem solid rgb(187, 187, 187);
border-radius: 0.635rem;
box-shadow: 0 0 0.314rem rgb(187, 187, 187);
}
.pane-container {

View File

@ -1,11 +1,12 @@
/* Header */
#navbar{
/* Header */
#navbar{
position: relative;
top: 0;
width: min(100%,116rem);
z-index: 1000;
z-index: 1000000000;
height: 5.26rem;
padding-left: 0rem;
padding-right: 0rem;
}
#navbar * {
@ -35,11 +36,9 @@
#navbar-logo-text {
position: relative;
width: auto;
word-wrap: normal;
height: 100%;
line-height: 400%;
white-space: nowrap;
text-align: center;
font-size: 1.4rem;
color: #292222;
font-family: var(--sans-serif, sans-serif);
@ -56,20 +55,17 @@
overflow: hidden;
z-index: 0;
}
#menu a {
#menu > li > a, .options-button {
display: block;
padding: 1.9rem;
padding: 1.2rem;
padding-top: 1rem;
padding-bottom: 1rem;
color: black;
font-size: 1.4rem;
}
#menu a:hover {
background-color: rgb(220, 220, 220);
}
#menu a:active{
background-color: #f7aec0;
#menu > li > a:active{
}
#menu li {
@ -145,49 +141,88 @@
#side-menu:checked ~ #hamb #hamb-line::before {
transform: rotate(-45deg);
top:0;
top: 0;
}
#side-menu:checked ~ #hamb #hamb-line::after {
transform: rotate(45deg);
top:0;
top: 0;
}
#cart-icon {
height: 1.8rem;
pointer-events: none;
/* Options */
.options-dropdown {
position: absolute;
display: none;
top: 5.6rem;
right: 1.8rem;
border: #404040 solid 0.1rem;
background-color: white;
z-index: 10;
}
#menu a:hover div {
filter: saturate(50%) brightness(140%);
.options-dropdown button, .options-dropdown a {
display: block;
font-family: var(--sans-serif,sans-serif);
font-size: 1.2rem;
width: 100%;
padding: 1rem;
text-align: left;
}
#menu a:hover svg {
stroke: rgb(127, 127, 127);;
}
#cart-counter {
position: relative;
top: -2.8rem;
left: 1.6rem;
width: 1.3rem;
height: 1.3rem;
border-radius: 3.4rem;
font-family: var(--sans-serif, sans-serif);
font-size: 1rem;
text-align: center;
.options-dropdown button:hover, .options-dropdown a:hover {
background-color: var(--red);
color: white;
background: var(--pink);
}
.options-button {
width: 100%;
text-align: left;
}
/* Localization */
#locales {
position: relative;
}
#locales button {
width: 100%;
text-align: left;
height: 4rem;
}
#locales button:hover {
opacity: 0.5;
}
#locales-img {
position: relative;
top: 0rem;
height: 2rem;
margin-left: 1.2rem;
}
/*
#options-dropdown>:first-child {
padding-bottom: 0.5rem;
}
#options-dropdown>:nth-child(2) {
padding-top: 0.5rem;
}
*/
/* Responsiveness */
@media only screen and (min-width: 1500px) {
@media only screen and (min-width: 1200px) {
#navbar {
position: relative;
width: min(100%,116rem);
left: 50%;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
padding-right: 4rem;
padding-left: 4rem;
}
#nav {
@ -197,23 +232,37 @@
float: right;
width: fit-content;
background-color: transparent;
overflow: visible;
}
#side-menu:checked ~ nav {
padding-top: 0;
}
#menu li {
float: left;
}
#menu a:hover {
background-color: transparent;
#menu > li > a:hover, .options-button:hover, #navbar-logo-text:hover {
color: rgb(127, 127, 127);
}
#menu a {
padding: 1.9rem;
#menu > li > a, .options-button {
padding: 0.9rem;
padding-top: 1.9rem;
padding-bottom: 1.9rem;
}
#hamb {
display: none;
}
#locales {
position: relative;
margin-right: 1.8rem;
}
#locales-img {
top: 0.9rem;
}
}

View File

@ -0,0 +1,55 @@
/*
#notifications-div>button {
cursor: pointer;
margin-left: 0.341rem;
}
#notifications-div>:nth-child(1) {
width: 1.706rem;
height: 1.706rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>:nth-child(2) {
width: 2.047rem;
height: 2.047rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>:nth-child(3) {
width: 2.389rem;
height: 2.389rem;
background-color: #ccc;
border-radius: 100%;
}
#notifications-div>button>div {
cursor: pointer;
margin: auto;
background-color: white;
border-radius: 100%;
}
#notifications-div>:nth-child(1)>div {
width: 1.228rem;
height: 1.228rem;
}
#notifications-div>:nth-child(2)>div {
width: 1.57rem;
height: 1.57rem;
}
#notifications-div>:nth-child(3)>div {
width: 1.843rem;
height: 1.843rem;
}
*/

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#636363" d="M12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9M12,4.5C17,4.5 21.27,7.61 23,12C21.27,16.39 17,19.5 12,19.5C7,19.5 2.73,16.39 1,12C2.73,7.61 7,4.5 12,4.5M3.18,12C4.83,15.36 8.24,17.5 12,17.5C15.76,17.5 19.17,15.36 20.82,12C19.17,8.64 15.76,6.5 12,6.5C8.24,6.5 4.83,8.64 3.18,12Z" /></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="100%" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" version="1.1" viewBox="0 0 512 512" width="100%" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:serif="http://www.serif.com/" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M512,256c0,-141.385 -114.615,-256 -256,-256c-141.385,0 -256,114.615 -256,256c0,127.777 93.616,233.685 216,252.89l0,-178.89l-65,0l0,-74l65,0l0,-56.4c0,-64.16 38.219,-99.6 96.695,-99.6c28.009,0 57.305,5 57.305,5l0,63l-32.281,0c-31.801,0 -41.719,19.733 -41.719,39.978l0,48.022l71,0l-11.35,74l-59.65,0l0,178.89c122.385,-19.205 216,-125.113 216,-252.89Z" style="fill:#1877f2;fill-rule:nonzero;"/><path d="M355.65,330l11.35,-74l-71,0l0,-48.022c0,-20.245 9.917,-39.978 41.719,-39.978l32.281,0l0,-63c0,0 -29.297,-5 -57.305,-5c-58.476,0 -96.695,35.44 -96.695,99.6l0,56.4l-65,0l0,74l65,0l0,178.89c13.033,2.045 26.392,3.11 40,3.11c13.608,0 26.966,-1.065 40,-3.11l0,-178.89l59.65,0Z" style="fill:#fff;fill-rule:nonzero;"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 128 128" id="Social_Icons" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="_x31__stroke"><g id="Google"><rect clip-rule="evenodd" fill="none" fill-rule="evenodd" height="128" width="128"/><path clip-rule="evenodd" d="M27.585,64c0-4.157,0.69-8.143,1.923-11.881L7.938,35.648 C3.734,44.183,1.366,53.801,1.366,64c0,10.191,2.366,19.802,6.563,28.332l21.558-16.503C28.266,72.108,27.585,68.137,27.585,64" fill="#FBBC05" fill-rule="evenodd"/><path clip-rule="evenodd" d="M65.457,26.182c9.031,0,17.188,3.2,23.597,8.436L107.698,16 C96.337,6.109,81.771,0,65.457,0C40.129,0,18.361,14.484,7.938,35.648l21.569,16.471C34.477,37.033,48.644,26.182,65.457,26.182" fill="#EA4335" fill-rule="evenodd"/><path clip-rule="evenodd" d="M65.457,101.818c-16.812,0-30.979-10.851-35.949-25.937 L7.938,92.349C18.361,113.516,40.129,128,65.457,128c15.632,0,30.557-5.551,41.758-15.951L86.741,96.221 C80.964,99.86,73.689,101.818,65.457,101.818" fill="#34A853" fill-rule="evenodd"/><path clip-rule="evenodd" d="M126.634,64c0-3.782-0.583-7.855-1.457-11.636H65.457v24.727 h34.376c-1.719,8.431-6.397,14.912-13.092,19.13l20.474,15.828C118.981,101.129,126.634,84.861,126.634,64" fill="#4285F4" fill-rule="evenodd"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'><svg enable-background="new 0 0 32 32" height="32px" id="Layer_1" version="1.0" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><circle clip-rule="evenodd" cx="16" cy="16" fill="#007BB5" fill-rule="evenodd" r="16"/><g><rect fill="#FFFFFF" height="14" width="4" x="7" y="11"/><path d="M20.499,11c-2.791,0-3.271,1.018-3.499,2v-2h-4v14h4v-8c0-1.297,0.703-2,2-2c1.266,0,2,0.688,2,2v8h4v-7 C25,14,24.479,11,20.499,11z" fill="#FFFFFF"/><circle cx="9" cy="8" fill="#FFFFFF" r="2"/></g></g><g/><g/><g/><g/><g/><g/></svg>

After

Width:  |  Height:  |  Size: 732 B

View File

@ -1 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" fill="#5B6970"><path d="m0 0h16v16h-16z" fill="none"/><path d="m0 8v7h3v-4h3v4h6v-7l-6-5zm10-7-2.4 2 5.4 4.5v5.5h3v-7z"/></svg>
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" fill="none"><path d="m0 8v7h3v-4h3v4h6v-7l-6-5zm10-7-2.4 2 5.4 4.5v5.5h3v-7z" fill="#5B6970"/><path d="m0 0h16v16h-16z"/></svg>

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 210 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m12 2a10 10 0 1 0 10 10 10 10 0 0 0 -10-10zm1 14a1 1 0 0 1 -2 0v-5a1 1 0 0 1 2 0zm-1-7a1 1 0 1 1 1-1 1 1 0 0 1 -1 1z" fill="#5B6970"/><path d="m0 0h24v24h-24z" fill="#fff" opacity="0" transform="matrix(-1 0 0 -1 24 24)"/></svg>

After

Width:  |  Height:  |  Size: 296 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36">
<path id="logout_FILL0_wght400_GRAD0_opsz48" d="M9,42a2.878,2.878,0,0,1-2.1-.9A2.878,2.878,0,0,1,6,39V9a2.878,2.878,0,0,1,.9-2.1A2.878,2.878,0,0,1,9,6H23.55V9H9V39H23.55v3Zm24.3-9.25L31.15,30.6l5.1-5.1H18.75v-3h17.4l-5.1-5.1,2.15-2.15,8.8,8.8Z" transform="translate(-6 -6)" fill="#5d6162"/>
</svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="41" height="41" viewBox="0 0 41 41">
<image id="edit_FILL0_wght400_GRAD0_opsz48" width="41" height="41" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABjUlEQVRoQ+2Y4W0CMQxGYYNuwAilG5QN2g3KBJRNygSwQbtB2YAyQjfoBtSWOBRFiZ3cnZ0P6ZDyK7njvXyOpdx8due/+Z3zzyaB1glOCRgksKF3Lmn80Nhp70dK4IFgv6/wHTdLrGj85URQBFLwRRIoAi9E+ymUCyfxlJpHEWC2Nxp7QWJLcx/xfEsBLptnGl8BlCTBB/odRSCs+TVBHQokYBJIHVhN4hx1p5uvdwlJ3SYnwfBcaslW6imQgv8lsEVQPrEEd6djDp6f8xLIlQ0fYAZ8FCSExuQjoNU8z3OfD5N4jbpTVsI6AQ2eweLWKda8Zxs1h7c8Ay7wVgJu8BYCrvBjC7jDjynQBH4sgWbwYwg0hR8q0Bx+iAAEfF8BGPg+AlDwtQJw8DUCkPClArDwJQLQ8JoAPLwmcKIFS+GuOugmJV50KyalK+UFHV5LIBQIRSF2vtvc0gS6dVDwNQmkqrLq60FFWVctLU0gfikEfN8EYOA1gaooWy22/jJn7jUJmG+x8gdTAq0T+AcZc54xVe/5CAAAAABJRU5ErkJggg=="/>
</svg>

After

Width:  |  Height:  |  Size: 849 B

View File

@ -0,0 +1,686 @@
<svg xmlns="http://www.w3.org/2000/svg" id="account-pic" viewBox="0 0 513 513" enable-background="new 0 0 513 513" xml:space="preserve">
<path fill="#EA7B7D" opacity="1.000000" stroke="none" d="M514.000000,236.000000
C514.000000,251.020889 514.000000,266.041779 513.649536,281.201965
C512.888672,281.849335 512.190796,282.316132 512.111084,282.871674
C511.019714,290.475494 510.695221,298.261810 508.845551,305.669434
C504.923767,321.375336 501.127411,337.220123 495.550781,352.371674
C490.953735,364.861694 484.648285,376.914246 477.675354,388.287201
C469.958191,400.874146 461.249268,412.991150 451.799774,424.336212
C443.546783,434.244781 433.838074,442.979706 424.431122,451.871979
C419.719513,456.325775 414.246552,459.974152 408.756470,463.912781
C406.608948,459.028748 404.249908,454.344727 403.158661,449.381897
C401.025513,439.680664 400.117157,429.698151 397.773376,420.057770
C393.074188,400.728973 384.557465,382.831238 375.020050,365.521027
C370.586609,357.474365 364.448792,350.366730 359.100708,342.458130
C359.567963,339.037079 360.018005,335.991302 360.566040,332.584503
C360.707428,331.478424 360.750854,330.733398 360.890717,329.536377
C360.411621,324.276947 359.836090,319.469543 359.273193,314.487061
C359.285858,314.312012 359.183441,313.976257 359.182800,313.601257
C358.505737,311.450165 357.829315,309.674072 357.096497,307.959045
C357.040070,308.020111 357.166382,307.909119 357.151794,307.616943
C356.839508,306.841309 356.541809,306.357819 356.198853,305.873474
C356.153595,305.872589 356.227966,305.820984 356.195923,305.534912
C355.841400,304.789337 355.518951,304.329834 355.107544,303.936646
C355.018646,304.003021 355.206238,303.883575 355.201660,303.558167
C354.802307,302.493439 354.407501,301.754120 354.014099,301.003906
C354.015472,300.993073 353.996704,300.981689 353.974152,300.709167
C353.612366,300.027954 353.273102,299.619293 352.827759,298.924225
C352.619659,298.461639 352.517670,298.285431 352.363861,297.868530
C351.916229,297.361511 351.520416,297.095215 351.106384,296.868866
C351.088196,296.908844 351.173798,296.889099 351.129639,296.601501
C350.711761,295.891174 350.338104,295.468414 349.980988,295.022186
C349.997528,294.998688 349.950073,295.031586 349.907898,294.706573
C348.991486,293.459564 348.117218,292.537537 347.137085,291.316650
C346.814484,290.655609 346.597687,290.293457 346.404358,289.813019
C346.427826,289.694702 346.026001,289.538177 345.831299,289.364624
C345.493134,289.092743 345.349701,288.994446 345.110474,288.603455
C344.399200,287.814423 343.783722,287.318054 343.097412,286.873566
C343.026550,286.925415 343.187195,286.854584 343.139221,286.574432
C342.705017,285.895844 342.318726,285.497437 341.947144,285.071167
C341.961853,285.043304 341.898895,285.041687 341.825653,284.817505
C341.570374,284.314148 341.316101,284.141113 340.860748,283.863190
C340.534851,283.381866 340.293518,283.159637 339.918762,282.747803
C339.566040,282.306152 339.302551,282.102081 338.925476,281.706421
C338.628143,281.275024 338.403107,281.083923 338.024841,280.701233
C337.633545,280.251709 337.354034,280.042511 336.967102,279.644562
C336.686890,279.215118 336.467560,279.029480 336.081360,278.657837
C335.684631,278.193085 335.408264,277.969360 335.026672,277.563873
C334.747864,277.155334 334.533020,276.978760 334.153137,276.605957
C333.751953,276.122772 333.474518,275.885986 333.151886,275.480957
C333.106720,275.312744 332.931213,275.011841 332.844269,274.735840
C332.190033,273.954315 331.622772,273.448761 331.033813,272.973236
C331.012177,273.003265 331.064667,272.946136 330.995422,272.653015
C330.347473,271.846222 329.768768,271.332581 329.121246,270.828735
C329.052429,270.838593 329.189789,270.859924 329.123840,270.555267
C328.399048,269.473419 327.740234,268.696228 327.045868,267.946045
C327.010345,267.973022 327.097870,267.955719 327.064514,267.665863
C326.693390,266.921082 326.355591,266.466156 326.017242,266.000183
C326.016663,265.989105 325.997223,265.978363 325.930542,265.739441
C325.715973,265.179169 325.473114,264.966003 325.148438,264.902954
C325.161621,264.944885 325.198792,264.865234 325.160889,264.486053
C323.690247,259.741913 322.257446,255.376953 320.813232,250.683548
C320.713593,249.926819 320.625397,249.498550 320.616821,248.673187
C318.847839,239.206482 316.999298,230.136887 315.425018,220.882874
C325.048187,209.279083 335.349365,198.496567 343.515594,186.286224
C354.837616,169.357254 364.731812,151.456390 374.865417,133.758698
C378.457886,127.484657 380.960876,120.586807 383.978271,113.988556
C383.995361,114.003372 383.972076,113.966606 384.223877,113.807907
C384.651276,113.098106 384.826904,112.546997 385.080566,111.819031
C385.158661,111.642181 385.225739,111.261406 385.438354,111.033051
C385.774200,110.208626 385.897369,109.612564 386.076050,108.631439
C386.765503,105.528435 387.815369,102.831184 387.951752,100.088501
C388.295837,93.171082 385.811890,90.546196 378.774231,90.999680
C374.370697,91.283432 369.826141,92.191185 365.721436,93.787331
C351.728424,99.228638 339.691040,107.922951 328.617279,117.867462
C306.773834,137.483337 292.288147,162.370239 278.644653,188.104599
C272.459381,187.474991 266.699493,186.854324 260.486023,186.126190
C250.358963,186.672348 240.685547,187.325974 230.909729,187.645721
C229.120026,184.063156 227.497009,180.779190 225.735123,177.571503
C213.024612,154.430664 199.118484,132.157196 178.481827,115.038651
C166.522659,105.118279 153.893494,96.158127 138.698120,91.789146
C133.921600,90.415802 128.469727,89.127167 124.438560,92.944710
C120.207924,96.951149 122.360313,102.267998 123.497704,107.109436
C123.649200,107.754311 123.920364,108.371086 124.157280,109.243233
C124.285248,109.822701 124.506912,110.045639 124.816811,110.130013
C124.790176,110.105629 124.768829,110.174622 124.780151,110.393814
C124.791473,110.613014 124.988998,111.005028 125.016037,111.293442
C125.319313,112.075935 125.595558,112.570015 125.893593,113.436935
C126.583313,115.505013 127.155907,117.245331 127.933197,118.888847
C144.348648,153.597961 163.473175,186.584579 189.715347,214.954758
C193.574753,219.127121 196.253342,223.201889 193.785187,229.250885
C193.701797,229.835007 193.707275,230.176071 193.464661,230.832916
C192.601898,235.115036 191.987228,239.081406 191.297699,243.371857
C191.214508,244.138901 191.206192,244.581833 191.083099,245.406372
C190.934235,246.846115 190.900131,247.904266 190.906418,248.975723
C190.946808,248.989044 190.902969,248.916153 190.664886,249.212097
C189.924973,252.341217 189.423157,255.174377 188.859940,258.166199
C188.798553,258.324829 188.814636,258.664642 188.595612,258.903992
C188.231720,259.761871 188.086868,260.380402 187.878387,261.142761
C187.814743,261.286621 187.833328,261.600647 187.626129,261.783325
C187.308975,262.309631 187.199005,262.653198 186.905792,263.292511
C180.095764,271.633270 173.740311,279.922699 166.771301,287.659485
C158.658325,296.666260 152.450134,306.611176 151.203049,318.753967
C150.380264,326.765442 151.042725,334.929443 150.742172,343.179291
C131.598190,366.845032 119.361450,393.641113 112.062065,422.656494
C108.743324,435.848633 106.941971,449.422546 104.142502,462.869812
C102.728333,462.276062 101.453682,461.817688 100.545471,460.967773
C89.038895,450.200043 77.500648,439.463287 66.169266,428.512939
C62.778244,425.235962 59.635670,421.568146 57.011787,417.655273
C47.914017,404.088165 38.088184,390.868927 30.456888,376.498993
C23.072790,362.594574 16.979519,347.767090 12.310448,332.726868
C7.856178,318.378571 5.988375,303.229218 2.887524,288.452118
C2.763702,287.862030 1.651623,287.479340 1.000001,287.000000
C1.000000,273.979095 1.000000,260.958221 1.300240,247.244965
C1.400320,244.701752 1.200160,242.850876 1.000000,241.000000
C1.000000,237.307098 1.000000,233.614182 1.359694,229.772308
C2.134809,228.777603 2.800037,227.967499 2.926459,227.080582
C4.019210,219.414719 4.331128,211.567627 6.161662,204.089325
C12.963929,176.299896 22.437262,149.626877 37.761063,125.080322
C46.061306,111.784515 54.983459,99.032440 65.320732,87.433167
C79.799126,71.187210 95.901649,56.426167 114.537239,45.041058
C128.742203,36.362766 143.368484,28.091438 158.589172,21.423813
C182.200195,11.080681 206.977005,4.323318 232.962723,2.935838
C234.006638,2.880100 234.988983,1.671474 236.000000,1.000003
C236.750000,1.000000 237.500000,1.000000 238.300781,1.336543
C240.756851,3.317096 243.110275,4.448632 245.000000,1.000000
C254.687561,1.000000 264.375122,1.000000 274.245056,1.372616
C275.272766,2.162385 276.082520,2.833764 276.968842,2.957769
C286.931580,4.351604 296.925415,5.532055 306.873566,7.019388
C330.479279,10.548635 352.717285,18.978046 373.655762,29.652117
C394.968109,40.516788 415.260193,53.665508 431.960632,71.418442
C439.880066,79.836937 448.534271,87.619072 455.928741,96.464340
C468.436066,111.425522 478.673035,127.927826 487.275574,145.506210
C501.093628,173.741943 509.968414,203.254761 512.140442,234.698105
C512.173157,235.172623 513.354614,235.567795 514.000000,236.000000
z"/>
<path fill="#CCC2C8" opacity="1.000000" stroke="none"
d="
M104.452324,462.823273
C106.941971,449.422546 108.743324,435.848633 112.062065,422.656494
C119.361450,393.641113 131.598190,366.845032 150.967651,343.447449
C154.454727,348.673889 156.743530,354.358185 160.503967,358.795258
C173.642975,374.298462 190.905792,380.693787 211.090744,380.318542
C217.213715,386.160065 224.525116,389.438049 232.365631,390.669281
C241.485840,392.101410 250.799500,392.301697 260.026062,393.502930
C260.349823,433.642670 260.676758,473.316071 260.802063,513.122742
C260.328339,513.445984 260.128204,513.693970 260.000000,514.000000
C257.299988,514.000000 254.599976,514.000000 251.196747,513.737671
C248.662369,513.650146 246.831177,513.825073 245.000000,514.000000
C240.975449,514.000000 236.950912,514.000000 232.802765,513.657043
C231.673462,512.878235 230.688232,512.120300 229.658966,512.054810
C214.090561,511.064941 199.069077,507.365173 184.156845,503.069458
C164.687897,497.461121 146.085800,489.765076 128.753891,479.274200
C120.396240,474.215393 112.537163,468.332855 104.452324,462.823273
z"/>
<path fill="#BEB4BA" opacity="1.000000" stroke="none"
d="
M261.003723,512.989441
C260.676758,473.316071 260.349823,433.642670 260.027679,393.498047
C260.032471,393.026764 260.046661,393.029877 260.479736,393.022827
C268.225647,391.842438 275.731262,391.334930 282.792023,389.290771
C288.733490,387.570587 295.382141,386.137482 299.457214,379.965576
C326.667816,381.643250 350.343445,363.588776 358.265259,342.943604
C358.720856,342.890320 358.902191,342.861877 359.083557,342.833435
C364.448792,350.366730 370.586609,357.474365 375.020050,365.521027
C384.557465,382.831238 393.074188,400.728973 397.773376,420.057770
C400.117157,429.698151 401.025513,439.680664 403.158661,449.381897
C404.249908,454.344727 406.608948,459.028748 408.607849,464.239624
C408.249542,464.933716 407.628784,465.158295 407.114380,465.530029
C387.010834,480.059845 365.242401,491.453125 341.821869,499.552490
C323.366272,505.934784 304.468719,510.559082 284.909668,512.097473
C284.227417,512.151184 283.634521,513.340149 283.000000,514.000000
C278.975464,514.000000 274.950897,514.000000 270.176331,513.711792
C266.618774,513.278931 263.811249,513.134216 261.003723,512.989441
z"/>
<path fill="#EBE7E9" opacity="1.000000" stroke="none"
d="
M260.802063,513.122742
C263.811249,513.134216 266.618774,513.278931 269.713135,513.711792
C266.969391,514.000000 263.938812,514.000000 260.454102,514.000000
C260.128204,513.693970 260.328339,513.445984 260.802063,513.122742
z"/>
<path fill="#EBE7E9" opacity="1.000000" stroke="none"
d="
M245.428467,514.000000
C246.831177,513.825073 248.662369,513.650146 250.746765,513.737671
C249.285645,514.000000 247.571289,514.000000 245.428467,514.000000
z"/>
<path fill="#FFD2D7" opacity="1.000000" stroke="none"
d="
M1.000000,241.428467
C1.200160,242.850876 1.400320,244.701752 1.300240,246.776306
C1.000000,245.285645 1.000000,243.571289 1.000000,241.428467
z"/>
<path fill="#E0DCDD" opacity="1.000000" stroke="none"
d="
M211.001480,379.990509
C190.905792,380.693787 173.642975,374.298462 160.503967,358.795258
C156.743530,354.358185 154.454727,348.673889 151.265640,343.293335
C151.042725,334.929443 150.380264,326.765442 151.203049,318.753967
C152.450134,306.611176 158.658325,296.666260 166.771301,287.659485
C173.740311,279.922699 180.095764,271.633270 187.103149,263.105896
C187.600281,262.282562 187.716797,261.941620 187.833328,261.600647
C187.833328,261.600647 187.814743,261.286621 188.116455,260.917572
C188.550308,259.920563 188.682480,259.292603 188.814636,258.664642
C188.814636,258.664642 188.798553,258.324829 189.144714,257.913574
C189.961578,254.640259 190.432266,251.778214 190.902969,248.916153
C190.902969,248.916153 190.946808,248.989044 191.154480,248.769028
C191.307404,247.374268 191.252640,246.199524 191.197876,245.024780
C191.206192,244.581833 191.214508,244.138901 191.613251,243.201141
C192.573395,238.643280 193.143082,234.580215 193.712769,230.517151
C193.707275,230.176071 193.701797,229.835007 194.077423,229.011169
C195.974625,223.018661 197.490738,217.508926 199.345795,211.940918
C201.817001,209.876862 203.919922,207.838776 206.087585,205.872009
C210.563675,201.810776 214.826965,197.455246 219.655197,193.862289
C223.030762,191.350327 227.198303,189.902634 231.012131,187.979614
C240.685547,187.325974 250.358963,186.672348 260.342468,186.444824
C260.761017,227.451370 260.869507,268.031799 260.740112,308.725891
C260.337189,308.897888 260.172150,308.956177 259.539825,308.970581
C253.740875,308.942719 248.406372,308.871613 243.079636,309.042511
C241.901611,309.080292 240.121613,309.705597 239.762634,310.547913
C239.394333,311.411987 240.189651,313.050537 240.913452,314.025024
C241.671799,315.045959 242.933426,315.703979 244.004745,316.479614
C248.055679,319.412415 252.118683,322.328552 256.157166,325.236542
C257.067139,324.407806 258.512390,323.091522 259.972778,322.216309
C260.165009,323.798157 260.511475,324.941833 260.492035,326.079285
C260.355835,334.053802 260.144104,342.027039 259.706207,349.770752
C258.157684,346.950043 256.859558,344.359283 255.144684,340.936829
C252.956131,345.465698 251.631958,349.636414 249.175873,352.974274
C244.801987,358.918427 239.743393,364.358734 234.675751,370.178711
C226.587387,373.561920 218.794434,376.776215 211.001480,379.990509
M226.835876,263.789429
C225.998428,258.384125 223.202942,254.496170 217.832474,252.988266
C212.991531,251.629059 208.577087,252.691727 205.026184,256.397003
C201.140274,260.451782 200.496170,266.823517 203.371155,271.646027
C206.080322,276.190369 211.542465,278.603760 216.621262,277.500458
C222.893372,276.137970 226.318207,271.820679 226.835876,263.789429
z"/>
<path fill="#D9D3D5" opacity="1.000000" stroke="none"
d="
M259.956543,350.000671
C260.144104,342.027039 260.355835,334.053802 260.492035,326.079285
C260.511475,324.941833 260.165009,323.798157 260.096252,321.998993
C260.477997,320.914337 260.751404,320.488037 261.376526,319.995789
C264.343079,318.488281 267.129303,317.276123 269.499115,315.506348
C270.649780,314.647034 271.746368,312.613403 271.519806,311.353821
C271.332062,310.310150 269.260468,309.164429 267.858429,308.881287
C265.637390,308.432770 263.278534,308.666931 260.977966,308.612244
C260.869507,268.031799 260.761017,227.451370 260.796082,186.552277
C266.699493,186.854324 272.459381,187.474991 278.914307,188.319595
C291.576019,192.217087 300.389923,199.895523 307.288422,210.109360
C308.122162,211.343781 309.729248,212.055908 310.997894,213.387329
C312.022064,216.177170 313.026245,218.589783 314.020935,221.414658
C315.108368,226.666641 316.162292,231.516632 317.315125,236.343002
C318.331757,240.599136 319.458862,244.828888 320.537231,249.070282
C320.625397,249.498550 320.713593,249.926819 320.623291,250.968689
C322.029449,256.009918 323.614105,260.437592 325.198792,264.865234
C325.198792,264.865234 325.161621,264.944885 325.223755,265.136627
C325.450226,265.624634 325.687347,265.841309 325.997223,265.978363
C325.997223,265.978363 326.016663,265.989105 326.042725,266.292328
C326.411804,267.048950 326.754852,267.502319 327.097870,267.955719
C327.097870,267.955719 327.010345,267.973022 327.056396,268.275726
C327.798218,269.338928 328.493988,270.099426 329.189789,270.859924
C329.189789,270.859924 329.052429,270.838593 329.129364,271.154846
C329.825745,271.962769 330.445221,272.454437 331.064667,272.946136
C331.064667,272.946136 331.012177,273.003265 331.072632,273.281616
C331.732452,274.043915 332.331848,274.527893 332.931213,275.011841
C332.931213,275.011841 333.106720,275.312744 333.240234,275.754425
C333.674774,276.414856 333.975830,276.633636 334.276886,276.852386
C334.533020,276.978760 334.747864,277.155334 335.123718,277.823120
C335.617859,278.475677 335.909760,278.687286 336.201660,278.898865
C336.467560,279.029480 336.686890,279.215118 337.074341,279.889191
C337.571594,280.528931 337.854126,280.735229 338.136658,280.941559
C338.403107,281.083923 338.628143,281.275024 339.024841,281.937866
C339.494598,282.569092 339.751312,282.777252 340.008026,282.985413
C340.293518,283.159637 340.534851,283.381866 340.966736,284.063202
C341.383728,284.725189 341.616180,284.914307 341.898895,285.041687
C341.898895,285.041687 341.961853,285.043304 341.999207,285.352783
C342.420074,286.059692 342.803650,286.457123 343.187195,286.854584
C343.187195,286.854584 343.026550,286.925415 343.178345,287.173981
C343.955536,287.913757 344.580902,288.404968 345.206268,288.896179
C345.349701,288.994446 345.493134,289.092743 345.876648,289.438477
C346.116699,289.685883 346.380920,289.931335 346.380920,289.931335
C346.597687,290.293457 346.814484,290.655609 347.184052,291.658813
C348.207916,293.210449 349.079010,294.121002 349.950073,295.031586
C349.950073,295.031586 349.997528,294.998688 350.022308,295.312134
C350.422668,296.046722 350.798248,296.467896 351.173798,296.889099
C351.173798,296.889099 351.088196,296.908844 351.187195,297.098938
C351.662689,297.562439 352.039185,297.835846 352.415680,298.109222
C352.517670,298.285431 352.619659,298.461639 352.871033,299.188538
C353.345856,300.153381 353.671265,300.567535 353.996704,300.981689
C353.996704,300.981689 354.015472,300.993073 354.007812,301.335846
C354.402161,302.413605 354.804199,303.148590 355.206238,303.883575
C355.206238,303.883575 355.018646,304.003021 355.090637,304.240479
C355.517731,304.925629 355.872864,305.373291 356.227966,305.820984
C356.227966,305.820984 356.153595,305.872589 356.172882,306.176147
C356.516907,306.956207 356.841644,307.432648 357.166382,307.909119
C357.166382,307.909119 357.040070,308.020111 357.049561,308.341949
C357.767151,310.434601 358.475311,312.205444 359.183441,313.976257
C359.183441,313.976257 359.285858,314.312012 359.201691,314.954956
C359.676453,320.394745 360.235382,325.191528 360.794250,329.988342
C360.750854,330.733398 360.707428,331.478424 360.285736,332.761963
C359.268677,336.523132 358.629852,339.745819 357.991058,342.968475
C350.343445,363.588776 326.667816,381.643250 299.167816,379.732178
C290.352600,376.355316 282.288818,373.212067 274.104248,369.733521
C272.627472,367.771759 271.155029,366.227570 269.937866,364.503143
C266.554016,359.709045 263.276215,354.840057 259.956543,350.000671
M292.954834,252.577637
C284.554016,254.437103 280.101990,261.094727 282.225677,268.622253
C284.200958,275.623901 292.044647,279.524200 298.966736,276.946747
C304.560913,274.863800 307.829224,269.117188 306.961121,262.890320
C306.101593,256.724701 301.323578,252.973465 292.954834,252.577637
z"/>
<path fill="#E8E1E3" opacity="1.000000" stroke="none"
d="
M230.909729,187.645721
C227.198303,189.902634 223.030762,191.350327 219.655197,193.862289
C214.826965,197.455246 210.563675,201.810776 206.087585,205.872009
C203.919922,207.838776 201.817001,209.876862 199.271240,211.543579
C198.672882,210.565842 198.382446,209.937576 198.318497,209.287033
C196.320541,188.961105 186.114044,172.083221 175.258102,155.637466
C166.415649,142.241974 154.793076,131.261627 141.908539,121.791832
C137.049515,118.220581 131.245193,115.935486 125.871803,113.064095
C125.595558,112.570015 125.319313,112.075935 125.138161,111.187073
C125.233253,110.792297 125.195129,110.356216 125.195129,110.356216
C125.195129,110.356216 124.768829,110.174622 124.768829,110.174622
C124.768829,110.174622 124.790176,110.105629 124.767502,109.900299
C124.613037,109.401230 124.410126,109.169861 124.136108,109.000877
C123.920364,108.371086 123.649200,107.754311 123.497704,107.109436
C122.360313,102.267998 120.207924,96.951149 124.438560,92.944710
C128.469727,89.127167 133.921600,90.415802 138.698120,91.789146
C153.893494,96.158127 166.522659,105.118279 178.481827,115.038651
C199.118484,132.157196 213.024612,154.430664 225.735123,177.571503
C227.497009,180.779190 229.120026,184.063156 230.909729,187.645721
z"/>
<path fill="#E2DBDC" opacity="1.000000" stroke="none"
d="
M310.977905,213.010101
C309.729248,212.055908 308.122162,211.343781 307.288422,210.109360
C300.389923,199.895523 291.576019,192.217087 279.339722,188.328522
C292.288147,162.370239 306.773834,137.483337 328.617279,117.867462
C339.691040,107.922951 351.728424,99.228638 365.721436,93.787331
C369.826141,92.191185 374.370697,91.283432 378.774231,90.999680
C385.811890,90.546196 388.295837,93.171082 387.951752,100.088501
C387.815369,102.831184 386.765503,105.528435 385.853577,108.852493
C385.459015,110.059547 385.342377,110.660477 385.225739,111.261406
C385.225739,111.261406 385.158661,111.642181 384.852539,112.001289
C384.354950,112.895798 384.163513,113.431198 383.972076,113.966599
C383.972076,113.966606 383.995361,114.003372 383.606873,113.994675
C373.302124,118.110992 364.961212,124.463203 356.839417,131.438354
C339.437592,146.383347 327.000336,164.799469 318.375488,185.727234
C314.813660,194.369843 313.379669,203.889404 310.977905,213.010101
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M125.893593,113.436935
C131.245193,115.935486 137.049515,118.220581 141.908539,121.791832
C154.793076,131.261627 166.415649,142.241974 175.258102,155.637466
C186.114044,172.083221 196.320541,188.961105 198.318497,209.287033
C198.382446,209.937576 198.672882,210.565842 198.932297,211.601837
C197.490738,217.508926 195.974625,223.018661 194.166290,228.768127
C196.253342,223.201889 193.574753,219.127121 189.715347,214.954758
C163.473175,186.584579 144.348648,153.597961 127.933197,118.888847
C127.155907,117.245331 126.583313,115.505013 125.893593,113.436935
z"/>
<path fill="#F1BBBC" opacity="1.000000" stroke="none"
d="
M310.997925,213.387329
C313.379669,203.889404 314.813660,194.369843 318.375488,185.727234
C327.000336,164.799469 339.437592,146.383347 356.839417,131.438354
C364.961212,124.463203 373.302124,118.110992 383.589783,113.979858
C380.960876,120.586807 378.457886,127.484657 374.865417,133.758698
C364.731812,151.456390 354.837616,169.357254 343.515594,186.286224
C335.349365,198.496567 325.048187,209.279083 315.144836,220.863037
C314.403778,221.019226 314.217133,221.010818 314.030457,221.002396
C313.026245,218.589783 312.022064,216.177170 310.997925,213.387329
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M314.020935,221.414658
C314.217133,221.010818 314.403778,221.019226 314.870605,221.047455
C316.999298,230.136887 318.847839,239.206482 320.616791,248.673187
C319.458862,244.828888 318.331757,240.599136 317.315125,236.343002
C316.162292,231.516632 315.108368,226.666641 314.020935,221.414658
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M325.160889,264.486053
C323.614105,260.437592 322.029449,256.009918 320.634705,251.297119
C322.257446,255.376953 323.690247,259.741913 325.160889,264.486053
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M360.890747,329.536377
C360.235382,325.191528 359.676453,320.394745 359.189026,315.130005
C359.836090,319.469543 360.411621,324.276947 360.890747,329.536377
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M193.464661,230.832916
C193.143082,234.580215 192.573395,238.643280 191.688126,242.877060
C191.987228,239.081406 192.601898,235.115036 193.464661,230.832916
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M358.265259,342.943604
C358.629852,339.745819 359.268677,336.523132 360.187744,333.122986
C360.018005,335.991302 359.567963,339.037079 359.100708,342.458130
C358.902191,342.861877 358.720856,342.890320 358.265259,342.943604
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M190.664886,249.212097
C190.432266,251.778214 189.961578,254.640259 189.206100,257.754944
C189.423157,255.174377 189.924973,252.341217 190.664886,249.212097
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M359.182800,313.601257
C358.475311,312.205444 357.767151,310.434601 357.105957,308.280884
C357.829315,309.674072 358.505737,311.450165 359.182800,313.601257
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M349.907898,294.706573
C349.079010,294.121002 348.207916,293.210449 347.289917,291.957703
C348.117218,292.537537 348.991486,293.459564 349.907898,294.706573
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M191.083099,245.406372
C191.252640,246.199524 191.307404,247.374268 191.114105,248.755707
C190.900131,247.904266 190.934235,246.846115 191.083099,245.406372
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M330.995422,272.653015
C330.445221,272.454437 329.825745,271.962769 329.198181,271.144989
C329.768768,271.332581 330.347473,271.846222 330.995422,272.653015
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M329.123840,270.555267
C328.493988,270.099426 327.798218,269.338928 327.091919,268.248749
C327.740234,268.696228 328.399048,269.473419 329.123840,270.555267
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M332.844269,274.735840
C332.331848,274.527893 331.732452,274.043915 331.094299,273.251587
C331.622772,273.448761 332.190033,273.954315 332.844269,274.735840
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M345.110474,288.603455
C344.580902,288.404968 343.955536,287.913757 343.249207,287.122131
C343.783722,287.318054 344.399200,287.814423 345.110474,288.603455
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M355.201660,303.558167
C354.804199,303.148590 354.402161,302.413605 354.006409,301.346680
C354.407501,301.754120 354.802307,302.493439 355.201660,303.558167
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M351.129639,296.601501
C350.798248,296.467896 350.422668,296.046722 350.005768,295.335632
C350.338104,295.468414 350.711761,295.891174 351.129639,296.601501
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M188.595612,258.903992
C188.682480,259.292603 188.550308,259.920563 188.180084,260.773743
C188.086868,260.380402 188.231720,259.761871 188.595612,258.903992
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M327.064514,267.665863
C326.754852,267.502319 326.411804,267.048950 326.043304,266.303406
C326.355591,266.466156 326.693390,266.921082 327.064514,267.665863
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M357.151794,307.616943
C356.841644,307.432648 356.516907,306.956207 356.218140,306.177032
C356.541809,306.357819 356.839508,306.841309 357.151794,307.616943
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M356.195923,305.534912
C355.872864,305.373291 355.517731,304.925629 355.179565,304.174133
C355.518951,304.329834 355.841400,304.789337 356.195923,305.534912
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M353.974121,300.709167
C353.671265,300.567535 353.345856,300.153381 352.977142,299.474915
C353.273102,299.619293 353.612366,300.027954 353.974121,300.709167
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M352.363861,297.868530
C352.039185,297.835846 351.662689,297.562439 351.205383,297.058960
C351.520416,297.095215 351.916229,297.361511 352.363861,297.868530
z"/>
<path fill="#F1BBBC" opacity="1.000000" stroke="none"
d="
M384.223877,113.807907
C384.163513,113.431198 384.354950,112.895798 384.774475,112.178146
C384.826904,112.546997 384.651276,113.098106 384.223877,113.807907
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M343.139221,286.574432
C342.803650,286.457123 342.420074,286.059692 341.984497,285.380615
C342.318726,285.497437 342.705017,285.895844 343.139221,286.574432
z"/>
<path fill="#F1BBBC" opacity="1.000000" stroke="none"
d="
M385.438354,111.033051
C385.342377,110.660477 385.459015,110.059547 385.798126,109.237564
C385.897369,109.612564 385.774200,110.208626 385.438354,111.033051
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M124.157272,109.243233
C124.410126,109.169861 124.613037,109.401230 124.794144,109.924683
C124.506912,110.045639 124.285248,109.822701 124.157272,109.243233
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M124.780151,110.393814
C124.768829,110.174622 125.195129,110.356216 125.195129,110.356216
C125.195129,110.356216 125.233253,110.792297 125.111130,110.898666
C124.988998,111.005028 124.791473,110.613014 124.780151,110.393814
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M334.153137,276.605957
C333.975830,276.633636 333.674774,276.414856 333.285400,275.922668
C333.474518,275.885986 333.751953,276.122772 334.153137,276.605957
z"/>
<path fill="#F4C9C9" opacity="1.000000" stroke="none"
d="
M187.626129,261.783325
C187.716797,261.941620 187.600281,262.282562 187.286407,262.810181
C187.199005,262.653198 187.308975,262.309631 187.626129,261.783325
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M325.930542,265.739441
C325.687347,265.841309 325.450226,265.624634 325.210571,265.094696
C325.473114,264.966003 325.715973,265.179169 325.930542,265.739441
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M346.404358,289.813019
C346.380920,289.931335 346.116699,289.685883 346.071350,289.612030
C346.026001,289.538177 346.427826,289.694702 346.404358,289.813019
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M341.825653,284.817505
C341.616180,284.914307 341.383728,284.725189 341.095520,284.274323
C341.316101,284.141113 341.570374,284.314148 341.825653,284.817505
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M339.918762,282.747803
C339.751312,282.777252 339.494598,282.569092 339.138489,282.129456
C339.302551,282.102081 339.566040,282.306152 339.918762,282.747803
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M338.024841,280.701233
C337.854126,280.735229 337.571594,280.528931 337.181824,280.077972
C337.354034,280.042511 337.633545,280.251709 338.024841,280.701233
z"/>
<path fill="#F3B7BD" opacity="1.000000" stroke="none"
d="
M336.081360,278.657837
C335.909760,278.687286 335.617859,278.475677 335.228912,278.004883
C335.408264,277.969360 335.684631,278.193085 336.081360,278.657837
z"/>
<path fill="#D4CED0" opacity="1.000000" stroke="none"
d="
M211.090744,380.318542
C218.794434,376.776215 226.587387,373.561920 234.703705,370.575012
C236.225922,378.957703 237.774445,380.741486 243.869568,380.988098
C255.511353,381.459229 255.656281,381.319641 256.060913,369.487579
C256.072998,369.134949 256.282776,368.789062 256.569244,367.938904
C257.852478,371.579498 258.939972,374.664795 260.081329,378.163086
C260.105682,383.393982 260.076202,388.211945 260.046692,393.029877
C260.046661,393.029877 260.032471,393.026764 260.030823,393.031677
C250.799500,392.301697 241.485840,392.101410 232.365631,390.669281
C224.525116,389.438049 217.213715,386.160065 211.090744,380.318542
z"/>
<path fill="#C9C0C5" opacity="1.000000" stroke="none"
d="
M260.479736,393.022827
C260.076202,388.211945 260.105682,383.393982 260.526337,378.237915
C271.721008,378.474976 273.500488,377.427826 274.225067,370.068817
C282.288818,373.212067 290.352600,376.355316 298.705750,379.731995
C295.382141,386.137482 288.733490,387.570587 282.792023,389.290771
C275.731262,391.334930 268.225647,391.842438 260.479736,393.022827
z"/>
<path fill="#FEFEFE" opacity="1.000000" stroke="none"
d="
M274.104248,369.733521
C273.500488,377.427826 271.721008,378.474976 260.472473,377.824951
C258.939972,374.664795 257.852478,371.579498 256.569244,367.938904
C256.282776,368.789062 256.072998,369.134949 256.060913,369.487579
C255.656281,381.319641 255.511353,381.459229 243.869568,380.988098
C237.774445,380.741486 236.225922,378.957703 234.999115,370.406067
C239.743393,364.358734 244.801987,358.918427 249.175873,352.974274
C251.631958,349.636414 252.956131,345.465698 255.144684,340.936829
C256.859558,344.359283 258.157684,346.950043 259.706177,349.770752
C263.276215,354.840057 266.554016,359.709045 269.937866,364.503143
C271.155029,366.227570 272.627472,367.771759 274.104248,369.733521
z"/>
<path fill="#545152" opacity="1.000000" stroke="none"
d="
M226.843674,264.196960
C226.318207,271.820679 222.893372,276.137970 216.621262,277.500458
C211.542465,278.603760 206.080322,276.190369 203.371155,271.646027
C200.496170,266.823517 201.140274,260.451782 205.026184,256.397003
C208.577087,252.691727 212.991531,251.629059 217.832474,252.988266
C223.202942,254.496170 225.998428,258.384125 226.843674,264.196960
M213.714188,261.026093
C212.543411,262.555603 210.993347,263.952118 210.387665,265.679718
C210.143784,266.375305 211.970764,268.695496 212.853363,268.694427
C214.525513,268.692444 217.100754,267.989136 217.685272,266.789825
C219.031723,264.027191 217.859756,261.682312 213.714188,261.026093
z"/>
<path fill="#E79F9F" opacity="1.000000" stroke="none"
d="
M261.024841,320.061737
C260.751404,320.488037 260.477997,320.914337 260.081116,321.557922
C258.512390,323.091522 257.067139,324.407806 256.157166,325.236542
C252.118683,322.328552 248.055679,319.412415 244.004745,316.479614
C242.933426,315.703979 241.671799,315.045959 240.913452,314.025024
C240.189651,313.050537 239.394333,311.411987 239.762634,310.547913
C240.121613,309.705597 241.901611,309.080292 243.079636,309.042511
C248.406372,308.871613 253.740875,308.942719 259.541077,309.416077
C260.347992,313.290894 260.686401,316.676331 261.024841,320.061737
z"/>
<path fill="#E08A8A" opacity="1.000000" stroke="none"
d="
M261.376526,319.995789
C260.686401,316.676331 260.347992,313.290894 260.008301,309.459991
C260.172150,308.956177 260.337189,308.897888 260.740112,308.725891
C263.278534,308.666931 265.637390,308.432770 267.858429,308.881287
C269.260468,309.164429 271.332062,310.310150 271.519806,311.353821
C271.746368,312.613403 270.649780,314.647034 269.499115,315.506348
C267.129303,317.276123 264.343079,318.488281 261.376526,319.995789
z"/>
<path fill="#292426" opacity="1.000000" stroke="none"
d="
M293.341583,252.539398
C301.323578,252.973465 306.101593,256.724701 306.961121,262.890320
C307.829224,269.117188 304.560913,274.863800 298.966736,276.946747
C292.044647,279.524200 284.200958,275.623901 282.225677,268.622253
C280.101990,261.094727 284.554016,254.437103 293.341583,252.539398
M292.354797,261.570312
C289.490845,264.210541 289.588135,266.860138 292.953247,268.696503
C293.991150,269.262878 296.293427,268.619019 297.330811,267.729370
C298.160095,267.018188 298.628174,264.405273 298.062531,263.847595
C296.772827,262.576111 294.750092,262.048157 292.354797,261.570312
z"/>
<path fill="#F3F3F3" opacity="1.000000" stroke="none"
d="
M214.107452,261.017273
C217.859756,261.682312 219.031723,264.027191 217.685272,266.789825
C217.100754,267.989136 214.525513,268.692444 212.853363,268.694427
C211.970764,268.695496 210.143784,266.375305 210.387665,265.679718
C210.993347,263.952118 212.543411,262.555603 214.107452,261.017273
z"/>
<path fill="#EDEBEC" opacity="1.000000" stroke="none"
d="
M292.690002,261.394226
C294.750092,262.048157 296.772827,262.576111 298.062531,263.847595
C298.628174,264.405273 298.160095,267.018188 297.330811,267.729370
C296.293427,268.619019 293.991150,269.262878 292.953247,268.696503
C289.588135,266.860138 289.490845,264.210541 292.690002,261.394226
z"/>
</svg>

After

Width:  |  Height:  |  Size: 46 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1 @@
import{S as t,i as o,a as e,b as s,s as n,e as i,c as r,n as a,d as u,f as c,l as m,g as l,r as g,h as p,j as d}from"./index-db20528a.js";import{sendText as f}from"../../../../../../../../../js/libraries/serverTools.js";import*as h from"../../../../../../../../../js/libraries/authTools.js";import"../../../../../../../../../js/components/login-component.js";import"../../../../../../../../../js/components/signup-component.js";function j(t){let o,e,n,p,d,f,h,j,w;return{c(){o=i("div"),e=i("div"),n=i("login-component"),p=r(),d=i("signup-component"),f=r(),h=i("div"),h.innerHTML="<span>OR</span>",this.c=a,u(e,"id","auth-grid-group"),u(h,"id","auth-or"),u(h,"class","pane"),u(o,"id","auth-group")},m(i,r){s(i,o,r),c(o,e),c(e,n),t[3](n),c(e,p),c(e,d),t[5](d),c(o,f),c(o,h),j||(w=[m(n,"click",t[4]),m(n,"keydown",y),m(d,"click",t[6]),m(d,"keydown",b)],j=!0)},p:a,i:a,o:a,d(e){e&&l(o),t[3](null),t[5](null),j=!1,g(w)}}}const y=()=>"",b=()=>"";function w(t,o,e){let s,n;h.redirectLogged();let i={googleInit:!1};function r(t){t==s?(e(0,s.focused=!0,s),e(1,n.focused=!1,n)):(e(0,s.focused=!1,s),e(1,n.focused=!0,n))}function a(t){console.log(t),f("/signup-google",t.credential,(t=>h.processLoginResponse(t,i.msgs,i.remember.checked)))}p("auth",i),function t(){"undefined"!=typeof google?(google.accounts.id.initialize({client_id:"93612176787-sr8qjqem4e3kok4msrnj8s1illt85a9g.apps.googleusercontent.com",callback:a,auto_select:!0,context:"signin"}),i.googleInit=!0):setTimeout(t,100)}();return[s,n,r,function(t){d[t?"unshift":"push"]((()=>{s=t,e(0,s)}))},()=>r(s),function(t){d[t?"unshift":"push"]((()=>{n=t,e(1,n)}))},()=>r(n)]}class k extends t{constructor(t){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';@import '/css/auth.css';span{font-size:1.4rem;font-family:var(--sans-serif,sans-serif)}#auth-group{margin:auto;width:auto;margin-bottom:3rem}#auth-grid-group{display:grid;grid-template-columns:30rem 30rem;justify-content:center;gap:1.37rem;width:100%}#auth-or{display:flex;position:absolute;margin:auto;top:40%;left:50%;transform:translate(-50%, -50%);width:5.4rem;height:5.4rem;border-radius:6.8rem;background-color:white;align-items:center;justify-content:center;font-family:var(--sans-serif,sans-serif);font-weight:500}@media only screen and (max-width: 1200px){#auth-grid-group{display:grid;grid-template-columns:30rem;grid-template-rows:auto auto;justify-content:center;gap:1.37rem;width:100%}#auth-or{top:40rem}#auth-group{margin-top:2rem;margin-bottom:3rem}}</style>",o(this,{target:this.shadowRoot,props:e(this.attributes),customElement:!0},w,j,n,{},null),t&&t.target&&s(t.target,this,t.anchor)}}customElements.define("auth-component",k);export{k as default};

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 one or more lines are too long

View File

@ -0,0 +1 @@
import{S as t,i as n,a as i,b as a,s,e as o,c as e,n as r,d as u,f as l,l as m,g as p,r as f,o as h,j as c}from"./index-db20528a.js";import*as d from"../../../../../../../../../js/libraries/authTools.js";function g(t){let n,i,s,h,c,d,g,x,b,C,y,v,I,w,j,k,E,T,z,M,O,R,D;return{c(){n=o("div"),i=o("h2"),i.textContent="CONFIRMATION CODE",s=e(),h=o("div"),c=o("input"),d=o("span"),d.textContent="-",g=e(),x=o("input"),b=o("span"),b.textContent="-",C=e(),y=o("input"),v=o("span"),v.textContent="-",I=e(),w=o("input"),j=o("span"),j.textContent="-",k=e(),E=o("input"),T=e(),z=o("span"),M=e(),O=o("button"),O.textContent="Confirm",this.c=r,u(i,"class","auth-title title-highlight"),u(c,"class","authConfirmationInput"),u(c,"type","text"),u(c,"maxlength","1"),u(d,"class","dash"),u(x,"class","authConfirmationInput"),u(x,"type","text"),u(x,"maxlength","1"),u(b,"class","dash"),u(y,"class","authConfirmationInput"),u(y,"type","text"),u(y,"maxlength","1"),u(v,"class","dash"),u(w,"class","authConfirmationInput"),u(w,"type","text"),u(w,"maxlength","1"),u(j,"class","dash"),u(E,"class","authConfirmationInput"),u(E,"type","text"),u(E,"maxlength","1"),u(h,"id","confirmationInputs"),u(z,"id","confirmation-msg"),u(O,"class","auth-button"),u(n,"class","pane auth-pane")},m(o,e){a(o,n,e),l(n,i),l(n,s),l(n,h),l(h,c),t[6](c),l(h,d),l(h,g),l(h,x),t[8](x),l(h,b),l(h,C),l(h,y),t[10](y),l(h,v),l(h,I),l(h,w),t[12](w),l(h,j),l(h,k),l(h,E),t[14](E),l(n,T),l(n,z),t[16](z),l(n,M),l(n,O),t[17](O),R||(D=[m(c,"input",t[7]),m(x,"input",t[9]),m(y,"input",t[11]),m(w,"input",t[13]),m(E,"input",t[15]),m(O,"click",t[18])],R=!0)},p:r,i:r,o:r,d(i){i&&p(n),t[6](null),t[8](null),t[10](null),t[12](null),t[14](null),t[16](null),t[17](null),R=!1,f(D)}}}function x(t,n,i){let a,s,o=[];function e(t,n){n.data in["0","1","2","3","4","5","6","7","8","9"]?t<4?o[t+1].focus():d.confirmEmail(a,r(),u):i(0,o[t].value="",o)}function r(){let t="";for(let n of o)t+=n.value;return parseInt(t)}function u(t){"true"==t?d.toDashboard():i(1,a.innerHTML="Wrong code",a)}h((()=>{}));return[o,a,s,e,r,u,function(t){c[t?"unshift":"push"]((()=>{o[0]=t,i(0,o)}))},t=>e(0,t),function(t){c[t?"unshift":"push"]((()=>{o[1]=t,i(0,o)}))},t=>e(1,t),function(t){c[t?"unshift":"push"]((()=>{o[2]=t,i(0,o)}))},t=>e(2,t),function(t){c[t?"unshift":"push"]((()=>{o[3]=t,i(0,o)}))},t=>e(3,t),function(t){c[t?"unshift":"push"]((()=>{o[4]=t,i(0,o)}))},t=>e(4,t),function(t){c[t?"unshift":"push"]((()=>{a=t,i(1,a)}))},function(t){c[t?"unshift":"push"]((()=>{s=t,i(2,s)}))},()=>d.confirmEmail(a,r(),u)]}class b extends t{constructor(t){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';.auth-pane{position:relative;padding:3.4rem;padding-top:5.5rem;padding-bottom:5.5rem;width:33rem;height:auto;margin:auto}.auth-title{position:relative;left:0.7rem;top:0.2rem;margin-bottom:1.4rem}.authConfirmationInput{position:relative;width:3.16rem;font-family:var(--serif,serif);font-size:3rem;border-radius:0.34rem;margin-bottom:0.7rem;text-align:center;padding-left:0;padding-bottom:0.3 rem}.dash{display:block;font-size:3rem;font-family:var(--serif,serif)}#confirmationInputs{margin:auto;display:grid;justify-content:space-between;grid-auto-flow:column}.auth-button{margin-top:1.4rem;height:3.4rem;width:100%;font-family:var(--sans-serif,sans-serif);font-size:1.6rem;color:white;background-color:var(--pink);border-color:var(--pink);border-radius:0.5rem;filter:drop-shadow(0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4))}#confirmation-msg{display:inline;color:red}</style>",n(this,{target:this.shadowRoot,props:i(this.attributes),customElement:!0},x,g,s,{},null),t&&t.target&&a(t.target,this,t.anchor)}}customElements.define("confirmation-component",b);export{b as default};

View File

@ -1 +1 @@
import{S as e,i as s,a as t,b as o,s as i,g as a,n as r,j as n,d as c,o as d}from"./index-8c09578c.js";function p(e){let s;return{c(){s=a("div"),s.innerHTML="<div><p>We use cookies to improve your experience, personalise your content and analyse site usage. By clicking “OK”, you agree to the use of cookies.</p></div>",this.c=r,n(s,"id","wrapper")},m(e,t){o(e,s,t)},p:r,i:r,o:r,d(e){e&&c(s)}}}function u(e){return d((()=>{})),[]}class h extends e{constructor(e){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';#wrapper{display:none;position:relative;height:5rem;width:100%;background:white;box-shadow:0 0 0.314rem rgb(187, 187, 187);;}</style>",s(this,{target:this.shadowRoot,props:t(this.attributes),customElement:!0},u,p,i,{},null),e&&e.target&&o(e.target,this,e.anchor)}}customElements.define("cookies-dialog",h);export{h as default};
import{S as e,i as s,a as t,b as o,s as i,e as a,n as r,d as n,g as c,o as d}from"./index-db20528a.js";function p(e){let s;return{c(){s=a("div"),s.innerHTML="<div><p>We use cookies to improve your experience, personalise your content and analyse site usage. By clicking “OK”, you agree to the use of cookies.</p></div>",this.c=r,n(s,"id","wrapper")},m(e,t){o(e,s,t)},p:r,i:r,o:r,d(e){e&&c(s)}}}function u(e){return d((()=>{})),[]}class h extends e{constructor(e){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';#wrapper{display:none;position:relative;height:5rem;width:100%;background:white;box-shadow:0 0 0.314rem rgb(187, 187, 187);;}</style>",s(this,{target:this.shadowRoot,props:t(this.attributes),customElement:!0},u,p,i,{},null),e&&e.target&&o(e.target,this,e.anchor)}}customElements.define("cookies-dialog",h);export{h as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{S as t,i as e,a as o,b as r,s as a,e as i,n,d as s,c as m,g as c,t as f,h as d,j as l,w as g,m as p,p as h,q as u}from"./index-8c09578c.js";import{w}from"./index-77787e10.js";import{loadLocaleContent as b}from"../../../../../../../../../js/libraries/serverTools.js";function v(t){let e,o,a,i,n,m,w,b,v,k,y,x,L,j,z,U,_,C,T,B,H,M,R=t[1].contactUs+"",A=t[1].inviteLink+"",D=t[1].inviteLink+"";return{c(){e=c("footer"),o=c("div"),a=c("div"),i=c("div"),n=c("h2"),m=f(R),w=d(),b=c("p"),v=f("Discord: "),k=c("a"),y=f(A),x=d(),L=c("p"),j=f("WhatsApp: "),z=c("a"),U=f(D),_=d(),C=c("button"),C.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="42.545" height="72.601" viewBox="0 0 42.545 72.601"><g id="Group_268" data-name="Group 268" transform="translate(-6.177 -2.399)"><rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="#cb1816"></rect><path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="#DD1C1A"></path></g></svg>',T=d(),B=c("p"),B.innerHTML='Licensed under a Creative Commons <a href="https://creativecommons.org/licenses/by/4.0/legalcode " target="_blank" rel="noreferrer">CC BY 4.0 license</a>',l(k,"href","https://discord.gg/Qk8KUk787z"),l(k,"target","_blank"),l(k,"rel","noreferrer"),g(k,"margin-left","1.8rem"),l(z,"href","https://chat.whatsapp.com/BhnmUNljUxJ2AjeHUwyTKh"),l(z,"target","_blank"),l(z,"rel","noreferrer"),g(z,"margin-left","0.5rem"),l(i,"id","contact-us-container"),l(a,"id","footer-grid-content-container"),l(a,"class","logged"),l(C,"id","footer-up"),l(C,"aria-label","go up"),l(B,"id","footer-copyright"),l(o,"id","footer-content-container")},m(s,c){r(s,e,c),p(e,o),p(o,a),p(a,i),p(i,n),p(n,m),p(i,w),p(i,b),p(b,v),p(b,k),p(k,y),p(i,x),p(i,L),p(L,j),p(L,z),p(z,U),p(o,_),p(o,C),p(o,T),p(o,B),H||(M=h(C,"click",t[4]),H=!0)},p(t,e){2&e&&R!==(R=t[1].contactUs+"")&&u(m,R),2&e&&A!==(A=t[1].inviteLink+"")&&u(y,A),2&e&&D!==(D=t[1].inviteLink+"")&&u(U,D)},d(t){t&&s(e),H=!1,M()}}}function k(t){let e,o=2==t[0]&&v(t);return{c(){o&&o.c(),e=i()},m(t,a){o&&o.m(t,a),r(t,e,a)},p(t,r){2==t[0]?o?o.p(t,r):(o=v(t),o.c(),o.m(e.parentNode,e)):o&&(o.d(1),o=null)},d(t){o&&o.d(t),t&&s(e)}}}function y(t){let e,o=t[0],m=k(t);return{c(){m.c(),e=i(),this.c=n},m(t,o){m.m(t,o),r(t,e,o)},p(t,[r]){1&r&&a(o,o=t[0])?(m.d(1),m=k(t),m.c(),m.m(e.parentNode,e)):m.p(t,r)},i:n,o:n,d(t){t&&s(e),m.d(t)}}}function x(t,e,o){let r,a,i=w(0);m(t,i,(t=>o(0,r=t)));let n=w({});m(t,n,(t=>o(1,a=t))),b(n,"countries",i),b(n,"footer-component",i);return[r,a,i,n,()=>{location.href="#"}]}class L extends t{constructor(t){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';footer{position:relative;bottom:0;width:100%;height:auto;background:#5B6970;border-top:#cb1816 solid 0.5rem}footer p,footer a{font-family:var(--sans-serif)}#footer-content-container{position:relative;margin:auto;padding-top:2rem;max-width:116rem;width:97vw}#footer-grid-content-container{display:grid;margin-left:2rem;margin-right:2rem;margin-bottom:1rem}.logged{grid-template-columns:auto auto 2rem}footer h2{color:#ffffff;font-size:1.3rem;margin-bottom:0.5rem}#footer-copyright{position:relative;margin:auto;width:100%;bottom:0rem;height:3rem;top:0rem;margin-bottom:0;font-size:1rem;text-align:center}#footer-copyright *{font-size:1rem}footer a{font-size:1.1rem;color:#ffffff}footer p{display:block;font-size:1.1rem;color:#d8d8d8;font-family:var(--sans-serif,sans-serif);margin-bottom:0.5rem}#contact-us-container{width:16rem}#footer-up{position:absolute;width:4.8rem;height:4.8rem;border-radius:3.4rem;top:4rem;right:2rem;background:#ffffff}#footer-up svg{width:40%;height:auto}@media only screen and (max-width: 1170px){.logged{grid-template-rows:auto auto auto;grid-template-columns:auto;row-gap:2rem}#footer-copyright{height:1rem;top:-2rem}}</style>",e(this,{target:this.shadowRoot,props:o(this.attributes),customElement:!0},x,y,a,{},null),t&&t.target&&r(t.target,this,t.anchor)}}customElements.define("footer-component",L);export{L as default};
import{S as t,i as e,a as r,b as o,s as a,p as i,n,g as s,q as m,e as c,v as d,c as f,d as l,y as g,f as p,l as h,w as u}from"./index-db20528a.js";import{w as v}from"./index-720c0a59.js";import{loadLocaleContent as w}from"../../../../../../../../../js/libraries/serverTools.js";function b(t){let e,r,a,i,n,m,v,w,b,y,k,x,L,j,z,U,_,T,C,H,M,R,B=t[1].contactUs+"",N=t[1].inviteLink+"",A=t[1].inviteLink+"";return{c(){e=c("footer"),r=c("div"),a=c("div"),i=c("div"),n=c("h2"),m=d(B),v=f(),w=c("p"),b=d("Discord: "),y=c("a"),k=d(N),x=f(),L=c("p"),j=d("WhatsApp: "),z=c("a"),U=d(A),_=f(),T=c("button"),T.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="42.545" height="72.601" viewBox="0 0 42.545 72.601"><g id="Group_268" data-name="Group 268" transform="translate(-6.177 -2.399)"><rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="var(--red)"></rect><path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="var(--red)"></path></g></svg>',C=f(),H=c("p"),H.innerHTML='Licensed under a Creative Commons <a href="https://creativecommons.org/licenses/by/4.0/legalcode " target="_blank" rel="noreferrer">CC BY 4.0 license</a>',l(y,"href","https://discord.gg/Qk8KUk787z"),l(y,"target","_blank"),l(y,"rel","noreferrer"),g(y,"margin-left","1.8rem"),l(z,"href","https://chat.whatsapp.com/BhnmUNljUxJ2AjeHUwyTKh"),l(z,"target","_blank"),l(z,"rel","noreferrer"),g(z,"margin-left","0.5rem"),l(i,"id","contact-us-container"),l(a,"id","footer-grid-content-container"),l(a,"class","logged"),l(T,"id","footer-up"),l(T,"aria-label","go up"),l(H,"id","footer-copyright"),l(r,"id","footer-content-container")},m(s,c){o(s,e,c),p(e,r),p(r,a),p(a,i),p(i,n),p(n,m),p(i,v),p(i,w),p(w,b),p(w,y),p(y,k),p(i,x),p(i,L),p(L,j),p(L,z),p(z,U),p(r,_),p(r,T),p(r,C),p(r,H),M||(R=h(T,"click",t[4]),M=!0)},p(t,e){2&e&&B!==(B=t[1].contactUs+"")&&u(m,B),2&e&&N!==(N=t[1].inviteLink+"")&&u(k,N),2&e&&A!==(A=t[1].inviteLink+"")&&u(U,A)},d(t){t&&s(e),M=!1,R()}}}function y(t){let e,r=2==t[0]&&b(t);return{c(){r&&r.c(),e=i()},m(t,a){r&&r.m(t,a),o(t,e,a)},p(t,o){2==t[0]?r?r.p(t,o):(r=b(t),r.c(),r.m(e.parentNode,e)):r&&(r.d(1),r=null)},d(t){r&&r.d(t),t&&s(e)}}}function k(t){let e,r=t[0],m=y(t);return{c(){m.c(),e=i(),this.c=n},m(t,r){m.m(t,r),o(t,e,r)},p(t,[o]){1&o&&a(r,r=t[0])?(m.d(1),m=y(t),m.c(),m.m(e.parentNode,e)):m.p(t,o)},i:n,o:n,d(t){t&&s(e),m.d(t)}}}function x(t,e,r){let o,a,i=v(0);m(t,i,(t=>r(0,o=t)));let n=v({});m(t,n,(t=>r(1,a=t))),w(n,"countries",i),w(n,"footer-component",i);return[o,a,i,n,()=>{location.href="#"}]}class L extends t{constructor(t){super(),this.shadowRoot.innerHTML="<style>@import '/css/common.css';footer{position:relative;bottom:0;width:100%;height:auto;background:var(--gray);border-top:var(--red) solid 0.5rem}footer p,footer a{font-family:var(--sans-serif)}#footer-content-container{position:relative;margin:auto;padding-top:2rem;max-width:116rem;width:97vw}#footer-grid-content-container{display:grid;margin-left:2rem;margin-right:2rem;margin-bottom:1rem}.logged{grid-template-columns:auto auto 2rem}footer h2{color:#ffffff;font-size:1.3rem;margin-bottom:0.5rem}#footer-copyright{position:relative;margin:auto;width:100%;bottom:0rem;height:3rem;top:0rem;margin-bottom:0;font-size:1rem;text-align:center}#footer-copyright *{font-size:1rem}footer a{font-size:1.1rem;color:#ffffff}footer p{display:block;font-size:1.1rem;color:#d8d8d8;font-family:var(--sans-serif,sans-serif);margin-bottom:0.5rem}#contact-us-container{width:16rem}#footer-up{position:absolute;width:4.8rem;height:4.8rem;border-radius:3.4rem;top:4rem;right:2rem;background:#ffffff}#footer-up svg{width:40%;height:auto}@media only screen and (max-width: 1170px){.logged{grid-template-rows:auto auto auto;grid-template-columns:auto;row-gap:2rem}#footer-copyright{height:1rem;top:-2rem}}</style>",e(this,{target:this.shadowRoot,props:r(this.attributes),customElement:!0},x,k,a,{},null),t&&t.target&&o(t.target,this,t.anchor)}}customElements.define("footer-component",L);export{L as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,53 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
import { n as noop, s as safe_not_equal } from './index-e7d4b1a1.js';
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
export { writable as w };

View File

@ -0,0 +1,513 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
let src_url_equal_anchor;
function src_url_equal(element_src, url) {
if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a');
}
src_url_equal_anchor.href = url;
return element_src === src_url_equal_anchor.href;
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function set_custom_element_data(node, prop, value) {
if (prop in node) {
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;
}
else {
attr(node, prop, value);
}
}
function children(element) {
return Array.from(element.childNodes);
}
function set_style(node, key, value, important) {
if (value === null) {
node.style.removeProperty(key);
}
else {
node.style.setProperty(key, value, important ? 'important' : '');
}
}
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, cancelable, detail);
return e;
}
class HtmlTag {
constructor(is_svg = false) {
this.is_svg = false;
this.is_svg = is_svg;
this.e = this.n = null;
}
c(html) {
this.h(html);
}
m(html, target, anchor = null) {
if (!this.e) {
if (this.is_svg)
this.e = svg_element(target.nodeName);
else
this.e = element(target.nodeName);
this.t = target;
this.c(html);
}
this.i(anchor);
}
h(html) {
this.e.innerHTML = html;
this.n = Array.from(this.e.childNodes);
}
i(anchor) {
for (let i = 0; i < this.n.length; i += 1) {
insert(this.t, this.n[i], anchor);
}
}
p(html) {
this.d();
this.h(html);
this.i(this.a);
}
d() {
this.n.forEach(detach);
}
}
function attribute_to_object(attributes) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
* It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module).
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
*
* https://svelte.dev/docs#run-time-svelte-onmount
*/
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-setcontext
*/
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-getcontext
*/
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
function flush() {
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global);
function mount_component(component, target, anchor, customElement) {
const { fragment, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
// if the component was destroyed immediately
// it will update the `$$.on_destroy` reference to `null`.
// the destructured on_destroy may still reference to the old array
if (component.$$.on_destroy) {
component.$$.on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: [],
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const { on_mount } = this.$$;
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, _oldValue, newValue) {
this[attr] = newValue;
}
disconnectedCallback() {
run_all(this.$$.on_disconnect);
}
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
if (!is_function(callback)) {
return noop;
}
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.52.0' }, detail), { bubbles: true }));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
export { validate_each_argument as A, text as B, set_data_dev as C, destroy_each as D, prop_dev as E, set_style as F, svg_element as G, is_function as H, HtmlTag as I, SvelteElement as S, attribute_to_object as a, insert_dev as b, setContext as c, dispatch_dev as d, globals as e, element as f, getContext as g, space as h, init as i, add_location as j, attr_dev as k, append_dev as l, listen_dev as m, noop as n, onMount as o, detach_dev as p, binding_callbacks as q, run_all as r, safe_not_equal as s, flush as t, src_url_equal as u, validate_slots as v, validate_store as w, component_subscribe as x, empty as y, set_custom_element_data as z };

View File

@ -0,0 +1,521 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
let src_url_equal_anchor;
function src_url_equal(element_src, url) {
if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a');
}
src_url_equal_anchor.href = url;
return element_src === src_url_equal_anchor.href;
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function set_custom_element_data(node, prop, value) {
if (prop in node) {
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;
}
else {
attr(node, prop, value);
}
}
function children(element) {
return Array.from(element.childNodes);
}
function set_style(node, key, value, important) {
if (value === null) {
node.style.removeProperty(key);
}
else {
node.style.setProperty(key, value, important ? 'important' : '');
}
}
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, cancelable, detail);
return e;
}
class HtmlTag {
constructor(is_svg = false) {
this.is_svg = false;
this.is_svg = is_svg;
this.e = this.n = null;
}
c(html) {
this.h(html);
}
m(html, target, anchor = null) {
if (!this.e) {
if (this.is_svg)
this.e = svg_element(target.nodeName);
else
this.e = element(target.nodeName);
this.t = target;
this.c(html);
}
this.i(anchor);
}
h(html) {
this.e.innerHTML = html;
this.n = Array.from(this.e.childNodes);
}
i(anchor) {
for (let i = 0; i < this.n.length; i += 1) {
insert(this.t, this.n[i], anchor);
}
}
p(html) {
this.d();
this.h(html);
this.i(this.a);
}
d() {
this.n.forEach(detach);
}
}
function attribute_to_object(attributes) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
* It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module).
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
*
* https://svelte.dev/docs#run-time-svelte-onmount
*/
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
/**
* Schedules a callback to run immediately after the component has been updated.
*
* The first time the callback runs will be after the initial `onMount`
*/
function afterUpdate(fn) {
get_current_component().$$.after_update.push(fn);
}
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-setcontext
*/
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-getcontext
*/
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
function flush() {
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global);
function mount_component(component, target, anchor, customElement) {
const { fragment, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
// if the component was destroyed immediately
// it will update the `$$.on_destroy` reference to `null`.
// the destructured on_destroy may still reference to the old array
if (component.$$.on_destroy) {
component.$$.on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: [],
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const { on_mount } = this.$$;
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, _oldValue, newValue) {
this[attr] = newValue;
}
disconnectedCallback() {
run_all(this.$$.on_disconnect);
}
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
if (!is_function(callback)) {
return noop;
}
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.52.0' }, detail), { bubbles: true }));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
export { validate_each_argument as A, text as B, set_data_dev as C, destroy_each as D, prop_dev as E, set_style as F, svg_element as G, is_function as H, HtmlTag as I, afterUpdate as J, SvelteElement as S, attribute_to_object as a, insert_dev as b, setContext as c, dispatch_dev as d, globals as e, element as f, getContext as g, space as h, init as i, add_location as j, attr_dev as k, append_dev as l, listen_dev as m, noop as n, onMount as o, detach_dev as p, binding_callbacks as q, run_all as r, safe_not_equal as s, flush as t, src_url_equal as u, validate_slots as v, validate_store as w, component_subscribe as x, empty as y, set_custom_element_data as z };

View File

@ -0,0 +1 @@
import{n,s as t}from"./index-db20528a.js";const e=[];function s(s,o=n){let i;const c=new Set;function f(n){if(t(s,n)&&(s=n,i)){const n=!e.length;for(const n of c)n[1](),e.push(n,s);if(n){for(let n=0;n<e.length;n+=2)e[n][0](e[n+1]);e.length=0}}}return{set:f,update:function(n){f(n(s))},subscribe:function(t,e=n){const r=[t,e];return c.add(r),1===c.size&&(i=o(f)||n),t(s),()=>{c.delete(r),0===c.size&&(i(),i=null)}}}}export{s as w};

View File

@ -0,0 +1,524 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
let src_url_equal_anchor;
function src_url_equal(element_src, url) {
if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a');
}
src_url_equal_anchor.href = url;
return element_src === src_url_equal_anchor.href;
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function set_custom_element_data(node, prop, value) {
if (prop in node) {
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;
}
else {
attr(node, prop, value);
}
}
function children(element) {
return Array.from(element.childNodes);
}
function set_input_value(input, value) {
input.value = value == null ? '' : value;
}
function set_style(node, key, value, important) {
if (value === null) {
node.style.removeProperty(key);
}
else {
node.style.setProperty(key, value, important ? 'important' : '');
}
}
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, cancelable, detail);
return e;
}
class HtmlTag {
constructor(is_svg = false) {
this.is_svg = false;
this.is_svg = is_svg;
this.e = this.n = null;
}
c(html) {
this.h(html);
}
m(html, target, anchor = null) {
if (!this.e) {
if (this.is_svg)
this.e = svg_element(target.nodeName);
else
this.e = element(target.nodeName);
this.t = target;
this.c(html);
}
this.i(anchor);
}
h(html) {
this.e.innerHTML = html;
this.n = Array.from(this.e.childNodes);
}
i(anchor) {
for (let i = 0; i < this.n.length; i += 1) {
insert(this.t, this.n[i], anchor);
}
}
p(html) {
this.d();
this.h(html);
this.i(this.a);
}
d() {
this.n.forEach(detach);
}
}
function attribute_to_object(attributes) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
* It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module).
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
*
* https://svelte.dev/docs#run-time-svelte-onmount
*/
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
/**
* Schedules a callback to run immediately after the component has been updated.
*
* The first time the callback runs will be after the initial `onMount`
*/
function afterUpdate(fn) {
get_current_component().$$.after_update.push(fn);
}
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-setcontext
*/
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-getcontext
*/
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
function flush() {
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global);
function mount_component(component, target, anchor, customElement) {
const { fragment, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
// if the component was destroyed immediately
// it will update the `$$.on_destroy` reference to `null`.
// the destructured on_destroy may still reference to the old array
if (component.$$.on_destroy) {
component.$$.on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: [],
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const { on_mount } = this.$$;
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, _oldValue, newValue) {
this[attr] = newValue;
}
disconnectedCallback() {
run_all(this.$$.on_disconnect);
}
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
if (!is_function(callback)) {
return noop;
}
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.52.0' }, detail), { bubbles: true }));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
export { validate_each_argument as A, text as B, set_data_dev as C, destroy_each as D, prop_dev as E, set_style as F, svg_element as G, is_function as H, HtmlTag as I, afterUpdate as J, set_input_value as K, SvelteElement as S, attribute_to_object as a, insert_dev as b, setContext as c, dispatch_dev as d, globals as e, element as f, getContext as g, space as h, init as i, add_location as j, attr_dev as k, append_dev as l, listen_dev as m, noop as n, onMount as o, detach_dev as p, binding_callbacks as q, run_all as r, safe_not_equal as s, flush as t, src_url_equal as u, validate_slots as v, validate_store as w, component_subscribe as x, empty as y, set_custom_element_data as z };

View File

@ -0,0 +1 @@
function t(){}function n(t){return t()}function e(){return Object.create(null)}function o(t){t.forEach(n)}function s(t){return"function"==typeof t}function r(t,n){return t!=t?n==n:t!==n||t&&"object"==typeof t||"function"==typeof t}let i,c;function u(t,n){return i||(i=document.createElement("a")),i.href=n,t===i.href}function a(n,e,o){n.$$.on_destroy.push(function(n,...e){if(null==n)return t;const o=n.subscribe(...e);return o.unsubscribe?()=>o.unsubscribe():o}(e,o))}function f(t,n){t.appendChild(n)}function l(t,n,e){t.insertBefore(n,e||null)}function h(t){t.parentNode.removeChild(t)}function d(t,n){for(let e=0;e<t.length;e+=1)t[e]&&t[e].d(n)}function $(t){return document.createElement(t)}function p(t){return document.createTextNode(t)}function m(){return p(" ")}function g(){return p("")}function b(t,n,e,o){return t.addEventListener(n,e,o),()=>t.removeEventListener(n,e,o)}function y(t,n,e){null==e?t.removeAttribute(n):t.getAttribute(n)!==e&&t.setAttribute(n,e)}function _(t,n,e){n in t?t[n]="boolean"==typeof t[n]&&""===e||e:y(t,n,e)}function x(t,n){n=""+n,t.wholeText!==n&&(t.data=n)}function v(t,n){t.value=null==n?"":n}function E(t,n,e,o){null===e?t.style.removeProperty(n):t.style.setProperty(n,e,o?"important":"")}class w{constructor(t=!1){this.is_svg=!1,this.is_svg=t,this.e=this.n=null}c(t){this.h(t)}m(t,n,e=null){var o;this.e||(this.is_svg?this.e=(o=n.nodeName,document.createElementNS("http://www.w3.org/2000/svg",o)):this.e=$(n.nodeName),this.t=n,this.c(t)),this.i(e)}h(t){this.e.innerHTML=t,this.n=Array.from(this.e.childNodes)}i(t){for(let n=0;n<this.n.length;n+=1)l(this.t,this.n[n],t)}p(t){this.d(),this.h(t),this.i(this.a)}d(){this.n.forEach(h)}}function k(t){const n={};for(const e of t)n[e.name]=e.value;return n}function C(t){c=t}function N(){if(!c)throw new Error("Function called outside component initialization");return c}function A(t){N().$$.on_mount.push(t)}function L(t,n){return N().$$.context.set(t,n),n}function S(t){return N().$$.context.get(t)}const T=[],j=[],H=[],M=[],O=Promise.resolve();let P=!1;function q(t){H.push(t)}const z=new Set;let B=0;function F(){const t=c;do{for(;B<T.length;){const t=T[B];B++,C(t),D(t.$$)}for(C(null),T.length=0,B=0;j.length;)j.pop()();for(let t=0;t<H.length;t+=1){const n=H[t];z.has(n)||(z.add(n),n())}H.length=0}while(T.length);for(;M.length;)M.pop()();P=!1,z.clear(),C(t)}function D(t){if(null!==t.fragment){t.update(),o(t.before_update);const n=t.dirty;t.dirty=[-1],t.fragment&&t.fragment.p(t.ctx,n),t.after_update.forEach(q)}}const G=new Set;function I(t,n){-1===t.$$.dirty[0]&&(T.push(t),P||(P=!0,O.then(F)),t.$$.dirty.fill(0)),t.$$.dirty[n/31|0]|=1<<n%31}function J(r,i,u,a,f,l,d,$=[-1]){const p=c;C(r);const m=r.$$={fragment:null,ctx:[],props:l,update:t,not_equal:f,bound:e(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(i.context||(p?p.$$.context:[])),callbacks:e(),dirty:$,skip_bound:!1,root:i.target||p.$$.root};d&&d(m.root);let g=!1;if(m.ctx=u?u(r,i.props||{},((t,n,...e)=>{const o=e.length?e[0]:n;return m.ctx&&f(m.ctx[t],m.ctx[t]=o)&&(!m.skip_bound&&m.bound[t]&&m.bound[t](o),g&&I(r,t)),n})):[],m.update(),g=!0,o(m.before_update),m.fragment=!!a&&a(m.ctx),i.target){if(i.hydrate){const t=function(t){return Array.from(t.childNodes)}(i.target);m.fragment&&m.fragment.l(t),t.forEach(h)}else m.fragment&&m.fragment.c();i.intro&&((b=r.$$.fragment)&&b.i&&(G.delete(b),b.i(y))),function(t,e,r,i){const{fragment:c,after_update:u}=t.$$;c&&c.m(e,r),i||q((()=>{const e=t.$$.on_mount.map(n).filter(s);t.$$.on_destroy?t.$$.on_destroy.push(...e):o(e),t.$$.on_mount=[]})),u.forEach(q)}(r,i.target,i.anchor,i.customElement),F()}var b,y;C(p)}let K;"function"==typeof HTMLElement&&(K=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){const{on_mount:t}=this.$$;this.$$.on_disconnect=t.map(n).filter(s);for(const t in this.$$.slotted)this.appendChild(this.$$.slotted[t])}attributeChangedCallback(t,n,e){this[t]=e}disconnectedCallback(){o(this.$$.on_disconnect)}$destroy(){!function(t,n){const e=t.$$;null!==e.fragment&&(o(e.on_destroy),e.fragment&&e.fragment.d(n),e.on_destroy=e.fragment=null,e.ctx=[])}(this,1),this.$destroy=t}$on(n,e){if(!s(e))return t;const o=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return o.push(e),()=>{const t=o.indexOf(e);-1!==t&&o.splice(t,1)}}$set(t){var n;this.$$set&&(n=t,0!==Object.keys(n).length)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}});export{v as A,w as H,K as S,k as a,l as b,m as c,y as d,$ as e,f,h as g,L as h,J as i,j,F as k,b as l,S as m,t as n,A as o,g as p,a as q,o as r,r as s,u as t,_ as u,p as v,x as w,d as x,E as y,s as z};

View File

@ -0,0 +1,524 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
let src_url_equal_anchor;
function src_url_equal(element_src, url) {
if (!src_url_equal_anchor) {
src_url_equal_anchor = document.createElement('a');
}
src_url_equal_anchor.href = url;
return element_src === src_url_equal_anchor.href;
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
}
}
function subscribe(store, ...callbacks) {
if (store == null) {
return noop;
}
const unsub = store.subscribe(...callbacks);
return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
}
function component_subscribe(component, store, callback) {
component.$$.on_destroy.push(subscribe(store, callback));
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function svg_element(name) {
return document.createElementNS('http://www.w3.org/2000/svg', name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function empty() {
return text('');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function set_custom_element_data(node, prop, value) {
if (prop in node) {
node[prop] = typeof node[prop] === 'boolean' && value === '' ? true : value;
}
else {
attr(node, prop, value);
}
}
function children(element) {
return Array.from(element.childNodes);
}
function set_input_value(input, value) {
input.value = value == null ? '' : value;
}
function set_style(node, key, value, important) {
if (value === null) {
node.style.removeProperty(key);
}
else {
node.style.setProperty(key, value, important ? 'important' : '');
}
}
function custom_event(type, detail, { bubbles = false, cancelable = false } = {}) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, cancelable, detail);
return e;
}
class HtmlTag {
constructor(is_svg = false) {
this.is_svg = false;
this.is_svg = is_svg;
this.e = this.n = null;
}
c(html) {
this.h(html);
}
m(html, target, anchor = null) {
if (!this.e) {
if (this.is_svg)
this.e = svg_element(target.nodeName);
else
this.e = element(target.nodeName);
this.t = target;
this.c(html);
}
this.i(anchor);
}
h(html) {
this.e.innerHTML = html;
this.n = Array.from(this.e.childNodes);
}
i(anchor) {
for (let i = 0; i < this.n.length; i += 1) {
insert(this.t, this.n[i], anchor);
}
}
p(html) {
this.d();
this.h(html);
this.i(this.a);
}
d() {
this.n.forEach(detach);
}
}
function attribute_to_object(attributes) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
function get_current_component() {
if (!current_component)
throw new Error('Function called outside component initialization');
return current_component;
}
/**
* The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM.
* It must be called during the component's initialisation (but doesn't need to live *inside* the component;
* it can be called from an external module).
*
* `onMount` does not run inside a [server-side component](/docs#run-time-server-side-component-api).
*
* https://svelte.dev/docs#run-time-svelte-onmount
*/
function onMount(fn) {
get_current_component().$$.on_mount.push(fn);
}
/**
* Schedules a callback to run immediately after the component has been updated.
*
* The first time the callback runs will be after the initial `onMount`
*/
function afterUpdate(fn) {
get_current_component().$$.after_update.push(fn);
}
/**
* Associates an arbitrary `context` object with the current component and the specified `key`
* and returns that object. The context is then available to children of the component
* (including slotted content) with `getContext`.
*
* Like lifecycle functions, this must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-setcontext
*/
function setContext(key, context) {
get_current_component().$$.context.set(key, context);
return context;
}
/**
* Retrieves the context that belongs to the closest parent component with the specified `key`.
* Must be called during component initialisation.
*
* https://svelte.dev/docs#run-time-svelte-getcontext
*/
function getContext(key) {
return get_current_component().$$.context.get(key);
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
function flush() {
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
const globals = (typeof window !== 'undefined'
? window
: typeof globalThis !== 'undefined'
? globalThis
: global);
function mount_component(component, target, anchor, customElement) {
const { fragment, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = component.$$.on_mount.map(run).filter(is_function);
// if the component was destroyed immediately
// it will update the `$$.on_destroy` reference to `null`.
// the destructured on_destroy may still reference to the old array
if (component.$$.on_destroy) {
component.$$.on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: [],
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(options.context || (parent_component ? parent_component.$$.context : [])),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const { on_mount } = this.$$;
this.$$.on_disconnect = on_mount.map(run).filter(is_function);
// @ts-ignore todo: improve typings
for (const key in this.$$.slotted) {
// @ts-ignore todo: improve typings
this.appendChild(this.$$.slotted[key]);
}
}
attributeChangedCallback(attr, _oldValue, newValue) {
this[attr] = newValue;
}
disconnectedCallback() {
run_all(this.$$.on_disconnect);
}
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
// TODO should this delegate to addEventListener?
if (!is_function(callback)) {
return noop;
}
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.52.0' }, detail), { bubbles: true }));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
export { validate_each_argument as A, text as B, set_data_dev as C, destroy_each as D, prop_dev as E, set_style as F, svg_element as G, is_function as H, HtmlTag as I, afterUpdate as J, set_input_value as K, SvelteElement as S, attribute_to_object as a, insert_dev as b, setContext as c, dispatch_dev as d, globals as e, element as f, getContext as g, space as h, init as i, add_location as j, attr_dev as k, append_dev as l, listen_dev as m, noop as n, onMount as o, detach_dev as p, binding_callbacks as q, run_all as r, safe_not_equal as s, flush as t, validate_store as u, validate_slots as v, component_subscribe as w, empty as x, src_url_equal as y, set_custom_element_data as z };

View File

@ -0,0 +1,53 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
import { n as noop, s as safe_not_equal } from './index-6b4fe380.js';
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
export { writable as w };

View File

@ -0,0 +1,53 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
import { n as noop, s as safe_not_equal } from './index-998178c7.js';
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
export { writable as w };

View File

@ -0,0 +1,53 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
import { n as noop, s as safe_not_equal } from './index-122ecbb4.js';
const subscriber_queue = [];
/**
* Create a `Writable` store that allows both updating and reading by subscription.
* @param {*=}value initial value
* @param {StartStopNotifier=}start start and stop notifications for subscriptions
*/
function writable(value, start = noop) {
let stop;
const subscribers = new Set();
function set(new_value) {
if (safe_not_equal(value, new_value)) {
value = new_value;
if (stop) { // store is ready
const run_queue = !subscriber_queue.length;
for (const subscriber of subscribers) {
subscriber[1]();
subscriber_queue.push(subscriber, value);
}
if (run_queue) {
for (let i = 0; i < subscriber_queue.length; i += 2) {
subscriber_queue[i][0](subscriber_queue[i + 1]);
}
subscriber_queue.length = 0;
}
}
}
}
function update(fn) {
set(fn(value));
}
function subscribe(run, invalidate = noop) {
const subscriber = [run, invalidate];
subscribers.add(subscriber);
if (subscribers.size === 1) {
stop = start(set) || noop;
}
run(value);
return () => {
subscribers.delete(subscriber);
if (subscribers.size === 0) {
stop();
stop = null;
}
};
}
return { set, update, subscribe };
}
export { writable as w };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More