From f1f87afbae58fe5a28aaf8017dd890ddbb4a1cb0 Mon Sep 17 00:00:00 2001 From: Samuel ORTION Date: Wed, 24 Aug 2022 18:37:57 +0200 Subject: [PATCH] Add about, game, with feather-icons and leafletjs --- .editorconfig | 15 ++++ .gitignore | 4 + app.js | 4 + controllers/api.js | 3 + controllers/home.js | 7 +- locales/en.js | 9 +- locales/fr.js | 9 +- package-lock.json | 55 ++++++++++++ package.json | 2 + public/favicon.ico | Bin 0 -> 41662 bytes public/images/black-bird.svg | 46 ++++++++++ public/images/logo.png | Bin 0 -> 6029 bytes public/images/logo.svg | 162 +++++++++++++++++++++++++++++++++++ public/javascripts/app.js | 13 +++ public/javascripts/game.js | 15 ++++ public/javascripts/map.js | 44 ++++++++++ public/javascripts/utils.js | 27 ++++++ public/stylesheets/style.css | 28 +++++- routes/index.js | 6 +- views/about.pug | 8 ++ views/game.pug | 8 ++ views/index.pug | 2 +- views/layout.pug | 18 ++-- 23 files changed, 469 insertions(+), 16 deletions(-) create mode 100644 .editorconfig create mode 100644 controllers/api.js create mode 100644 public/favicon.ico create mode 100644 public/images/black-bird.svg create mode 100644 public/images/logo.png create mode 100644 public/images/logo.svg create mode 100644 public/javascripts/app.js create mode 100644 public/javascripts/game.js create mode 100644 public/javascripts/map.js create mode 100644 public/javascripts/utils.js create mode 100644 views/about.pug create mode 100644 views/game.pug diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a850bf9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false + +[pug] +indent_size = 2 diff --git a/.gitignore b/.gitignore index d1bed12..8ee743b 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ typings/ # next.js build output .next + +# IDE junks +.vscode +.ideas \ No newline at end of file diff --git a/app.js b/app.js index 2d0ebbd..ef6a911 100644 --- a/app.js +++ b/app.js @@ -25,6 +25,10 @@ i18n.expressBind(app, { cookieName: 'locale' }); +app.use('/dist/leaflet', express.static('node_modules/leaflet/dist')); +app.use('/dist/feather', express.static('node_modules/feather-icons/dist')); + + app.use(function(req, res, next) { req.i18n.setLocaleFromQuery(); req.i18n.setLocaleFromCookie(); diff --git a/controllers/api.js b/controllers/api.js new file mode 100644 index 0000000..8b35959 --- /dev/null +++ b/controllers/api.js @@ -0,0 +1,3 @@ + +function getRecord(req, res) { +} \ No newline at end of file diff --git a/controllers/home.js b/controllers/home.js index cfdf147..53a7ca8 100644 --- a/controllers/home.js +++ b/controllers/home.js @@ -1,8 +1,13 @@ -function getIndex(req, res, next) { +function getIndex(req, res) { res.render('index', { title: 'SoundBirder' }); } +function getAbout(req, res) { + res.render('about', { title: 'About SoundBirder' }); +} + module.exports = { getIndex, + getAbout } \ No newline at end of file diff --git a/locales/en.js b/locales/en.js index c0ef3b1..32ff642 100644 --- a/locales/en.js +++ b/locales/en.js @@ -1,4 +1,11 @@ { "Home": "Home", - "About": "About" + "About": "About", + "Game": "Game", + "Contact": "Contact", + "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird": "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird", + "Author": "Author", + "The project is made with ♥ by Samuel ORTION": "The project is made with ♥ by Samuel ORTION", + "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.": "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.", + "The project is made with ♥ by Samuel ORTION.": "The project is made with ♥ by Samuel ORTION." } \ No newline at end of file diff --git a/locales/fr.js b/locales/fr.js index 9e26dfe..94d518e 100644 --- a/locales/fr.js +++ b/locales/fr.js @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "Game": "Jeu", + "About": "À propos", + "Contact": "Contact", + "SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird": "SoundBirder est une application web open-source qui permet d'apprendre à reconnaitre les chants d'oiseaux. Elle se base sur les enregistrements audio de Xeno-Canto est sur les données de répartitions d'eBird", + "Author": "Auteur", + "The project is made with ♥ by Samuel ORTION": "Ce projet est fait avec ♥ par Samuel ORTION" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3f942cc..55c4921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", + "feather-icons": "^4.29.0", "http-errors": "~1.6.3", "i18n-2": "^0.7.3", + "leaflet": "^1.8.0", "morgan": "~1.9.1", "pug": "^3.0.2" } @@ -180,6 +182,11 @@ "is-regex": "^1.0.3" } }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -241,6 +248,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", + "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -349,6 +366,15 @@ "node": ">= 0.6" } }, + "node_modules/feather-icons": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.29.0.tgz", + "integrity": "sha512-Y7VqN9FYb8KdaSF0qD1081HCkm0v4Eq/fpfQYQnubpqi0hXx14k+gF9iqtRys1SIcTEi97xDi/fmsPFZ8xo0GQ==", + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^3.1.3" + } + }, "node_modules/finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -585,6 +611,11 @@ "promise": "^7.0.1" } }, + "node_modules/leaflet": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz", + "integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1156,6 +1187,11 @@ "is-regex": "^1.0.3" } }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1202,6 +1238,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-js": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", + "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1294,6 +1335,15 @@ } } }, + "feather-icons": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.29.0.tgz", + "integrity": "sha512-Y7VqN9FYb8KdaSF0qD1081HCkm0v4Eq/fpfQYQnubpqi0hXx14k+gF9iqtRys1SIcTEi97xDi/fmsPFZ8xo0GQ==", + "requires": { + "classnames": "^2.2.5", + "core-js": "^3.1.3" + } + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -1467,6 +1517,11 @@ "promise": "^7.0.1" } }, + "leaflet": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.8.0.tgz", + "integrity": "sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", diff --git a/package.json b/package.json index 0753315..049db1c 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", + "feather-icons": "^4.29.0", "http-errors": "~1.6.3", "i18n-2": "^0.7.3", + "leaflet": "^1.8.0", "morgan": "~1.9.1", "pug": "^3.0.2" } diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b8abf6921086a38377d142ee1b85cd5f2ba64ab9 GIT binary patch literal 41662 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 Sf1{XR3FH0Xb)*r{@c#q#beiY@ literal 0 HcmV?d00001 diff --git a/public/images/black-bird.svg b/public/images/black-bird.svg new file mode 100644 index 0000000..e69bc89 --- /dev/null +++ b/public/images/black-bird.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f6fb4a7439caa488650d77fdc7fdbab9b212402f GIT binary patch literal 6029 zcmV;87jo!{P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H109bTI zSad^gaCvfRXJ~W)LqjkiP<3K#X=5NnZ*5^|ZXiTuWNBkzbZKvHAZT=Sa5^t9V{&C- zbZK^FV{dJ3Z*FrgZ*pfZaCKsAX=7w>ZDDC{FM4HiZ!a+}FfYdAz4-tD7Pd)5K~#90 z?VWj?RK>N&zjc@H=~-Y#92NoD8B`KA7?P+UOU#oG4+RyU0;1u`3kh*aVge=~7eG)n zQD0O+kadt{n5Cz;dvDeI;=?~1k zx2w*rU!OWvr%s&`N-6%069-0T3y=gf;8dWb$)`7v2Nc!)n+NDXEl^$esRhb`4}mh^ z17J5`cdYoS;Y1|F1V#emfU&^WfLxEtwt#;EF9ClAUI#)RmF)?{3DRW+fXTo#ToUp+ zsNN_pDNg~v1u8qJ-Zs&hga}|XFcY{O=+aq&)FA>q2b2OE>7=f$GYHXuiNI~ZIUQYj zlGp>>53B&f9a(Wl5@G^Z0k;6dI;zqPaTs_6mz=7OsIa35xd3<=IHMyf%L2-Q`MBL# zRvRqag!BaN0cPOZjL$k=2Id0qWvc=$TLlBNamW8x^Z6hHU<|Mo*Z%3*s302}QUEN+ z9m0N|@e1xc9`v$MuL(IDcp4buWuYf9j^Ga4U+}6xFT1QOaVJ?{6fyvcfepZqy(-Wv zLgoT%fE();SsSuKfOUCJ`%k_oE5!?y$fn**|rQ2D?XMl`QC`4stC6P#^ zvqh;#G4LcwE|({(Av1AD4krxu?AgQS&6|1Uz!Y1H|kY0|pG>J9ECn)TvYH)~#DdiEjfVfOir- zlnf!i0cLq5R9e`&bt`w=eg~Uh-R!=W1%{S>z>Fm=j2r_gBvY@L%1?j#Q~LGm=bgZ9 z<5k?nx;7iVJwiqTZ_r6Y-2MCaGk5M>UfujEPT0ZJOoWF^D@rLG%>f_Gm@$LJix<2&EJcJ@gO@7cRsJJLobiYeCKGb99G5mjI7G`Y4ko zP0D6Uw?|+5t9kz7o;vCuzUAzF8t<&9Q*VbhU}c6N`*rC z6sMhW8XNw&fv#Pb_7n8q-9*Jd zp2M<|^FZ&~wTrQ1#!`Kx8sh{BH#Vffflm%Fe*AcU~r2m{mG#bsg zyzSe!^ZmKsCm0L@^Z_nz`Aj@Qu1=>iUDt7n`ciL_{D#I!yZ%Jv>6@u4-%UJQGGF;}smoY4)9a?zxAZJ9Z{*GlVJ7 zay0aO9W&3w2pA0?Gtb1xH_-eV!W5oHf(c8o_Q|zu*svkx?{3|?m2r zLN>}$TiYT>n!& zgs9U9zm5>8HLeiCB`}~Nw9JVyG=p$3%pJGi(eAU+Xq4&Grt{!09;Et6b*mpxG!i8i zi`D~cG3w1OV_`RQyW7`C;l@+panEf?SP)6XRIkg2%NCPqD711-ruri3**L&0c6@z;-!Q(hIr6gooMC@0QFB_AnwQOU?A z{F#?&3X_e0+{nIt`2uF?_X?kgF{eQH07c+s;Ybch} zHX--FvLDN8>_3EZP>w~zc-a8m%#Qy`)3uBZy+9x>$*+F(tA=Zf?_A8S^KYg4aN;>v zJ(N-$J9@0?r3PhaO32qTlC%!yNqw-*{J8fj!#1ydQia*VVJMVDITlJOG&6ybSCYQI zAm5M)*oMSTe$5pf9;DL+F{k zs#KEds%pyKDNBo3Aq16`t#)9SK%<6~WF%pW%Jg1CvENsiEnfV!-Bk&Px|&w^`%tRx z=OHkZ#jL?GigN@CSpP4@ru~AdD406*Es)F^F6a>u-25e*Tiy;+tpZ-XPAfaN_@vV0c-Z7{(Bz-xes2961TE+h5DY7b#Vxxj(jCFs(xxU_gDFikw7CAcq#&TW zwbQj3A-&QoIX=w79#}br&8~fPG=!3ER0|ITMZi0)e@)mq__aGM2KmcI42Km z*1iX-qD1X>ez{ib`&E`JV$WPbQ6T>GSE1-x7j1kXM7t9+)s7rZ5wRmgtO!08e|-TNO^5R+^4Bt@Kb+r7aDKxOL{Q)Q zTMof*_Xd!nZ4FV5N@}B7$O<7HiJ`K$z8@zDL_s4VK`$!{1*?f|xQmlSjQR#E3>ZnX zt|B%Vn!CCML@`;rw}2g$VgBP3n{$dm7w#LpcN{kE*V$T?OO(XRz{zMw)Q;jfPGZfd zhe&@29fyz_Xe6X2ty1G49IPR_vdex%TOC)kDU zWs2J$cZ?7MUH6`ldJXvy_jQui!EmUS+UM?~=+Hmok|C8M<|v}M-HG-- z8)N7wCYBsy-K^QD@ClKcHYwxjd;jg!W!P>?$V<4prV=82 z>>wQ3#qj~-;N$`L`j5cZy+8RrUs8SQ4L9Dv>eZ`JG3Dug!H$BV8~D2T=-0O&dp_KQ zQ0cux3k~^e!}WL<6CuBQU%F1epKeorM)3tR=-Tfz0)ar%25!iZA&eO_21}-$QmS)d z!i_iH$mT7Z8U3x%`0~>81RHv@iRVl8|7l7{8Lk~k2u;)Q`Ft4}v_*F=LJ#QaFM)Ov z9O>W-__+T1>&eT@FZ`+$A%X+FlhLol;VeTavHKB{+%W}a$6FzHf>>3#KJ`j z33Lg3X1Xp*SqvFEgln(8w$;6%Lx-|?%VsXU>QZtG+c)+~DT<03;;Wx*dM}=awA*cY zfuf=!R-NXk+fhen;1KGEOWj)2jxF;36V-d4lH|Q8G{E8Zuj{K6DF{E%Vs8CHnB}@Q7BIB zdn&m38JmSL#Fn)T<-JMU!th2v2^?@3WgWu$!y1%9~c2RAWwYP&On1{4$&@I>ho zJn--XbU(E_vc)34rfc*%r5Cu#{hQy5Q{H>HX|y{8nx?U;bQNcuc?OP=LBMStlv3EX zO)M6}vMemiA{L8b+jdHO=j#&)TsrkqZoT!^v}BnwWeTtV{dF#$dNBpv3L3hxyn;N6 zi;EB8YDhD*HFjN`mIP@cCntv&UU-3Toc|4!Po*7K9(&?3);+(D)6PB(f4-lb+#CiB7;rbP4QYl%sWn~)zLB;h zX&@GhF=x&k)<3&Gr93C~TG7%2E2TtA8J7S}(=d$ItAdua5IKT*^XD;t{`^+=GsSDK zy~eSl$Btio$;G{ib9zrALM{QG^<1JPuyW-}e)8j=V2A8>_peJSaU2I-*V~+|Dy487 zr|;e%vFUS|y%= z#EHoiZo=fuEo~YoDJfy|tDE`d!@s2Hlp;hxG@C@GN(Q*ylxdm`&)do-szovLO(tD3 ziRF(j&o&tVKcEdVAh{HO4+G!xQleC0Sr%*8uI1i)?&aXYgG7T-aKLZ+Q}U@Pl$#UF z5Ln70H#e7(VI}nJ>E=opIB+1h-F6#!d09I~@h`Z^8QMfLgml4ObN6)1LOPIAvSi5; z)~s1WG!mtvq5{9qPb?h6wrn)qMI+&EkV7ejR1!1C#OL=Bj)oaFY#5hJzJzJhrcqvA z&a!38nx&LI>#VbwH*a2CgP$Q{z?W%fxjdO#tAU$vPtNHyD5ZG%>8IJYZ5u0AtYFlr zQ5-&em~Gp(QC3#Q_U+p_eE2X`l~sfzVe)eGC@d_bcc0#zdFGi688U|;n=xz zC#zPiqF1k8Oq({%^P~bJM~>vO%Pwp9+qP|1u3U*7%REOG@F{LSlcWYmsSpyuJsBan zt*#y+6bi9?`Ene`VfO6V1OnOF&^cww6b20%1VDeiimB#SqR;#+(_$qs9XAtcQb={# z@8h}(I^^J9r4*&5r5rkRi1FjcGko~)tkq>02D7fZlsP^3v0!*LU(OGab8s86%EOry zdn2$Zl`o{T4*NXrIZIjhnQz^?l}(#AQCxg7GiJ>2ysK&or?!%TZ!c$D{{XqUuG?Hk zV!xkMs6jj|0v<~BOX)6*uO^K|nW>e;yYy5=1^TALIBZ ztwWdIT0`u3d8$R00=K6lOD4flOL32j^BnfkJ=m-MhadLY$H=@2@>LjnU&0*rX+y@s zIO~3fdE~>!`n#NhmEQxUz_e+Lok^e^qJxHdrpLw{E^Y`OXY7T4fHnivNpeR}m zO+2^i;7&Mwj2Bfjmg(5Y=N4d=yLnmcZi7%GufiBM1`yb_Rmj61VT6-T6Z<&s8Ng|~ zo4h)Dbtdp@XlGAZJFwo{g#MR%(VW)V7%VdnzP$=FzbMW((|}Ug8}G*W;4fI+hr!5c z7=zDs1HRM4Qrt7AGJ=<9;ZDL`k56lhb?miOn16o)QK#urH*xxofa3=+tK4K^p^?{; zd;UyMgtcB?j&|f@)LD};hMb>CQ8sV`@Q4?3d3`u>39gGJ=@S){k~o_l#CYcqC>^#K7J5kN6?Gg&JUR+_TwfF-IB3Tb;!o? z-8s1GdqaBUv<2ao|Bry(uzP2AI_J2cEF1?Roo*RuA=kLNvr{$0agJolXMa%Z_(L=)WgI*x?wsn9``WIjzAE% z@t*?LQMb>pGjXC2(i9qQFu=IF&$%>7R-Yk)xCbo1gu8|BO**(sY7ZwOA@NX*8(daW z_c;~!^y>V&Pd#B(xQ$+m+Xa>5M$CLjl3VsV8=n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/public/javascripts/app.js b/public/javascripts/app.js new file mode 100644 index 0000000..8368e80 --- /dev/null +++ b/public/javascripts/app.js @@ -0,0 +1,13 @@ + +import { geoLocationHandler } from './map.js'; +import './game.js'; + +feather.replace(); + +(function () { + if (document.getElementById('map') != undefined) + geoLocationHandler(); + let start_button = document.querySelector('.game .start-button'); + if (start_button != undefined) + start_button.addEventListener('click', launchGame); +}()); diff --git a/public/javascripts/game.js b/public/javascripts/game.js new file mode 100644 index 0000000..2510ac9 --- /dev/null +++ b/public/javascripts/game.js @@ -0,0 +1,15 @@ +const API_VERSION = "1"; + +function launch() { + +} + +function get_new_record() { + const endpoint = `/api/${API_VERSION}/record`; +} + +const Game = { + launch +} + +export default Game; \ No newline at end of file diff --git a/public/javascripts/map.js b/public/javascripts/map.js new file mode 100644 index 0000000..44888a9 --- /dev/null +++ b/public/javascripts/map.js @@ -0,0 +1,44 @@ +import { getCookie, setCookie } from './utils.js' + +function geoLocationHandler() { + let location = [51.505, -0.09]; // London by default on leaflet + let lat = getCookie("lat"); + let lng = getCookie("lng"); + if (lat != undefined && lng != undefined) { + location = [lat, lng]; + console.log(`Got a previous geolocation cookie at ${location[0]}, ${location[1]}`) + } + let message = document.querySelector('.message'); + // Init map + let map = L.map('map').setView(location, 15); + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap' + }).addTo(map); + // Init marker + let marker = L.marker(location).addTo(map); + + // Get geolocation + function getLocation() { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(setLocation); + } else { + message.innerHTML = "Geolocation is not supported by this browser."; + } + } + + function setLocation(position) { + location = [position.coords.latitude, position.coords.longitude]; + marker.setLatLng(location); + map.setView(location, 15); + setCookie("lat", position.coords.latitude, 10); + setCookie("lng", position.coords.longitude, 10); + console.log("Geolocation cookie saved for future games"); + } + + document.querySelector('.geolocation-button') + .addEventListener('click', getLocation); +} + +export { + geoLocationHandler +} \ No newline at end of file diff --git a/public/javascripts/utils.js b/public/javascripts/utils.js new file mode 100644 index 0000000..faf1f8a --- /dev/null +++ b/public/javascripts/utils.js @@ -0,0 +1,27 @@ +function setCookie(cname, cvalue, exdays) { + const d = new Date(); + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + let expires = "expires=" + d.toUTCString(); + document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; +} + +function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return undefined; +} + +export { + getCookie, + setCookie +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index df84cfc..ef198aa 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -7,6 +7,10 @@ body { margin: 0; } +body > * { + padding: 1em; +} + .link { position: relative; display: inline-block; @@ -37,13 +41,31 @@ body { footer { color: white; background-color: black; - position: absolute; text-align: center; - bottom: 0; width: 100vw; padding: 2rem; } footer .link:after { border-bottom: 2px dotted white; -} \ No newline at end of file +} + +main { + min-height: calc(100vh - 15em); +} + +#map { height: 50vh; } + +nav { + +} + +nav li { + list-style: none; +} + +nav ul { + display: flex; + flex-direction: row; + justify-content: space-evenly; +} diff --git a/routes/index.js b/routes/index.js index d5d0d9c..1d79505 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,6 +3,10 @@ var router = express.Router(); var homeController = require('../controllers/home'); /* GET home page. */ router.route('/') - .get(homeController.getIndex); + .get(homeController.getIndex); + +router.route('/about') + .get(homeController.getAbout); + module.exports = router; diff --git a/views/about.pug b/views/about.pug new file mode 100644 index 0000000..b15f494 --- /dev/null +++ b/views/about.pug @@ -0,0 +1,8 @@ +extends layout.pug + +block content + p #{ __("SoundBirder is an open-source web application to learn bird song identification. It is based on bird records from Xeno-Canto and data from eBird.") } + + h2 #{ __("Author") } + p #{ __("The project is made with ♥ by Samuel ORTION.") } + diff --git a/views/game.pug b/views/game.pug new file mode 100644 index 0000000..39053f1 --- /dev/null +++ b/views/game.pug @@ -0,0 +1,8 @@ +.game + #map + button.button.geolocation-button + i(data-feather="map-pin") + button.button.start-button + i(data-feather="play") + link(rel="stylesheet", href="/dist/leaflet/leaflet.css") + script(src="/dist/leaflet/leaflet.js") \ No newline at end of file diff --git a/views/index.pug b/views/index.pug index 8216d41..7cfe919 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,4 +1,4 @@ extends layout block content - + include game diff --git a/views/layout.pug b/views/layout.pug index 6ef3fba..3a821ef 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -2,23 +2,25 @@ doctype html html head title= title - meta(name="viewport", content="width=device-width, initial-scale=1.0") - link(rel='stylesheet', href='/stylesheets/style.css') + meta(name="iewport", content="width=device-width, initial-scale=1.0") + link(rel="stylesheet", href="/stylesheets/style.css") body header h1= title nav ul li - a(href='/') #{ __('Home') } + a(href="/") #{ __("Game") } li - a(href='/about') #{ __('About') } + a(href="/about") #{ __("About") } li - a(href='/contact') Contact - block content + a(href="/contact") #{ __("Contact") } + main + block content footer .description - .copyright Copyright © 2022 - span.author - a(href="//samuel.ortion.fr", class="link") Samuel ORTION \ No newline at end of file + a(href="https://samuel.ortion.fr", class="link") Samuel ORTION + script(src="/javascripts/app.js" type="module") + script(src="/dist/feather/feather.min.js") \ No newline at end of file