Compare commits

...

112 Commits

Author SHA1 Message Date
6a69bbcaf7 Global refactor 2019-07-24 12:36:43 +02:00
577f041928 mong.tags 2019-07-24 12:16:02 +02:00
285249254a Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/routes/msgCard.js
2019-07-23 21:42:14 +02:00
9f9d3c1480 bookmars 2019-07-23 21:41:27 +02:00
9a16ed0480 Geht bei der Suche zurück zum Dashboard 2019-07-23 21:25:44 +02:00
ce740b61ec Suche nach Links 2019-07-23 18:39:31 +02:00
499f0764da fixed subscribe-btn 2019-07-23 17:44:03 +02:00
bac8786ab4 Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/routes/home.js
#	public/style/style.css
#	public/style/style.less
2019-07-23 17:38:11 +02:00
6529b1d937 verschiedenes 2019-07-23 17:34:57 +02:00
2d4850520d Vue Rerender function 2019-07-23 17:22:01 +02:00
b1d0a0f095 bookmark without color change 2019-07-23 16:07:14 +02:00
ae5360386a Session object deletion on logout; 2019-07-23 13:31:39 +02:00
85bca37a8c User creation on login (server-seitig) 2019-07-23 10:34:20 +02:00
a6dd34cf81 adding/ loading of bookmarks implemented (only for fixed User) 2019-07-22 23:01:01 +02:00
Edwina Barbalan
9eaf71ec3d Desktopansicht Profil und Home angepasst 2019-07-22 18:51:02 +02:00
6eb741760f Button und Link für die Suche 2019-07-22 18:40:51 +02:00
3550f1558b Merge remote-tracking branch 'origin/developer' into developer 2019-07-22 16:56:33 +02:00
Edwina Barbalan
b62927c28d Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/routes/createMessage.js
#	public/routes/files.js
2019-07-22 16:51:34 +02:00
Edwina Barbalan
0f06d8fcd7 Kleiner Fehler in buefy.css ausgebessert 2019-07-22 16:36:56 +02:00
Edwina Barbalan
613041d5cb Desktopansicht bei Suche/CreateMsg "verbessert" (Ohne doppelte Komponente" 2019-07-22 16:22:27 +02:00
783938821c Outsource global variables into 'initGlobals.js' 2019-07-22 14:15:20 +02:00
1f6261dba0 Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/routes/files.js
2019-07-21 15:27:04 +02:00
4aa5856fbc fixed createMessage/ files Tag autocomplete 2019-07-21 15:24:40 +02:00
Edwina Barbalan
57901853a9 Merge remote-tracking branch 'origin/developer' into developer 2019-07-21 15:20:42 +02:00
Edwina Barbalan
3eb0a99813 Desktopansicht auf allen Seiten angepasst 2019-07-21 15:20:32 +02:00
74232689fb Farbe der Tags geändert 2019-07-21 14:39:33 +02:00
d003166e33 merge conflict 2019-07-21 14:22:37 +02:00
0097de5122 Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/index.html
#	public/routes/createMessage.js
2019-07-21 14:22:16 +02:00
5a4faca86f extended search 2019-07-21 14:12:16 +02:00
92220fc11c Update 'userField' from createMsg after Login; Add API for user get, post, put; Refactor API /tag/id 2019-07-20 23:16:42 +02:00
633dbc90f0 Refactored mong.js; Implemented majorList for ProfilCard; 2019-07-20 18:24:14 +02:00
5f0c8c1d2f Login + Logout implemented; ProfilCard v1 implemented; 2019-07-19 23:12:38 +02:00
Edwina Barbalan
1fe9f22109 Extra Logo für Desktop / Anpassung des Contents 2019-07-19 17:01:07 +02:00
b7e3737a70 - 2019-07-19 16:27:37 +02:00
Edwina Barbalan
4f2316481e Merge remote-tracking branch 'origin/developer' into developer 2019-07-19 16:13:09 +02:00
a49b0ee6a5 searchbar 2019-07-19 16:13:00 +02:00
Edwina Barbalan
6318b358d2 Merge remote-tracking branch 'origin/developer' into developer 2019-07-19 16:03:59 +02:00
Edwina Barbalan
35bdcd9684 Navigation Desktopansicht angepasst 2019-07-19 16:03:49 +02:00
c1db18b323 fixid searchbar 2019-07-19 15:58:13 +02:00
c8aee0c979 x bei suche geändert 2019-07-19 15:36:38 +02:00
d8091ecafd x bei suche formatiert 2019-07-19 15:35:11 +02:00
a56ab9cadd Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/index.html
#	public/routes/navRoutes.js
2019-07-19 15:34:10 +02:00
a4da6243f5 few style changes 2019-07-19 15:28:26 +02:00
Edwina Barbalan
35b5748af9 Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/index.html
2019-07-19 15:24:46 +02:00
70befa7c4d extended search 2019-07-19 12:52:43 +02:00
Edwina Barbalan
3770d747ba Merge remote-tracking branch 'origin/developer' into developer 2019-07-19 12:29:25 +02:00
2f07441fb7 Merge remote-tracking branch 'origin/developer' into developer 2019-07-19 12:10:26 +02:00
2b73aaabb2 Frontend - erweiterte Suche 2019-07-19 12:10:22 +02:00
Edwina Barbalan
7eed075963 Index-Datei "aufgeräumt" (auskommentierte/alte sachen weg) 2019-07-19 11:59:03 +02:00
Edwina Barbalan
d66be879cd Merge remote-tracking branch 'origin/developer' into developer 2019-07-19 11:54:26 +02:00
Edwina Barbalan
373d644134 Desktopansicht angepasst 2019-07-19 11:54:13 +02:00
0c8b9da3f6 Merge remote-tracking branch 'origin/developer' into developer 2019-07-09 12:38:56 +02:00
d1a5a0b4cf the x in search is working 2019-07-09 12:38:46 +02:00
8ddf59666b link zu button geändert -> Tagsuche durch "link" 2019-07-09 12:37:21 +02:00
10f349b948 Merge remote-tracking branch 'origin/developer' into developer 2019-07-09 11:37:07 +02:00
f4ab8813f3 Clear-Funktion bei der Suche 2019-07-09 11:36:54 +02:00
45315d7c47 search not in mounted 2019-07-09 11:24:12 +02:00
a30bbe961c Merge remote-tracking branch 'origin/developer' into developer 2019-07-09 11:23:03 +02:00
b029846985 added tags 2019-07-09 11:22:56 +02:00
Edwina Barbalan
9d3d1b707e Merge remote-tracking branch 'origin/developer' into developer 2019-07-09 10:43:18 +02:00
Edwina Barbalan
6c9cb12480 Navbar is-active colorchange 2019-07-09 10:43:05 +02:00
9dab5e2dbc Update Node_modules 2019-07-02 16:05:15 +02:00
Edwina Barbalan
95a0ff3913 Auskommentiertes Array wieder hinzugefügt 2019-07-02 15:06:46 +02:00
Edwina Barbalan
83d1d4dea8 Merge remote-tracking branch 'origin/developer' into developer 2019-07-02 14:59:17 +02:00
Edwina Barbalan
9375316956 Router Componente erstellt / Navbar bearbeitet 2019-07-02 14:59:11 +02:00
a4dbafe3ef autocomplete fro tags working 2019-07-02 13:08:11 +02:00
79475fe48d autocomplete bei der suche 2019-07-02 12:21:11 +02:00
e15cf25745 Tag-Darstellung in der Msg-Card 2019-07-02 11:49:35 +02:00
3478901450 search repaired 2019-07-02 11:37:59 +02:00
d48a3bf5ed search for tags / normal text search not working 2019-06-27 12:08:08 +02:00
ab358abe98 Merge remote-tracking branch 'origin/developer' into developer
# Conflicts:
#	public/routes/createMessage.js
2019-06-27 11:15:42 +02:00
5280c47477 first Try to autocomplete the search 2019-06-27 11:14:37 +02:00
8c0d3dc669 # in front of tags and changed index of messages 2019-06-27 11:13:40 +02:00
d45ab87a8b # bei der Tag-Ausgabe hinzugefügt 2019-06-27 10:56:34 +02:00
156ec36d1e easy search implemented 2019-06-27 10:28:48 +02:00
e93526c639 tags from database implemented 2019-06-25 14:43:33 +02:00
cad0230c47 Fix: tags were not visible on "home" page -> solved 2019-06-20 23:30:51 +02:00
82f8e4bbde outsource db routes, schemata and logic into dbs.js 2019-06-20 17:17:45 +02:00
5c26a11023 restructure server.js, adding ldap access, part 1/2 role authorization 2019-06-18 15:54:07 +02:00
4fe1bbd0e5 renaming library includes 2019-06-18 15:52:34 +02:00
eb89e2c3bf renaming library (adding version nr.) 2019-06-18 15:52:03 +02:00
37e7d757c3 moved db-model-schemata, adjust user-model 2019-06-18 15:47:42 +02:00
Edwina Barbalan
d61def25b9 Merge remote-tracking branch 'origin/developer' into developer 2019-06-18 14:47:38 +02:00
e108be1fb8 Erweiterung zum Log in 2019-06-18 14:25:09 +02:00
773408dc12 Log-in Modal überarbeitet 2019-06-18 14:18:20 +02:00
Edwina Barbalan
d71c50ef22 Profil änderungen 2019-06-18 09:37:48 +02:00
Edwina Barbalan
c3073101b1 Merge remote-tracking branch 'origin/developer' into developer 2019-06-04 16:42:52 +02:00
Edwina Barbalan
d77c8d69e3 Speicherfunktion / Buefy auf Profil angewendet 2019-06-04 16:42:45 +02:00
ae4203ccf9 Merge remote-tracking branch 'origin/developer' into developer 2019-06-04 16:37:35 +02:00
d9b249c828 #buefy-changes 2019-06-04 16:32:37 +02:00
aba5a9ce8e Mongodb Script added 2019-06-04 15:27:49 +02:00
8341ed24d0 user model added; message model changed; tag model added 2019-06-04 15:26:38 +02:00
d27216c8a8 Merge remote-tracking branch 'origin/developer' into developer 2019-06-04 14:47:40 +02:00
5d28bc36e1 node_modules updated, buefy add to lib folder 2019-06-04 14:29:48 +02:00
4603f7a7c1 User schema created 2019-05-28 14:40:14 +02:00
0a3f6ff240 add buefy + vuetify not minified css 2019-05-14 18:26:45 +02:00
0fcf2f7b97 add buefy + vuetify libs; added script tags to index.html 2019-05-14 18:20:33 +02:00
9bf9f5faec Merge remote-tracking branch 'origin/developer' into developer 2019-04-17 15:58:39 +02:00
56f4a3f940 vuetify eingebunden + erster Versuch Combobox 2019-04-17 15:58:15 +02:00
c7a835ad1c Merge remote-tracking branch 'origin/developer' into developer 2019-04-16 21:24:10 +02:00
2f90b14625 added array to tag type 2019-04-16 21:23:50 +02:00
24642ca1a8 Log-in/Log-out prototyp 2019-04-16 21:21:35 +02:00
Edwina Barbalan
1523cc93c2 Modal height angepasst 2019-04-10 12:16:30 +02:00
Edwina Barbalan
bee7427176 Modal bugfix + Änderungen in CSS/LESS 2019-04-09 14:25:29 +02:00
Edwina Barbalan
e921e6a9e6 Profil Edit-Funktion 2019-04-09 14:03:55 +02:00
e471a76377 Erster Code für die Login UI 2019-04-07 23:15:09 +02:00
091a1c87a2 Finale Abgabe: PrStA - Gesamtdokumentation (v.2) [Literaturverzeichnis-Einträge haben gefehlt.] 2019-03-06 11:50:48 +01:00
3f58fa194f Finale Abgabe: PrStA - Gesamtdokumentation 2019-03-05 23:35:35 +01:00
7977b21a15 gerhört zu vorherigen commit 2019-03-05 17:38:22 +01:00
1dbba098dd Quellen angepasst + Kap. Konzepterstellung überarbeitet 2019-03-05 17:37:54 +01:00
9ce2dcf199 Gruppen PrStA - v1 2019-03-05 00:50:05 +01:00
Edwina Barbalan
47d47b8b22 Präsentation beigefügt 2019-02-26 23:48:18 +01:00
4655 changed files with 520990 additions and 281327 deletions

5
.gitignore vendored
View File

@ -18,4 +18,7 @@ latex-files/*.run.xml
latex-files/*.toc
# config
mongodb.config.js
mongodb.config.js
# exclude Mac-File .DS_Store
*.DS_Store

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,307 @@
@online{spa:1,
author = {t3n},
title = {Was ist eigentlich eine Single-Page-Webanwendung?},
month = {04},
year = {2018},
url = {https://t3n.de/news/single-page-webanwendung-1023623/},
note = {Zuletzt besucht am 26.02.2019}
}
@online{spa:2,
author = {Nebil Aboobacker},
title = {Was ist eine Single Page Application (SPA)?},
month = {05},
year = {2018},
url = {https://www.yuhiro.de/was-ist-eine-single-page-application-spa/},
note = {Zuletzt besucht am 26.02.2019}
}
@online{vue,
author = {Marc Teufel},
title = {Vue.js Tutorial: So entwickelt man Komponenten mit Vue.js},
month = {07},
year = {2018},
url = {https://entwickler.de/online/javascript/einfuehrung-vuejs-vue-579851816.html},
note = {Zuletzt besucht am 26.02.2019}
}
@online{vue:warum,
author = {Verena Sappelt},
title = {Vue.js Vorteile: Das Beste aus zwei Welten},
month = {01},
year = {2019},
url = {https://www.ppw.de/blog/vue-js-vorteile-das-beste-aus-zwei-welten/},
note = {Zuletzt besucht am 26.02.2019}
}
@online{vue:router,
author = {Said Hayani},
title = {How to use routing in Vue.js to create a better user experience},
month = {06},
year = {2018},
url = {https://medium.freecodecamp.org/how-to-use-routing-in-vue-js-to-create-a-better-user-experience-98d225bbcdd9},
note = {Zuletzt besucht am 26.02.2019}
}
@online{vue:router2,
title = {Vue Router - Getting Started},
month = {01},
year = {2014},
url = {https://router.vuejs.org/guide/#javascript},
note = {Zuletzt besucht am 26.02.2019}
}
@online{vue:router3,
author = {Raymond Camden},
title = {Working with Routes in Vue.js},
month = {012},
year = {2017},
url = {https://www.raymondcamden.com/2017/11/12/working-with-routes-in-vue},
note = {Zuletzt besucht am 26.02.2019}
}
@online{api2,
author = {Björn Behrendt},
title = {Application-Programming-Interface (API)},
month = {06},
year = {2018},
url = {https://www.gruenderszene.de/lexikon/begriffe/application-programming-interface-api?interstitial},
note = {Zuletzt besucht am 26.02.2019}
}
@online{scrib,
title = {Scribble},
month = {08},
year = {2017},
url = {https://de.wikipedia.org/wiki/Scribble},
note = {Zuletzt besucht am 26.02.2019}
}
@online{md,
title = {Material Design},
month = {01},
year = {2019},
url = {https://de.wikipedia.org/wiki/Material_Design},
note = {Zuletzt besucht am 26.02.2019}
}
@online{md2,
author = {Google},
title = {Homepage Material Design},
url = {https://material.io},
note = {(o.D.), Zuletzt besucht am 26.02.2019}
}
@online{bootstrap,
author = {Federico Zivolo and Bootstrap contributors},
title = {Bootstrap Material Design},
month = {08},
year = {2018},
url = {https://fezvrasta.github.io/bootstrap-material-design/},
note = {Zuletzt besucht am 26.02.2019}
}
@online{less,
author = {Alexis Sellier },
title = {LESS - Die dynamische Stylesheet Sprache},
month = {01},
year = {2013},
url = {http://www.lesscss.de},
note = {Zuletzt besucht am 26.02.2019}
}
@online{ajax1,
author = {Jesse James Garret},
title = {Ajax: A New Approach to Web Applications},
month = {07},
year = {2008},
url = {https://web.archive.org/web/20080702075113/http://www.adaptivepath.com/ideas/essays/archives/000385.php},
note = {Zuletzt besucht am 26.02.2019}
}
@online{ajax2,
author = {Matthias Hertel},
title = {The AJAX Engine},
month = {02},
year = {2007},
url = {http://www.mathertel.de/AJAXEngine/#view=Home},
note = {Zuletzt besucht am 26.02.2019}
}
@book{ajax3,
author = {David Sawyer McFarland},
title = {JavaScript \& jQuery - the missing manual},
year = {2012},
publisher = {O'Reilly Media, inc.},
address = {1005 Gravenstein Highway North, Sebastopol, CA 95472},
}
@online{api,
title = {Programmierschnittstelle},
month = {02},
year = {2019},
url = {https://de.wikipedia.org/wiki/Programmierschnittstelle},
note = {Zuletzt besucht am 26.02.2019}
}
@online{rest,
author = {Thomas Bayer},
title = {REST Web Services},
month = {11},
year = {2002},
url = {https://www.oio.de/public/xml/rest-webservices.htm},
note = {Zuletzt besucht am 26.02.2019}
}
@online{mongo,
title = {What is MongoDB?},
year = {2019},
url = {https://www.mongodb.com/what-is-mongodb},
note = {Zuletzt besucht am 26.02.2019}
}
@online{mongoose,
author = {Nick Karnik},
title = {Introduction to Mongoose for MongoDB},
month = {02},
year = {2018},
url = {https://medium.freecodecamp.org/introduction-to-mongoose-for-mongodb-d2a7aa593c57},
note = {Zuletzt besucht am 26.02.2019}
}
@online{Bear,
author = {Sevilleja, Chris},
month = {02},
year = {2019},
title = {Build a RESTful API Using Node and Express 4},
url = {https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4},
note = {Zuletzt besucht am 01.03.219}
}
@online{gitefi,
author = {Matthias Hopf},
month = {02},
year = {2019},
title = {Git efi},
url = {https://git.efi.th-nuernberg.de/gitea/hopfma/om/wiki/_pages},
note = {Zuletzt besucht am 25.02.2019}
}
@online{Powtoon,
title = {Powtoon - Brings Awesomeness to your presentations},
month = {02},
year = {2019},
url = {https://www.powtoon.com/my-powtoons/#/},
note = {Zuletzt besucht am 01.03.219}
}
@online{Risingstack,
author = {Gergely Nemeth},
year = {2017},
title = {10 Best Practices for Writing Node.js REST APIs | @RisingStack},
url = {https://blog.risingstack.com/10-best-practices-for-writing-node-js-rest-apis/},
note = {Zuletzt besucht am 01.03.2019}
}
@online{trello:uri,
author = {ATLASSIAN},
title = {Trello},
year = {2019},
url = {https://trello.com/home},
note = {(Zuletzt besucht am 25.02.2019)}
}
@online{trello:price,
author = {ATLASSIAN},
title = {Trello-Preisliste},
year = {2019},
url = {https://trello.com/pricing},
note = {(Zuletzt besucht am 25.02.2019)}
}
@online{bitrix:uri,
author = {Bitrix, Inc.},
title = {Führende Kostenlose Plattform Für Kommunikation Und Zusammenarbeit Mit Aufgaben, Projekten, CRM, Dokumenten, Chat u.v.a.m.},
year = {2019},
url = {https://www.bitrix24.de/},
note = {(Zuletzt besucht am 25.02.2019)}
}
@online{bitrix:feature,
author = {Bitrix, Inc.},
title = {Bitrix24: Führende kostenlose Software zum Projektmanagement},
year = {2019},
url = {https://www.bitrix24.de/tools/tasks_and_projects/},
note = {(Zuletzt besucht am 25.02.2019)}
}
@online{sw:staleWhileRevalidate,
author = {Jake Archibald},
title = {The offline cookbook - JakeArchibald.com},
month = {12},
year = {2014},
url = {https://jakearchibald.com/2014/offline-cookbook/#stale-while-revalidate},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{sw:cacheBackToNetwork,
author = {Jake Archibald},
title = {The offline cookbook - JakeArchibald.com},
month = {12},
year = {2014},
url = {https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{sw:networkBackToCache,
author = {Jake Archibald},
title = {The offline cookbook - JakeArchibald.com},
month = {12},
year = {2014},
url = {https://jakearchibald.com/2014/offline-cookbook/#network-falling-back-to-cache},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{sw:cacheThenNetwork,
author = {Jake Archibald},
title = {The offline cookbook - JakeArchibald.com},
month = {12},
year = {2014},
url = {https://jakearchibald.com/2014/offline-cookbook/#cache-then-network},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{api:doku,
author = {Erik Römmelt},
title = {Build, Collaborate \& Integrate APIs | SwaggerHub - OHM News API},
month = {02},
year = {2019},
url = {https://app.swaggerhub.com/apis-docs/roemmelter/om_api/1.0},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{dev:homescreen,
author = {Pete LePage},
title = {Your First Progressive Web App | Web Fundamentals | Google Developers},
month = {01},
year = {2019},
url = {https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/#support_native_integration},
note = {(Zuletzt besucht am 26.02.2019)}
}
@online{dev:codeSource,
author = {Pete LePage},
title = {Your First Progressive Web App | Web Fundamentals | Google Developers},
month = {01},
year = {2019},
url = {https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/},
note = {(Zuletzt besucht am 26.02.2019)}
}

View File

@ -0,0 +1,832 @@
% !TeX spellcheck = de_DE
% ---
% Preamble
% ---
\documentclass[12pt]{report}
\usepackage{defaultPreamble}
\bibfilename{Gruppenbericht.bib}
%
% ---
% Document Body
% ---
\begin{document}
\pagenumbering{arabic}
%
% Deckblatt
\setcounter{page}{0} % setze seitenzähler auf 0 -> alle Seitenzahlen <1 werden nicht angezeigt
{\centering
\clear % Select ClearSans for the title page
\vspace*{2\baselineskip}
%
{\large Technische Hochschule}\\
\vspace{0.3ex}
{\large Georg-Simon Ohm Nürnberg}\\
\vspace{0.3ex}
{\large Fakultät Elektrotechnik Feinwerktechnik Informationstechnik}\\
\vspace{2.5ex}
%
{\large Projekt OHM News}\\
\vspace{3.5ex}
%
{\Large\b{Projektstudienarbeit}}\\
\vspace{0.3ex}
{\Large\b{B-ME 5}}\\
\vspace{4.5ex}
{\Large\b{OHM News - See only what matters}}\\
\vspace{3.5ex}
%
{\large Wintersemester 2018/2019}\\
\vspace{10\baselineskip}
%
\begin{table}[h]
\centering \large
\begin{tabular}{rl}
\rule{0pt}{14pt}\b{Senta Mandutz} & \b{Edwina Barbalan} \\
Matrikel-Nr.: 3022812 & Matrikel-Nr.: 3064088 \\
\rule{0pt}{14pt}\b{Vivianne Pham} & \b{Xenia Gr\"unzinger} \\
Matrikel-Nr.: 2928261 & Matrikel-Nr.: 3046080 \\
\rule{0pt}{14pt}\b{Erik R\"ommelt} & \\
Matrikel-Nr.: 2843345 & \\
\end{tabular}
\end{table}
} % Need 1 empty line above for centering
%
\newpage % Seitenumbruch nach Titelblatt
\paragraph{\Large Projekt-Roadmap}$~~$\\
%
\begin{table}[h]
\centering%\small
\begin{tabularx}{\textwidth}{|X|X|X|}
\hline
\rule{0pt}{14pt}\b{Tätigkeit} & \b{Dokument} & \b{Beteiligung} \\
\hline
\rule{0pt}{14pt}Konzeption & Projektidee & Alle \\
Organisation und Pflege & Trello & Erik \\
Organisation und Pflege & Bitrix24 & Erik \\
Protokollierung der Treffen & Handschriftlich & Erik, Vivianne \\
Transkription der Protokolle & LaTeX Protokolle & Erik \\
Organisation und Pflege & Wiki & Vivianne \\
Erstformulierung & Pflichtenheft & Alle \\
Erste Entwürfe des App UI & Scribbles, Skizzen & Alle \\
Namensüberlegungen & Notizen zur Namensfindung & Alle \\
Corporate Design & Styleguide & Xenia \\
Entwicklung & Erstellung der Mockups & Xenia, Edwina \\
Entwicklung & Frontenddesign mit Bootstrap & Xenia, Edwina \\
Entwicklung & Single-Page mit Vue.js & Xenia, Edwina \\
Entwicklung & ServiceWorker & Erik \\
Entwicklung & Manifest mit Json & Erik \\
Entwicklung & Meta-Tags mit HTML & Erik \\
Entwicklung & Erstellung der API-Schnittstelle & Senta, Edwina, Vivianne, \mbox{Xenia} \\
Dokumentation RESTful API & Digital mit Swagger.io & Erik \\
Entwicklung & Server mit Node.js/Express.js & Vivianne \\
Entwicklung & Datenbank mit MongoDB & Senta \\
Entwicklung & DB-Anbindung mit Mongoose & Senta \\
Designentwürfe Plakat & Plakatgestaltung & Vivianne \\
\rule{0pt}{14pt}Design und Ausformulierung & Projektpräsentation & Edwina, Vivianne \\
\hline
\end{tabularx}
\caption{Projekt-Roadmap Tabelle}
\end{table}
%
% Inhaltsverzeichnis
\newpage
\tableofcontents
%
% Bericht
\newpage % Seitenumbruch nach Titelblatt
\section*{1. Einleitung}
\phantomsection\addcontentsline{toc}{section}{1. Einleitung}
In der heutigten Zeit werden die meisten Menschen tagtäglich mit einer Vielzahl an Reizen und Informationen überflutet. Dabei ist es nicht immer einfach den Blick auf das Wesentliche zu halten. Häufig werden wir durch Werbung, irrelevante Mails, Social Media Beiträge und weiteres schnell abgelenkt. So geht es auch vielen Studierenden und Angehörigen der Technischen Hochschule Nürnberg.\\
Für uns war das Anlass genug. Es sollte doch eine Möglichkeit zur qualitativen Verbesserung der Informationsflut im Studienalltag geben. Auf der einen Seite müssen die Hochschulangehörigen entlastet werden, damit die Konzentration für die relevanten Themen gesteigert werden kann und auf der anderen Seite sollte genügend Freiraum für die individuellen Interessen bestehen.\\
Im Folgenden wird die Entwicklung einer Idee zur Lösung dieser Problematik, über deren Ausarbeitung in Form eines Konzeptes bis hin zur praktischen Entwicklung eines lauffähigen Prototypen detailliert beschrieben.
%
\section*{2. Vorwort zur Projektidee}
\phantomsection\addcontentsline{toc}{section}{2. Vorwort zur Projektidee}
Die erste Konzeptversion zu der Projektidee von dem Projekt ''Ohm Management App'', dass im Verlauf der ersten Projektphase in ''OHM-News - See only what matters'' umbenannt wurde, wurde von Erik Römmelt erstellt. Nach gemeinsamer Überarbeitung durch Prof.Dr. Matthias Hopf, dem zukünftig betreuenden Professor und Erik Römmelt wurde die zu Projektbeginn gegebene Version des Projektkonzeptes ausformuliert.
%
\section*{3. Projektidee}
\phantomsection\addcontentsline{toc}{section}{3. Projektidee}
Die Projektidee basiert darauf, dass eine plattformunabhängige App für Studierende an der TH Nürnberg entwickelt werden soll, die Kurznachrichten filtert und in einem Nachrichtenfeed anzeigt. Das Filtern funktioniert durch Tags. Es können beim Nachrichten erstellen mehrere, jedoch mindestens eine eingegeben werden. Diese werden bei einer Überschneidung bei der Suche durch eine logische UND-Verknüpfung eingeschränkt. Angezeigt werden die Nachrichten abhängig von ihrer Priorität und Aktualität. \\
Dazu können in einer Dateiablage von Professoren und Befugten wichtige Links oder andere Dateien gesammelt werden oder als Referenz darauf verwiesen werden. Damit es den Status ''Befugte'' gibt, muss es zudem eine Rollenverteilung geben, wem welche Rechte zu stehen oder beispielsweise wer nur Nachrichten empfangen kann.
Zudem ist eine Speicherung einzelner Nachrichten aus dem Feed möglich, welche im Nachhinein in den Bookmarks oder auch Lesezeichen einsehbar sind.\\
Des Weiteren können Push-Benachrichtigungen angezeigt werden, sodass das Risiko, wichtige Nachrichten zu übersehen, minimiert wird.
Zuletzt gibt es noch ein Nutzerprofil. Hier können Daten zur Person eingetragen und angezeigt werden wie zum Beispiel das Profilbild, deren Status an der technischen Hochschule oder die Matrikelnummer.
Für den Optimalfall einer Übernahme der App von der Hochschule soll modulorientiert und wartungsaufwandsarm gearbeitet werden, um die spätere Verwaltung zu erleichtern. Auch wird Wert gelegt auf eine nutzerfreundliche und möglichst intuitive Bedienoberfläche.
%
\subsection*{3.1. Technische Umsetzung}
\phantomsection\addcontentsline{toc}{subsection}{3.1. Technische Umsetzung}
Zur Umsetzung des Projektes fiel die Zielsetzung auf die Entwicklung einer progressiven Web-App. Aufgrund ihrer Plattformunabhängigkeit und Kompatibilität mit den TH-Servern (VPN und Eduroam) und laut Erfahrungen von Professor Dr. Hopf stellt dies eine gute Lösung und sichere Wahl dar. Ebenfalls, was die Datenschutzvorkehrungen der Hochschule betrifft. \\
Es werden die Codesprachen HTML, CSS und JavaScript verwendet und als Frameworks Vue.js, Bootstrap und node.js. \\
Als Datenbank dient MongoDB und das zugehörige Framework mongoose.
%
\section*{4. Projektorganisation}
\phantomsection\addcontentsline{toc}{section}{4. Projektorganisation}
Wöchentlich fand ein Treffen, zum Austausch und zur Besprechung zurückliegender und anstehender Aufgaben, statt. An diesem Treffen nahmen in der Regel alle Mitglieder dieser Projektgruppe und der Projektbetreuer Prof.Dr. Matthias Hopf teil. Konnten Teammitglieder, gleich welcher Gründe, an einem Treffen nicht teilnehmen, so haben alle sich unverzüglich über Absprachen und den neu anstehenden Aufgaben selbstständig erkundigt.
%
\subsection*{4.1. Trello}
\phantomsection\addcontentsline{toc}{subsection}{4.1. Trello}
Zu Beginn des Projekts wurde entschieden, dass zur Verbesserung des Arbeitsablaufs ein Organisationstool hilfreich wäre. So fiel ist Trello in Gespräch gekommen.\\
Trello ist ein graphisch sehr gut aufbereitetes Projektmanagement-Tool in Form von Kanban-Boards. In der kostenfreien Version, ist es möglich ''unbegrenzt [viele] Boards, Listen, Karten, Mitglieder, Checklisten und Anhänge'' hinzuzufügen und zu nutzen\cite{trello:price}. Zur typografischen Gestaltung der Karten, um zum Beispiel eine Liste einzufügen ist die Nutzung von einigen Markdown-Markup Elementen möglich.\\
Dem Projekt zugute kommend ist die sehr einfache Bedienbarkeit, Plattform unabhängige Verfügbarkeit, sowie die gute Übersichtlichkeit der Boards. Nachteilig ist, dass es nur sehr eingeschränkt die Zeitkomponente abbilden kann. Dies ist nur über die sogenannten ''Plugins'' möglich, wobei in der kostenfreien Version nur ein Plugin je Board zugleich genutzt werden kann. So kann einem Board entweder die Funktion ''Gantt-Diagramme nutzen'' oder ''Storypoints den Karten hinzufügen'' verwendet werden.\\
Diese Einschränkung führte zur der Entscheidung gegen die Nutzung von Trello im Team.
%
\subsection*{4.2. Bitrix24}
\phantomsection\addcontentsline{toc}{subsection}{4.2. Bitrix24}
Nach dem Ausschluss von Trello begann die Umsicht nach einem anderen Tool, dass kostenfrei mit einer Gruppe von fünf Personen genutzt werden kann. Und es sollte die Funktionen Gantt-Diagramme erstellen, Aufgaben an Mitglieder verteilen und den Gewichtungs-Punkte oder ''Storypoints'' an Aufgaben zuweisen erfüllen.\\
Bitrix24 hat diese Anforderungen erfüllt, war jedoch bereits auf den ersten Blick deutlich unübersichtlicher und komplexer in der Handhabung. Da es sich bei Bitrix24 in erster Linie um ein umfangreiches CRM-System handelt, mit vielen weiteren Funktionen wie ''Aufgabenverwaltung für Gruppen'', ''Projektplanung und -management'' und ''Team-Chat''\cite{bitrix:feature}.\\
Auch nach der Deaktivierung und Ausblendung sämtlicher Funktionen, die die Nutzung von Bitrix24 komplizierter machten, blieb die Benutzeroberfläche noch unübersichtlich. Was die Umsetzung der Aufgaben als Kanban-Karten mit Gewichtungs-Punkten und der zeitlichen Darstellung der Aufgaben in einem Gantt-Diagramm betrifft, so war dies sehr angenehm und vollends zufriedenstellend möglich.\\
So wurden zahlreiche organisatorische Fristen, sowie Aufgaben organisatorischer und entwicklungs-technischer Natur in Bitrix24 erfasst und anschließend in einem wöchentlichen Treffen vorgestellt. Im Weiteren wurde die Nutzung und Pflege des Tools an alle Mitglieder übertragen.\\
Es stellte sich am Ende des ersten Projektabschnittes heraus, dass das Tool Bitrix24 aufgrund der komplexen und unübersichtliche Benutzeroberfläche kaum genutzt wurde.
%
\section*{5. Entwicklerblog}
\phantomsection\addcontentsline{toc}{section}{5. Entwicklerblog}
Die Wiki Blogeinträge in dem hochschulinternen github Repository dienen dazu, die Projektfortschritte transparent zu gestalten und eine kurze prägnante Zusammenfassung für Professoren und Studierende zu gewährleisten. Einzusehen sind die Beiträge auf der git efi Seite \cite{gitefi}.
%
\subsection*{5.1. Protokollierung}
\phantomsection\addcontentsline{toc}{subsection}{5.1. Protokollierung}
Für die Protokollierung wurden in regelmäßigen Intervallen Dokumentationen über die Tätigkeiten aller Teammitglieder geführt und diese folglich in Schrift festgehalten. Enthalten sind die Aufgabenaufteilung und die bisherige Umsetzung aller Projektteilnehmer in einer bestimmten Zeit, welche anschließend gebündelt zusammengefasst wurden.
%
\subsection*{5.2. Blogentwurf}
\phantomsection\addcontentsline{toc}{subsection}{5.2. Blogentwurf}
Da das git efi nicht nur für Professoren sondern auch Studierenden der Fakultät efi zugänglich ist, werden die Vorgänge des Projekts in kurzen Sätzen und möglichst verständlich wiedergegeben. So können sich auch Studierende außerhalb des Fachgebietes ein ungefähres Bild von dem Fortschritt der OHM News-App machen. Nach dem Umformulieren und Reduzieren auf die Kernaussagen wurden Einträge in kontinuierlichen Abständen auf dem Hochschul github\cite{gitefi} zum Abrufen hochgeladen. Diese berufen sich auf etwa ein Monats-Abstände oder kürzer. Bei neuen Einträgen wird Professor Dr. Hopf informiert, sodass er stets den Verlauf des Projektes nachvollziehen kann. In zeitlich chronologischer Reihenfolge aufgelistet, vereinfacht dies den Überblick. Insgesamt wurden im Laufe des OHM News-App Projektes sechs Wiki Blogeinträge erstellt.
%
\section*{6. Pflichtenheft}
\phantomsection\addcontentsline{toc}{section}{6. Pflichtenheft}
Das Pflichtenheft hat die Gruppe gemeinsam zusammengestellt. Dabei sollte der Datenzugriff per MongoDB erzeugt werden, alternativ könnte auch eine Schnittstelle zur Verfügung gestellt werden. Die Grafische UI der PWA sollte einen Login, ein Menü, eine Suche, ein Profil, einen Filter nach Hashtags, ein Dashboard, Einstellungen, das Nachrichtfeld und ein Formular zum Verfassen von Nachrichten umfassen. Ein Abhängigkeitsdiagramm soll erstellt werden. Die Anwendungslogik sollte im Bereich des Routings, der Darstellung und Speicherung von Nachrichten und dem LDAP-Login umgesetzt werden. Codetests sollten durchgeführt werden und das Rollenkonzept der Nutzer sollte festgelegt werden.\\
Des Weiteren sollte ein Anwendungsname gefunden und ein Logo erstellt werden. Ein UX Konzept sollte aufgestellt werden. Wenn es zeitlich noch möglich wäre, hätte man die Funktionen umsetzen sollen um die Datenbankabrufe zu filtern und Kanälen zu folgen. Ebenso hätte man die Anzeigekriterien festlegen können.
%
\section*{7. Konzepterstellung}
\phantomsection\addcontentsline{toc}{section}{7. Konzepterstellung}
Der erste Schritt zur Definition eines Layoutentwurfs war die Erstellung von Scribbles. Unter einem Scribble versteht man einen groben ersten Entwurf, der zur Ideenfindung beitragen soll. Nach Festlegung der grundsätzlichen Projektidee, zeichnete jedes unserer Teammitglieder Scribbles für verschiedene Seiten der App, die wir im Anschluss zusammen diskutierten. Wichtig war uns hierbei, dass jedes Teammitglied erst eigene Ideen entwickelte, um so viele verschiedene Aspekte zu sammeln.\cite{scrib} \\
Wir versuchten alle Ideen zu vereinen und einigten uns auf einen Layoutentwurf, welcher im Anschluss in einem detaillierten Screendesign umgesetzt wurde. In Abbildung \ref{layout} ist das Screendesign für die Startseite zu sehen. Wir legten vor allem den Fokus auf die Startseite, da diese der erste Baustein der App werden sollte. Die Navigation auf die anderen Seiten erfolgt über eine Leiste am unteren Displayrand. Die weiteren Seiten legten wir zu Beginn fest, arbeiteten diese allerdings erst mit der Zeit detaillierter aus. Ein genauer Aufbau der App wird unter 9.1. beschrieben. \\
\begin{figure}[H]
\centering
\includegraphics[width=4.5cm]{Layout}
\caption{Layoutentwurf der Startseite}
\label{layout}
\end{figure}
Bevorstehende Implementierungen von Funktionalitäten sollten, wenn bereits im Vorfeld möglich, ebenfalls in den Layoutentwürfen berücksichtigt werden. Änderungen sind natürlich auch zu einem späteren Zeitpunkt möglich, erhöhen allerdings den Aufwand. Deswegen wurde versucht, möglichst viele Funktionalitäten bereits in die Layoutentwürfe miteinzubeziehen.\\\\
Wir einigten uns im Projektteam darauf die App gemäß den Material Design Richtlinien von Google zu gestalten. Material Design ist eine von Google entwickelte Designvorgabe, die auf materienartigen Flächen basiert und dadurch zu einem sehr minimalistischen \mbox{Design} führt. Ziel dieser Designsprache ist unter anderem eine intuitive Benutzeroberfläche. Die Richtlinien wurden bei der Erstellung des Screendesigns berücksichtigt. \cite{md,md2}
%
\section*{8. Namensfindung}
\phantomsection\addcontentsline{toc}{section}{8. Namensfindung}
Zu Anfang haben wir uns für den Titel OHM-Management entschieden, mit dem Wissen, dass es höchstwahrscheinlich nicht dabei bleibt, da das Wort Management mit unserer angestrebten Funktionalität der App nur einschränkend passte.
Die Verwendung des Wortstammes Kommunikation war für unsere Benennung auch nicht zutreffend, insofern der Austausch von Information nur in eine Richtung erfolgt.
Zusätzlich gestaltete sich die Namensfindung etwas schwierig, da wir sozusagen eine App für die TH-Nürnberg entwickelten, die man auch in Verbindung mit dem OHM-Zeichen kennt. Diesbezüglich wollten wir uns bei der Benennung auch an dieses anlehnen. Um dies tun zu können, mussten wir uns erst einmal informieren, ob es ähnliche Namen bereits gibt die man aus diesem Grund dementsprechend nicht verwenden kann.
%
\subsection*{8.1. Recherche}
\phantomsection\addcontentsline{toc}{subsection}{8.1. Recherche}
Bevor also der erste Schritt in Richtung Namensfindung gemacht werden konnte, recherchierten wir über bereits vorhandene Namen, die von der TH-Nürnberg genutzt werden. Neben Namen wie InfoTHek, OHMdoc und OHM-Shop, welche für unser Thema eher irrelevant wirkten, fanden wir auch den Titel OHM-News. Dies war ein Online Journal, welches jedoch, wie sich später rausgestellt hatte, eingestellt wurde.
%
\subsection*{8.2. Brainstorming}
\phantomsection\addcontentsline{toc}{subsection}{8.2. Brainstorming}
In der Zwischenzeit setzten wir jedoch die Ideensammlung weiter fort und präsentierten in der Gruppe einzelne Namensvorschläge. Hieraus entstand eine Liste an kreativen Gedanken. Unsere Favoriten :
\begin{itemize}
\item \b{ON AIR}
\begin{itemize}
\item \b{O}HM \b{N}ews \b{A}pp for \b{I}nformation and \b{R}ediscovery/\b{R}ecommendations
\item \b{O}HM \b{N}ews Stream for \b{A}ctivities, \b{I}nformation and \b{R}ecommendations
\end{itemize}
%
\item \b{ON STREAM}
%
\begin{itemize}
\item \b{O}HM \b{N}ews \b{Stream}
\end{itemize}
%
\item \b{OHM News}
%
\item \b{TH i s}
%
\item \b{INFOhm}
\end{itemize}
Alle Vorschläge waren sehr durchdacht und kreativ. Jedoch hatten einige nicht ganz die Wirkung die wir uns erhofften, denn wir waren der Meinung, dass man das Wort On Air eher mit einem Radiosender verbindet. Ebenfalls der Nachteil bei der Idee On Stream war es, dass man stark an Streaming Dienste wie zum Beispiel Netflix erinnert wird.
%
\subsection*{8.3. Ergebnis}
\phantomsection\addcontentsline{toc}{subsection}{8.3. Ergebnis}
Nach langem Überlegen kamen wir auf den Namen OHM News zurück und dieser überzeugte uns im Endeffekt alle. Dieser Titel war recht kurz, er ist leicht zu verstehen ohne viel nachzudenken, man verbindet ihn auf Anhieb mit der TH-Nürnberg auf Grund des Wortes OHM und er verweist auf die Funktionalität die unsere App bereitstellt, die da wären Neuigkeiten, sprich News, für die Studenten der Hochschule bereitzustellen.
%
\subsection*{8.4. Claim}
\phantomsection\addcontentsline{toc}{subsection}{8.4. Claim}
Parallel zur Namensfindung, befassten wir uns auch mit dem Claim unseres Produktes. Da ein Claim die Funktion unserer App auf den Punkt bringen soll, und dabei kurz, leicht zu merken und aussagekräftig sein sollte, entschieden wir uns für eine Formulierung der Art ''nur relevante Themen werden angezeigt''. Nach einer kurzen Denkphase kamen wir auf den Claim ''See only what matters'', beziehungsweise ''Only see what matters'' vor. Dies kam bei uns allen sehr gut an, jedoch wussten wir nicht, welche von den beiden Optionen die erhoffte Bedeutung am besten hervorbringt. Sie haben zwar die selbe Bedeutung, trotz alle dem empfanden wir, dass die Betonung des Wortes „Only“ auf zwei verschiedene Faktoren des Satzes liegt. Nach reichlicher Überlegung entschieden wir uns also für die Formulierung ''See only what matters'' als Claim für unsere App.
%
\section*{9. Corporate Design}
\phantomsection\addcontentsline{toc}{section}{9. Corporate Design}
Um das ganze Konzept stimmig zu gestalten, wurde ein Corporate Design erstellt.
Wichtigste Grundlage hierfür ist das Farbkonzept. Da die App im Hochschulrahmen verwendet werden soll, wurde für die Grundfarbe der Blauton des Hochschullogos gewählt, umso eine Zugehörigkeit zu symbolisieren. Ergänzend zu diesem Blauton werden noch zwei dezente Grautöne verwendet, wie in Abbildung \ref{farbschema} zu sehen ist. \\
%
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{Farbschema_CD}
\caption{Farbkonzept}
\label{farbschema}
\end{figure}
\noindent Für die Schrift wird in der gesamten App die von Google Material Design vorgegebene Schriftart Roboto verwendet. Diese ist eine serifenlosen Schriftart und deshalb durchweg gut lesbar.
Auch ein Logo ist wichtig, um einen gewissen wiedererkennungswert für die App zu schaffen. Da wir uns in der Gruppe für den App-Namen ''Ohm-News'' entschieden haben, war der erster Gedanke eine Zeitung für das Logo herzunehmen (siehe Abbildung \ref{logo1}). Die Grafik war allerdings schwierig in das Produkt einzubinden, weil sie sehr detailreich ist. \\
%
\begin{figure}[H]
\centering
\includegraphics[width=4cm]{Logo1_CD}
\caption{Erster Logoentwurf}
\label{logo1}
\end{figure}
\noindent Ein zweiter etwas minimalistischer Entwurf ist in Abbildung \ref{logo2} zu sehen, für welchen sich das Projektteam schlussendlich auch entschied. Es zeigt ein Globussymbol mit dem Hochschullogo im Zentrum, um auch hier wieder die Verbindung zur Hochschule schaffen. \\
%
\begin{figure}[H]
\centering
\includegraphics[width=4cm]{Logo2_CD}
\caption{Finales Logo}
\label{logo2}
\end{figure}
%
\noindent Das Logo wurde dann auch zu einem App Icon erweitert. In Abbildung \ref{icons} ist das Icon für Android sowie IOS Geräte zu sehen. Das Android Icon wurde analog den Material-Design Richtlinien gestaltet und besitzt deshalb einen leichten Schatten. Bei dem IOS Icon wurde das Logo lediglich auf einen einfarbigen Hintergrund gesetzt. \\
%
\begin{figure}[H]
\centering
\includegraphics[width=10cm]{Icons_CD}
\caption{App Icons}
\label{icons}
\end{figure}
%
\section*{10. Erstellung der Mockups}
\phantomsection\addcontentsline{toc}{section}{10. Erstellung der Mockups}
Auf Basis des Screendesigns wurden erste Mockups erstellt, welche mittels HTML und CSS umgesetzt wurden. Um die Elemente entsprechend den Material-Design Richtlinien zu gestalten, wurde das CSS-Framework Bootstrap mit eingebunden, das eine Auswahl an vordefinierten Gestaltungselementen und Stilen in Form von CSS-Stylesheets bereitstellt. Dazu gehören Elemente wie Buttons, Formulare, Navigationszeilen und viele mehr. Wir verwendeten Bootstrap Dateien, die auf Material Design abgestimmt sind. So wird ein einheitliches Design gewährleistet und die Styles müssen nicht selbst erstellt werden. Ein weitere Vorteil von Bootstrap, ist, dass es ein responsives Webdesign unterstützt und man deshalb plattformunabhängig entwickeln kann. Dafür sorgt ein Grid-Layout, welches den Benutzerbildschirm in 12 Spalten teilt und die Elemente, je nach verfügbarer Auflösung, anzeigt. Im ersten Projektabschnitt legten wir den Fokus besonders auf eine mobile Ansicht. Eine Desktopansicht wird ebenfalls unterstüzt, kann allerdings noch stark optimiert werden. \cite{bootstrap} \\
Durch die Ergänzung von eigenen CSS-Dateien konnten Änderungen an den vordefinierten Stilen vorgenommen werden. Sowohl unsere als auch die von Bootstrap bereitgestellten Dateien, wurden auf Grundlage von Less-Stylesheets erstellt. Dabei wird auf effiziente Weise CSS generiert. Variablen sowie Funktionen sind möglich und Vererbung wird durch \mbox{verschachtelte} Selektoren dargestellt. \cite{less} \\
Wir habe für jeden Menüpunkt eine HTML-Seite erstellt, welche im Unterpunkt genauer beschrieben werden.
%
\subsection*{10.1. Aufbau der App}
\phantomsection\addcontentsline{toc}{subsection}{10.1. Aufbau der App}
%
\begin{figure}[H]
\begin{minipage}[t]{5cm}
\vspace{0pt}
\centering
\includegraphics[width=4cm]{Homescreen_MU}
\caption{Startseite}
\label{abbildung_homescreen}
\end{minipage}
\hfill
\begin{minipage}[t]{11cm}
\vspace{0pt}
Auf der Startseite der App gelangt der Nutzer als erstes auf einen Newsfeed. Der Newsfeed besteht aus einzelnen Nachrichten in Form von Karten und soll für jeden Nutzer nur \mbox{relevante} Nachrichten anzeigen. Jede Nachricht besitzt einen oder \mbox{mehrere} Tags. Eine Nachricht wird für einen Nutzer als relevant eingestuft, wenn dieser mindestens einen Tag aus der Nachricht abonniert hat. Somit erhält jeder Nutzer eine individuelle Startseite. Zusätzlich befindet sich auf der Nachrichtenkarte oben rechts ein Icon, welches die Nachricht zu einer Fakultät zuordnet. Unten rechts kann das Lesezeichen-Icon geklickt werden, um eine Nachricht zu speichern. Auf diese Weise können die Studierenden wichtige Informationen schnell wiederfinden.\\\\
Im Header ist die Suche zu finden, in der Tags explizit gefiltert werden können. Der \mbox{Header} ist von jeder Seite aus zugängig. Im Footer befindet sich die Navigationszeile, von wo aus der Nutzer auf die weiteren Seiten gelangt.
\end{minipage}
\end{figure}
%
\begin{figure}[H]
\begin{minipage}[t]{5cm}
\vspace{0pt}
\centering
\includegraphics[width=5cm]{Links_MU}
\caption{Infoseite}
\label{abbildung_infoseite}
\end{minipage}
\hfill
\begin{minipage}[t]{11cm}
\vspace{0pt}
Der zweiten Menüpunkt, der durch ein Globus-Icon dargestellt wird, soll eine Informationsseite beinhalten. Das Konzept hierfür ist allerdings noch nicht konkret ausgearbeitet, da wir uns zuerst auf die Mitteilungsfunktionen konzentrieren wollten. Aktuell wird ein Empty State angezeigt.
\end{minipage}
\end{figure}
%
\medskip
\begin{figure}[H]
\begin{minipage}[t]{5cm}
\vspace{0pt}
\centering
\includegraphics[width=5cm]{CreateMessage_MU}
\caption{Formular}
\label{abbildung_createmsg}
\end{minipage}
\hfill
\begin{minipage}[t]{10cm}
\vspace{0pt}
Über das Plus-Icon, in der Mitte der Navigationszeile, gelangt man zu einem Formular. Hier können neue Nachrichten erstellt werden. Nachrichten sollen nur von ausgewählten Nutzergruppen, wie Fakultäten, Studienbüro und ähnlichen gesendet werden können.
Für eine Nachricht wird ein Betreff, mindestens ein Tag und der Nachrichteninhalt benötigt. Es soll eine Auswahl an vordefinierten Tags geben, damit am Ende nicht jeder Verfasser wahllos Tags setzten kann und \mbox{Duplikate} vermieden werden. Dieser Menüpunkt ist der einzige in dem keine Navigationszeile angezeigt wird. Nach Absenden einer Nachricht, gelangt man zurück auf die Startseite. Über den Button 'Abbrechen' wird man auf den vorherigen Menüpunkt zurückgeleitet.
\end{minipage}
\end{figure}
%
\begin{figure}[H]
\begin{minipage}[t]{5cm}
\vspace{0pt}
\centering
\includegraphics[width=5cm]{Gespeicherte_MU}
\caption{Gespeicherte}
\label{abbildung_gespeichert}
\end{minipage}
\hfill
\begin{minipage}[t]{11cm}
\vspace{0pt}
Der nächste Menüpunkt wird über das Lesezeichen-Icon erreicht und zeigt die gespeicherten Nachrichten an. Diese Seite ist ähnlich aufgebaut wie der Newsfeed der Startseite, beinhaltet allerdings nur die vom Nutzer gespeicherten Nachrichten.
\end{minipage}
\end{figure}
%
\medskip
\begin{figure}[H]
\begin{minipage}[t]{5cm}
\vspace{0pt}
\centering
\includegraphics[width=5cm]{Profil_MU}
\caption{Profil}
\label{abbildung_profil}
\end{minipage}
\hfill
\begin{minipage}[t]{11cm}
\vspace{0pt}
Der letzte Menüpunkt ist das Profil. \\
Oben sieht man zuerst das Profilbild, hier verwendeten wir die runde Form anstatt der üblichen Quadratischen. Darunter sieht man dann die im Vorfeld angegebene Information des Benutzers. Am Ende der sogenannten Karte befinden sich zwei Knöpfe, die Anzeigen wie vielen Kanälen man folgt und wie viele Beiträge man bis jetzt gespeichert hat. Auf der Karte rechts oben im Eck findet man den Bearbeitungs-Knopf, in Form eines kleinen Stiftes, dies ermöglicht dem Benutzer, die Seite gemäß seinen Wünschen anzupassen.
\end{minipage}
\end{figure}
%
\section*{11. Single-Page Entwicklung}
\phantomsection\addcontentsline{toc}{section}{11. Single-Page Entwicklung}
Neben klassischen Webanwendungen gibt es ebenfalls Single-Page Webanwendungen. Bei klassischen Webapplikationen gibt es mehrere untereinander verlinkte HTML-Dokumente, die dann gemeinsam eine komplette Anwendung ausmachen. Der Nachteil hierbei ist, dass die Seite beim Wechsel durch die erneute Kommunikation mit dem Server neu heruntergeladen werden muss. SPAs jedoch bestehen nur aus einer HTML-Datei. Dies bewirkt, dass neue oder veränderte Daten dynamisch geladen werden und somit die Kommunikation zwischen Server und Client verringert wird. Es ermöglicht dem Benutzer, nach dem einmaligen laden der Seite, diese auch weiterhin im offline Modus zu steuern. Sollte also die Interaktion mit dem Server unterbrochen werden, werden die zwischengespeicherten Daten einfach aus dem Webbrowser verwendet.\cite{spa:1}\cite{spa:2}
%
\subsection*{11.1. Warum Vue.js?}
\phantomsection\addcontentsline{toc}{subsection}{11.1. Warum Vue.js?}
Neben dem Vue.js Framework gibt es noch weitere wie zum Beispiel react.js, angular.js, meteor.js und viele mehr. Warum wir uns jedoch für das Vue.js Framework entschieden haben, werden wir Ihnen nun erläutern. Vue.js das jüngste von den drei größten Frameworks, es hat jedoch den Vorteil, dass es aus den Fehlern der Vorgänger lernen konnte und diese schon im Vorfeld überdenken und ausmerzen konnte. Da es auf Sprachen wie HTML, CSS und JavaScript aufbaut, lässt es sich problemlos in schon bestehende Projekte einbauen.
Die Syntax ist hierbei, im Vergleich zu anderen, besser lesbar. Hierfür werden nämlich sogenannte ''Single File Components'' verwendet.\cite{vue:warum} Sogenannte Komponenten sind wiederverwendbare Vue-Instanzen mit einem Namen, die als benutzerdefiniertes Element in einer Vue-Stamminstanz verwendet werden. Wie so eine erstellte Komponente aussieht, werden wir Ihnen im folgenden erörtern.
%
\subsection*{11.2. Vue.js in vorhandene Mockups einbinden}
\phantomsection\addcontentsline{toc}{subsection}{11.2. Vue.js in vorhandene Mockups einbinden}
Nach reichlicher Recherche und dem einlesen in das uns vorher unbekannte Framework, war es an der Zeit unsere HTML-Seiten in Vue-Komponenten umzuwandern. Wir setzten uns erstmal zusammen und erstellten die Startseite.
Im Folgenden wird der Aufbau der Home-Komponente näher erläutert. In Listing 1 ist der dazugehörige Quellcode zu sehen. Nicht enthalten sind zur Komponente zugehörige Methoden. Die Home-Komponente wird in unserem Projekt als HomeRouter betitelt.
%
\begin{center}
\begin{lstlisting}[caption={HomeRouter-Komponente},captionpos=b]
const HomeRouter = {
template: `
<div id="om-msg-cards">
<MsgCard
v-for="id in messagelist.slice().reverse()"
:key="id"
:msg="messages[id] || {}"
></MsgCard>
</div>`,
//Restliche Komponente
...
\end{lstlisting}
\end{center}
%
Im Newsfeed, welcher der Hauptbestand der Startseite ist, wird eine Liste von Nachrichten dynamisch generiert, sodass jeder Nutzer später einmal eine individuelle Ansicht erhält, abhängig von den Tags, die dieser abonniert. \\
Jede Komponente besitzt ein Template in Form eines Strings, welcher HTML-Code \mbox{beinhaltet}. Die Komponente HomeRouter ist verschachtelt und enthält die Komponente MsgCard. Der Quellcode für diese Komponete ist in Listing 2 zu sehen.
%
\begin{center}
\begin{lstlisting}[caption={MsgCard-Komponente},captionpos=b]
Vue.component('MsgCard', {
template: `<div class="om-card card">
<h6 class="msg-head">
<b>{{ msg.subject }}</b>
<img src="favicon.ico" width=20px height=20px>
</h6>
{{ msg.message }}<br><br>
<a href="#">{{ msg.tag }}</a></p>
<div class="om-card-footer"> <div class="om-user-line">
<i class="material-icons">account_circle</i>
Erstellt von {{ msg.user }}</div>
<i class="material-icons">bookmark_border</i>
</div></div>`,
props: ['msg']
});
\end{lstlisting}
\end{center}
%
Eine MsgCard stellt eine einzelne Nachricht in Form einer Karte dar. In der HomeRouter-Komponete geht eine For-Schleife alle vorhandenen Nachrichten-Ids durch. Dabei werden der MsgCard-Komponente alle relevanten Informationen übergeben, die für eine Darstellung benötig wird. Am Ende stellt die App eine fortlaufende Liste mit allen Nachrichten dar.\\\\
Da wir nun ein bisschen geübter im Umgang mit Vue waren, teilten wir uns die restlichen Seiten auf. Dabei gingen wir ähnlich vor, wie bei der Home-Komponente.
%
\subsection*{11.3. Routing}
\phantomsection\addcontentsline{toc}{subsection}{11.3. Routing}
Vue.js bietet eine Reihe von Funktionen, mit denen Sie wiederverwendbare Webkomponenten erstellen können. Routing ist eine dieser Methoden. Der Benutzer kann somit zwischen den Seiten wechseln, ohne diese erneut zu aktualisieren. Dies ermöglicht dem Benutzer eine einfache und dynamische Navigation durch die Webanwendung. Wir werden Ihnen nun, anhand unseres Codes erklären, wie ein Vue.js-Router funktioniert.\cite{vue:router} \\
Zunächst richten wir hierfür die Navigation über die Navigationsbar ein, die wir mittels einem Router-Link-Element erstellt haben. Dieser gibt an, auf welche der angegebenen JS-Datei zugegriffen wird sobald man auf das ausgewählte Item klickt.
%
\begin{center}
\begin{lstlisting}[caption={Navbar},captionpos=b]
<nav class="nav nav-tabs nav-justified om-nav" v-if="$route.path !=='/createMessage' ">
<router-link to="/home" class="nav-item nav-link">
<i class="material-icons">home</i></router-link>
<router-link to="/files" class="nav-item nav-link">
<i class="material-icons">language</i></router-link>
<router-link to="/createMessage" class="nav-item nav-link outlined">
<i class="material-icons">add_circle</i></router-link>
<router-link to="/bookmark" class="nav-item nav-link">
<i class="material-icons">bookmark</i></router-link>
<router-link to="/profil" class="nav-item nav-link">
<i class="material-icons">person</i></router-link>
</nav>
\end{lstlisting}
\end{center}
%
Im Folgenden werden Komponenten definiert und dann in ein neues Array von Objekten, so genannte Routen, eingefügt. Zu beachten ist die Zuordnung eines URL-Pfads zu der dazugehörigen Komponente. Daraufhin wird ein Vue-Router definiert, der an ein neues Vue-Objekt übergeben wird. Wenn nun beispielsweise ''/home'' geladen ist, wird die Home-Komponente in der Router-Ansicht gerendert und aufgerufen. Die erste Zeile mit dem ''/'' steht für die Startseite, auf die man gelangt, sobald man die App öffnet.\cite{vue:router3} \\
%
\begin{center}
\begin{lstlisting}[caption={Routing},captionpos=b]
const routes = [
{ path: "/", component: HomeRouter },
{ path: "/home", component: HomeRouter },
{ path: "/files", component: FileRouter },
{ path: "/createMessage", component: CreateMsgRouter },
{ path: "/bookmark", component: BookmarkRouter },
{ path: "/profil", component: ProfilRouter },
];
const router = new VueRouter({
routes
});
var app = new Vue({
router,
el: '#api',
methods: {
...
},
});
\end{lstlisting}
\end{center}
%
\section*{12. Service Worker}
\phantomsection\addcontentsline{toc}{section}{12. Service Worker}
Der ServiceWorker ist eine JavaScript Datei, die üblicherweise im root-Verzeichnis der Webanwendung liegt. Für die Funktionsweise ist die Namensgebung der Datei nicht maßgebend. Damit der ServiceWorker überhaupt funktionieren kann, wird eine SSL-Verbindung benötigt. Ist eine sicher Verbindung gegeben, kann der ServiceWorker (im Weiteren auch mit ''SW'' abgekürzt) nach erfolgreichem Laden der Webseite im Browser des Clients registriert werden. Nachdem der SW zeitlich unabhängig, also asynchron im Browser läuft, muss bei der Entwicklung und Umsetzung des ServiceWorker-Codes darauf geachtet werden, dass in Promises geschrieben wird. Da Promises ein wenig schwieriger nachzuvollziehen sind und insbesondere das Problem der sogenannten Callback-Hell nicht lösen, bietet es dich an den SW in Async/Await Struktur zu schreiben. Diese basiert auf dem asynchronen Prinzip der Promises und lässt sich dennoch lesen wie synchrone Funktionen.\\
Zunächst wird der ServiceWorker, nach dem vollständigen Laden der Webanwendung, versuchen sich im Browser des Clients zu registrieren. Schlägt die Registrierung fehl, ist dies ein Anzeichen dafür, dass der genutzte Browser keine ServiceWorker unterstützt. Es folgen zwei Code Umsetzungen, beide dienen der Registrierung:\\
%
\begin{center}
\begin{lstlisting}[caption={Register, Promise/Then-Codestruktur},captionpos=b]
/* Promise / Then */
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/serviceWorker.js').then(function(registration) {
// Registration was successful
}, function(err) {
// registration failed
});
});
}
\end{lstlisting}
\end{center}
%
\begin{center}
\begin{lstlisting}[caption={Register, Async/Await-Codestruktur},captionpos=b]
/* Async / Await */
const registerServiceWorker = async () => {
try {
const registration = await navigator.serviceWorker.register('/serviceWorker.js', {scope: '/'});
// Registration successful
} catch (err) {
// Registration failed
}
return;
}
if ('serviceWorker' in navigator) {
window.addEventListener('load', registerServiceWorker());
}
\end{lstlisting}
\end{center}
%
Einmal in der Promise.then Schreibweise und einmal in der Async/Await Schreibweise.\\
Nach erfolgreicher Registrierung, folgt das ServiceWorker Event Installation. Dabei wird eine neue ServiceWorker Version im Browser angelegt und ein Abbild der festgelegten Dateien, hier als Beispiel nur die ''index.html'' (siehe filesToCache), im sogenannten ''Local Storage'' mittels Cache-Managements (auch Cache API genannt) gespeichert. Diese beiden Code Blöcke zeigen den Install-Vorgang:
%
\begin{center}
\begin{lstlisting}[caption={Install, Promise/Then-Codestruktur},captionpos=b]
/* Promise / Then */
const cacheName = 'appname-v1-0';
const filesToCache = [ '/index.html' ];
self.addEventListener('install', function(event) {
console.log('[ServiceWorker] Install');
event.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
\end{lstlisting}
\end{center}
%
\begin{center}
\begin{lstlisting}[caption={Install, Async/Await-Codestruktur},captionpos=b]
/* Async / Await */
const installNewCache = async (event) => {
const cacheName = 'appname-v1-0';
const filesToCache = [ '/index.html' ];
const cacheStatic = await caches.open(cacheName);
cacheStatic.addAll(filesToCache);
console.log('[ServiceWorker] Cache static files.');
return;
}
self.addEventListener('install', event => {
// don't wait
self.skipWaiting();
// cache static files
event.waitUntil(installNewCache());
console.log('[ServiceWorker] Install');
});
\end{lstlisting}
\end{center}
%
Anschließend folgt die Aktivierung. Hier wird aufgeräumt und zwar die alten ServiceWorker Versionen. Es sollte immer nur eine ServiceWorker Version je Webanwendung im Browser registriert und installiert sein.
%
\begin{center}
\begin{lstlisting}[caption={Activate, Promise/Then-Codestruktur},captionpos=b]
/* Promise / Then */
self.addEventListener('activate', function(event) {
// ServiceWorker activated
event.waitUntil(
caches.keys().then(function(cacheKeyList) {
return Promise.all(
cacheKeyList.map(function(cacheKeyList) {
if (cacheWhitelist.indexOf(cacheKeyList) === -1) {
// Old Cache removed
return caches.delete(cacheKeyList);
}
})
);
})
);
return self.clients.claim();
});
\end{lstlisting}
\end{center}
%
\begin{center}
\begin{lstlisting}[caption={Activate, Async/Await-Codestruktur},captionpos=b]
/* Async / Await */
const cacheCleanUp = async () => {
const cacheKeyList = await caches.keys();
const deletions = cacheKeyList
.filter(key => key.startsWith(appPrefix) && !cacheKeyList.includes(key))
.map(key => {
caches.delete(key)
// Old Cache removed
});
for (const success of deletions) {
await success;
}
return;
}
self.addEventListener('activate', event => {
event.waitUntil(cacheCleanUp());
clients.claim();
// ServiceWorker activated
});
\end{lstlisting}
\end{center}
%
Als letzter Schritt bleibt nur noch der 'fetch'. Das ist jenes Event in dem der ServiceWorker neue, aktuelle Dateien aus dem Netzwerk bezieht und die Dateiversionen im Cache, lokalen Speicher aktualisiert. Hier gibt es verschiedene Vorgehensweisen. Wir haben uns entschieden, das der ServiceWorker nur jene API GET-Anfragen speichern soll. Dies dient der Vermeidung doppelter und korrupter Datensätze. Im Weiteren soll überprüft werden, ob die angefragten Dateien mit den gespeicherten Dateien übereinstimmt. Falls nicht, gibt es keine gespeicherten Daten und eine Netzwerkanfrage wird zwingend benötigt. Andernfalls zeige die gespeicherten Daten an und tätige anschließend eine Anfrage ans Netzwerk. Wenn aktuellere Daten aus dem Netzwerk empfangen werden, so aktualisiere die gespeicherten Dateien und zeige die aktuelleren Daten an. Zusätzlich wird noch ein wenig der Ursprung der Anfrage überprüft.
%
\begin{center}
\begin{lstlisting}[caption={Fetch, Promise/Then-Codestruktur},captionpos=b]
/* Promise / Then */
self.addEventListener('fetch', function(event) {
/* We should only cache GET requests */
if (event.request.method !== 'GET') {
/* If we don't block the event as shown below,
then the request will go to the network as usual. */
console.log('[ServiceWorker] Fetch event ignored.',
event.request.method, event.request.url);
return;
}
event.respondWith(
caches.match(event.request)
.then(function(response) {
return fetch(event.request)
.then(function(response) {
// Check if response is valid, status is 200, response type is basic
// (indicates request is from origin, means that requests to third party
// assets aren't cached as well.
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
caches.open(CACHE_NAME)
.then(function(cache) {
// We have to clone the response here because request bodies can only
// be read once. Placing a response in the cache counts as a read.
cache.put(event.request, response.clone());
});
// Cache hit - return response
if (response) return response;
return response;
});
})
.catch(function(err) {
// Report a lack of connectivity to the user.
});
});
\end{lstlisting}
\end{center}
%
\begin{center}
\begin{lstlisting}[caption={Fetch, Async/Await-Codestruktur},captionpos=b]
/* Async / Await */
self.addEventListener('fetch', event => {
/* We should only cache GET requests */
if (event.request.method !== 'GET') {
/* If we don't block the event as shown below,
then the request will go to the network as usual. */
console.log('[ServiceWorker] Fetch event ignored.',
event.request.method, event.request.url);
return;
}
event.respondWith(async function update() {
try {
var requestURL = new URL(event.request.url);
// Start the network request as soon as possible.
const networkPromise = fetch(requestURL);
const cachedResponse = await caches.match(event.request);
const networkResponse = await networkPromise;
// Check if response is valid, status is 200, response type is basic
// (indicates request is from origin, means that requests to third party
// assets aren't cached as well.
if (!networkResponse || networkResponse.status !== 200
|| networkResponse.type !== 'basic') return networkResponse;
const cache = await caches.open(staticCacheKey);
// We have to clone the response here because request bodies can only
// be read once. Placing a response in the cache counts as a read.
cache.put(event.request, networkResponse.clone());
if (cachedResponse) return cachedResponse;
// ServiceWorker fetch
return networkResponse;
} catch (err) {
// Report a lack of connectivity to the user.
}
}());
});
\end{lstlisting}
\end{center}
%
Die Bildausschnitte mit dem Code wurden teils von mir erstellt, teils stammen diese von dieser Internetseite\cite{dev:codeSource}\cite{sw:cacheThenNetwork}.
%
\pagebreak
\section*{13. ''Add-To-Homescreen''-Funktion}
\phantomsection\addcontentsline{toc}{section}{13. ''Add-To-Homescreen''-Funktion}
Für die Implementierung der ''Add-to-Homescreen''-Funktionalität sind für Android, iOS und die diversen Browser unterschiedliche Schritte nötig.\\
Bei Android genügt es eine manifest.json Datei im root-Verzeichnis der App abzulegen. Diese Manifest-Datei enthält Meta-Informationen von App-Icon, über App-Name, über die Start-URL bis hin zu der Theme-Farbe für den Hintergrund des App-Icons oder dem Navigationsmenü im Browser. Wenn ein Nutzer im Abstand von 5 Minuten (Standard-Einstellung) die Webseite zweimalig aufruft, so wird dieser gefragt, ob er die App zum Homescreen hinzufügen möchte. Ab da kann er diese über das App-Icon als auch über den Browser starten.\\
Für iOS müssen die Meta-Informationen im Bereich des Head-Elements, der index.html eingefügt werden. So wird dort das Aussehen von mehreren App-Icon Versionen definiert. Dies wird benötigt, da die meisten Apple-Geräte unterschiedliche Density (Pixel per Inch) haben. Des Weiteren werden Konfigurationseinstellungen aktiviert oder deaktiviert, als auch das Aussehen des Ladescreens für iOS Geräte festgelegt.\\
Auch die Windows-Mobilgeräte muss in der index.html Datei im Head-Bereich mittels Meta-Informationen mitgeteilt werden, wie der Ladebildschirm oder das Appicon aussehen soll (vgl.\cite{dev:homescreen}).
%
\section*{14. Erstellung der API-Schnittstelle}
\phantomsection\addcontentsline{toc}{section}{14. Erstellung der API-Schnittstelle}
Nachdem Server und die Clientseite der PWA funktionsfähig waren, war es noch relevant, dass zwischen diesen beiden kommuniziert werden kann. Dafür verwendet man die API.\cite{api} API steht für ''Application Programming Interface'' und diese Programmierschnittstelle kann wie in diesem Projekt zum Zugriff auf die Datenbank verwendet werden. Dabei handelt es sich in diesem Fall um einen RESTful Webservice.\cite{rest} \\
%
\begin{figure}[H]
\centering
\includegraphics[width=0.3\textwidth]{ajax.png}
\caption{Ajax Modell einer Webanwendung. Quelle: nach Haischt, Daniel; https://de.wikipedia.org/wiki/Ajax\_(Programmierung)\#/media/File:Ajax-vergleich.svg}
\label{ajax}
\end{figure}
%
\noindent Das asynchrone Datenübertragungskonzept AJAX wurde zur Kommunikation zwischen Browser und Server verwendet.\cite{ajax1} \cite{ajax2} \cite{ajax3} Dabei werden wie in Abbildung \ref{ajax} zu sehen ist HTTP Requests von dem Browser an den Server geschickt. Diese werden daraufhin von dem Server verarbeitet und es werden entweder Daten zurückgegeben oder ein HTTP Response mit einer Rückmeldung. Mit der Verwendung von AJAX hat man einige Vorteile, zum Beispiel, dass man die Seite nicht neu laden muss, damit die neuen Inhalte erscheinen und dass der Browser bereits weiter Requests schicken kann, ohne auf die Responses des Servers warten zu müssen. Da die gesamte PWA des Projekts in JavaScript geschrieben ist, ist es zudem vorteilhaft, dass die AJAX-Engine in JavaScript geschrieben wird. Die Daten aus der Datenbank werden im JSON-Format transportiert.\\
\\
Um die Funktionsweise einer derartigen Schnittstelle zu verstehen, wurde der Code einer PWA die Professor Matthias Hopf geschrieben und der Gruppe zur Verfügung gestellt hat, analysiert. Dieser war jedoch etwas zu komplex für einen Einsteiger in das Thema. Deswegen wurde auf simplere Beispiele in Tutorials zurückgegriffen. Diese Beispiele konnten nun auf das eigene Projekt übertragen und getestet werden. Nach einer ausführlichen Erläuterung durch Professor Hopf anhand eines Beispiels im Projektzusammenhang, war ein grundsätzliches Verständnis über die Schnittstelle vorhanden. Nun konnte die konkrete Umsetzung der API beginnen.\\
Die erste Methode die umgesetzt wurde war ''list\_messages''. Diese zeigt nun alle Nachrichten der Datenbank auf der Startseite der PWA an. Dazu musste zuerst vom Server die IDs aller Nachrichten abgefragt werden. Man schreibt eine API Funktion, die ein GET Request an den Server schickt. Dieser sucht nach den IDs per find Anfrage in der Datenbank und antwortet mit einem HTTP Response, der das Array mit den IDs enthält. Auf der Client Seite wird nun für jede ID per GET Request alle Informationen der Nachricht angefragt. Der Server sucht die gefragten Information und schickt diese zurück mit dem dazugehörigen Nachrichtentext an den Browser.\\
%
\begin{center}
\begin{lstlisting}[caption={''list\_messages''-Methode},captionpos=b]
list_messages: function () {
$.ajax({url: "api/ids", method: "GET"})
.done(jd => {
_messagelist.splice(0, _messagelist.length);
_messagelist.push.apply(_messagelist, jd);
console.log("jd: "+jd);
for (var e in jd) {
if (!_messages[jd[e]]) {
get_insert_message(jd[e]);
}
}
}).fail(function (e, f, g) {
console.log("err: " + e + f + g);
});
}
\end{lstlisting}
\end{center}
%
Was für Vorgänge beim Senden einer Nachricht in einer API getätigt werden, ist im Folgenden aufgeführt. Hierfür muss die Mongodb heruntergeladen und angebunden sein.\\
In der Server.js Datei wird durch die ''App.post''-Methode eine Nachricht erstellt. Dabei wird in ''createMsg'' die Funktion aufgerufen, in einen String umgewandelt und die Nachricht anschließend in der Datenbank gespeichert. Mit der POST-Methode können Nachrichten in die Datenbank gespeichert werden und anhand von einer GET-Methode angezeigt werden.\\
Nun müssen in der createMsg.js alle Werte einsetzbar gestaltet werden. Wichtig dabei ist, sich an das vorgegebene Mongo Schema zu halten. Durch die ajax-Methode ''post'' können große Datenmengen an den Server geschickt werden. Zuletzt soll die Konsole auch einen Error anzeigen, falls der Abruf der Funktion erfolglos war. \\
In Abbildung \ref{sys} kann man diese Vorgänge nachverfolgen. Diese beiden Funktionen haben am Ende funktioniert und können zusammen verwendet werden. \\
\begin{figure}[H]
\centering
\includegraphics[width=0.7\textwidth]{system.png}
\caption{Erstellen einer Nachricht und Anzeigen aller Nachrichten}
\label{sys}
\end{figure}
%
\section*{15. Aufsetzung des Servers}
\phantomsection\addcontentsline{toc}{section}{15. Aufsetzung des Servers}
Zunächst muss ein http port eingerichtet werden. Über einen lokalen Server über node.js ist die Ohm News-App abrufbar. Mit der createServer Methode
%
\begin{center}
\begin{lstlisting}[caption={''list\_messages''-Methode},captionpos=b]
http.createServer(app).listen(http_port, function () {
console.log("Express http server listening on port " + http_port);
});
\end{lstlisting}
\end{center}
%
lässt sich ein Server aufsetzen. Hier wird auch der Listener für den Port impliziert und auf der Konsole ausgegeben, wenn die Verbindung erfolgreich war. \\
Um die wichtigsten Fehlermeldungen und Bugs auszugeben, wird für unsere node.js App der Logger ''Morgan'' verwendet. Zusätzlich wird eine try-catch Funktion eingeordnet, da Zertifikat und Schlüssel noch nicht zertifiziert sind (siehe SSL und Keys) und somit vorerst ein Fehler angezeigt wird.
%
\subsection*{15.1. SSL und Keys}
\phantomsection\addcontentsline{toc}{subsection}{15.1. SSL und Keys}
Um die Applikation für die Nutzer sicher zu gestalten und eine Verschlüsselung sensibler Daten zu gewährleisten, sorgt ein SSL(Secure Socket Layer). Dadurch wird ein sicherer Kanal zwischen Web-Server und Client bereitgestellt und aus der http- wird eine https-Verbindung.
\setlength{\parindent}{0cm}
Übergangsweise werden ein selbst verifizierter Schlüssel und Zertifikat erstellt, welche später durch echte ausgetauscht werden. In einem neu erstellten Ordner ''keys'' werden das Zertifikat und der Schlüssel platziert.
%
\section*{16. Datenbankanbindung}
\phantomsection\addcontentsline{toc}{section}{16. Datenbankanbindung}
Für diese Anwendung wird eine Datenbank benötigt, weil die geschriebenen Nachrichten gespeichert werden müssen. Später sollen auch User mit ihren Userinformationen gespeichert werden. Im Falle dieses Projekts wurde sich für eine MongoDB entschieden.\\
MongoDB ist eine dokumentenorientierte NoSQL Datenbank. Diese ist für diese Anwendung sinnvoll, da sie im Vergleich zu SQL Datenbanken flexibler handhabbar ist und somit auch viele Einstellungen noch im Nachhinein geändert werden können.\cite{mongo} \\
Um den Umgang mit MongoDB zu vereinfachen wurde Mongoose, eine Object Data Modeling Bibliothek, verwendet.\cite{mongoose} \\
Da man zu Beginn des Projekts noch nicht mit NoSQL Datenbanken gearbeitet hatte, mussten zuerst Tutorials und Erklärungen gelesen werden. Anhand von Beispieltests mit einer lokalen Datenbank konnte die grundsätzliche Funktionsweise bald verstanden werden. Daraufhin wurde das grobe Schema für eine Nachricht aufgebaut. Eine Nachricht sollte einen Betreff, eine Nachricht, den schreibenden User und die Tags beinhalten wie man in Listing \ref{lst:json} an einer beispielhaften JSON-Datei sehen kann. Die Datenbank musste konfiguriert und gestartet werden.
%
\begin{center}
\lstset{language=xml}
\begin{lstlisting}[caption={Nachricht im JSON-Format},captionpos=b,label=lst:json]
{
"messages": [{
"subject": "Test"
"message": "Hallo Welt!"
"tag": "#OHM_News"
"user": "User1"
}]
}
\end{lstlisting}
\lstsetdefault
\end{center}
%
Die serverseitigen Codeteile zur API und der Code für die Datenbank sind momentan zur Vereinfachung in der server.js Datei. Später sollen diese zur Übersichtbarkeit und Wartbarkeit ausgelagert werden. Das Schema für die Nachricht und die Konfiguration der Datenbank sind zu diesem Zweck bereits in eigenen js-Dateien zu finden.\\
Sobald all das lokal funktioniert hat, wurde die Portierung auf einen Server der Hochschule vorgenommen.
%
\section*{17. Plakatgestaltung}
\phantomsection\addcontentsline{toc}{section}{17. Plakatgestaltung}
Zu dem Plakat für die OHM News App wurde eine Ideensammlung in Form von Skizzen angefertigt.
\begin{figure}[H]
\centering
\includegraphics[width=6.5cm, angle=90, scale=1.409]{IMG_Skizze1.jpg}\quad
\includegraphics[width=6.5cm]{IMG_Skizze2.jpg}
\includegraphics[width=6.5cm, angle=270, scale=1.409]{IMG_Skizze3.jpg}\quad
\includegraphics[width=6.5cm, angle=270, scale=1.409]{IMG_Skizze4.jpg}
\end{figure}
%
Die Umsetzung derer folgte in Form von ersten Entwürfen in Illustrator, welche der gesamten Gruppe vorgestellt wurden. \\
Zu den hier unten aufgeführten Plakatentwürfen wurden jeweils zwei Versionen erstellt. Diese zwei kamen jedoch in die engere Auswahl und sollten noch weiter ausgearbeitet werden.
\begin{figure}[H]
\centering
\includegraphics[width=6.5cm]{PDF_Plakat1.pdf}\quad
\includegraphics[width=6.5cm]{PDF_Plakat2.pdf}
\end{figure}
%
Bei der nächsten Vorauswahl an fünf OHM News Plakat-Versionen wurde daraufhin auf zwei reduziert, welche hier zu sehen sind.
\begin{figure}[H]
\centering
\includegraphics[width=6.5cm]{PDF_Plakat3.pdf}\quad
\includegraphics[width=6.5cm]{PDF_Plakat4.pdf}
\end{figure}
%
Diese wurden weiter bearbeitet und Änderungen in Absprache mit der Projektgruppe vor\-genommen. Auch die Meinung von Professor Dr. Hopf beeinflusste die Plakatgestaltung.\\
Einige Ansätze später und nach etlichem Experimentieren stand die Endauswahl bei diesen nächsten zwei Exemplaren. Besonderen Zeitaufwand nahm die Suche nach einer geeigneten Schrift für die zweite Version in Anspruch. Es gestaltete sich als äußerst schwierig, den künstlerischen leicht verspielten Ausdruck des Plakatlayouts mit der Seriösität des Hochschulcharakters in einer Schrift zu vereinen.
\begin{figure}[H]
\centering
\includegraphics[width=6.5cm]{PDF_Plakat5.pdf}\quad
\includegraphics[width=6.5cm]{PDF_Plakat6.pdf}
\end{figure}
%
Zuletzt fiel die Wahl auf das linke mit blauem Verlauf versehene Plakat. Denn es vermittelt genau das, was der Slogan des Projektes aussagt. Der Fokus liegt auf der Nachrichtenbox, also das was wichtig ist, und der Rest verschwimmt im Hintergrund. \\
Bei den wöchentlichen Treffen wurde im Allgemeinen der Fortschritt des Plakatdesigns analysiert und mit konstruktiver Kritik versehen. Dieses Feedback wurde im Rahmen des Möglichen umgesetzt und so kristallisierte sich die finale Version heraus. Nach gründlichem Finetuning und einer Umformatierung von DIN A3 auf DIN A1 wurde die Endfassung per Mail eingeschickt und schließlich ausgedruckt. Im Anhang ist sie in voller Auflösung aufzufinden.
%
\section*{18. PowerPoint Präsentation}
\phantomsection\addcontentsline{toc}{section}{18. PowerPoint Präsentation}
Als sich das Projekt für dieses Semester zum Ende hinneigte, war es an der Zeit sich für die Projektpräsentation vorzubereiten. Hierfür erstellte ich für die Gruppe eine PowerPoint Präsentation. Zunächst besprachen wir Gemeinschaftlich, wie diese ungefähr aussehen sollte. Wir waren alle einstimmig der Meinung, dass wir die Präsentation sehr einfach gestalten wollen, ohne zu lange Textpassagen mit einzubinden. Wir wollten es nämlich verhindern, dass das Publikum von zu vielen Faktoren in der Darstellung abgelenkt werden, zum Beispiel durch lesen von langem Fließtext. Durch den Gebrauch von bildlicher Veranschaulichung, versuchten wir es den Zuschauern zu ermöglichen, das Verständnis zu erleichtern.
Auch hatten wir die Idee, eine fiktive Persona zu erstellen, zu dem der Betrachter einen Bezug finden kann und diese durch das vorgetragene Thema begleitet.
%
\subsection*{18.1. Gestaltungselemente}
\phantomsection\addcontentsline{toc}{subsection}{18.1. Gestaltungselemente}
Da man jedoch, aus Urheberrechtlichen Gründen, nicht einfach wahllos Charaktere aus dem Internet benutzen kann, suchte ich nach einer Seite, auf der man solche Animationen erstellen kann. Ich bin auch direkt fündig geworden, trotz alle dem war es erstmal schwer etwas zu finden das man auch Gratis verwenden konnte.
Dann bin ich auf die Seite www.Powtoon.com gestoßen. Dort fing ich als nächstes an, unseren sogenannten Klaus in all den Stimmungen, die wir für unsere Präsentation brauchten, zu gestalten. \\
Wir entschieden im Vorfeld als Gruppe, dass sich unser Männchen so wenig wie möglich bewegen soll, aus dem gleichen Grund wie auch oben schon erwähnt und zwar um den Fokus nicht zu sehr darauf zu richten und das Publikum nicht zu sehr abzulenken. Nachdem das erledigt war, war es an der Zeit, die eben erstellten Videos von unserer Persona einzeln so zu kürzen und zu beschneiden, dass man sie gut auf die gewünschte Position einsetzen konnte. Als dieses erledigt wurde, besprach ich weitere Vorgehen einzeln mit den anderen Gruppenmitgliedern. Ich Informierte mich darüber, über welches Thema jeder einzelne vorträgt und wie sie sich ihren Präsentationsteil vorstellen. Diese Wünsche und Vorstellung setzte ich dann dementsprechend in unserer Präsentation um.
Falls sie an dem Ergebnis interessiert sind, haben sie die Möglichkeit sich dies im Git-Repository im Ordner ''presentation'' anzusehen.
%
\section*{19. Weiterverwendung für Infoscreens}
\phantomsection\addcontentsline{toc}{section}{19. Weiterverwendung für Infoscreens}
Am Mittwoch, den 24.10.2018 fand das Info-Treffen mit Frau Fabi statt, welches von Prof.Dr. \mbox{Matthias} Hopf organisiert wurde. Dieses Treffen diente der Besprechung und Erörterung zukünftiger Nutzungsmöglichkeiten der Info-Screens, das sind geplante und teils bereits montierte Bildschirme zur Information-Bereitstellung für die Hochschulangehörigen auf dem Campusgelände. Frau Fabi ist Angehörige der Fakultät efi und in ihrer Funktion für die Dokumentenlenkung und Webredaktion an dem ''Tag''-basiertem Nachrichten-System unserer Webanwendung interessiert, welches zukünftig auch auf den Bildschirmen dargestellt werden könnten. Sie ließ auch das Interesse von Herr Pöhlau, dem Dekan der Fakultät efi bekunden.\\
Außerdem informierte Frau Fabi uns, die Projektmitglieder und unseren Projektbetreuer, dass die Fakultät efi sicher die restlichen Info-Screens montieren werde und derzeit Besprechungen hinsichtlich weiterer Sicherheitsaspekte zu den Montagebereichen am Laufen sind. Ergänzend würden sie sich eine Softwarelösung wünschen, für die Ansteuerung und ''Befüllung'' der Monitore mit den Daten unserer App. Diese wird jedoch nicht mehr im Rahmen unseres Projektes umgesetzt werden. Die Umsetzung einer Softwarelösung wäre als Idee für ein zukünftiges Projekt denkbar, nach Aussage von Prof.Dr. Matthias Hopf.
%
\section*{20. Soll/Ist Vergleich auf Basis des Projektplanes}
\phantomsection\addcontentsline{toc}{section}{20. Soll/Ist Vergleich auf Basis des Projektplanes}
Zu Beginn des Semesters hat die Gruppe zusammen ein Pflichtenheft für den ersten Abschnitt des Projekts erstellt. Ziel war es den Datenzugriff zu ermöglichen und eine Schnittstelle zur Verfügung zu stellen. Es besteht eine Datenbankanbindung über den Server der Technischen Hochschule Nürnberg. Darüber hinaus sollten die folgenden Ziele im Bereich der grafischen Benutzerschnittstelle erreicht werden.\\
Die Oberfläche der PWA sollte über eine Login Möglichkeit, eine Menüleiste, eine Suchoption, ein Profil, eine Filterfunktion, ein Dashboard als Hauptseite, ein Einstellungsmenü, Nachrichtenfelder und ein Formular zum Verfassen einer Nachricht verfügen. Die Login Funktion ist noch nicht implementiert und die Filterfunktion soll im nächsten Semester in das Suchfeld und die Profilanzeige eingebaut werden. Die restlichen Funktionen sind in der PWA grafisch umgesetzt worden. Es sollte zusätzlich ein Abhängigkeitsdiagramm erstellt werden. Allerdings war dies aufgrund fehlender komplexer Zusammenhänge noch nicht notwendig. Für die API-Schnittstelle wurde eine Dokumentation erstellt, um den Überblick zu bewahren. Im Bereich der Anwendungslogik wurde das Routing, die Darstellung der Nachrichten, die Speicherung der Nachrichten in der Datenbank und der LDAP-Login besonders behandelt. Das Routing sowie die Erstellung, Speicherung und Darstellung der Nachrichten funktioniert. Die Login Funktion wurde noch nicht näher betrachtet. Codetests wurden bisher noch keine durchgeführt. Das Rollenkonzept der Nutzer wurde bereits besprochen, allerdings noch nicht umgesetzt.\\
Darüber hinaus gibt noch einige nachrangige Punkte. Dazu gehört die Festlegung des Anwendungsnamens, die Erstellung eines Logos, das UX-Konzept, das Filtern der Datenbankabrufe, das Festlegen der Anzeigekriterien und die Funktion einem Kanal zu folgen. Wie oben bereits erwähnt wird das Filtern der Abrufe im nächsten Semester umgesetzt. Auch die Anzeigekriterien und die Möglichkeit einem Kanal zu folgen wird erst zu einem späteren Zeitpunkt möglich sein.
%
\section*{21. Fazit}
\phantomsection\addcontentsline{toc}{section}{21. Fazit}
Im ersten Projektabschnitt ist viel Zeit ist in der Konzeptionsphase vergangen, da es ein sehr iterativer Prozess ist und deshalb mehrere Durchläufe bis zum Endentwurf nötig waren. Verschiedene Meinungen mussten mit einbezogen werden. Doch ein gutes Konzept erspart am Ende viel Änderungsarbeit. \\
Für die weitere Erarbeitung der App haben sich die Projektmitglieder auf die Bereiche Front\-end, Datenbank, Sever und Service-Worker aufgeteilt. Diese Aufgaben\-verteilung hat gut funktioniert. Trotzdem ist unser Team sehr oft ins Stocken geraten, da sich die Aufgaben\-bereiche gerade am Anfang oft überschnitten haben. Hinzukommt, dass wir insgesamt \mbox{wenig} Erfahrung in diesen Bereichen hatten. Wir haben uns deshalb zu Beginn intensiv in die Themen eingearbeitet, was länger dauerte als erwartet. Zusätzlich mussten wir auf \mbox{fachliche} Unterstützung zurückgreifen.\\\\
Alles in einem kann man trotzdem von einem erfolgreichen Projektabschnitt sprechen. Ein erster funktionierender Prototyp ist fertig und kann im nächsten Zug erweitert werden. Parallele individuelle Arbeiten sind einfacher möglich, da das Grundgerüst steht. Das Team hat sich in die Materie eingearbeitet und kann im zweiten Projektabschnitt schneller durchstarten. Die Idee und das Konzept für die App sind soweit ausgereift, sodass diese nur noch umgesetzt werden müssen. Abläufe innerhalb der Gruppe sind klarer geworden, allerdings gibt es noch einiges strukturelle Abläufe, die im zweiten Projektabschnitt weiter optimiert werden können.
%
% Literaturverzeichnis
\newpage
\section*{Quellen- und Literaturverzeichnis}
\phantomsection\addcontentsline{toc}{section}{Quellen- und Literaturverzeichnis}
\medskip
%\nocite{*} % Alle Inhalte zur Ausgabe markieren, nicht nur jene die zitiert wurden.
{\setstretch{1.0}%
\printbibliography[heading=none]}
%
\end{document}

View File

@ -0,0 +1,171 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% defaultPreamble.sty %%
%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Setup a preamble for document class: article %%
%% 1.Pagesettings: %%
%% - DIN A4 Paper %%
%% - Margin: Top = 20mm %%
%% Right = 20mm %%
%% Bottom = 30mm %%
%% Left = 20mm %%
%% - Header Separation = 10mm %%
%% - Footer Separation = 10mm %%
%% - Paragraph Spacing = 1.5 * line height %%
%% %%
%% 2.Redefine Pagelayout %%
%% - Remove printed page numbers on page 1. %%
%% - Page 1+2 counts for the table of content. %%
%% - Print the page numbering like "Seite" a "von" lastpage %%
%% to the right bottom corner. %%
%% %%
%% 3.Contain Packages %%
%% - geometry = Set page settings. %%
%% - setspace = Set linespacing, prevent math formula %%
%% streching. %%
%% - pageslts = Get last page number. %%
%% - fancyhr = Set header, footer and page layout. %%
%% - babel = Set hyphenation rules to [ngerman]. %%
%% - inputenc = Set input encoding to UTF-8 (Umlaute etc.). %%
%% - ClearSans = Provide fontfamily ClearSans. %%
%% - helvet = Set fontfamily to Helvetica (sans serif). %%
%% - fontenc = Set font encoding to 8bit (support <,=,>,"). %%
%% - xcolor = Provides support of font colors. %%
%% - upquote = Provides support quotes in verbatim and listings. %%
%% - listings = Provides support of indention and code colering. %%
%% - float = Provides support of fiqures and environment float.%%
%% - hyperref = Provides support of links (pdf anchor), URL. %%
%% - graphicx = Provides support to add images. %%
%% - tabularx = Provides advanced tabular support. %%
%% - pdfpages = Provides support to include pdf pages. %%
%% - amsmath = Provides advanced math typesetting support. %%
%% - amsfonts = Provides advanced font typesetting support. %%
%% - amssymb = Provides advanced symbol typesetting support. %%
%% - blindtext = Provides support to use Lorem Ipsum text. %%
%% - enumitem = Provides support change enum type in options. %%
%% - biblatex = Set biblatex for citation and bibliography %%
%% with biber as backend and the %%
%% bibliography style 'ieee'. %%
%% %%
%% 4.Defines New Commands %%
%% - \includePdf{filename} = Include a PDF file. %%
%% - \bibfilename{file.bib} = Set bib file. %%
%% - \printbib = Print bibliography. %%
%% - \b{text} = Set text to bold format. %%
%% - \mono{text} = Set text to monospace. %%
%% - \minsep = Set item spacing to minimum. %%
%% - \clear = Set fontfamily to ClearSans. %%
%% Default fontfamily is Helvetica. %%
%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
\NeedsTeXFormat{LaTeX2e}%
\ProvidesPackage{defaultPreamble}[2019/03/04 v1.4 DefaultPreamble Package from Erik Roemmelt]%
\ProcessOptions\relax%
%%% Packages %%%
% ___Document___
\RequirePackage[a4paper, left=20mm, right=20mm, top=20mm, bottom=30mm, headsep=10mm, footskip=10mm]{geometry} % Set page settings
\RequirePackage[nodisplayskipstretch]{setspace} % Set linespacing and prevent math formula streching
\setstretch{1.5} % Change typeset line spacing by multiplicator 1.0
\RequirePackage[pagecontinue=false,alphMult=0,AlphMulti=0,fnsymbolmult=false,romanMult=false,RomanMulti=false]{pageslts} % Get last page number with \lastpageref{LastPage}
\RequirePackage{fancyhdr} % Set header, footer and page layout.
\pagestyle{fancy} % Set the pagestyle to fancyhdr settings
%%% Fancyhdr Settings %%%
\fancyhf{} % Clear all header and footer fields
\fancyfoot[R]{\ifnum\value{page}<1\relax\else{Seite \thepage \hspace{1pt} von \lastpageref{VeryLastPage}}\fi} % Set page numbering "n / last" in the right field of footer
\renewcommand{\headrulewidth}{0pt} % Set header ruler hidden
\renewcommand{\footrulewidth}{0pt} % Set footer ruler hidden
\fancypagestyle{plain}{% Redefine plain style
\fancyhf{} % Clear all header and footer fields
\fancyfoot[R]{\ifnum\value{page}<2\relax\else{Seite \thepage \hspace{1pt} von \lastpageref{VeryLastPage}}\fi} % Set page numbering "n / last" in the right field of footer, skip page numbering on page 1+2
\renewcommand{\headrulewidth}{0pt} % Set header ruler hidden
\renewcommand{\footrulewidth}{0pt} % Set footer ruler hidden
}%
% ___Font___
\RequirePackage[ngerman]{babel} % Changes hyphenation rules
\RequirePackage[utf8]{inputenc} % Input encoding: Unicode support (Umlaute etc.)
\RequirePackage{ClearSans} % Provide fontfamily Clear Sans (sans serif) for title page
\RequirePackage[scaled]{helvet} % Scales fontfamily Helvetica to .95 (sans serif)
\renewcommand{\familydefault}{\sfdefault} % Changes default font to sans serif Helvetica
%\RequirePackage[default]{sourcesanspro}
%\RequirePackage[sfdefault]{FiraSans} % option 'sfdefault' activates Fira Sans as the default text font
%\renewcommand*\oldstylenums[1]{{\firaoldstyle #1}}
%\RequirePackage[sfdefault,light]{roboto} % Option 'sfdefault' only if the base font of the document is to be sans serif
\RequirePackage[T1]{fontenc} % Font encoding: (8bit) Accents support (<,=,>,",...)
\RequirePackage[dvipsnames]{xcolor} % Provides font coloring
\definecolor{editorGray}{rgb}{0.95, 0.95, 0.95} % Defines a new Color: gray
\definecolor{editorOcher}{rgb}{0.8, 0.4, 0} % #CC6600 -> rgb(204, 102, 0) % New Color Ocher
\definecolor{editorGreen}{rgb}{0, 0.5, 0} % #007C00 -> rgb(0, 124, 0) % New Color Green
\RequirePackage{upquote} % Allow quotes in verbatim and lstlisting
\RequirePackage{listings} % Allow indention and code colering
\lstdefinelanguage{JavaScript}{% Defines Javascript for lstlisting
morekeywords={typeof,new,true,false,catch,function,return,null,catch,switch,var,if,in,while,do,else,case,break},morecomment=[s]{/*}{*/},morecomment=[l]//,morestring=[b]",morestring=[b]'}%
\lstdefinelanguage{HTML5}{% Redefines HTML for lstlisting
language=html,sensitive=true,alsoletter={<>=-},otherkeywords={% HTML tags
<html>,<head>,<title>,</title>,<meta,/>,</head>,<body>,<canvas,\/canvas>,<script>,</script>,</body>,</html>,<!,html>,<style>,</style>,><},%
ndkeywords={%
% General
=,%
% HTML attributes
charset=,id=,width=,height=,%
% CSS properties
border:,transform:,-moz-transform:,transition-duration:,transition-property:,transition-timing-function:},%
morecomment=[s]{<!--}{-->},tag=[s]}%
\newcommand{\lstsetdefault}{%
\lstset{%
% Basic design
backgroundcolor=\color{editorGray},basicstyle={\small\ttfamily},frame=single,columns=fullflexible,%
% Line numbers
xleftmargin={0.75cm},numbers=left,stepnumber=1,firstnumber=1,numberfirstline=true,%
% Code design
keywordstyle=\color{blue}\bfseries,commentstyle=\color{darkgray}\ttfamily,ndkeywordstyle=\color{editorGreen}\bfseries,stringstyle=\color{editorOcher},%
% Code
language=HTML5,alsolanguage=JavaScript,alsodigit={.:;},tabsize=4,showtabs=false,showspaces=false,showstringspaces=false,extendedchars=true,breaklines=true,%
% Support for German umlauts
literate=%
{Ö}{{\"O}}1%
{Ä}{{\"A}}1%
{Ü}{{\"U}}1%
{ß}{{\ss}}1%
{ü}{{\"u}}1%
{ä}{{\"a}}1%
{ö}{{\"o}}1%
}}%
\lstsetdefault%
% ___Other___
\RequirePackage{float} % Allow fiqures and enviroments to float
\RequirePackage[hidelinks,pdfpagelabels=true]{hyperref} % Add a link to your document
\RequirePackage{graphicx} % Add pictures to your document
\RequirePackage{tabularx} % Advanced tabular support
\RequirePackage{pdfpages} % Allows to include pdf pages
\RequirePackage{amsmath} % Advanced math typesetting
\RequirePackage{amsfonts} % Advanced font typesetting
\RequirePackage{amssymb} % Advanced symoblic typesetting
\RequirePackage{blindtext} % Blindtext, Lorem Ipsum text
\RequirePackage[shortlabels]{enumitem} % Allows to change enumerate type easily
% __Bibliography___
\RequirePackage[backend=biber,style=ieee,sorting=nyt]{biblatex} % Use biblatex package
%%% New Commands %%%
% New command for closer spacing in list
\newcommand{\minsep}{\topsep=0pt\itemsep=0pt\parsep=0pt\partopsep=0pt}%
% Add new command to include a pdf file
\newcommand*{\@includePdf}{%
\PackageWarning{defaultPreamble}{PDF-File does not exist}%
NoN%
}%
\newcommand{\includePdf}[1]{\includepdf[pages=-,fitpaper=true,frame=false,pagecommand=\thispagestyle{plain}]{#1}}% Insert the name of the pdf file
% Add new command to set file name for *.bib file
\newcommand*{\@bibfilename}{%
\PackageWarning{defaultPreamble}{Bib-File-Name undefined}%
NoN%
}%
\newcommand{\bibfilename}[1]{\addbibresource{#1}}% Insert the name of the BibLatex file
% Add new command to print the bibliography
\newcommand{\printbib}{\printbibliography[heading=bibintoc]}% Insert the name of the BibLatex file
% Add new commant to shorten text format command: \textbf
\renewcommand{\b}{\textbf}%
% Add new commant to shorten text format command: \texttt
\newcommand{\mono}{\texttt}%
\endinput % Das ist das Ende von dieser .sty Datei

37
ldap_test.js Normal file
View File

@ -0,0 +1,37 @@
// Terminal call: node server/ldap_test.js - needs VPN or eduroam
const inquirer = require('inquirer'),
ldap = require('./server/ldap_ohm.js');
inquirer.prompt([
{
name: 'username',
type: 'input',
message: 'Enter your VirtuOhm username:',
validate: function( value ) {
if (value.length) {
return true;
} else {
return 'Please enter your username.';
}
}
},
{
name: 'password',
type: 'password',
message: 'Enter your password:',
validate: function(value) {
if (value.length) {
return true;
} else {
return 'Please enter your password.';
}
}
}])
.then(answers => {
ldap.init(null);
ldap.authorize(answers.username,answers.password,function(user) {
console.log(JSON.stringify(user));
process.exit();
});
});

View File

@ -1,20 +0,0 @@
const mongoose = require('mongoose');
//const Schema = mongoose.Schema,
//ObjectId = Schema.ObjectId;
const MessageSchema = mongoose.Schema({
//_id: ObjectId,
/*id: {
type: String,
getter: function(val) { return this._id.toString(); },
unique: true
},*/
//id: {type: String, required: true},
subject: { type: String, required: true },
message: { type: String, required: true },
user: { type: String, required: true },
tag: {type: String },
});
module.exports = mongoose.model('Message', MessageSchema);

22
mong.js Normal file
View File

@ -0,0 +1,22 @@
var db = db.getSiblingDB('omdb');
// Delete all collections + all records
db.messages.deleteMany({});
db.tags.deleteMany({});
db.users.deleteMany({});
db.sessions.deleteMany({});
// Insert data in collections
load('mong_msg.js');
load('mong_tag.js');
load('mong_usr.js');
// Print all collections + records count
var collections = db.getCollectionNames();
print('Collections inside the db:');
for(var i = 0; i < collections.length; i++){
var name = collections[i];
if(name.substr(0, 6) != 'system')
print(name + ' - ' + db[name].count() + ' records');
}

9
mong_msg.js Normal file
View File

@ -0,0 +1,9 @@
db.messages.insert([
{"subject":"DOLORE LABORIS AUTE","message":"Ex sunt elit elit in ex ad labore minim. Elit amet irure sit aliquip excepteur sit qui et aute qui mollit cillum.","user":"Jaime Potter","tag":["sit","in","dolore","ullamco"]},
{"subject":"NON IPSUM MOLLIT","message":"Ut laborum proident magna magna qui non in voluptate reprehenderit labore. Qui consectetur dolor nisi culpa cillum quis.","user":"Goff Durham","tag":["aliqua","sunt","labore","minim"]},
{"subject": "ESSE IPSUM INCIDIDUNT","message":"Esse enim amet laboris proident do do ut cupidatat laborum ex. Sit esse id laborum aliqua id.","user":"Helena Mercado","tag":["fugiat","nisi","incididunt","ullamco"]},
{"subject":"PARIATUR DOLOR SUNT","message":"Exercitation officia ad consequat est voluptate est incididunt ut culpa consectetur. Consectetur irure veniam ipsum sint sit anim minim aute exercitation ullamco nostrud reprehenderit consequat.","user":"Christi Walker","tag":["consectetur","sint","qui","ipsum"]},
{"subject":"TEMPOR ID VOLUPTATE","message":"Anim ex commodo officia consequat in magna esse sunt mollit sunt commodo. Ex sit aute reprehenderit elit magna sunt irure pariatur cupidatat labore.","user":"Kent Schroeder","tag":["adipisicing","nulla","tempor","veniam"]},
{"subject":"LOREM EST AUTE","message":"Irure excepteur laborum quis tempor officia cillum officia est ea adipisicing duis enim aliqua. Est dolor laborum officia elit ullamco.","user":"Dominguez Stevens","tag":["mollit","amet","consectetur","dolore"]},
{"subject":"ESSE MOLLIT CILLUM","message":"Eu culpa laborum consequat sunt aliqua excepteur. Voluptate pariatur deserunt consequat id nostrud do magna occaecat sint nulla non nostrud.","user":"Terrell Padilla","tag":["Lorem","consequat","fugiat","aliqua"]}
])

3
mong_tag.js Normal file
View File

@ -0,0 +1,3 @@
db.tags.insert([
{"name":"efi"},{"name":"wichtig"},{"name":"Wintersemester19/20"},{"name":"Rückmeldung"},{"name":"bme6"},{"name":"Mkz"},{"name":"Freizeit"},{"name":"Prüfungsanmeldung"},{"name":"THNürnberg"},{"name":"Prüfung"},{"name":"Stundenplan"}
])

11
mong_usr.js Normal file
View File

@ -0,0 +1,11 @@
db.users.insert([
// User: author, Pwd: author
{"_id":"author","name":"Author","type":"MA@EFI;","roles":'{"user":true,"author":true}',"hash":"sha256","salt":"SIzKNsNKsCRVr8a9U90q6A==","pwd":"HZly68TSAKHioz6Kz0QCbXVTHpT6hMmabUbFYVlCMeE=","abos":["nulla","tempor","veniam","ipsum"],"bookmarks":[],},
// User: admin, Pwd: SwenMho
{"_id":"admin","name":"Test Admin","type":"MA@AMP;","roles":'{"user":true,"author":true,"admin":true}',"hash":"sha256","salt":"z3PNXGmQaWvaT7m2ZlT+0w==","pwd":"nfUfNv032J745xj3Hzya3Mkk43Dz/H0BmNTZhtx8UM0=","abos":["veniam","ipsum"],"bookmarks":[],},
])
//var c = require('./server/crypto.js'),
// e = {"_id":"writer","hash":"sha256"};
//c.fillLocalAuth(e,"pwd");
//console.info(JSON.stringify(e));

View File

@ -1,3 +0,0 @@
module.exports = {
url: 'mongodb://localhost:27017/mydb'
}

1
node_modules/.bin/acorn generated vendored
View File

@ -1 +0,0 @@
../acorn/bin/acorn

1
node_modules/.bin/bunyan generated vendored Symbolic link
View File

@ -0,0 +1 @@
../bunyan/bin/bunyan

1
node_modules/.bin/eslint generated vendored
View File

@ -1 +0,0 @@
../eslint/bin/eslint.js

1
node_modules/.bin/esparse generated vendored
View File

@ -1 +0,0 @@
../esprima/bin/esparse.js

1
node_modules/.bin/esvalidate generated vendored
View File

@ -1 +0,0 @@
../esprima/bin/esvalidate.js

1
node_modules/.bin/js-yaml generated vendored
View File

@ -1 +0,0 @@
../js-yaml/bin/js-yaml.js

1
node_modules/.bin/ldapjs-add generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ldapjs/bin/ldapjs-add

1
node_modules/.bin/ldapjs-compare generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ldapjs/bin/ldapjs-compare

1
node_modules/.bin/ldapjs-delete generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ldapjs/bin/ldapjs-delete

1
node_modules/.bin/ldapjs-modify generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ldapjs/bin/ldapjs-modify

1
node_modules/.bin/ldapjs-search generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ldapjs/bin/ldapjs-search

1
node_modules/.bin/ncp generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ncp/bin/ncp

1
node_modules/.bin/which generated vendored
View File

@ -1 +0,0 @@
../which/bin/which

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014-2018 Sebastian McKenzie <sebmck@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,19 +0,0 @@
# @babel/code-frame
> Generate errors that contain a code frame that point to source locations.
See our website [@babel/code-frame](https://babeljs.io/docs/en/next/babel-code-frame.html) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/code-frame
```
or using yarn:
```sh
yarn add @babel/code-frame --dev
```

View File

@ -1,173 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.codeFrameColumns = codeFrameColumns;
exports.default = _default;
function _highlight() {
const data = _interopRequireWildcard(require("@babel/highlight"));
_highlight = function () {
return data;
};
return data;
}
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
let deprecationWarningShown = false;
function getDefs(chalk) {
return {
gutter: chalk.grey,
marker: chalk.red.bold,
message: chalk.red.bold
};
}
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
function getMarkerLines(loc, source, opts) {
const startLoc = Object.assign({
column: 0,
line: -1
}, loc.start);
const endLoc = Object.assign({}, startLoc, loc.end);
const {
linesAbove = 2,
linesBelow = 3
} = opts || {};
const startLine = startLoc.line;
const startColumn = startLoc.column;
const endLine = endLoc.line;
const endColumn = endLoc.column;
let start = Math.max(startLine - (linesAbove + 1), 0);
let end = Math.min(source.length, endLine + linesBelow);
if (startLine === -1) {
start = 0;
}
if (endLine === -1) {
end = source.length;
}
const lineDiff = endLine - startLine;
const markerLines = {};
if (lineDiff) {
for (let i = 0; i <= lineDiff; i++) {
const lineNumber = i + startLine;
if (!startColumn) {
markerLines[lineNumber] = true;
} else if (i === 0) {
const sourceLength = source[lineNumber - 1].length;
markerLines[lineNumber] = [startColumn, sourceLength - startColumn];
} else if (i === lineDiff) {
markerLines[lineNumber] = [0, endColumn];
} else {
const sourceLength = source[lineNumber - i].length;
markerLines[lineNumber] = [0, sourceLength];
}
}
} else {
if (startColumn === endColumn) {
if (startColumn) {
markerLines[startLine] = [startColumn, 0];
} else {
markerLines[startLine] = true;
}
} else {
markerLines[startLine] = [startColumn, endColumn - startColumn];
}
}
return {
start,
end,
markerLines
};
}
function codeFrameColumns(rawLines, loc, opts = {}) {
const highlighted = (opts.highlightCode || opts.forceColor) && (0, _highlight().shouldHighlight)(opts);
const chalk = (0, _highlight().getChalk)(opts);
const defs = getDefs(chalk);
const maybeHighlight = (chalkFn, string) => {
return highlighted ? chalkFn(string) : string;
};
if (highlighted) rawLines = (0, _highlight().default)(rawLines, opts);
const lines = rawLines.split(NEWLINE);
const {
start,
end,
markerLines
} = getMarkerLines(loc, lines, opts);
const hasColumns = loc.start && typeof loc.start.column === "number";
const numberMaxWidth = String(end).length;
let frame = lines.slice(start, end).map((line, index) => {
const number = start + 1 + index;
const paddedNumber = ` ${number}`.slice(-numberMaxWidth);
const gutter = ` ${paddedNumber} | `;
const hasMarker = markerLines[number];
const lastMarkerLine = !markerLines[number + 1];
if (hasMarker) {
let markerLine = "";
if (Array.isArray(hasMarker)) {
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " ");
const numberOfMarkers = hasMarker[1] || 1;
markerLine = ["\n ", maybeHighlight(defs.gutter, gutter.replace(/\d/g, " ")), markerSpacing, maybeHighlight(defs.marker, "^").repeat(numberOfMarkers)].join("");
if (lastMarkerLine && opts.message) {
markerLine += " " + maybeHighlight(defs.message, opts.message);
}
}
return [maybeHighlight(defs.marker, ">"), maybeHighlight(defs.gutter, gutter), line, markerLine].join("");
} else {
return ` ${maybeHighlight(defs.gutter, gutter)}${line}`;
}
}).join("\n");
if (opts.message && !hasColumns) {
frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`;
}
if (highlighted) {
return chalk.reset(frame);
} else {
return frame;
}
}
function _default(rawLines, lineNumber, colNumber, opts = {}) {
if (!deprecationWarningShown) {
deprecationWarningShown = true;
const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`.";
if (process.emitWarning) {
process.emitWarning(message, "DeprecationWarning");
} else {
const deprecationError = new Error(message);
deprecationError.name = "DeprecationWarning";
console.warn(new Error(message));
}
}
colNumber = Math.max(colNumber, 0);
const location = {
start: {
column: colNumber,
line: lineNumber
}
};
return codeFrameColumns(rawLines, location, opts);
}

View File

@ -1,49 +0,0 @@
{
"_from": "@babel/code-frame@^7.0.0",
"_id": "@babel/code-frame@7.0.0",
"_inBundle": false,
"_integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
"_location": "/@babel/code-frame",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@babel/code-frame@^7.0.0",
"name": "@babel/code-frame",
"escapedName": "@babel%2fcode-frame",
"scope": "@babel",
"rawSpec": "^7.0.0",
"saveSpec": null,
"fetchSpec": "^7.0.0"
},
"_requiredBy": [
"/eslint"
],
"_resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
"_shasum": "06e2ab19bdb535385559aabb5ba59729482800f8",
"_spec": "@babel/code-frame@^7.0.0",
"_where": "/home/erik/Documents/workspace_brackets/BME_Project_Ohm/om/node_modules/eslint",
"author": {
"name": "Sebastian McKenzie",
"email": "sebmck@gmail.com"
},
"bundleDependencies": false,
"dependencies": {
"@babel/highlight": "^7.0.0"
},
"deprecated": false,
"description": "Generate errors that contain a code frame that point to source locations.",
"devDependencies": {
"chalk": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"homepage": "https://babeljs.io/",
"license": "MIT",
"main": "lib/index.js",
"name": "@babel/code-frame",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel/tree/master/packages/babel-code-frame"
},
"version": "7.0.0"
}

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2014-2018 Sebastian McKenzie <sebmck@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,19 +0,0 @@
# @babel/highlight
> Syntax highlight JavaScript strings for output in terminals.
See our website [@babel/highlight](https://babeljs.io/docs/en/next/babel-highlight.html) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/highlight
```
or using yarn:
```sh
yarn add @babel/highlight --dev
```

View File

@ -1,129 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shouldHighlight = shouldHighlight;
exports.getChalk = getChalk;
exports.default = highlight;
function _jsTokens() {
const data = _interopRequireWildcard(require("js-tokens"));
_jsTokens = function () {
return data;
};
return data;
}
function _esutils() {
const data = _interopRequireDefault(require("esutils"));
_esutils = function () {
return data;
};
return data;
}
function _chalk() {
const data = _interopRequireDefault(require("chalk"));
_chalk = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
function getDefs(chalk) {
return {
keyword: chalk.cyan,
capitalized: chalk.yellow,
jsx_tag: chalk.yellow,
punctuator: chalk.yellow,
number: chalk.magenta,
string: chalk.green,
regex: chalk.magenta,
comment: chalk.grey,
invalid: chalk.white.bgRed.bold
};
}
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
const JSX_TAG = /^[a-z][\w-]*$/i;
const BRACKET = /^[()[\]{}]$/;
function getTokenType(match) {
const [offset, text] = match.slice(-2);
const token = (0, _jsTokens().matchToToken)(match);
if (token.type === "name") {
if (_esutils().default.keyword.isReservedWordES6(token.value)) {
return "keyword";
}
if (JSX_TAG.test(token.value) && (text[offset - 1] === "<" || text.substr(offset - 2, 2) == "</")) {
return "jsx_tag";
}
if (token.value[0] !== token.value[0].toLowerCase()) {
return "capitalized";
}
}
if (token.type === "punctuator" && BRACKET.test(token.value)) {
return "bracket";
}
if (token.type === "invalid" && (token.value === "@" || token.value === "#")) {
return "punctuator";
}
return token.type;
}
function highlightTokens(defs, text) {
return text.replace(_jsTokens().default, function (...args) {
const type = getTokenType(args);
const colorize = defs[type];
if (colorize) {
return args[0].split(NEWLINE).map(str => colorize(str)).join("\n");
} else {
return args[0];
}
});
}
function shouldHighlight(options) {
return _chalk().default.supportsColor || options.forceColor;
}
function getChalk(options) {
let chalk = _chalk().default;
if (options.forceColor) {
chalk = new (_chalk().default.constructor)({
enabled: true,
level: 1
});
}
return chalk;
}
function highlight(code, options = {}) {
if (shouldHighlight(options)) {
const chalk = getChalk(options);
const defs = getDefs(chalk);
return highlightTokens(defs, code);
} else {
return code;
}
}

View File

@ -1,50 +0,0 @@
{
"_from": "@babel/highlight@^7.0.0",
"_id": "@babel/highlight@7.0.0",
"_inBundle": false,
"_integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
"_location": "/@babel/highlight",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "@babel/highlight@^7.0.0",
"name": "@babel/highlight",
"escapedName": "@babel%2fhighlight",
"scope": "@babel",
"rawSpec": "^7.0.0",
"saveSpec": null,
"fetchSpec": "^7.0.0"
},
"_requiredBy": [
"/@babel/code-frame"
],
"_resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
"_shasum": "f710c38c8d458e6dd9a201afb637fcb781ce99e4",
"_spec": "@babel/highlight@^7.0.0",
"_where": "/home/erik/Documents/workspace_brackets/BME_Project_Ohm/om/node_modules/@babel/code-frame",
"author": {
"name": "suchipi",
"email": "me@suchipi.com"
},
"bundleDependencies": false,
"dependencies": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
},
"deprecated": false,
"description": "Syntax highlight JavaScript strings for output in terminals.",
"devDependencies": {
"strip-ansi": "^4.0.0"
},
"homepage": "https://babeljs.io/",
"license": "MIT",
"main": "lib/index.js",
"name": "@babel/highlight",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel/tree/master/packages/babel-highlight"
},
"version": "7.0.0"
}

12
node_modules/accepts/HISTORY.md generated vendored
View File

@ -1,3 +1,15 @@
1.3.7 / 2019-04-29
==================
* deps: negotiator@0.6.2
- Fix sorting charset, encoding, and language with extra parameters
1.3.6 / 2019-04-28
==================
* deps: mime-types@~2.1.24
- deps: mime-db@~1.40.0
1.3.5 / 2018-02-28
==================

19
node_modules/accepts/README.md generated vendored
View File

@ -1,7 +1,7 @@
# accepts
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][npm-downloads-image]][npm-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]
@ -131,13 +131,12 @@ curl -I -H'Accept: text/html' http://localhost:3000/
[MIT](LICENSE)
[npm-image]: https://img.shields.io/npm/v/accepts.svg
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/accepts/master
[coveralls-url]: https://coveralls.io/r/jshttp/accepts?branch=master
[node-version-image]: https://badgen.net/npm/node/accepts
[node-version-url]: https://nodejs.org/en/download
[npm-downloads-image]: https://badgen.net/npm/dm/accepts
[npm-url]: https://npmjs.org/package/accepts
[node-version-image]: https://img.shields.io/node/v/accepts.svg
[node-version-url]: https://nodejs.org/en/download/
[travis-image]: https://img.shields.io/travis/jshttp/accepts/master.svg
[npm-version-image]: https://badgen.net/npm/v/accepts
[travis-image]: https://badgen.net/travis/jshttp/accepts/master
[travis-url]: https://travis-ci.org/jshttp/accepts
[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts/master.svg
[coveralls-url]: https://coveralls.io/r/jshttp/accepts
[downloads-image]: https://img.shields.io/npm/dm/accepts.svg
[downloads-url]: https://npmjs.org/package/accepts

49
node_modules/accepts/package.json generated vendored
View File

@ -1,27 +1,27 @@
{
"_from": "accepts@~1.3.5",
"_id": "accepts@1.3.5",
"_from": "accepts@~1.3.7",
"_id": "accepts@1.3.7",
"_inBundle": false,
"_integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"_integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"_location": "/accepts",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "accepts@~1.3.5",
"raw": "accepts@~1.3.7",
"name": "accepts",
"escapedName": "accepts",
"rawSpec": "~1.3.5",
"rawSpec": "~1.3.7",
"saveSpec": null,
"fetchSpec": "~1.3.5"
"fetchSpec": "~1.3.7"
},
"_requiredBy": [
"/express"
],
"_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"_shasum": "eb777df6011723a3b14e8a72c0805c8e86746bd2",
"_spec": "accepts@~1.3.5",
"_where": "/home/erik/Documents/workspace_brackets/BME_Project_Ohm/om/node_modules/express",
"_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"_shasum": "531bc726517a3b2b41f850021c6cc15eaab507cd",
"_spec": "accepts@~1.3.7",
"_where": "/home/erik/Documents/workspace_brackets/a1_BME_Project_Ohm/om/node_modules/express",
"bugs": {
"url": "https://github.com/jshttp/accepts/issues"
},
@ -38,21 +38,22 @@
}
],
"dependencies": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
},
"deprecated": false,
"description": "Higher-level content negotiation",
"devDependencies": {
"eslint": "4.18.1",
"eslint-config-standard": "11.0.0",
"eslint-plugin-import": "2.9.0",
"eslint-plugin-markdown": "1.0.0-beta.6",
"eslint-plugin-node": "6.0.1",
"eslint-plugin-promise": "3.6.0",
"eslint-plugin-standard": "3.0.1",
"istanbul": "0.4.5",
"mocha": "~1.21.5"
"deep-equal": "1.0.1",
"eslint": "5.16.0",
"eslint-config-standard": "12.0.0",
"eslint-plugin-import": "2.17.2",
"eslint-plugin-markdown": "1.0.0",
"eslint-plugin-node": "8.0.1",
"eslint-plugin-promise": "4.1.1",
"eslint-plugin-standard": "4.0.0",
"mocha": "6.1.4",
"nyc": "14.0.0"
},
"engines": {
"node": ">= 0.6"
@ -78,8 +79,8 @@
"scripts": {
"lint": "eslint --plugin markdown --ext js,md .",
"test": "mocha --reporter spec --check-leaks --bail test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
"test-cov": "nyc --reporter=html --reporter=text npm test",
"test-travis": "nyc --reporter=text npm test"
},
"version": "1.3.5"
"version": "1.3.7"
}

19
node_modules/acorn-jsx/LICENSE generated vendored
View File

@ -1,19 +0,0 @@
Copyright (C) 2012-2017 by Ingvar Stepanyan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

44
node_modules/acorn-jsx/README.md generated vendored
View File

@ -1,44 +0,0 @@
# Acorn-JSX
[![Build Status](https://travis-ci.org/RReverser/acorn-jsx.svg?branch=master)](https://travis-ci.org/RReverser/acorn-jsx)
[![NPM version](https://img.shields.io/npm/v/acorn-jsx.svg)](https://www.npmjs.org/package/acorn-jsx)
This is plugin for [Acorn](http://marijnhaverbeke.nl/acorn/) - a tiny, fast JavaScript parser, written completely in JavaScript.
It was created as an experimental alternative, faster [React.js JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) parser.
According to [benchmarks](https://github.com/RReverser/acorn-jsx/blob/master/test/bench.html), Acorn-JSX is 2x faster than official [Esprima-based parser](https://github.com/facebook/esprima) when location tracking is turned on in both (call it "source maps enabled mode"). At the same time, it consumes all the ES6+JSX syntax that can be consumed by Esprima-FB (this is proved by [official tests](https://github.com/RReverser/acorn-jsx/blob/master/test/tests-jsx.js)).
**UPDATE [14-Apr-2015]**: Facebook implementation started [deprecation process](https://github.com/facebook/esprima/issues/111) in favor of Acorn + Acorn-JSX + Babel for parsing and transpiling JSX syntax.
## Transpiler
Please note that this tool only parses source code to JSX AST, which is useful for various language tools and services. If you want to transpile your code to regular ES5-compliant JavaScript with source map, check out the [babel transpiler](https://babeljs.io/) which uses `acorn-jsx` under the hood.
## Usage
Requiring this module provides you with an Acorn plugin that you can use like this:
```javascript
var acorn = require("acorn");
var jsx = require("acorn-jsx");
acorn.Parser.extend(jsx()).parse("my(<jsx/>, 'code');");
```
Note that official spec doesn't support mix of XML namespaces and object-style access in tag names (#27) like in `<namespace:Object.Property />`, so it was deprecated in `acorn-jsx@3.0`. If you still want to opt-in to support of such constructions, you can pass the following option:
```javascript
acorn.Parser.extend(jsx({ allowNamespacedObjects: true }))
```
Also, since most apps use pure React transformer, a new option was introduced that allows to prohibit namespaces completely:
```javascript
acorn.Parser.extend(jsx({ allowNamespaces: false }))
```
Note that by default `allowNamespaces` is enabled for spec compliancy.
## License
This plugin is issued under the [MIT license](./LICENSE).

441
node_modules/acorn-jsx/index.js generated vendored
View File

@ -1,441 +0,0 @@
'use strict';
const XHTMLEntities = require('./xhtml');
const hexNumber = /^[\da-fA-F]+$/;
const decimalNumber = /^\d+$/;
const acorn = require("acorn");
const tt = acorn.tokTypes;
const TokContext = acorn.TokContext;
const tokContexts = acorn.tokContexts;
const TokenType = acorn.TokenType;
const isNewLine = acorn.isNewLine;
const isIdentifierStart = acorn.isIdentifierStart;
const isIdentifierChar = acorn.isIdentifierChar;
const tc_oTag = new TokContext('<tag', false);
const tc_cTag = new TokContext('</tag', false);
const tc_expr = new TokContext('<tag>...</tag>', true, true);
const tok = {
jsxName: new TokenType('jsxName'),
jsxText: new TokenType('jsxText', {beforeExpr: true}),
jsxTagStart: new TokenType('jsxTagStart'),
jsxTagEnd: new TokenType('jsxTagEnd')
}
tok.jsxTagStart.updateContext = function() {
this.context.push(tc_expr); // treat as beginning of JSX expression
this.context.push(tc_oTag); // start opening tag context
this.exprAllowed = false;
};
tok.jsxTagEnd.updateContext = function(prevType) {
let out = this.context.pop();
if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
this.context.pop();
this.exprAllowed = this.curContext() === tc_expr;
} else {
this.exprAllowed = true;
}
};
// Transforms JSX element name to string.
function getQualifiedJSXName(object) {
if (!object)
return object;
if (object.type === 'JSXIdentifier')
return object.name;
if (object.type === 'JSXNamespacedName')
return object.namespace.name + ':' + object.name.name;
if (object.type === 'JSXMemberExpression')
return getQualifiedJSXName(object.object) + '.' +
getQualifiedJSXName(object.property);
}
module.exports = function(options) {
options = options || {};
return function(Parser) {
return plugin({
allowNamespaces: options.allowNamespaces !== false,
allowNamespacedObjects: !!options.allowNamespacedObjects
}, Parser);
}
};
module.exports.tokTypes = tok;
function plugin(options, Parser) {
return class extends Parser {
// Reads inline JSX contents token.
jsx_readToken() {
let out = '', chunkStart = this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, 'Unterminated JSX contents');
let ch = this.input.charCodeAt(this.pos);
switch (ch) {
case 60: // '<'
case 123: // '{'
if (this.pos === this.start) {
if (ch === 60 && this.exprAllowed) {
++this.pos;
return this.finishToken(tok.jsxTagStart);
}
return this.getTokenFromCode(ch);
}
out += this.input.slice(chunkStart, this.pos);
return this.finishToken(tok.jsxText, out);
case 38: // '&'
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readEntity();
chunkStart = this.pos;
break;
default:
if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readNewLine(true);
chunkStart = this.pos;
} else {
++this.pos;
}
}
}
}
jsx_readNewLine(normalizeCRLF) {
let ch = this.input.charCodeAt(this.pos);
let out;
++this.pos;
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
++this.pos;
out = normalizeCRLF ? '\n' : '\r\n';
} else {
out = String.fromCharCode(ch);
}
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
}
return out;
}
jsx_readString(quote) {
let out = '', chunkStart = ++this.pos;
for (;;) {
if (this.pos >= this.input.length)
this.raise(this.start, 'Unterminated string constant');
let ch = this.input.charCodeAt(this.pos);
if (ch === quote) break;
if (ch === 38) { // '&'
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readEntity();
chunkStart = this.pos;
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
out += this.jsx_readNewLine(false);
chunkStart = this.pos;
} else {
++this.pos;
}
}
out += this.input.slice(chunkStart, this.pos++);
return this.finishToken(tt.string, out);
}
jsx_readEntity() {
let str = '', count = 0, entity;
let ch = this.input[this.pos];
if (ch !== '&')
this.raise(this.pos, 'Entity must start with an ampersand');
let startPos = ++this.pos;
while (this.pos < this.input.length && count++ < 10) {
ch = this.input[this.pos++];
if (ch === ';') {
if (str[0] === '#') {
if (str[1] === 'x') {
str = str.substr(2);
if (hexNumber.test(str))
entity = String.fromCharCode(parseInt(str, 16));
} else {
str = str.substr(1);
if (decimalNumber.test(str))
entity = String.fromCharCode(parseInt(str, 10));
}
} else {
entity = XHTMLEntities[str];
}
break;
}
str += ch;
}
if (!entity) {
this.pos = startPos;
return '&';
}
return entity;
}
// Read a JSX identifier (valid tag or attribute name).
//
// Optimized version since JSX identifiers can't contain
// escape characters and so can be read as single slice.
// Also assumes that first character was already checked
// by isIdentifierStart in readToken.
jsx_readWord() {
let ch, start = this.pos;
do {
ch = this.input.charCodeAt(++this.pos);
} while (isIdentifierChar(ch) || ch === 45); // '-'
return this.finishToken(tok.jsxName, this.input.slice(start, this.pos));
}
// Parse next token as JSX identifier
jsx_parseIdentifier() {
let node = this.startNode();
if (this.type === tok.jsxName)
node.name = this.value;
else if (this.type.keyword)
node.name = this.type.keyword;
else
this.unexpected();
this.next();
return this.finishNode(node, 'JSXIdentifier');
}
// Parse namespaced identifier.
jsx_parseNamespacedName() {
let startPos = this.start, startLoc = this.startLoc;
let name = this.jsx_parseIdentifier();
if (!options.allowNamespaces || !this.eat(tt.colon)) return name;
var node = this.startNodeAt(startPos, startLoc);
node.namespace = name;
node.name = this.jsx_parseIdentifier();
return this.finishNode(node, 'JSXNamespacedName');
}
// Parses element name in any form - namespaced, member
// or single identifier.
jsx_parseElementName() {
if (this.type === tok.jsxTagEnd) return '';
let startPos = this.start, startLoc = this.startLoc;
let node = this.jsx_parseNamespacedName();
if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !options.allowNamespacedObjects) {
this.unexpected();
}
while (this.eat(tt.dot)) {
let newNode = this.startNodeAt(startPos, startLoc);
newNode.object = node;
newNode.property = this.jsx_parseIdentifier();
node = this.finishNode(newNode, 'JSXMemberExpression');
}
return node;
}
// Parses any type of JSX attribute value.
jsx_parseAttributeValue() {
switch (this.type) {
case tt.braceL:
let node = this.jsx_parseExpressionContainer();
if (node.expression.type === 'JSXEmptyExpression')
this.raise(node.start, 'JSX attributes must only be assigned a non-empty expression');
return node;
case tok.jsxTagStart:
case tt.string:
return this.parseExprAtom();
default:
this.raise(this.start, 'JSX value should be either an expression or a quoted JSX text');
}
}
// JSXEmptyExpression is unique type since it doesn't actually parse anything,
// and so it should start at the end of last read token (left brace) and finish
// at the beginning of the next one (right brace).
jsx_parseEmptyExpression() {
let node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc);
return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc);
}
// Parses JSX expression enclosed into curly brackets.
jsx_parseExpressionContainer() {
let node = this.startNode();
this.next();
node.expression = this.type === tt.braceR
? this.jsx_parseEmptyExpression()
: this.parseExpression();
this.expect(tt.braceR);
return this.finishNode(node, 'JSXExpressionContainer');
}
// Parses following JSX attribute name-value pair.
jsx_parseAttribute() {
let node = this.startNode();
if (this.eat(tt.braceL)) {
this.expect(tt.ellipsis);
node.argument = this.parseMaybeAssign();
this.expect(tt.braceR);
return this.finishNode(node, 'JSXSpreadAttribute');
}
node.name = this.jsx_parseNamespacedName();
node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null;
return this.finishNode(node, 'JSXAttribute');
}
// Parses JSX opening tag starting after '<'.
jsx_parseOpeningElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
node.attributes = [];
let nodeName = this.jsx_parseElementName();
if (nodeName) node.name = nodeName;
while (this.type !== tt.slash && this.type !== tok.jsxTagEnd)
node.attributes.push(this.jsx_parseAttribute());
node.selfClosing = this.eat(tt.slash);
this.expect(tok.jsxTagEnd);
return this.finishNode(node, nodeName ? 'JSXOpeningElement' : 'JSXOpeningFragment');
}
// Parses JSX closing tag starting after '</'.
jsx_parseClosingElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
let nodeName = this.jsx_parseElementName();
if (nodeName) node.name = nodeName;
this.expect(tok.jsxTagEnd);
return this.finishNode(node, nodeName ? 'JSXClosingElement' : 'JSXClosingFragment');
}
// Parses entire JSX element, including it's opening tag
// (starting after '<'), attributes, contents and closing tag.
jsx_parseElementAt(startPos, startLoc) {
let node = this.startNodeAt(startPos, startLoc);
let children = [];
let openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);
let closingElement = null;
if (!openingElement.selfClosing) {
contents: for (;;) {
switch (this.type) {
case tok.jsxTagStart:
startPos = this.start; startLoc = this.startLoc;
this.next();
if (this.eat(tt.slash)) {
closingElement = this.jsx_parseClosingElementAt(startPos, startLoc);
break contents;
}
children.push(this.jsx_parseElementAt(startPos, startLoc));
break;
case tok.jsxText:
children.push(this.parseExprAtom());
break;
case tt.braceL:
children.push(this.jsx_parseExpressionContainer());
break;
default:
this.unexpected();
}
}
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
this.raise(
closingElement.start,
'Expected corresponding JSX closing tag for <' + getQualifiedJSXName(openingElement.name) + '>');
}
}
let fragmentOrElement = openingElement.name ? 'Element' : 'Fragment';
node['opening' + fragmentOrElement] = openingElement;
node['closing' + fragmentOrElement] = closingElement;
node.children = children;
if (this.type === tt.relational && this.value === "<") {
this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");
}
return this.finishNode(node, 'JSX' + fragmentOrElement);
}
// Parse JSX text
jsx_parseText(value) {
let node = this.parseLiteral(value);
node.type = "JSXText";
return node;
}
// Parses entire JSX element from current position.
jsx_parseElement() {
let startPos = this.start, startLoc = this.startLoc;
this.next();
return this.jsx_parseElementAt(startPos, startLoc);
}
parseExprAtom(refShortHandDefaultPos) {
if (this.type === tok.jsxText)
return this.jsx_parseText(this.value);
else if (this.type === tok.jsxTagStart)
return this.jsx_parseElement();
else
return super.parseExprAtom(refShortHandDefaultPos);
}
readToken(code) {
let context = this.curContext();
if (context === tc_expr) return this.jsx_readToken();
if (context === tc_oTag || context === tc_cTag) {
if (isIdentifierStart(code)) return this.jsx_readWord();
if (code == 62) {
++this.pos;
return this.finishToken(tok.jsxTagEnd);
}
if ((code === 34 || code === 39) && context == tc_oTag)
return this.jsx_readString(code);
}
if (code === 60 && this.exprAllowed && this.input.charCodeAt(this.pos + 1) !== 33) {
++this.pos;
return this.finishToken(tok.jsxTagStart);
}
return super.readToken(code)
}
updateContext(prevType) {
if (this.type == tt.braceL) {
var curContext = this.curContext();
if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);
else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);
else super.updateContext(prevType)
this.exprAllowed = true;
} else if (this.type === tt.slash && prevType === tok.jsxTagStart) {
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
this.context.push(tc_cTag); // reconsider as closing tag context
this.exprAllowed = false;
} else {
return super.updateContext(prevType);
}
}
};
}

55
node_modules/acorn-jsx/package.json generated vendored
View File

@ -1,55 +0,0 @@
{
"_from": "acorn-jsx@^5.0.0",
"_id": "acorn-jsx@5.0.1",
"_inBundle": false,
"_integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
"_location": "/acorn-jsx",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "acorn-jsx@^5.0.0",
"name": "acorn-jsx",
"escapedName": "acorn-jsx",
"rawSpec": "^5.0.0",
"saveSpec": null,
"fetchSpec": "^5.0.0"
},
"_requiredBy": [
"/espree"
],
"_resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
"_shasum": "32a064fd925429216a09b141102bfdd185fae40e",
"_spec": "acorn-jsx@^5.0.0",
"_where": "/home/erik/Documents/workspace_brackets/BME_Project_Ohm/om/node_modules/espree",
"bugs": {
"url": "https://github.com/RReverser/acorn-jsx/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Alternative, faster React.js JSX parser",
"devDependencies": {
"acorn": "^6.0.0"
},
"homepage": "https://github.com/RReverser/acorn-jsx",
"license": "MIT",
"maintainers": [
{
"name": "Ingvar Stepanyan",
"email": "me@rreverser.com",
"url": "http://rreverser.com/"
}
],
"name": "acorn-jsx",
"peerDependencies": {
"acorn": "^6.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/RReverser/acorn-jsx.git"
},
"scripts": {
"test": "node test/run.js"
},
"version": "5.0.1"
}

255
node_modules/acorn-jsx/xhtml.js generated vendored
View File

@ -1,255 +0,0 @@
module.exports = {
quot: '\u0022',
amp: '&',
apos: '\u0027',
lt: '<',
gt: '>',
nbsp: '\u00A0',
iexcl: '\u00A1',
cent: '\u00A2',
pound: '\u00A3',
curren: '\u00A4',
yen: '\u00A5',
brvbar: '\u00A6',
sect: '\u00A7',
uml: '\u00A8',
copy: '\u00A9',
ordf: '\u00AA',
laquo: '\u00AB',
not: '\u00AC',
shy: '\u00AD',
reg: '\u00AE',
macr: '\u00AF',
deg: '\u00B0',
plusmn: '\u00B1',
sup2: '\u00B2',
sup3: '\u00B3',
acute: '\u00B4',
micro: '\u00B5',
para: '\u00B6',
middot: '\u00B7',
cedil: '\u00B8',
sup1: '\u00B9',
ordm: '\u00BA',
raquo: '\u00BB',
frac14: '\u00BC',
frac12: '\u00BD',
frac34: '\u00BE',
iquest: '\u00BF',
Agrave: '\u00C0',
Aacute: '\u00C1',
Acirc: '\u00C2',
Atilde: '\u00C3',
Auml: '\u00C4',
Aring: '\u00C5',
AElig: '\u00C6',
Ccedil: '\u00C7',
Egrave: '\u00C8',
Eacute: '\u00C9',
Ecirc: '\u00CA',
Euml: '\u00CB',
Igrave: '\u00CC',
Iacute: '\u00CD',
Icirc: '\u00CE',
Iuml: '\u00CF',
ETH: '\u00D0',
Ntilde: '\u00D1',
Ograve: '\u00D2',
Oacute: '\u00D3',
Ocirc: '\u00D4',
Otilde: '\u00D5',
Ouml: '\u00D6',
times: '\u00D7',
Oslash: '\u00D8',
Ugrave: '\u00D9',
Uacute: '\u00DA',
Ucirc: '\u00DB',
Uuml: '\u00DC',
Yacute: '\u00DD',
THORN: '\u00DE',
szlig: '\u00DF',
agrave: '\u00E0',
aacute: '\u00E1',
acirc: '\u00E2',
atilde: '\u00E3',
auml: '\u00E4',
aring: '\u00E5',
aelig: '\u00E6',
ccedil: '\u00E7',
egrave: '\u00E8',
eacute: '\u00E9',
ecirc: '\u00EA',
euml: '\u00EB',
igrave: '\u00EC',
iacute: '\u00ED',
icirc: '\u00EE',
iuml: '\u00EF',
eth: '\u00F0',
ntilde: '\u00F1',
ograve: '\u00F2',
oacute: '\u00F3',
ocirc: '\u00F4',
otilde: '\u00F5',
ouml: '\u00F6',
divide: '\u00F7',
oslash: '\u00F8',
ugrave: '\u00F9',
uacute: '\u00FA',
ucirc: '\u00FB',
uuml: '\u00FC',
yacute: '\u00FD',
thorn: '\u00FE',
yuml: '\u00FF',
OElig: '\u0152',
oelig: '\u0153',
Scaron: '\u0160',
scaron: '\u0161',
Yuml: '\u0178',
fnof: '\u0192',
circ: '\u02C6',
tilde: '\u02DC',
Alpha: '\u0391',
Beta: '\u0392',
Gamma: '\u0393',
Delta: '\u0394',
Epsilon: '\u0395',
Zeta: '\u0396',
Eta: '\u0397',
Theta: '\u0398',
Iota: '\u0399',
Kappa: '\u039A',
Lambda: '\u039B',
Mu: '\u039C',
Nu: '\u039D',
Xi: '\u039E',
Omicron: '\u039F',
Pi: '\u03A0',
Rho: '\u03A1',
Sigma: '\u03A3',
Tau: '\u03A4',
Upsilon: '\u03A5',
Phi: '\u03A6',
Chi: '\u03A7',
Psi: '\u03A8',
Omega: '\u03A9',
alpha: '\u03B1',
beta: '\u03B2',
gamma: '\u03B3',
delta: '\u03B4',
epsilon: '\u03B5',
zeta: '\u03B6',
eta: '\u03B7',
theta: '\u03B8',
iota: '\u03B9',
kappa: '\u03BA',
lambda: '\u03BB',
mu: '\u03BC',
nu: '\u03BD',
xi: '\u03BE',
omicron: '\u03BF',
pi: '\u03C0',
rho: '\u03C1',
sigmaf: '\u03C2',
sigma: '\u03C3',
tau: '\u03C4',
upsilon: '\u03C5',
phi: '\u03C6',
chi: '\u03C7',
psi: '\u03C8',
omega: '\u03C9',
thetasym: '\u03D1',
upsih: '\u03D2',
piv: '\u03D6',
ensp: '\u2002',
emsp: '\u2003',
thinsp: '\u2009',
zwnj: '\u200C',
zwj: '\u200D',
lrm: '\u200E',
rlm: '\u200F',
ndash: '\u2013',
mdash: '\u2014',
lsquo: '\u2018',
rsquo: '\u2019',
sbquo: '\u201A',
ldquo: '\u201C',
rdquo: '\u201D',
bdquo: '\u201E',
dagger: '\u2020',
Dagger: '\u2021',
bull: '\u2022',
hellip: '\u2026',
permil: '\u2030',
prime: '\u2032',
Prime: '\u2033',
lsaquo: '\u2039',
rsaquo: '\u203A',
oline: '\u203E',
frasl: '\u2044',
euro: '\u20AC',
image: '\u2111',
weierp: '\u2118',
real: '\u211C',
trade: '\u2122',
alefsym: '\u2135',
larr: '\u2190',
uarr: '\u2191',
rarr: '\u2192',
darr: '\u2193',
harr: '\u2194',
crarr: '\u21B5',
lArr: '\u21D0',
uArr: '\u21D1',
rArr: '\u21D2',
dArr: '\u21D3',
hArr: '\u21D4',
forall: '\u2200',
part: '\u2202',
exist: '\u2203',
empty: '\u2205',
nabla: '\u2207',
isin: '\u2208',
notin: '\u2209',
ni: '\u220B',
prod: '\u220F',
sum: '\u2211',
minus: '\u2212',
lowast: '\u2217',
radic: '\u221A',
prop: '\u221D',
infin: '\u221E',
ang: '\u2220',
and: '\u2227',
or: '\u2228',
cap: '\u2229',
cup: '\u222A',
'int': '\u222B',
there4: '\u2234',
sim: '\u223C',
cong: '\u2245',
asymp: '\u2248',
ne: '\u2260',
equiv: '\u2261',
le: '\u2264',
ge: '\u2265',
sub: '\u2282',
sup: '\u2283',
nsub: '\u2284',
sube: '\u2286',
supe: '\u2287',
oplus: '\u2295',
otimes: '\u2297',
perp: '\u22A5',
sdot: '\u22C5',
lceil: '\u2308',
rceil: '\u2309',
lfloor: '\u230A',
rfloor: '\u230B',
lang: '\u2329',
rang: '\u232A',
loz: '\u25CA',
spades: '\u2660',
clubs: '\u2663',
hearts: '\u2665',
diams: '\u2666'
};

452
node_modules/acorn/CHANGELOG.md generated vendored
View File

@ -1,452 +0,0 @@
## 6.0.4 (2018-11-05)
### Bug fixes
Further improvements to tokenizing regular expressions in corner cases.
## 6.0.3 (2018-11-04)
### Bug fixes
Fix bug in tokenizing an expression-less return followed by a function followed by a regular expression.
Remove stray symlink in the package tarball.
## 6.0.2 (2018-09-26)
### Bug fixes
Fix bug where default expressions could fail to parse inside an object destructuring assignment expression.
## 6.0.1 (2018-09-14)
### Bug fixes
Fix wrong value in `version` export.
## 6.0.0 (2018-09-14)
### Bug fixes
Better handle variable-redefinition checks for catch bindings and functions directly under if statements.
Forbid `new.target` in top-level arrow functions.
Fix issue with parsing a regexp after `yield` in some contexts.
### New features
The package now comes with TypeScript definitions.
### Breaking changes
The default value of the `ecmaVersion` option is now 9 (2018).
Plugins work differently, and will have to be rewritten to work with this version.
The loose parser and walker have been moved into separate packages (`acorn-loose` and `acorn-walk`).
## 5.7.3 (2018-09-10)
### Bug fixes
Fix failure to tokenize regexps after expressions like `x.of`.
Better error message for unterminated template literals.
## 5.7.2 (2018-08-24)
### Bug fixes
Properly handle `allowAwaitOutsideFunction` in for statements.
Treat function declarations at the top level of modules like let bindings.
Don't allow async function declarations as the only statement under a label.
## 5.7.0 (2018-06-15)
### New features
Upgraded to Unicode 11.
## 5.6.0 (2018-05-31)
### New features
Allow U+2028 and U+2029 in string when ECMAVersion >= 10.
Allow binding-less catch statements when ECMAVersion >= 10.
Add `allowAwaitOutsideFunction` option for parsing top-level `await`.
## 5.5.3 (2018-03-08)
### Bug fixes
A _second_ republish of the code in 5.5.1, this time with yarn, to hopefully get valid timestamps.
## 5.5.2 (2018-03-08)
### Bug fixes
A republish of the code in 5.5.1 in an attempt to solve an issue with the file timestamps in the npm package being 0.
## 5.5.1 (2018-03-06)
### Bug fixes
Fix misleading error message for octal escapes in template strings.
## 5.5.0 (2018-02-27)
### New features
The identifier character categorization is now based on Unicode version 10.
Acorn will now validate the content of regular expressions, including new ES9 features.
## 5.4.0 (2018-02-01)
### Bug fixes
Disallow duplicate or escaped flags on regular expressions.
Disallow octal escapes in strings in strict mode.
### New features
Add support for async iteration.
Add support for object spread and rest.
## 5.3.0 (2017-12-28)
### Bug fixes
Fix parsing of floating point literals with leading zeroes in loose mode.
Allow duplicate property names in object patterns.
Don't allow static class methods named `prototype`.
Disallow async functions directly under `if` or `else`.
Parse right-hand-side of `for`/`of` as an assignment expression.
Stricter parsing of `for`/`in`.
Don't allow unicode escapes in contextual keywords.
### New features
Parsing class members was factored into smaller methods to allow plugins to hook into it.
## 5.2.1 (2017-10-30)
### Bug fixes
Fix a token context corruption bug.
## 5.2.0 (2017-10-30)
### Bug fixes
Fix token context tracking for `class` and `function` in property-name position.
Make sure `%*` isn't parsed as a valid operator.
Allow shorthand properties `get` and `set` to be followed by default values.
Disallow `super` when not in callee or object position.
### New features
Support [`directive` property](https://github.com/estree/estree/compare/b3de58c9997504d6fba04b72f76e6dd1619ee4eb...1da8e603237144f44710360f8feb7a9977e905e0) on directive expression statements.
## 5.1.2 (2017-09-04)
### Bug fixes
Disable parsing of legacy HTML-style comments in modules.
Fix parsing of async methods whose names are keywords.
## 5.1.1 (2017-07-06)
### Bug fixes
Fix problem with disambiguating regexp and division after a class.
## 5.1.0 (2017-07-05)
### Bug fixes
Fix tokenizing of regexps in an object-desctructuring `for`/`of` loop and after `yield`.
Parse zero-prefixed numbers with non-octal digits as decimal.
Allow object/array patterns in rest parameters.
Don't error when `yield` is used as a property name.
Allow `async` as a shorthand object property.
### New features
Implement the [template literal revision proposal](https://github.com/tc39/proposal-template-literal-revision) for ES9.
## 5.0.3 (2017-04-01)
### Bug fixes
Fix spurious duplicate variable definition errors for named functions.
## 5.0.2 (2017-03-30)
### Bug fixes
A binary operator after a parenthesized arrow expression is no longer incorrectly treated as an error.
## 5.0.0 (2017-03-28)
### Bug fixes
Raise an error for duplicated lexical bindings.
Fix spurious error when an assignement expression occurred after a spread expression.
Accept regular expressions after `of` (in `for`/`of`), `yield` (in a generator), and braced arrow functions.
Allow labels in front or `var` declarations, even in strict mode.
### Breaking changes
Parse declarations following `export default` as declaration nodes, not expressions. This means that class and function declarations nodes can now have `null` as their `id`.
## 4.0.11 (2017-02-07)
### Bug fixes
Allow all forms of member expressions to be parenthesized as lvalue.
## 4.0.10 (2017-02-07)
### Bug fixes
Don't expect semicolons after default-exported functions or classes, even when they are expressions.
Check for use of `'use strict'` directives in non-simple parameter functions, even when already in strict mode.
## 4.0.9 (2017-02-06)
### Bug fixes
Fix incorrect error raised for parenthesized simple assignment targets, so that `(x) = 1` parses again.
## 4.0.8 (2017-02-03)
### Bug fixes
Solve spurious parenthesized pattern errors by temporarily erring on the side of accepting programs that our delayed errors don't handle correctly yet.
## 4.0.7 (2017-02-02)
### Bug fixes
Accept invalidly rejected code like `(x).y = 2` again.
Don't raise an error when a function _inside_ strict code has a non-simple parameter list.
## 4.0.6 (2017-02-02)
### Bug fixes
Fix exponential behavior (manifesting itself as a complete hang for even relatively small source files) introduced by the new 'use strict' check.
## 4.0.5 (2017-02-02)
### Bug fixes
Disallow parenthesized pattern expressions.
Allow keywords as export names.
Don't allow the `async` keyword to be parenthesized.
Properly raise an error when a keyword contains a character escape.
Allow `"use strict"` to appear after other string literal expressions.
Disallow labeled declarations.
## 4.0.4 (2016-12-19)
### Bug fixes
Fix crash when `export` was followed by a keyword that can't be
exported.
## 4.0.3 (2016-08-16)
### Bug fixes
Allow regular function declarations inside single-statement `if` branches in loose mode. Forbid them entirely in strict mode.
Properly parse properties named `async` in ES2017 mode.
Fix bug where reserved words were broken in ES2017 mode.
## 4.0.2 (2016-08-11)
### Bug fixes
Don't ignore period or 'e' characters after octal numbers.
Fix broken parsing for call expressions in default parameter values of arrow functions.
## 4.0.1 (2016-08-08)
### Bug fixes
Fix false positives in duplicated export name errors.
## 4.0.0 (2016-08-07)
### Breaking changes
The default `ecmaVersion` option value is now 7.
A number of internal method signatures changed, so plugins might need to be updated.
### Bug fixes
The parser now raises errors on duplicated export names.
`arguments` and `eval` can now be used in shorthand properties.
Duplicate parameter names in non-simple argument lists now always produce an error.
### New features
The `ecmaVersion` option now also accepts year-style version numbers
(2015, etc).
Support for `async`/`await` syntax when `ecmaVersion` is >= 8.
Support for trailing commas in call expressions when `ecmaVersion` is >= 8.
## 3.3.0 (2016-07-25)
### Bug fixes
Fix bug in tokenizing of regexp operator after a function declaration.
Fix parser crash when parsing an array pattern with a hole.
### New features
Implement check against complex argument lists in functions that enable strict mode in ES7.
## 3.2.0 (2016-06-07)
### Bug fixes
Improve handling of lack of unicode regexp support in host
environment.
Properly reject shorthand properties whose name is a keyword.
### New features
Visitors created with `visit.make` now have their base as _prototype_, rather than copying properties into a fresh object.
## 3.1.0 (2016-04-18)
### Bug fixes
Properly tokenize the division operator directly after a function expression.
Allow trailing comma in destructuring arrays.
## 3.0.4 (2016-02-25)
### Fixes
Allow update expressions as left-hand-side of the ES7 exponential operator.
## 3.0.2 (2016-02-10)
### Fixes
Fix bug that accidentally made `undefined` a reserved word when parsing ES7.
## 3.0.0 (2016-02-10)
### Breaking changes
The default value of the `ecmaVersion` option is now 6 (used to be 5).
Support for comprehension syntax (which was dropped from the draft spec) has been removed.
### Fixes
`let` and `yield` are now “contextual keywords”, meaning you can mostly use them as identifiers in ES5 non-strict code.
A parenthesized class or function expression after `export default` is now parsed correctly.
### New features
When `ecmaVersion` is set to 7, Acorn will parse the exponentiation operator (`**`).
The identifier character ranges are now based on Unicode 8.0.0.
Plugins can now override the `raiseRecoverable` method to override the way non-critical errors are handled.
## 2.7.0 (2016-01-04)
### Fixes
Stop allowing rest parameters in setters.
Disallow `y` rexexp flag in ES5.
Disallow `\00` and `\000` escapes in strict mode.
Raise an error when an import name is a reserved word.
## 2.6.2 (2015-11-10)
### Fixes
Don't crash when no options object is passed.
## 2.6.0 (2015-11-09)
### Fixes
Add `await` as a reserved word in module sources.
Disallow `yield` in a parameter default value for a generator.
Forbid using a comma after a rest pattern in an array destructuring.
### New features
Support parsing stdin in command-line tool.
## 2.5.0 (2015-10-27)
### Fixes
Fix tokenizer support in the command-line tool.
Stop allowing `new.target` outside of functions.
Remove legacy `guard` and `guardedHandler` properties from try nodes.
Stop allowing multiple `__proto__` properties on an object literal in strict mode.
Don't allow rest parameters to be non-identifier patterns.
Check for duplicate paramter names in arrow functions.

19
node_modules/acorn/LICENSE generated vendored
View File

@ -1,19 +0,0 @@
Copyright (C) 2012-2018 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

269
node_modules/acorn/README.md generated vendored
View File

@ -1,269 +0,0 @@
# Acorn
A tiny, fast JavaScript parser written in JavaScript.
## Community
Acorn is open source software released under an
[MIT license](https://github.com/acornjs/acorn/blob/master/LICENSE).
You are welcome to
[report bugs](https://github.com/acornjs/acorn/issues) or create pull
requests on [github](https://github.com/acornjs/acorn). For questions
and discussion, please use the
[Tern discussion forum](https://discuss.ternjs.net).
## Installation
The easiest way to install acorn is from [`npm`](https://www.npmjs.com/):
```sh
npm install acorn
```
Alternately, you can download the source and build acorn yourself:
```sh
git clone https://github.com/acornjs/acorn.git
cd acorn
npm install
```
## Interface
**parse**`(input, options)` is the main interface to the library. The
`input` parameter is a string, `options` can be undefined or an object
setting some of the options listed below. The return value will be an
abstract syntax tree object as specified by the [ESTree
spec](https://github.com/estree/estree).
```javascript
let acorn = require("acorn");
console.log(acorn.parse("1 + 1"));
```
When encountering a syntax error, the parser will raise a
`SyntaxError` object with a meaningful message. The error object will
have a `pos` property that indicates the string offset at which the
error occurred, and a `loc` object that contains a `{line, column}`
object referring to that same position.
Options can be provided by passing a second argument, which should be
an object containing any of these fields:
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018) or 10 (2019, partial
support). This influences support for strict mode, the set of
reserved words, and support for new syntax features. Default is 7.
**NOTE**: Only 'stage 4' (finalized) ECMAScript features are being
implemented by Acorn. Other proposed new features can be implemented
through plugins.
- **sourceType**: Indicate the mode the code should be parsed in. Can be
either `"script"` or `"module"`. This influences global strict mode
and parsing of `import` and `export` declarations.
- **onInsertedSemicolon**: If given a callback, that callback will be
called whenever a missing semicolon is inserted by the parser. The
callback will be given the character offset of the point where the
semicolon is inserted as argument, and if `locations` is on, also a
`{line, column}` object representing this position.
- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
commas.
- **allowReserved**: If `false`, using a reserved word will generate
an error. Defaults to `true` for `ecmaVersion` 3, `false` for higher
versions. When given the value `"never"`, reserved words and
keywords can also not be used as property names (as in Internet
Explorer's old parser).
- **allowReturnOutsideFunction**: By default, a return statement at
the top level raises an error. Set this to `true` to accept such
code.
- **allowImportExportEverywhere**: By default, `import` and `export`
declarations can only appear at a program's top level. Setting this
option to `true` allows them anywhere where a statement is allowed.
- **allowAwaitOutsideFunction**: By default, `await` expressions can
only appear inside `async` functions. Setting this option to
`true` allows to have top-level `await` expressions. They are
still not allowed in non-`async` functions, though.
- **allowHashBang**: When this is enabled (off by default), if the
code starts with the characters `#!` (as in a shellscript), the
first line will be treated as a comment.
- **locations**: When `true`, each node has a `loc` object attached
with `start` and `end` subobjects, each of which contains the
one-based line and zero-based column numbers in `{line, column}`
form. Default is `false`.
- **onToken**: If a function is passed for this option, each found
token will be passed in same format as tokens returned from
`tokenizer().getToken()`.
If array is passed, each found token is pushed to it.
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **onComment**: If a function is passed for this option, whenever a
comment is encountered the function will be called with the
following parameters:
- `block`: `true` if the comment is a block comment, false if it
is a line comment.
- `text`: The content of the comment.
- `start`: Character offset of the start of the comment.
- `end`: Character offset of the end of the comment.
When the `locations` options is on, the `{line, column}` locations
of the comments start and end are passed as two additional
parameters.
If array is passed for this option, each found comment is pushed
to it as object in Esprima format:
```javascript
{
"type": "Line" | "Block",
"value": "comment text",
"start": Number,
"end": Number,
// If `locations` option is on:
"loc": {
"start": {line: Number, column: Number}
"end": {line: Number, column: Number}
},
// If `ranges` option is on:
"range": [Number, Number]
}
```
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **ranges**: Nodes have their start and end characters offsets
recorded in `start` and `end` properties (directly on the node,
rather than the `loc` object, which holds line/column data. To also
add a
[semi-standardized](https://bugzilla.mozilla.org/show_bug.cgi?id=745678)
`range` property holding a `[start, end]` array with the same
numbers, set the `ranges` option to `true`.
- **program**: It is possible to parse multiple files into a single
AST by passing the tree produced by parsing the first file as the
`program` option in subsequent parses. This will add the toplevel
forms of the parsed file to the "Program" (top) node of an existing
parse tree.
- **sourceFile**: When the `locations` option is `true`, you can pass
this option to add a `source` attribute in every nodes `loc`
object. Note that the contents of this option are not examined or
processed in any way; you are free to use whatever format you
choose.
- **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
will be added (regardless of the `location` option) directly to the
nodes, rather than the `loc` object.
- **preserveParens**: If this option is `true`, parenthesized expressions
are represented by (non-standard) `ParenthesizedExpression` nodes
that have a single `expression` property containing the expression
inside parentheses.
**parseExpressionAt**`(input, offset, options)` will parse a single
expression in a string, and return its AST. It will not complain if
there is more of the string left after the expression.
**tokenizer**`(input, options)` returns an object with a `getToken`
method that can be called repeatedly to get the next token, a `{start,
end, type, value}` object (with added `loc` property when the
`locations` option is enabled and `range` property when the `ranges`
option is enabled). When the token's type is `tokTypes.eof`, you
should stop calling the method, since it will keep returning that same
token forever.
In ES6 environment, returned result can be used as any other
protocol-compliant iterable:
```javascript
for (let token of acorn.tokenizer(str)) {
// iterate over the tokens
}
// transform code to array of tokens:
var tokens = [...acorn.tokenizer(str)];
```
**tokTypes** holds an object mapping names to the token type objects
that end up in the `type` properties of tokens.
**getLineInfo**`(input, offset)` can be used to get a `{line,
column}` object for a given program string and offset.
### The `Parser` class
Instances of the **`Parser`** class contain all the state and logic
that drives a parse. It has static methods `parse`,
`parseExpressionAt`, and `tokenizer` that match the top-level
functions by the same name.
When extending the parser with plugins, you need to call these methods
on the extended version of the class. To extend a parser with plugins,
you can use its static `extend` method.
```javascript
var acorn = require("acorn");
var jsx = require("acorn-jsx");
var JSXParser = acorn.Parser.extend(jsx());
JSXParser.parse("foo(<bar/>)");
```
The `extend` method takes any number of plugin values, and returns a
new `Parser` class that includes the extra parser logic provided by
the plugins.
## Command line interface
The `bin/acorn` utility can be used to parse a file from the command
line. It accepts as arguments its input file and the following
options:
- `--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|--ecma10`: Sets the ECMAScript version
to parse. Default is version 9.
- `--module`: Sets the parsing mode to `"module"`. Is set to `"script"` otherwise.
- `--locations`: Attaches a "loc" object to each node with "start" and
"end" subobjects, each of which contains the one-based line and
zero-based column numbers in `{line, column}` form.
- `--allow-hash-bang`: If the code starts with the characters #! (as
in a shellscript), the first line will be treated as a comment.
- `--compact`: No whitespace is used in the AST output.
- `--silent`: Do not output the AST, just return the exit status.
- `--help`: Print the usage information and quit.
The utility spits out the syntax tree as JSON data.
## Existing plugins
- [`acorn-jsx`](https://github.com/RReverser/acorn-jsx): Parse [Facebook JSX syntax extensions](https://github.com/facebook/jsx)
Plugins for ECMAScript proposals:
- [`acorn-stage3`](https://github.com/acornjs/acorn-stage3): Parse most stage 3 proposals, bundling:
- [`acorn-async-iteration`](https://github.com/acornjs/acorn-async-iteration): Parse [async iteration proposal](https://github.com/tc39/proposal-async-iteration)
- [`acorn-bigint`](https://github.com/acornjs/acorn-bigint): Parse [BigInt proposal](https://github.com/tc39/proposal-bigint)
- [`acorn-class-fields`](https://github.com/acornjs/acorn-class-fields): Parse [class fields proposal](https://github.com/tc39/proposal-class-fields)
- [`acorn-dynamic-import`](https://github.com/kesne/acorn-dynamic-import): Parse [import() proposal](https://github.com/tc39/proposal-dynamic-import)
- [`acorn-import-meta`](https://github.com/acornjs/acorn-import-meta): Parse [import.meta proposal](https://github.com/tc39/proposal-import-meta)
- [`acorn-numeric-separator`](https://github.com/acornjs/acorn-numeric-separator): Parse [numeric separator proposal](https://github.com/tc39/proposal-numeric-separator)
- [`acorn-private-methods`](https://github.com/acornjs/acorn-private-methods): parse [private methods, getters and setters proposal](https://github.com/tc39/proposal-private-methods)n

4
node_modules/acorn/bin/acorn generated vendored
View File

@ -1,4 +0,0 @@
#!/usr/bin/env node
'use strict';
require('../dist/bin.js');

208
node_modules/acorn/dist/acorn.d.ts generated vendored
View File

@ -1,208 +0,0 @@
export as namespace acorn
export = acorn
declare namespace acorn {
function parse(input: string, options?: Options): Node
function parseExpressionAt(input: string, pos?: number, options?: Options): Node
function tokenizer(input: string, options?: Options): {
getToken(): Token
[Symbol.iterator](): Iterator<Token>
}
interface Options {
ecmaVersion?: 3 | 5 | 6 | 7 | 8 | 9 | 10 | 2015 | 2016 | 2017 | 2018 | 2019
sourceType?: 'script' | 'module'
onInsertedSemicolon?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
onTrailingComma?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
allowReserved?: boolean
allowReturnOutsideFunction?: boolean
allowImportExportEverywhere?: boolean
allowHashBang?: boolean
locations?: boolean
onToken?: ((token: Token) => any) | Token[]
onComment?: ((
isBlock: boolean, text: string, start: number, end: number, startLoc?: Position,
endLoc?: Position
) => void) | Comment[]
ranges?: boolean
program?: Node
sourceFile?: string
directSourceFile?: string
preserveParens?: boolean
}
class Parser {
constructor(options: Options, input: string, startPos?: number)
parse(): Node
static parse(input: string, options?: Options): Node
static parseExpressionAt(input: string, pos: number, options?: Options): Node
static tokenizer(input: string, options?: Options): {
getToken(): Token
[Symbol.iterator](): Iterator<Token>
}
static extend(...plugins: (typeof Parser)[]): typeof Parser
}
interface Position { line: number; column: number; offset: number }
const defaultOptions: Options
function getLineInfo(input: string, offset: number): Position
class SourceLocation {
start: Position
end: Position
source?: string | null
constructor(p: Parser, start: Position, end: Position)
}
class Node {
type: string
start: number
end: number
loc?: SourceLocation
sourceFile?: string
range?: [number, number]
constructor(parser: Parser, pos: number, loc?: SourceLocation)
}
class TokenType {
label: string
keyword: string
beforeExpr: boolean
startsExpr: boolean
isLoop: boolean
isAssign: boolean
prefix: boolean
postfix: boolean
binop: number
updateContext?: (prevType: TokenType) => void
constructor(label: string, conf?: any)
}
const tokTypes: {
num: TokenType
regexp: TokenType
string: TokenType
name: TokenType
eof: TokenType
bracketL: TokenType
bracketR: TokenType
braceL: TokenType
braceR: TokenType
parenL: TokenType
parenR: TokenType
comma: TokenType
semi: TokenType
colon: TokenType
dot: TokenType
question: TokenType
arrow: TokenType
template: TokenType
ellipsis: TokenType
backQuote: TokenType
dollarBraceL: TokenType
eq: TokenType
assign: TokenType
incDec: TokenType
prefix: TokenType
logicalOR: TokenType
logicalAND: TokenType
bitwiseOR: TokenType
bitwiseXOR: TokenType
bitwiseAND: TokenType
equality: TokenType
relational: TokenType
bitShift: TokenType
plusMin: TokenType
modulo: TokenType
star: TokenType
slash: TokenType
starstar: TokenType
_break: TokenType
_case: TokenType
_catch: TokenType
_continue: TokenType
_debugger: TokenType
_default: TokenType
_do: TokenType
_else: TokenType
_finally: TokenType
_for: TokenType
_function: TokenType
_if: TokenType
_return: TokenType
_switch: TokenType
_throw: TokenType
_try: TokenType
_var: TokenType
_const: TokenType
_while: TokenType
_with: TokenType
_new: TokenType
_this: TokenType
_super: TokenType
_class: TokenType
_extends: TokenType
_export: TokenType
_import: TokenType
_null: TokenType
_true: TokenType
_false: TokenType
_in: TokenType
_instanceof: TokenType
_typeof: TokenType
_void: TokenType
_delete: TokenType
}
class TokContext {
constructor(token: string, isExpr: boolean, preserveSpace: boolean, override?: (p: Parser) => void)
}
const tokContexts: {
b_stat: TokContext
b_expr: TokContext
b_tmpl: TokContext
p_stat: TokContext
p_expr: TokContext
q_tmpl: TokContext
f_expr: TokContext
}
function isIdentifierStart(code: number, astral?: boolean): boolean
function isIdentifierChar(code: number, astral?: boolean): boolean
interface AbstractToken {
}
interface Comment extends AbstractToken {
type: string
value: string
start: number
end: number
loc?: SourceLocation
range?: [number, number]
}
class Token {
type: TokenType
value: any
start: number
end: number
loc?: SourceLocation
range?: [number, number]
constructor(p: Parser)
}
function isNewLine(code: number): boolean
const lineBreak: RegExp
const lineBreakG: RegExp
const version: string
}

5332
node_modules/acorn/dist/acorn.js generated vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

5301
node_modules/acorn/dist/acorn.mjs generated vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

68
node_modules/acorn/dist/bin.js generated vendored
View File

@ -1,68 +0,0 @@
'use strict';
var path = require('path');
var fs = require('fs');
var acorn = require('./acorn.js');
var infile;
var forceFile;
var silent = false;
var compact = false;
var tokenize = false;
var options = {};
function help(status) {
var print = (status === 0) ? console.log : console.error;
print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|--ecma8|--ecma9|...|--ecma2015|--ecma2016|--ecma2017|--ecma2018|...]");
print(" [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] [infile]");
process.exit(status);
}
for (var i = 2; i < process.argv.length; ++i) {
var arg = process.argv[i];
if ((arg === "-" || arg[0] !== "-") && !infile) { infile = arg; }
else if (arg === "--" && !infile && i + 2 === process.argv.length) { forceFile = infile = process.argv[++i]; }
else if (arg === "--locations") { options.locations = true; }
else if (arg === "--allow-hash-bang") { options.allowHashBang = true; }
else if (arg === "--silent") { silent = true; }
else if (arg === "--compact") { compact = true; }
else if (arg === "--help") { help(0); }
else if (arg === "--tokenize") { tokenize = true; }
else if (arg === "--module") { options.sourceType = "module"; }
else {
var match = arg.match(/^--ecma(\d+)$/);
if (match)
{ options.ecmaVersion = +match[1]; }
else
{ help(1); }
}
}
function run(code) {
var result;
try {
if (!tokenize) {
result = acorn.parse(code, options);
} else {
result = [];
var tokenizer$$1 = acorn.tokenizer(code, options), token;
do {
token = tokenizer$$1.getToken();
result.push(token);
} while (token.type !== acorn.tokTypes.eof)
}
} catch (e) {
console.error(e.message);
process.exit(1);
}
if (!silent) { console.log(JSON.stringify(result, null, compact ? null : 2)); }
}
if (forceFile || infile && infile !== "-") {
run(fs.readFileSync(infile, "utf8"));
} else {
var code = "";
process.stdin.resume();
process.stdin.on("data", function (chunk) { return code += chunk; });
process.stdin.on("end", function () { return run(code); });
}

66
node_modules/acorn/package.json generated vendored
View File

@ -1,66 +0,0 @@
{
"_from": "acorn@^6.0.2",
"_id": "acorn@6.0.4",
"_inBundle": false,
"_integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==",
"_location": "/acorn",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "acorn@^6.0.2",
"name": "acorn",
"escapedName": "acorn",
"rawSpec": "^6.0.2",
"saveSpec": null,
"fetchSpec": "^6.0.2"
},
"_requiredBy": [
"/espree"
],
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
"_shasum": "77377e7353b72ec5104550aa2d2097a2fd40b754",
"_spec": "acorn@^6.0.2",
"_where": "/home/erik/Documents/workspace_brackets/BME_Project_Ohm/om/node_modules/espree",
"bin": {
"acorn": "./bin/acorn"
},
"bugs": {
"url": "https://github.com/acornjs/acorn/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "ECMAScript parser",
"engines": {
"node": ">=0.4.0"
},
"homepage": "https://github.com/acornjs/acorn",
"license": "MIT",
"main": "dist/acorn.js",
"maintainers": [
{
"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"url": "https://marijnhaverbeke.nl"
},
{
"name": "Ingvar Stepanyan",
"email": "me@rreverser.com",
"url": "https://rreverser.com/"
},
{
"name": "Adrian Heine",
"url": "http://adrianheine.de"
}
],
"module": "dist/acorn.mjs",
"name": "acorn",
"repository": {
"type": "git",
"url": "git+https://github.com/acornjs/acorn.git"
},
"scripts": {
"prepare": "cd ..; npm run build:main && npm run build:bin"
},
"version": "6.0.4"
}

20
node_modules/ajv/.tonic_example.js generated vendored
View File

@ -1,20 +0,0 @@
var Ajv = require('ajv');
var ajv = new Ajv({allErrors: true});
var schema = {
"properties": {
"foo": { "type": "string" },
"bar": { "type": "number", "maximum": 3 }
}
};
var validate = ajv.compile(schema);
test({"foo": "abc", "bar": 2});
test({"foo": 2, "bar": 4});
function test(data) {
var valid = validate(data);
if (valid) console.log('Valid!');
else console.log('Invalid: ' + ajv.errorsText(validate.errors));
}

22
node_modules/ajv/LICENSE generated vendored
View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015-2017 Evgeny Poberezkin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1295
node_modules/ajv/README.md generated vendored

File diff suppressed because it is too large Load Diff

7030
node_modules/ajv/dist/ajv.bundle.js generated vendored

File diff suppressed because it is too large Load Diff

3
node_modules/ajv/dist/ajv.min.js generated vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

370
node_modules/ajv/lib/ajv.d.ts generated vendored
View File

@ -1,370 +0,0 @@
declare var ajv: {
(options?: ajv.Options): ajv.Ajv;
new (options?: ajv.Options): ajv.Ajv;
ValidationError: ValidationError;
MissingRefError: MissingRefError;
$dataMetaSchema: object;
}
declare namespace ajv {
interface Ajv {
/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key, [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize by default).
* @param {string|object|Boolean} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated
* @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`).
*/
validate(schemaKeyRef: object | string | boolean, data: any): boolean | PromiseLike<any>;
/**
* Create validating function for passed schema.
* @param {object|Boolean} schema schema object
* @return {Function} validating function
*/
compile(schema: object | boolean): ValidateFunction;
/**
* Creates validating function for passed schema with asynchronous loading of missing schemas.
* `loadSchema` option should be a function that accepts schema uri and node-style callback.
* @this Ajv
* @param {object|Boolean} schema schema object
* @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped
* @param {Function} callback optional node-style callback, it is always called with 2 parameters: error (or null) and validating function.
* @return {PromiseLike<ValidateFunction>} validating function
*/
compileAsync(schema: object | boolean, meta?: Boolean, callback?: (err: Error, validate: ValidateFunction) => any): PromiseLike<ValidateFunction>;
/**
* Adds schema to the instance.
* @param {object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored.
* @param {string} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
* @return {Ajv} this for method chaining
*/
addSchema(schema: Array<object> | object, key?: string): Ajv;
/**
* Add schema that will be used to validate other schemas
* options in META_IGNORE_OPTIONS are alway set to false
* @param {object} schema schema object
* @param {string} key optional schema key
* @return {Ajv} this for method chaining
*/
addMetaSchema(schema: object, key?: string): Ajv;
/**
* Validate schema
* @param {object|Boolean} schema schema to validate
* @return {Boolean} true if schema is valid
*/
validateSchema(schema: object | boolean): boolean;
/**
* Get compiled schema from the instance by `key` or `ref`.
* @param {string} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id).
* @return {Function} schema validating function (with property `schema`).
*/
getSchema(keyRef: string): ValidateFunction;
/**
* Remove cached schema(s).
* If no parameter is passed all schemas but meta-schemas are removed.
* If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed.
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
* @param {string|object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object
* @return {Ajv} this for method chaining
*/
removeSchema(schemaKeyRef?: object | string | RegExp | boolean): Ajv;
/**
* Add custom format
* @param {string} name format name
* @param {string|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
* @return {Ajv} this for method chaining
*/
addFormat(name: string, format: FormatValidator | FormatDefinition): Ajv;
/**
* Define custom keyword
* @this Ajv
* @param {string} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords.
* @param {object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
* @return {Ajv} this for method chaining
*/
addKeyword(keyword: string, definition: KeywordDefinition): Ajv;
/**
* Get keyword definition
* @this Ajv
* @param {string} keyword pre-defined or custom keyword.
* @return {object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise.
*/
getKeyword(keyword: string): object | boolean;
/**
* Remove keyword
* @this Ajv
* @param {string} keyword pre-defined or custom keyword.
* @return {Ajv} this for method chaining
*/
removeKeyword(keyword: string): Ajv;
/**
* Convert array of error message objects to string
* @param {Array<object>} errors optional array of validation errors, if not passed errors from the instance are used.
* @param {object} options optional options with properties `separator` and `dataVar`.
* @return {string} human readable string with all errors descriptions
*/
errorsText(errors?: Array<ErrorObject> | null, options?: ErrorsTextOptions): string;
errors?: Array<ErrorObject>;
}
interface CustomLogger {
log(...args: any[]): any;
warn(...args: any[]): any;
error(...args: any[]): any;
}
interface ValidateFunction {
(
data: any,
dataPath?: string,
parentData?: object | Array<any>,
parentDataProperty?: string | number,
rootData?: object | Array<any>
): boolean | PromiseLike<any>;
schema?: object | boolean;
errors?: null | Array<ErrorObject>;
refs?: object;
refVal?: Array<any>;
root?: ValidateFunction | object;
$async?: true;
source?: object;
}
interface Options {
$data?: boolean;
allErrors?: boolean;
verbose?: boolean;
jsonPointers?: boolean;
uniqueItems?: boolean;
unicode?: boolean;
format?: string;
formats?: object;
unknownFormats?: true | string[] | 'ignore';
schemas?: Array<object> | object;
schemaId?: '$id' | 'id' | 'auto';
missingRefs?: true | 'ignore' | 'fail';
extendRefs?: true | 'ignore' | 'fail';
loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => PromiseLike<object | boolean>;
removeAdditional?: boolean | 'all' | 'failing';
useDefaults?: boolean | 'shared';
coerceTypes?: boolean | 'array';
async?: boolean | string;
transpile?: string | ((code: string) => string);
meta?: boolean | object;
validateSchema?: boolean | 'log';
addUsedSchema?: boolean;
inlineRefs?: boolean | number;
passContext?: boolean;
loopRequired?: number;
ownProperties?: boolean;
multipleOfPrecision?: boolean | number;
errorDataPath?: string,
messages?: boolean;
sourceCode?: boolean;
processCode?: (code: string) => string;
cache?: object;
logger?: CustomLogger | false
}
type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike<any>);
type NumberFormatValidator = ((data: number) => boolean | PromiseLike<any>);
interface NumberFormatDefinition {
type: "number",
validate: NumberFormatValidator;
compare?: (data1: number, data2: number) => number;
async?: boolean;
}
interface StringFormatDefinition {
type?: "string",
validate: FormatValidator;
compare?: (data1: string, data2: string) => number;
async?: boolean;
}
type FormatDefinition = NumberFormatDefinition | StringFormatDefinition;
interface KeywordDefinition {
type?: string | Array<string>;
async?: boolean;
$data?: boolean;
errors?: boolean | string;
metaSchema?: object;
// schema: false makes validate not to expect schema (ValidateFunction)
schema?: boolean;
modifying?: boolean;
valid?: boolean;
// one and only one of the following properties should be present
validate?: SchemaValidateFunction | ValidateFunction;
compile?: (schema: any, parentSchema: object, it: CompilationContext) => ValidateFunction;
macro?: (schema: any, parentSchema: object, it: CompilationContext) => object | boolean;
inline?: (it: CompilationContext, keyword: string, schema: any, parentSchema: object) => string;
}
interface CompilationContext {
level: number;
dataLevel: number;
schema: any;
schemaPath: string;
baseId: string;
async: boolean;
opts: Options;
formats: {
[index: string]: FormatDefinition | undefined;
};
compositeRule: boolean;
validate: (schema: object) => boolean;
util: {
copy(obj: any, target?: any): any;
toHash(source: string[]): { [index: string]: true | undefined };
equal(obj: any, target: any): boolean;
getProperty(str: string): string;
schemaHasRules(schema: object, rules: any): string;
escapeQuotes(str: string): string;
toQuotedString(str: string): string;
getData(jsonPointer: string, dataLevel: number, paths: string[]): string;
escapeJsonPointer(str: string): string;
unescapeJsonPointer(str: string): string;
escapeFragment(str: string): string;
unescapeFragment(str: string): string;
};
self: Ajv;
}
interface SchemaValidateFunction {
(
schema: any,
data: any,
parentSchema?: object,
dataPath?: string,
parentData?: object | Array<any>,
parentDataProperty?: string | number,
rootData?: object | Array<any>
): boolean | PromiseLike<any>;
errors?: Array<ErrorObject>;
}
interface ErrorsTextOptions {
separator?: string;
dataVar?: string;
}
interface ErrorObject {
keyword: string;
dataPath: string;
schemaPath: string;
params: ErrorParameters;
// Added to validation errors of propertyNames keyword schema
propertyName?: string;
// Excluded if messages set to false.
message?: string;
// These are added with the `verbose` option.
schema?: any;
parentSchema?: object;
data?: any;
}
type ErrorParameters = RefParams | LimitParams | AdditionalPropertiesParams |
DependenciesParams | FormatParams | ComparisonParams |
MultipleOfParams | PatternParams | RequiredParams |
TypeParams | UniqueItemsParams | CustomParams |
PatternRequiredParams | PropertyNamesParams |
IfParams | SwitchParams | NoParams | EnumParams;
interface RefParams {
ref: string;
}
interface LimitParams {
limit: number;
}
interface AdditionalPropertiesParams {
additionalProperty: string;
}
interface DependenciesParams {
property: string;
missingProperty: string;
depsCount: number;
deps: string;
}
interface FormatParams {
format: string
}
interface ComparisonParams {
comparison: string;
limit: number | string;
exclusive: boolean;
}
interface MultipleOfParams {
multipleOf: number;
}
interface PatternParams {
pattern: string;
}
interface RequiredParams {
missingProperty: string;
}
interface TypeParams {
type: string;
}
interface UniqueItemsParams {
i: number;
j: number;
}
interface CustomParams {
keyword: string;
}
interface PatternRequiredParams {
missingPattern: string;
}
interface PropertyNamesParams {
propertyName: string;
}
interface IfParams {
failingKeyword: string;
}
interface SwitchParams {
caseIndex: number;
}
interface NoParams {}
interface EnumParams {
allowedValues: Array<any>;
}
}
declare class ValidationError extends Error {
constructor(errors: Array<ajv.ErrorObject>);
message: string;
errors: Array<ajv.ErrorObject>;
ajv: true;
validation: true;
}
declare class MissingRefError extends Error {
constructor(baseId: string, ref: string, message?: string);
static message: (baseId: string, ref: string) => string;
message: string;
missingRef: string;
missingSchema: string;
}
export = ajv;

503
node_modules/ajv/lib/ajv.js generated vendored
View File

@ -1,503 +0,0 @@
'use strict';
var compileSchema = require('./compile')
, resolve = require('./compile/resolve')
, Cache = require('./cache')
, SchemaObject = require('./compile/schema_obj')
, stableStringify = require('fast-json-stable-stringify')
, formats = require('./compile/formats')
, rules = require('./compile/rules')
, $dataMetaSchema = require('./data')
, util = require('./compile/util');
module.exports = Ajv;
Ajv.prototype.validate = validate;
Ajv.prototype.compile = compile;
Ajv.prototype.addSchema = addSchema;
Ajv.prototype.addMetaSchema = addMetaSchema;
Ajv.prototype.validateSchema = validateSchema;
Ajv.prototype.getSchema = getSchema;
Ajv.prototype.removeSchema = removeSchema;
Ajv.prototype.addFormat = addFormat;
Ajv.prototype.errorsText = errorsText;
Ajv.prototype._addSchema = _addSchema;
Ajv.prototype._compile = _compile;
Ajv.prototype.compileAsync = require('./compile/async');
var customKeyword = require('./keyword');
Ajv.prototype.addKeyword = customKeyword.add;
Ajv.prototype.getKeyword = customKeyword.get;
Ajv.prototype.removeKeyword = customKeyword.remove;
var errorClasses = require('./compile/error_classes');
Ajv.ValidationError = errorClasses.Validation;
Ajv.MissingRefError = errorClasses.MissingRef;
Ajv.$dataMetaSchema = $dataMetaSchema;
var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema';
var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ];
var META_SUPPORT_DATA = ['/properties'];
/**
* Creates validator instance.
* Usage: `Ajv(opts)`
* @param {Object} opts optional options
* @return {Object} ajv instance
*/
function Ajv(opts) {
if (!(this instanceof Ajv)) return new Ajv(opts);
opts = this._opts = util.copy(opts) || {};
setLogger(this);
this._schemas = {};
this._refs = {};
this._fragments = {};
this._formats = formats(opts.format);
var schemaUriFormat = this._schemaUriFormat = this._formats['uri-reference'];
this._schemaUriFormatFunc = function (str) { return schemaUriFormat.test(str); };
this._cache = opts.cache || new Cache;
this._loadingSchemas = {};
this._compilations = [];
this.RULES = rules();
this._getId = chooseGetId(opts);
opts.loopRequired = opts.loopRequired || Infinity;
if (opts.errorDataPath == 'property') opts._errorDataPathProperty = true;
if (opts.serialize === undefined) opts.serialize = stableStringify;
this._metaOpts = getMetaSchemaOptions(this);
if (opts.formats) addInitialFormats(this);
addDraft6MetaSchema(this);
if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta);
addInitialSchemas(this);
}
/**
* Validate data using schema
* Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize.
* @this Ajv
* @param {String|Object} schemaKeyRef key, ref or schema object
* @param {Any} data to be validated
* @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`).
*/
function validate(schemaKeyRef, data) {
var v;
if (typeof schemaKeyRef == 'string') {
v = this.getSchema(schemaKeyRef);
if (!v) throw new Error('no schema with key or ref "' + schemaKeyRef + '"');
} else {
var schemaObj = this._addSchema(schemaKeyRef);
v = schemaObj.validate || this._compile(schemaObj);
}
var valid = v(data);
if (v.$async !== true) this.errors = v.errors;
return valid;
}
/**
* Create validating function for passed schema.
* @this Ajv
* @param {Object} schema schema object
* @param {Boolean} _meta true if schema is a meta-schema. Used internally to compile meta schemas of custom keywords.
* @return {Function} validating function
*/
function compile(schema, _meta) {
var schemaObj = this._addSchema(schema, undefined, _meta);
return schemaObj.validate || this._compile(schemaObj);
}
/**
* Adds schema to the instance.
* @this Ajv
* @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored.
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
* @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead.
* @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead.
* @return {Ajv} this for method chaining
*/
function addSchema(schema, key, _skipValidation, _meta) {
if (Array.isArray(schema)){
for (var i=0; i<schema.length; i++) this.addSchema(schema[i], undefined, _skipValidation, _meta);
return this;
}
var id = this._getId(schema);
if (id !== undefined && typeof id != 'string')
throw new Error('schema id must be string');
key = resolve.normalizeId(key || id);
checkUnique(this, key);
this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
return this;
}
/**
* Add schema that will be used to validate other schemas
* options in META_IGNORE_OPTIONS are alway set to false
* @this Ajv
* @param {Object} schema schema object
* @param {String} key optional schema key
* @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema
* @return {Ajv} this for method chaining
*/
function addMetaSchema(schema, key, skipValidation) {
this.addSchema(schema, key, skipValidation, true);
return this;
}
/**
* Validate schema
* @this Ajv
* @param {Object} schema schema to validate
* @param {Boolean} throwOrLogError pass true to throw (or log) an error if invalid
* @return {Boolean} true if schema is valid
*/
function validateSchema(schema, throwOrLogError) {
var $schema = schema.$schema;
if ($schema !== undefined && typeof $schema != 'string')
throw new Error('$schema must be a string');
$schema = $schema || this._opts.defaultMeta || defaultMeta(this);
if (!$schema) {
this.logger.warn('meta-schema not available');
this.errors = null;
return true;
}
var currentUriFormat = this._formats.uri;
this._formats.uri = typeof currentUriFormat == 'function'
? this._schemaUriFormatFunc
: this._schemaUriFormat;
var valid;
try { valid = this.validate($schema, schema); }
finally { this._formats.uri = currentUriFormat; }
if (!valid && throwOrLogError) {
var message = 'schema is invalid: ' + this.errorsText();
if (this._opts.validateSchema == 'log') this.logger.error(message);
else throw new Error(message);
}
return valid;
}
function defaultMeta(self) {
var meta = self._opts.meta;
self._opts.defaultMeta = typeof meta == 'object'
? self._getId(meta) || meta
: self.getSchema(META_SCHEMA_ID)
? META_SCHEMA_ID
: undefined;
return self._opts.defaultMeta;
}
/**
* Get compiled schema from the instance by `key` or `ref`.
* @this Ajv
* @param {String} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id).
* @return {Function} schema validating function (with property `schema`).
*/
function getSchema(keyRef) {
var schemaObj = _getSchemaObj(this, keyRef);
switch (typeof schemaObj) {
case 'object': return schemaObj.validate || this._compile(schemaObj);
case 'string': return this.getSchema(schemaObj);
case 'undefined': return _getSchemaFragment(this, keyRef);
}
}
function _getSchemaFragment(self, ref) {
var res = resolve.schema.call(self, { schema: {} }, ref);
if (res) {
var schema = res.schema
, root = res.root
, baseId = res.baseId;
var v = compileSchema.call(self, schema, root, undefined, baseId);
self._fragments[ref] = new SchemaObject({
ref: ref,
fragment: true,
schema: schema,
root: root,
baseId: baseId,
validate: v
});
return v;
}
}
function _getSchemaObj(self, keyRef) {
keyRef = resolve.normalizeId(keyRef);
return self._schemas[keyRef] || self._refs[keyRef] || self._fragments[keyRef];
}
/**
* Remove cached schema(s).
* If no parameter is passed all schemas but meta-schemas are removed.
* If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed.
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
* @this Ajv
* @param {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object
* @return {Ajv} this for method chaining
*/
function removeSchema(schemaKeyRef) {
if (schemaKeyRef instanceof RegExp) {
_removeAllSchemas(this, this._schemas, schemaKeyRef);
_removeAllSchemas(this, this._refs, schemaKeyRef);
return this;
}
switch (typeof schemaKeyRef) {
case 'undefined':
_removeAllSchemas(this, this._schemas);
_removeAllSchemas(this, this._refs);
this._cache.clear();
return this;
case 'string':
var schemaObj = _getSchemaObj(this, schemaKeyRef);
if (schemaObj) this._cache.del(schemaObj.cacheKey);
delete this._schemas[schemaKeyRef];
delete this._refs[schemaKeyRef];
return this;
case 'object':
var serialize = this._opts.serialize;
var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef;
this._cache.del(cacheKey);
var id = this._getId(schemaKeyRef);
if (id) {
id = resolve.normalizeId(id);
delete this._schemas[id];
delete this._refs[id];
}
}
return this;
}
function _removeAllSchemas(self, schemas, regex) {
for (var keyRef in schemas) {
var schemaObj = schemas[keyRef];
if (!schemaObj.meta && (!regex || regex.test(keyRef))) {
self._cache.del(schemaObj.cacheKey);
delete schemas[keyRef];
}
}
}
/* @this Ajv */
function _addSchema(schema, skipValidation, meta, shouldAddSchema) {
if (typeof schema != 'object' && typeof schema != 'boolean')
throw new Error('schema should be object or boolean');
var serialize = this._opts.serialize;
var cacheKey = serialize ? serialize(schema) : schema;
var cached = this._cache.get(cacheKey);
if (cached) return cached;
shouldAddSchema = shouldAddSchema || this._opts.addUsedSchema !== false;
var id = resolve.normalizeId(this._getId(schema));
if (id && shouldAddSchema) checkUnique(this, id);
var willValidate = this._opts.validateSchema !== false && !skipValidation;
var recursiveMeta;
if (willValidate && !(recursiveMeta = id && id == resolve.normalizeId(schema.$schema)))
this.validateSchema(schema, true);
var localRefs = resolve.ids.call(this, schema);
var schemaObj = new SchemaObject({
id: id,
schema: schema,
localRefs: localRefs,
cacheKey: cacheKey,
meta: meta
});
if (id[0] != '#' && shouldAddSchema) this._refs[id] = schemaObj;
this._cache.put(cacheKey, schemaObj);
if (willValidate && recursiveMeta) this.validateSchema(schema, true);
return schemaObj;
}
/* @this Ajv */
function _compile(schemaObj, root) {
if (schemaObj.compiling) {
schemaObj.validate = callValidate;
callValidate.schema = schemaObj.schema;
callValidate.errors = null;
callValidate.root = root ? root : callValidate;
if (schemaObj.schema.$async === true)
callValidate.$async = true;
return callValidate;
}
schemaObj.compiling = true;
var currentOpts;
if (schemaObj.meta) {
currentOpts = this._opts;
this._opts = this._metaOpts;
}
var v;
try { v = compileSchema.call(this, schemaObj.schema, root, schemaObj.localRefs); }
catch(e) {
delete schemaObj.validate;
throw e;
}
finally {
schemaObj.compiling = false;
if (schemaObj.meta) this._opts = currentOpts;
}
schemaObj.validate = v;
schemaObj.refs = v.refs;
schemaObj.refVal = v.refVal;
schemaObj.root = v.root;
return v;
/* @this {*} - custom context, see passContext option */
function callValidate() {
/* jshint validthis: true */
var _validate = schemaObj.validate;
var result = _validate.apply(this, arguments);
callValidate.errors = _validate.errors;
return result;
}
}
function chooseGetId(opts) {
switch (opts.schemaId) {
case 'auto': return _get$IdOrId;
case 'id': return _getId;
default: return _get$Id;
}
}
/* @this Ajv */
function _getId(schema) {
if (schema.$id) this.logger.warn('schema $id ignored', schema.$id);
return schema.id;
}
/* @this Ajv */
function _get$Id(schema) {
if (schema.id) this.logger.warn('schema id ignored', schema.id);
return schema.$id;
}
function _get$IdOrId(schema) {
if (schema.$id && schema.id && schema.$id != schema.id)
throw new Error('schema $id is different from id');
return schema.$id || schema.id;
}
/**
* Convert array of error message objects to string
* @this Ajv
* @param {Array<Object>} errors optional array of validation errors, if not passed errors from the instance are used.
* @param {Object} options optional options with properties `separator` and `dataVar`.
* @return {String} human readable string with all errors descriptions
*/
function errorsText(errors, options) {
errors = errors || this.errors;
if (!errors) return 'No errors';
options = options || {};
var separator = options.separator === undefined ? ', ' : options.separator;
var dataVar = options.dataVar === undefined ? 'data' : options.dataVar;
var text = '';
for (var i=0; i<errors.length; i++) {
var e = errors[i];
if (e) text += dataVar + e.dataPath + ' ' + e.message + separator;
}
return text.slice(0, -separator.length);
}
/**
* Add custom format
* @this Ajv
* @param {String} name format name
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
* @return {Ajv} this for method chaining
*/
function addFormat(name, format) {
if (typeof format == 'string') format = new RegExp(format);
this._formats[name] = format;
return this;
}
function addDraft6MetaSchema(self) {
var $dataSchema;
if (self._opts.$data) {
$dataSchema = require('./refs/data.json');
self.addMetaSchema($dataSchema, $dataSchema.$id, true);
}
if (self._opts.meta === false) return;
var metaSchema = require('./refs/json-schema-draft-07.json');
if (self._opts.$data) metaSchema = $dataMetaSchema(metaSchema, META_SUPPORT_DATA);
self.addMetaSchema(metaSchema, META_SCHEMA_ID, true);
self._refs['http://json-schema.org/schema'] = META_SCHEMA_ID;
}
function addInitialSchemas(self) {
var optsSchemas = self._opts.schemas;
if (!optsSchemas) return;
if (Array.isArray(optsSchemas)) self.addSchema(optsSchemas);
else for (var key in optsSchemas) self.addSchema(optsSchemas[key], key);
}
function addInitialFormats(self) {
for (var name in self._opts.formats) {
var format = self._opts.formats[name];
self.addFormat(name, format);
}
}
function checkUnique(self, id) {
if (self._schemas[id] || self._refs[id])
throw new Error('schema with key or id "' + id + '" already exists');
}
function getMetaSchemaOptions(self) {
var metaOpts = util.copy(self._opts);
for (var i=0; i<META_IGNORE_OPTIONS.length; i++)
delete metaOpts[META_IGNORE_OPTIONS[i]];
return metaOpts;
}
function setLogger(self) {
var logger = self._opts.logger;
if (logger === false) {
self.logger = {log: noop, warn: noop, error: noop};
} else {
if (logger === undefined) logger = console;
if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error))
throw new Error('logger must implement log, warn and error methods');
self.logger = logger;
}
}
function noop() {}

26
node_modules/ajv/lib/cache.js generated vendored
View File

@ -1,26 +0,0 @@
'use strict';
var Cache = module.exports = function Cache() {
this._cache = {};
};
Cache.prototype.put = function Cache_put(key, value) {
this._cache[key] = value;
};
Cache.prototype.get = function Cache_get(key) {
return this._cache[key];
};
Cache.prototype.del = function Cache_del(key) {
delete this._cache[key];
};
Cache.prototype.clear = function Cache_clear() {
this._cache = {};
};

View File

@ -1,90 +0,0 @@
'use strict';
var MissingRefError = require('./error_classes').MissingRef;
module.exports = compileAsync;
/**
* Creates validating function for passed schema with asynchronous loading of missing schemas.
* `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema.
* @this Ajv
* @param {Object} schema schema object
* @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped
* @param {Function} callback an optional node-style callback, it is called with 2 parameters: error (or null) and validating function.
* @return {Promise} promise that resolves with a validating function.
*/
function compileAsync(schema, meta, callback) {
/* eslint no-shadow: 0 */
/* global Promise */
/* jshint validthis: true */
var self = this;
if (typeof this._opts.loadSchema != 'function')
throw new Error('options.loadSchema should be a function');
if (typeof meta == 'function') {
callback = meta;
meta = undefined;
}
var p = loadMetaSchemaOf(schema).then(function () {
var schemaObj = self._addSchema(schema, undefined, meta);
return schemaObj.validate || _compileAsync(schemaObj);
});
if (callback) {
p.then(
function(v) { callback(null, v); },
callback
);
}
return p;
function loadMetaSchemaOf(sch) {
var $schema = sch.$schema;
return $schema && !self.getSchema($schema)
? compileAsync.call(self, { $ref: $schema }, true)
: Promise.resolve();
}
function _compileAsync(schemaObj) {
try { return self._compile(schemaObj); }
catch(e) {
if (e instanceof MissingRefError) return loadMissingSchema(e);
throw e;
}
function loadMissingSchema(e) {
var ref = e.missingSchema;
if (added(ref)) throw new Error('Schema ' + ref + ' is loaded but ' + e.missingRef + ' cannot be resolved');
var schemaPromise = self._loadingSchemas[ref];
if (!schemaPromise) {
schemaPromise = self._loadingSchemas[ref] = self._opts.loadSchema(ref);
schemaPromise.then(removePromise, removePromise);
}
return schemaPromise.then(function (sch) {
if (!added(ref)) {
return loadMetaSchemaOf(sch).then(function () {
if (!added(ref)) self.addSchema(sch, ref, undefined, meta);
});
}
}).then(function() {
return _compileAsync(schemaObj);
});
function removePromise() {
delete self._loadingSchemas[ref];
}
function added(ref) {
return self._refs[ref] || self._schemas[ref];
}
}
}
}

View File

@ -1,3 +0,0 @@
'use strict';
module.exports = require('fast-deep-equal');

View File

@ -1,34 +0,0 @@
'use strict';
var resolve = require('./resolve');
module.exports = {
Validation: errorSubclass(ValidationError),
MissingRef: errorSubclass(MissingRefError)
};
function ValidationError(errors) {
this.message = 'validation failed';
this.errors = errors;
this.ajv = this.validation = true;
}
MissingRefError.message = function (baseId, ref) {
return 'can\'t resolve reference ' + ref + ' from id ' + baseId;
};
function MissingRefError(baseId, ref, message) {
this.message = message || MissingRefError.message(baseId, ref);
this.missingRef = resolve.url(baseId, ref);
this.missingSchema = resolve.normalizeId(resolve.fullPath(this.missingRef));
}
function errorSubclass(Subclass) {
Subclass.prototype = Object.create(Error.prototype);
Subclass.prototype.constructor = Subclass;
return Subclass;
}

View File

@ -1,149 +0,0 @@
'use strict';
var util = require('./util');
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
var DAYS = [0,31,28,31,30,31,30,31,31,30,31,30,31];
var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i;
var HOSTNAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i;
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
// uri-template: https://tools.ietf.org/html/rfc6570
var URITEMPLATE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i;
// For the source: https://gist.github.com/dperini/729294
// For test cases: https://mathiasbynens.be/demo/url-regex
// @todo Delete current URL in favour of the commented out URL rule when this issue is fixed https://github.com/eslint/eslint/issues/7983.
// var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu;
var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i;
var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;
var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/;
var JSON_POINTER_URI_FRAGMENT = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;
var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;
module.exports = formats;
function formats(mode) {
mode = mode == 'full' ? 'full' : 'fast';
return util.copy(formats[mode]);
}
formats.fast = {
// date: http://tools.ietf.org/html/rfc3339#section-5.6
date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/,
// date-time: http://tools.ietf.org/html/rfc3339#section-5.6
time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,
'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,
// uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js
uri: /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i,
'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,
'uri-template': URITEMPLATE,
url: URL,
// email (sources from jsen validator):
// http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address#answer-8829363
// http://www.w3.org/TR/html5/forms.html#valid-e-mail-address (search for 'willful violation')
email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,
hostname: HOSTNAME,
// optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html
ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
// optimized http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
regex: regex,
// uuid: http://tools.ietf.org/html/rfc4122
uuid: UUID,
// JSON-pointer: https://tools.ietf.org/html/rfc6901
// uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A
'json-pointer': JSON_POINTER,
'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
// relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00
'relative-json-pointer': RELATIVE_JSON_POINTER
};
formats.full = {
date: date,
time: time,
'date-time': date_time,
uri: uri,
'uri-reference': URIREF,
'uri-template': URITEMPLATE,
url: URL,
email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,
hostname: hostname,
ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
regex: regex,
uuid: UUID,
'json-pointer': JSON_POINTER,
'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT,
'relative-json-pointer': RELATIVE_JSON_POINTER
};
function isLeapYear(year) {
// https://tools.ietf.org/html/rfc3339#appendix-C
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
}
function date(str) {
// full-date from http://tools.ietf.org/html/rfc3339#section-5.6
var matches = str.match(DATE);
if (!matches) return false;
var year = +matches[1];
var month = +matches[2];
var day = +matches[3];
return month >= 1 && month <= 12 && day >= 1 &&
day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]);
}
function time(str, full) {
var matches = str.match(TIME);
if (!matches) return false;
var hour = matches[1];
var minute = matches[2];
var second = matches[3];
var timeZone = matches[5];
return ((hour <= 23 && minute <= 59 && second <= 59) ||
(hour == 23 && minute == 59 && second == 60)) &&
(!full || timeZone);
}
var DATE_TIME_SEPARATOR = /t|\s/i;
function date_time(str) {
// http://tools.ietf.org/html/rfc3339#section-5.6
var dateTime = str.split(DATE_TIME_SEPARATOR);
return dateTime.length == 2 && date(dateTime[0]) && time(dateTime[1], true);
}
function hostname(str) {
// https://tools.ietf.org/html/rfc1034#section-3.5
// https://tools.ietf.org/html/rfc1123#section-2
return str.length <= 255 && HOSTNAME.test(str);
}
var NOT_URI_FRAGMENT = /\/|:/;
function uri(str) {
// http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "."
return NOT_URI_FRAGMENT.test(str) && URI.test(str);
}
var Z_ANCHOR = /[^\\]\\Z/;
function regex(str) {
if (Z_ANCHOR.test(str)) return false;
try {
new RegExp(str);
return true;
} catch(e) {
return false;
}
}

379
node_modules/ajv/lib/compile/index.js generated vendored
View File

@ -1,379 +0,0 @@
'use strict';
var resolve = require('./resolve')
, util = require('./util')
, errorClasses = require('./error_classes')
, stableStringify = require('fast-json-stable-stringify');
var validateGenerator = require('../dotjs/validate');
/**
* Functions below are used inside compiled validations function
*/
var ucs2length = util.ucs2length;
var equal = require('fast-deep-equal');
// this error is thrown by async schemas to return validation errors via exception
var ValidationError = errorClasses.Validation;
module.exports = compile;
/**
* Compiles schema to validation function
* @this Ajv
* @param {Object} schema schema object
* @param {Object} root object with information about the root schema for this schema
* @param {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution
* @param {String} baseId base ID for IDs in the schema
* @return {Function} validation function
*/
function compile(schema, root, localRefs, baseId) {
/* jshint validthis: true, evil: true */
/* eslint no-shadow: 0 */
var self = this
, opts = this._opts
, refVal = [ undefined ]
, refs = {}
, patterns = []
, patternsHash = {}
, defaults = []
, defaultsHash = {}
, customRules = [];
root = root || { schema: schema, refVal: refVal, refs: refs };
var c = checkCompiling.call(this, schema, root, baseId);
var compilation = this._compilations[c.index];
if (c.compiling) return (compilation.callValidate = callValidate);
var formats = this._formats;
var RULES = this.RULES;
try {
var v = localCompile(schema, root, localRefs, baseId);
compilation.validate = v;
var cv = compilation.callValidate;
if (cv) {
cv.schema = v.schema;
cv.errors = null;
cv.refs = v.refs;
cv.refVal = v.refVal;
cv.root = v.root;
cv.$async = v.$async;
if (opts.sourceCode) cv.source = v.source;
}
return v;
} finally {
endCompiling.call(this, schema, root, baseId);
}
/* @this {*} - custom context, see passContext option */
function callValidate() {
/* jshint validthis: true */
var validate = compilation.validate;
var result = validate.apply(this, arguments);
callValidate.errors = validate.errors;
return result;
}
function localCompile(_schema, _root, localRefs, baseId) {
var isRoot = !_root || (_root && _root.schema == _schema);
if (_root.schema != root.schema)
return compile.call(self, _schema, _root, localRefs, baseId);
var $async = _schema.$async === true;
var sourceCode = validateGenerator({
isTop: true,
schema: _schema,
isRoot: isRoot,
baseId: baseId,
root: _root,
schemaPath: '',
errSchemaPath: '#',
errorPath: '""',
MissingRefError: errorClasses.MissingRef,
RULES: RULES,
validate: validateGenerator,
util: util,
resolve: resolve,
resolveRef: resolveRef,
usePattern: usePattern,
useDefault: useDefault,
useCustomRule: useCustomRule,
opts: opts,
formats: formats,
logger: self.logger,
self: self
});
sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode)
+ vars(defaults, defaultCode) + vars(customRules, customRuleCode)
+ sourceCode;
if (opts.processCode) sourceCode = opts.processCode(sourceCode);
// console.log('\n\n\n *** \n', JSON.stringify(sourceCode));
var validate;
try {
var makeValidate = new Function(
'self',
'RULES',
'formats',
'root',
'refVal',
'defaults',
'customRules',
'equal',
'ucs2length',
'ValidationError',
sourceCode
);
validate = makeValidate(
self,
RULES,
formats,
root,
refVal,
defaults,
customRules,
equal,
ucs2length,
ValidationError
);
refVal[0] = validate;
} catch(e) {
self.logger.error('Error compiling schema, function code:', sourceCode);
throw e;
}
validate.schema = _schema;
validate.errors = null;
validate.refs = refs;
validate.refVal = refVal;
validate.root = isRoot ? validate : _root;
if ($async) validate.$async = true;
if (opts.sourceCode === true) {
validate.source = {
code: sourceCode,
patterns: patterns,
defaults: defaults
};
}
return validate;
}
function resolveRef(baseId, ref, isRoot) {
ref = resolve.url(baseId, ref);
var refIndex = refs[ref];
var _refVal, refCode;
if (refIndex !== undefined) {
_refVal = refVal[refIndex];
refCode = 'refVal[' + refIndex + ']';
return resolvedRef(_refVal, refCode);
}
if (!isRoot && root.refs) {
var rootRefId = root.refs[ref];
if (rootRefId !== undefined) {
_refVal = root.refVal[rootRefId];
refCode = addLocalRef(ref, _refVal);
return resolvedRef(_refVal, refCode);
}
}
refCode = addLocalRef(ref);
var v = resolve.call(self, localCompile, root, ref);
if (v === undefined) {
var localSchema = localRefs && localRefs[ref];
if (localSchema) {
v = resolve.inlineRef(localSchema, opts.inlineRefs)
? localSchema
: compile.call(self, localSchema, root, localRefs, baseId);
}
}
if (v === undefined) {
removeLocalRef(ref);
} else {
replaceLocalRef(ref, v);
return resolvedRef(v, refCode);
}
}
function addLocalRef(ref, v) {
var refId = refVal.length;
refVal[refId] = v;
refs[ref] = refId;
return 'refVal' + refId;
}
function removeLocalRef(ref) {
delete refs[ref];
}
function replaceLocalRef(ref, v) {
var refId = refs[ref];
refVal[refId] = v;
}
function resolvedRef(refVal, code) {
return typeof refVal == 'object' || typeof refVal == 'boolean'
? { code: code, schema: refVal, inline: true }
: { code: code, $async: refVal && !!refVal.$async };
}
function usePattern(regexStr) {
var index = patternsHash[regexStr];
if (index === undefined) {
index = patternsHash[regexStr] = patterns.length;
patterns[index] = regexStr;
}
return 'pattern' + index;
}
function useDefault(value) {
switch (typeof value) {
case 'boolean':
case 'number':
return '' + value;
case 'string':
return util.toQuotedString(value);
case 'object':
if (value === null) return 'null';
var valueStr = stableStringify(value);
var index = defaultsHash[valueStr];
if (index === undefined) {
index = defaultsHash[valueStr] = defaults.length;
defaults[index] = value;
}
return 'default' + index;
}
}
function useCustomRule(rule, schema, parentSchema, it) {
var validateSchema = rule.definition.validateSchema;
if (validateSchema && self._opts.validateSchema !== false) {
var valid = validateSchema(schema);
if (!valid) {
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
if (self._opts.validateSchema == 'log') self.logger.error(message);
else throw new Error(message);
}
}
var compile = rule.definition.compile
, inline = rule.definition.inline
, macro = rule.definition.macro;
var validate;
if (compile) {
validate = compile.call(self, schema, parentSchema, it);
} else if (macro) {
validate = macro.call(self, schema, parentSchema, it);
if (opts.validateSchema !== false) self.validateSchema(validate, true);
} else if (inline) {
validate = inline.call(self, it, rule.keyword, schema, parentSchema);
} else {
validate = rule.definition.validate;
if (!validate) return;
}
if (validate === undefined)
throw new Error('custom keyword "' + rule.keyword + '"failed to compile');
var index = customRules.length;
customRules[index] = validate;
return {
code: 'customRule' + index,
validate: validate
};
}
}
/**
* Checks if the schema is currently compiled
* @this Ajv
* @param {Object} schema schema to compile
* @param {Object} root root object
* @param {String} baseId base schema ID
* @return {Object} object with properties "index" (compilation index) and "compiling" (boolean)
*/
function checkCompiling(schema, root, baseId) {
/* jshint validthis: true */
var index = compIndex.call(this, schema, root, baseId);
if (index >= 0) return { index: index, compiling: true };
index = this._compilations.length;
this._compilations[index] = {
schema: schema,
root: root,
baseId: baseId
};
return { index: index, compiling: false };
}
/**
* Removes the schema from the currently compiled list
* @this Ajv
* @param {Object} schema schema to compile
* @param {Object} root root object
* @param {String} baseId base schema ID
*/
function endCompiling(schema, root, baseId) {
/* jshint validthis: true */
var i = compIndex.call(this, schema, root, baseId);
if (i >= 0) this._compilations.splice(i, 1);
}
/**
* Index of schema compilation in the currently compiled list
* @this Ajv
* @param {Object} schema schema to compile
* @param {Object} root root object
* @param {String} baseId base schema ID
* @return {Integer} compilation index
*/
function compIndex(schema, root, baseId) {
/* jshint validthis: true */
for (var i=0; i<this._compilations.length; i++) {
var c = this._compilations[i];
if (c.schema == schema && c.root == root && c.baseId == baseId) return i;
}
return -1;
}
function patternCode(i, patterns) {
return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');';
}
function defaultCode(i) {
return 'var default' + i + ' = defaults[' + i + '];';
}
function refValCode(i, refVal) {
return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];';
}
function customRuleCode(i) {
return 'var customRule' + i + ' = customRules[' + i + '];';
}
function vars(arr, statement) {
if (!arr.length) return '';
var code = '';
for (var i=0; i<arr.length; i++)
code += statement(i, arr);
return code;
}

View File

@ -1,270 +0,0 @@
'use strict';
var URI = require('uri-js')
, equal = require('fast-deep-equal')
, util = require('./util')
, SchemaObject = require('./schema_obj')
, traverse = require('json-schema-traverse');
module.exports = resolve;
resolve.normalizeId = normalizeId;
resolve.fullPath = getFullPath;
resolve.url = resolveUrl;
resolve.ids = resolveIds;
resolve.inlineRef = inlineRef;
resolve.schema = resolveSchema;
/**
* [resolve and compile the references ($ref)]
* @this Ajv
* @param {Function} compile reference to schema compilation funciton (localCompile)
* @param {Object} root object with information about the root schema for the current schema
* @param {String} ref reference to resolve
* @return {Object|Function} schema object (if the schema can be inlined) or validation function
*/
function resolve(compile, root, ref) {
/* jshint validthis: true */
var refVal = this._refs[ref];
if (typeof refVal == 'string') {
if (this._refs[refVal]) refVal = this._refs[refVal];
else return resolve.call(this, compile, root, refVal);
}
refVal = refVal || this._schemas[ref];
if (refVal instanceof SchemaObject) {
return inlineRef(refVal.schema, this._opts.inlineRefs)
? refVal.schema
: refVal.validate || this._compile(refVal);
}
var res = resolveSchema.call(this, root, ref);
var schema, v, baseId;
if (res) {
schema = res.schema;
root = res.root;
baseId = res.baseId;
}
if (schema instanceof SchemaObject) {
v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId);
} else if (schema !== undefined) {
v = inlineRef(schema, this._opts.inlineRefs)
? schema
: compile.call(this, schema, root, undefined, baseId);
}
return v;
}
/**
* Resolve schema, its root and baseId
* @this Ajv
* @param {Object} root root object with properties schema, refVal, refs
* @param {String} ref reference to resolve
* @return {Object} object with properties schema, root, baseId
*/
function resolveSchema(root, ref) {
/* jshint validthis: true */
var p = URI.parse(ref)
, refPath = _getFullPath(p)
, baseId = getFullPath(this._getId(root.schema));
if (Object.keys(root.schema).length === 0 || refPath !== baseId) {
var id = normalizeId(refPath);
var refVal = this._refs[id];
if (typeof refVal == 'string') {
return resolveRecursive.call(this, root, refVal, p);
} else if (refVal instanceof SchemaObject) {
if (!refVal.validate) this._compile(refVal);
root = refVal;
} else {
refVal = this._schemas[id];
if (refVal instanceof SchemaObject) {
if (!refVal.validate) this._compile(refVal);
if (id == normalizeId(ref))
return { schema: refVal, root: root, baseId: baseId };
root = refVal;
} else {
return;
}
}
if (!root.schema) return;
baseId = getFullPath(this._getId(root.schema));
}
return getJsonPointer.call(this, p, baseId, root.schema, root);
}
/* @this Ajv */
function resolveRecursive(root, ref, parsedRef) {
/* jshint validthis: true */
var res = resolveSchema.call(this, root, ref);
if (res) {
var schema = res.schema;
var baseId = res.baseId;
root = res.root;
var id = this._getId(schema);
if (id) baseId = resolveUrl(baseId, id);
return getJsonPointer.call(this, parsedRef, baseId, schema, root);
}
}
var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum', 'dependencies', 'definitions']);
/* @this Ajv */
function getJsonPointer(parsedRef, baseId, schema, root) {
/* jshint validthis: true */
parsedRef.fragment = parsedRef.fragment || '';
if (parsedRef.fragment.slice(0,1) != '/') return;
var parts = parsedRef.fragment.split('/');
for (var i = 1; i < parts.length; i++) {
var part = parts[i];
if (part) {
part = util.unescapeFragment(part);
schema = schema[part];
if (schema === undefined) break;
var id;
if (!PREVENT_SCOPE_CHANGE[part]) {
id = this._getId(schema);
if (id) baseId = resolveUrl(baseId, id);
if (schema.$ref) {
var $ref = resolveUrl(baseId, schema.$ref);
var res = resolveSchema.call(this, root, $ref);
if (res) {
schema = res.schema;
root = res.root;
baseId = res.baseId;
}
}
}
}
}
if (schema !== undefined && schema !== root.schema)
return { schema: schema, root: root, baseId: baseId };
}
var SIMPLE_INLINED = util.toHash([
'type', 'format', 'pattern',
'maxLength', 'minLength',
'maxProperties', 'minProperties',
'maxItems', 'minItems',
'maximum', 'minimum',
'uniqueItems', 'multipleOf',
'required', 'enum'
]);
function inlineRef(schema, limit) {
if (limit === false) return false;
if (limit === undefined || limit === true) return checkNoRef(schema);
else if (limit) return countKeys(schema) <= limit;
}
function checkNoRef(schema) {
var item;
if (Array.isArray(schema)) {
for (var i=0; i<schema.length; i++) {
item = schema[i];
if (typeof item == 'object' && !checkNoRef(item)) return false;
}
} else {
for (var key in schema) {
if (key == '$ref') return false;
item = schema[key];
if (typeof item == 'object' && !checkNoRef(item)) return false;
}
}
return true;
}
function countKeys(schema) {
var count = 0, item;
if (Array.isArray(schema)) {
for (var i=0; i<schema.length; i++) {
item = schema[i];
if (typeof item == 'object') count += countKeys(item);
if (count == Infinity) return Infinity;
}
} else {
for (var key in schema) {
if (key == '$ref') return Infinity;
if (SIMPLE_INLINED[key]) {
count++;
} else {
item = schema[key];
if (typeof item == 'object') count += countKeys(item) + 1;
if (count == Infinity) return Infinity;
}
}
}
return count;
}
function getFullPath(id, normalize) {
if (normalize !== false) id = normalizeId(id);
var p = URI.parse(id);
return _getFullPath(p);
}
function _getFullPath(p) {
return URI.serialize(p).split('#')[0] + '#';
}
var TRAILING_SLASH_HASH = /#\/?$/;
function normalizeId(id) {
return id ? id.replace(TRAILING_SLASH_HASH, '') : '';
}
function resolveUrl(baseId, id) {
id = normalizeId(id);
return URI.resolve(baseId, id);
}
/* @this Ajv */
function resolveIds(schema) {
var schemaId = normalizeId(this._getId(schema));
var baseIds = {'': schemaId};
var fullPaths = {'': getFullPath(schemaId, false)};
var localRefs = {};
var self = this;
traverse(schema, {allKeys: true}, function(sch, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
if (jsonPtr === '') return;
var id = self._getId(sch);
var baseId = baseIds[parentJsonPtr];
var fullPath = fullPaths[parentJsonPtr] + '/' + parentKeyword;
if (keyIndex !== undefined)
fullPath += '/' + (typeof keyIndex == 'number' ? keyIndex : util.escapeFragment(keyIndex));
if (typeof id == 'string') {
id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id);
var refVal = self._refs[id];
if (typeof refVal == 'string') refVal = self._refs[refVal];
if (refVal && refVal.schema) {
if (!equal(sch, refVal.schema))
throw new Error('id "' + id + '" resolves to more than one schema');
} else if (id != normalizeId(fullPath)) {
if (id[0] == '#') {
if (localRefs[id] && !equal(sch, localRefs[id]))
throw new Error('id "' + id + '" resolves to more than one schema');
localRefs[id] = sch;
} else {
self._refs[id] = fullPath;
}
}
}
baseIds[jsonPtr] = baseId;
fullPaths[jsonPtr] = fullPath;
});
return localRefs;
}

View File

@ -1,66 +0,0 @@
'use strict';
var ruleModules = require('../dotjs')
, toHash = require('./util').toHash;
module.exports = function rules() {
var RULES = [
{ type: 'number',
rules: [ { 'maximum': ['exclusiveMaximum'] },
{ 'minimum': ['exclusiveMinimum'] }, 'multipleOf', 'format'] },
{ type: 'string',
rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] },
{ type: 'array',
rules: [ 'maxItems', 'minItems', 'items', 'contains', 'uniqueItems' ] },
{ type: 'object',
rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames',
{ 'properties': ['additionalProperties', 'patternProperties'] } ] },
{ rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] }
];
var ALL = [ 'type', '$comment' ];
var KEYWORDS = [
'$schema', '$id', 'id', '$data', 'title',
'description', 'default', 'definitions',
'examples', 'readOnly', 'writeOnly',
'contentMediaType', 'contentEncoding',
'additionalItems', 'then', 'else'
];
var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ];
RULES.all = toHash(ALL);
RULES.types = toHash(TYPES);
RULES.forEach(function (group) {
group.rules = group.rules.map(function (keyword) {
var implKeywords;
if (typeof keyword == 'object') {
var key = Object.keys(keyword)[0];
implKeywords = keyword[key];
keyword = key;
implKeywords.forEach(function (k) {
ALL.push(k);
RULES.all[k] = true;
});
}
ALL.push(keyword);
var rule = RULES.all[keyword] = {
keyword: keyword,
code: ruleModules[keyword],
implements: implKeywords
};
return rule;
});
RULES.all.$comment = {
keyword: '$comment',
code: ruleModules.$comment
};
if (group.type) RULES.types[group.type] = group;
});
RULES.keywords = toHash(ALL.concat(KEYWORDS));
RULES.custom = {};
return RULES;
};

View File

@ -1,9 +0,0 @@
'use strict';
var util = require('./util');
module.exports = SchemaObject;
function SchemaObject(obj) {
util.copy(obj, this);
}

View File

@ -1,20 +0,0 @@
'use strict';
// https://mathiasbynens.be/notes/javascript-encoding
// https://github.com/bestiejs/punycode.js - punycode.ucs2.decode
module.exports = function ucs2length(str) {
var length = 0
, len = str.length
, pos = 0
, value;
while (pos < len) {
length++;
value = str.charCodeAt(pos++);
if (value >= 0xD800 && value <= 0xDBFF && pos < len) {
// high surrogate, and there is a next character
value = str.charCodeAt(pos);
if ((value & 0xFC00) == 0xDC00) pos++; // low surrogate
}
}
return length;
};

267
node_modules/ajv/lib/compile/util.js generated vendored
View File

@ -1,267 +0,0 @@
'use strict';
module.exports = {
copy: copy,
checkDataType: checkDataType,
checkDataTypes: checkDataTypes,
coerceToTypes: coerceToTypes,
toHash: toHash,
getProperty: getProperty,
escapeQuotes: escapeQuotes,
equal: require('fast-deep-equal'),
ucs2length: require('./ucs2length'),
varOccurences: varOccurences,
varReplace: varReplace,
cleanUpCode: cleanUpCode,
finalCleanUpCode: finalCleanUpCode,
schemaHasRules: schemaHasRules,
schemaHasRulesExcept: schemaHasRulesExcept,
toQuotedString: toQuotedString,
getPathExpr: getPathExpr,
getPath: getPath,
getData: getData,
unescapeFragment: unescapeFragment,
unescapeJsonPointer: unescapeJsonPointer,
escapeFragment: escapeFragment,
escapeJsonPointer: escapeJsonPointer
};
function copy(o, to) {
to = to || {};
for (var key in o) to[key] = o[key];
return to;
}
function checkDataType(dataType, data, negate) {
var EQUAL = negate ? ' !== ' : ' === '
, AND = negate ? ' || ' : ' && '
, OK = negate ? '!' : ''
, NOT = negate ? '' : '!';
switch (dataType) {
case 'null': return data + EQUAL + 'null';
case 'array': return OK + 'Array.isArray(' + data + ')';
case 'object': return '(' + OK + data + AND +
'typeof ' + data + EQUAL + '"object"' + AND +
NOT + 'Array.isArray(' + data + '))';
case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND +
NOT + '(' + data + ' % 1)' +
AND + data + EQUAL + data + ')';
default: return 'typeof ' + data + EQUAL + '"' + dataType + '"';
}
}
function checkDataTypes(dataTypes, data) {
switch (dataTypes.length) {
case 1: return checkDataType(dataTypes[0], data, true);
default:
var code = '';
var types = toHash(dataTypes);
if (types.array && types.object) {
code = types.null ? '(': '(!' + data + ' || ';
code += 'typeof ' + data + ' !== "object")';
delete types.null;
delete types.array;
delete types.object;
}
if (types.number) delete types.integer;
for (var t in types)
code += (code ? ' && ' : '' ) + checkDataType(t, data, true);
return code;
}
}
var COERCE_TO_TYPES = toHash([ 'string', 'number', 'integer', 'boolean', 'null' ]);
function coerceToTypes(optionCoerceTypes, dataTypes) {
if (Array.isArray(dataTypes)) {
var types = [];
for (var i=0; i<dataTypes.length; i++) {
var t = dataTypes[i];
if (COERCE_TO_TYPES[t]) types[types.length] = t;
else if (optionCoerceTypes === 'array' && t === 'array') types[types.length] = t;
}
if (types.length) return types;
} else if (COERCE_TO_TYPES[dataTypes]) {
return [dataTypes];
} else if (optionCoerceTypes === 'array' && dataTypes === 'array') {
return ['array'];
}
}
function toHash(arr) {
var hash = {};
for (var i=0; i<arr.length; i++) hash[arr[i]] = true;
return hash;
}
var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
var SINGLE_QUOTE = /'|\\/g;
function getProperty(key) {
return typeof key == 'number'
? '[' + key + ']'
: IDENTIFIER.test(key)
? '.' + key
: "['" + escapeQuotes(key) + "']";
}
function escapeQuotes(str) {
return str.replace(SINGLE_QUOTE, '\\$&')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\f/g, '\\f')
.replace(/\t/g, '\\t');
}
function varOccurences(str, dataVar) {
dataVar += '[^0-9]';
var matches = str.match(new RegExp(dataVar, 'g'));
return matches ? matches.length : 0;
}
function varReplace(str, dataVar, expr) {
dataVar += '([^0-9])';
expr = expr.replace(/\$/g, '$$$$');
return str.replace(new RegExp(dataVar, 'g'), expr + '$1');
}
var EMPTY_ELSE = /else\s*{\s*}/g
, EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g
, EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g;
function cleanUpCode(out) {
return out.replace(EMPTY_ELSE, '')
.replace(EMPTY_IF_NO_ELSE, '')
.replace(EMPTY_IF_WITH_ELSE, 'if (!($1))');
}
var ERRORS_REGEXP = /[^v.]errors/g
, REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g
, REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g
, RETURN_VALID = 'return errors === 0;'
, RETURN_TRUE = 'validate.errors = null; return true;'
, RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/
, RETURN_DATA_ASYNC = 'return data;'
, ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g
, REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/;
function finalCleanUpCode(out, async) {
var matches = out.match(ERRORS_REGEXP);
if (matches && matches.length == 2) {
out = async
? out.replace(REMOVE_ERRORS_ASYNC, '')
.replace(RETURN_ASYNC, RETURN_DATA_ASYNC)
: out.replace(REMOVE_ERRORS, '')
.replace(RETURN_VALID, RETURN_TRUE);
}
matches = out.match(ROOTDATA_REGEXP);
if (!matches || matches.length !== 3) return out;
return out.replace(REMOVE_ROOTDATA, '');
}
function schemaHasRules(schema, rules) {
if (typeof schema == 'boolean') return !schema;
for (var key in schema) if (rules[key]) return true;
}
function schemaHasRulesExcept(schema, rules, exceptKeyword) {
if (typeof schema == 'boolean') return !schema && exceptKeyword != 'not';
for (var key in schema) if (key != exceptKeyword && rules[key]) return true;
}
function toQuotedString(str) {
return '\'' + escapeQuotes(str) + '\'';
}
function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
var path = jsonPointers // false by default
? '\'/\' + ' + expr + (isNumber ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')')
: (isNumber ? '\'[\' + ' + expr + ' + \']\'' : '\'[\\\'\' + ' + expr + ' + \'\\\']\'');
return joinPaths(currentPath, path);
}
function getPath(currentPath, prop, jsonPointers) {
var path = jsonPointers // false by default
? toQuotedString('/' + escapeJsonPointer(prop))
: toQuotedString(getProperty(prop));
return joinPaths(currentPath, path);
}
var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/;
var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
function getData($data, lvl, paths) {
var up, jsonPointer, data, matches;
if ($data === '') return 'rootData';
if ($data[0] == '/') {
if (!JSON_POINTER.test($data)) throw new Error('Invalid JSON-pointer: ' + $data);
jsonPointer = $data;
data = 'rootData';
} else {
matches = $data.match(RELATIVE_JSON_POINTER);
if (!matches) throw new Error('Invalid JSON-pointer: ' + $data);
up = +matches[1];
jsonPointer = matches[2];
if (jsonPointer == '#') {
if (up >= lvl) throw new Error('Cannot access property/index ' + up + ' levels up, current level is ' + lvl);
return paths[lvl - up];
}
if (up > lvl) throw new Error('Cannot access data ' + up + ' levels up, current level is ' + lvl);
data = 'data' + ((lvl - up) || '');
if (!jsonPointer) return data;
}
var expr = data;
var segments = jsonPointer.split('/');
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
if (segment) {
data += getProperty(unescapeJsonPointer(segment));
expr += ' && ' + data;
}
}
return expr;
}
function joinPaths (a, b) {
if (a == '""') return b;
return (a + ' + ' + b).replace(/' \+ '/g, '');
}
function unescapeFragment(str) {
return unescapeJsonPointer(decodeURIComponent(str));
}
function escapeFragment(str) {
return encodeURIComponent(escapeJsonPointer(str));
}
function escapeJsonPointer(str) {
return str.replace(/~/g, '~0').replace(/\//g, '~1');
}
function unescapeJsonPointer(str) {
return str.replace(/~1/g, '/').replace(/~0/g, '~');
}

49
node_modules/ajv/lib/data.js generated vendored
View File

@ -1,49 +0,0 @@
'use strict';
var KEYWORDS = [
'multipleOf',
'maximum',
'exclusiveMaximum',
'minimum',
'exclusiveMinimum',
'maxLength',
'minLength',
'pattern',
'additionalItems',
'maxItems',
'minItems',
'uniqueItems',
'maxProperties',
'minProperties',
'required',
'additionalProperties',
'enum',
'format',
'const'
];
module.exports = function (metaSchema, keywordsJsonPointers) {
for (var i=0; i<keywordsJsonPointers.length; i++) {
metaSchema = JSON.parse(JSON.stringify(metaSchema));
var segments = keywordsJsonPointers[i].split('/');
var keywords = metaSchema;
var j;
for (j=1; j<segments.length; j++)
keywords = keywords[segments[j]];
for (j=0; j<KEYWORDS.length; j++) {
var key = KEYWORDS[j];
var schema = keywords[key];
if (schema) {
keywords[key] = {
anyOf: [
schema,
{ $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#' }
]
};
}
}
}
return metaSchema;
};

104
node_modules/ajv/lib/dot/_limit.jst generated vendored
View File

@ -1,104 +0,0 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.$data }}
{{## def.setExclusiveLimit:
$exclusive = true;
$errorKeyword = $exclusiveKeyword;
$errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
#}}
{{
var $isMax = $keyword == 'maximum'
, $exclusiveKeyword = $isMax ? 'exclusiveMaximum' : 'exclusiveMinimum'
, $schemaExcl = it.schema[$exclusiveKeyword]
, $isDataExcl = it.opts.$data && $schemaExcl && $schemaExcl.$data
, $op = $isMax ? '<' : '>'
, $notOp = $isMax ? '>' : '<'
, $errorKeyword = undefined;
}}
{{? $isDataExcl }}
{{
var $schemaValueExcl = it.util.getData($schemaExcl.$data, $dataLvl, it.dataPathArr)
, $exclusive = 'exclusive' + $lvl
, $exclType = 'exclType' + $lvl
, $exclIsNumber = 'exclIsNumber' + $lvl
, $opExpr = 'op' + $lvl
, $opStr = '\' + ' + $opExpr + ' + \'';
}}
var schemaExcl{{=$lvl}} = {{=$schemaValueExcl}};
{{ $schemaValueExcl = 'schemaExcl' + $lvl; }}
var {{=$exclusive}};
var {{=$exclType}} = typeof {{=$schemaValueExcl}};
if ({{=$exclType}} != 'boolean' && {{=$exclType}} != 'undefined' && {{=$exclType}} != 'number') {
{{ var $errorKeyword = $exclusiveKeyword; }}
{{# def.error:'_exclusiveLimit' }}
} else if ({{# def.$dataNotType:'number' }}
{{=$exclType}} == 'number'
? (
({{=$exclusive}} = {{=$schemaValue}} === undefined || {{=$schemaValueExcl}} {{=$op}}= {{=$schemaValue}})
? {{=$data}} {{=$notOp}}= {{=$schemaValueExcl}}
: {{=$data}} {{=$notOp}} {{=$schemaValue}}
)
: (
({{=$exclusive}} = {{=$schemaValueExcl}} === true)
? {{=$data}} {{=$notOp}}= {{=$schemaValue}}
: {{=$data}} {{=$notOp}} {{=$schemaValue}}
)
|| {{=$data}} !== {{=$data}}) {
var op{{=$lvl}} = {{=$exclusive}} ? '{{=$op}}' : '{{=$op}}=';
{{
if ($schema === undefined) {
$errorKeyword = $exclusiveKeyword;
$errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword;
$schemaValue = $schemaValueExcl;
$isData = $isDataExcl;
}
}}
{{??}}
{{
var $exclIsNumber = typeof $schemaExcl == 'number'
, $opStr = $op; /*used in error*/
}}
{{? $exclIsNumber && $isData }}
{{ var $opExpr = '\'' + $opStr + '\''; /*used in error*/ }}
if ({{# def.$dataNotType:'number' }}
( {{=$schemaValue}} === undefined
|| {{=$schemaExcl}} {{=$op}}= {{=$schemaValue}}
? {{=$data}} {{=$notOp}}= {{=$schemaExcl}}
: {{=$data}} {{=$notOp}} {{=$schemaValue}} )
|| {{=$data}} !== {{=$data}}) {
{{??}}
{{
if ($exclIsNumber && $schema === undefined) {
{{# def.setExclusiveLimit }}
$schemaValue = $schemaExcl;
$notOp += '=';
} else {
if ($exclIsNumber)
$schemaValue = Math[$isMax ? 'min' : 'max']($schemaExcl, $schema);
if ($schemaExcl === ($exclIsNumber ? $schemaValue : true)) {
{{# def.setExclusiveLimit }}
$notOp += '=';
} else {
$exclusive = false;
$opStr += '=';
}
}
var $opExpr = '\'' + $opStr + '\''; /*used in error*/
}}
if ({{# def.$dataNotType:'number' }}
{{=$data}} {{=$notOp}} {{=$schemaValue}}
|| {{=$data}} !== {{=$data}}) {
{{?}}
{{?}}
{{ $errorKeyword = $errorKeyword || $keyword; }}
{{# def.error:'_limit' }}
} {{? $breakOnError }} else { {{?}}

View File

@ -1,10 +0,0 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.$data }}
{{ var $op = $keyword == 'maxItems' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{=$data}}.length {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
{{# def.error:'_limitItems' }}
} {{? $breakOnError }} else { {{?}}

View File

@ -1,10 +0,0 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{# def.$data }}
{{ var $op = $keyword == 'maxLength' ? '>' : '<'; }}
if ({{# def.$dataNotType:'number' }} {{# def.strLength }} {{=$op}} {{=$schemaValue}}) {
{{ var $errorKeyword = $keyword; }}
{{# def.error:'_limitLength' }}
} {{? $breakOnError }} else { {{?}}

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