diff --git a/.gitignore b/.gitignore
index c0cdf6a..d4a4628 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
.gradle/
build/
.idea/
-/logs/
+logs/
!gradle/wrapper/gradle-wrapper.jar
/deploy.ps1
diff --git a/.space.kts b/.space.kts
index 80633f7..c504693 100644
--- a/.space.kts
+++ b/.space.kts
@@ -4,15 +4,15 @@ job("Deploy") {
}
container(image = "gradle:jdk17-alpine") {
- env["SPC_HOST"] = Params("spc-host")
- env["SPC_USER"] = Secrets("spc-webmaster-user")
- env["SPC_ID"] = Secrets("spc-webmaster-id")
+ env["SPC_HOST"] = "{{ project:spc-host }}"
+ env["SPC_USER"] = "{{ project:spc-webmaster-user }}"
+ env["SPC_ID"] = "{{ project:spc-webmaster-id }}"
kotlinScript { api ->
api.space().projects.automation.deployments.start(
project = api.projectIdentifier(),
targetIdentifier = TargetIdentifier.Key("spc-site"),
version = "current",
- // automatically update deployment status based on a status of a job
+ // automatically update deployment status based on the status of the job
syncWithAutomationJob = true
)
api.gradle("uploadDistribution")
@@ -26,9 +26,9 @@ job("Restart service"){
}
container(image = "gradle:jdk17-alpine") {
- env["SPC_HOST"] = Params("spc-host")
- env["SPC_USER"] = Secrets("spc-webmaster-user")
- env["SPC_ID"] = Secrets("spc-webmaster-id")
+ env["SPC_HOST"] = "{{ project:spc-host }}"
+ env["SPC_USER"] = "{{ project:spc-webmaster-user }}"
+ env["SPC_ID"] = "{{ project:spc-webmaster-id }}"
kotlinScript { api ->
api.gradle("reloadDistribution")
}
diff --git a/build.gradle.kts b/build.gradle.kts
index 2f8c983..c5b956c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -35,7 +35,7 @@ dependencies {
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.ktor:ktor-server-http-redirect:$ktorVersion")
implementation("io.ktor:ktor-server-forwarded-header:$ktorVersion")
- implementation("ch.qos.logback:logback-classic:1.2.11")
+ implementation("ch.qos.logback:logback-classic:1.4.12")
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
}
@@ -52,22 +52,23 @@ apiValidation{
val host = System.getenv("SPC_HOST")
val user = System.getenv("SPC_USER")
-//val password = System.getenv("SPC_PASSWORD")
-val identityString = System.getenv("SPC_ID")
+val password = System.getenv("SPC_PASSWORD")
+val privateKey = System.getenv("SPC_ID")
+//val publicKey = System.getenv("SPC_PUBKEY")
val serviceName = "sciprog-site"
-if (host != null && user != null || identityString != null) {
+if (host != null && user != null || privateKey != null) {
val uploadDistribution by tasks.creating {
group = "distribution"
dependsOn("installDist")
doLast {
JSch {
- addIdentity("spc-webmaster", identityString.encodeToByteArray(), null, null)
+ addIdentity("webmaster", privateKey.encodeToByteArray(), null, null)
}.useSession(host, user) {
//stopping service during the upload
execute("sudo systemctl stop $serviceName")
uploadDirectory(buildDir.resolve("install/spc-site"), "/opt")
- //adding executable flag to the entry point
+ //adding an executable flag to the entry point
execute("sudo chmod +x /opt/spc-site/bin/spc-site")
execute("sudo systemctl start $serviceName")
}
@@ -78,7 +79,7 @@ if (host != null && user != null || identityString != null) {
group = "distribution"
doLast {
JSch {
- addIdentity("spc-webmaster", identityString.encodeToByteArray(), null, null)
+ addIdentity("webmaster", privateKey.encodeToByteArray(), null, null)
}.useSession(host, user) {
execute("sudo systemctl restart $serviceName")
}
diff --git a/data/home/content/education/index.md b/data/home/content/education/index.md
index bea803d..cc04c0a 100644
--- a/data/home/content/education/index.md
+++ b/data/home/content/education/index.md
@@ -12,15 +12,15 @@ language: en
Master program
Master program "Scientific programming" at MIPT aims to prepare specialists both in application programming and domain field (such as physics, biology, biotechnology, computer science and other research areas).
-
In modern science and technology application level programming is an integral part of any major work. And in order to be successful in this field one needs to know both software engineering and the domain field.
+
In modern science and technology, application level programming is an integral part of any major work. And in order to be successful in this field, one needs to know both software engineering and the domain field.
-## Courses in 2022-2023:
+## Courses in 2022–2023:
### [Scientific literature seminar](#)
*curated by [Aleksandr Svetlichnyi](${resolvePageRef("team")}#svetlichnyi)*
@@ -45,11 +45,6 @@ Actual program: [SPC-A-6](https://npm.mipt.ru/youtrack/articles/SPC-A-6)
*by Konstantin Tikhonov and Rolan Grinis*
Actual program: [SPC-A-3](https://npm.mipt.ru/youtrack/articles/SPC-A-3)
-
-### [Instruments of development](https://npm.mipt.ru/youtrack/articles/SPC-A-5)
-*curated by [Alexander Nozik](${resolvePageRef("team.index")}#nozik)*
-
-Actual program: [SPC-A-5](https://npm.mipt.ru/youtrack/articles/SPC-A-5)
### [Advanced Python](https://npm.mipt.ru/youtrack/articles/SPC-A-4)
*by Mikhail Zelenyy*
diff --git a/data/home/content/index.md b/data/home/content/index.md
new file mode 100644
index 0000000..d0fe2e7
--- /dev/null
+++ b/data/home/content/index.md
@@ -0,0 +1,4 @@
+---
+pageName: SPC
+language: en
+---
\ No newline at end of file
diff --git a/data/home/content/people/Kaplenko.md b/data/home/content/people/Kaplenko.md
index 7ec6485..d59f223 100644
--- a/data/home/content/people/Kaplenko.md
+++ b/data/home/content/people/Kaplenko.md
@@ -1,3 +1,4 @@
+
---
type: team
title: Varvara Kaplenko
diff --git a/data/home/content/people/Nozik[info].md b/data/home/content/people/Nozik[info].md
index d7c3744..b35d0eb 100644
--- a/data/home/content/people/Nozik[info].md
+++ b/data/home/content/people/Nozik[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
**Director of the centre**
* PhD in particle physics.
diff --git a/data/home/content/people/Palmin.md b/data/home/content/people/Palmin.md
index 04ac686..9b44d5f 100644
--- a/data/home/content/people/Palmin.md
+++ b/data/home/content/people/Palmin.md
@@ -4,6 +4,7 @@ title: Vladimir Palmin
id: palmin
order: 10
language: en
+published: false
image:
path: images/people/palmin.jpg
position: left
diff --git a/data/home/content/people/Svetlichnyi[info].md b/data/home/content/people/Svetlichnyi[info].md
index ee01128..85e8520 100644
--- a/data/home/content/people/Svetlichnyi[info].md
+++ b/data/home/content/people/Svetlichnyi[info].md
@@ -1 +1,5 @@
+---
+language: en
+---
+
**Vice-director for education**
\ No newline at end of file
diff --git a/data/home/content/projects/bmn.md b/data/home/content/projects/bmn.md
new file mode 100644
index 0000000..07bf53b
--- /dev/null
+++ b/data/home/content/projects/bmn.md
@@ -0,0 +1,109 @@
+---
+type: project
+title: BM@N infrastructure
+order: 150
+language: en
+image: images/projects/bmn/nica.png
+---
+## Information Systems for BM@N
+
+Our team works on development of information systems and services for BM@N (Baryonic Matter at Nuclotron) experiment, part of NICA (Nuclotron-based Ion Collider fAсility) megaproject (located in Dubna, Russia). These works are performed together with scientists from JINR and other institutions.
+
+The overall view of NICA complex with already running experiment BM@N and future collider experiments MPD and SPD is shown in Fig. 1. BM@N studies collisions of elementary particles and ions with a fixed target with energies up to 6 GeV per nucleon, which is a very interesting energy region for fundamental nuclear physics research.
+
+
+ Fig. 1
+
+
+
+### Event Metadata System
+
+Event metadata systems (EMS) are widely used in the physics experiments on particle collisions, where large numbers of experimental events, typically billions, are collected. At the stage of particular physics analysis of experimental data, only a small subset of collected events meeting certain criteria is usually of interest, and passing over the whole amount of stored data to form the subset is overly time-consuming and resource-intensive. Instead, metadata systems allow one to obtain only the required events (or, at least, references to them) via searching and filtering based on given fields (event attributes) recorded in the metadata system.
+
+The NICA Event Metadata System has been developed for storing necessary event metadata for the NICA experiments. The information system makes it possible to quickly search for a necessary subset of physics events by required parameters to use in further event data processing. It provides summary description of collision events and their identifiers to search and select events for a desired analysis task, enables their management and convenient access; provides online and offline interfaces for selecting events of interest, such as Web and REST API services, and a dedicated C++/ROOT interface.
+
+The Event Metadata System, including the Event Catalogue based on PostgreSQL, Metadata API, Web Service, and other developed components, has been provisioned using a common deployment system for the first experiment of the NICA project, BM@N. The main interfaces to EMS, namely, Web UI and REST API have been developed using Kotlin multiplatform technology as a part of the single full-stack project. The EMS provides acceptable response times for the expected amount of the event metadata. The system and its services will be further evolved and improved following operational experience.
+
+Figs. 2 and 3 illustrate high-level EMS architecture and its basic Web UI view.
+
+
+ Fig. 2
+
+
+
+
+ Fig. 3
+
+
+
+### Next-Generation Event Visualization (Event Display) System
+
+In high-energy physics experiments, the ability to display both detector geometry and particle tracks has become an essential feature, required for physicists to better understand particular collision events as well as to present the physical results to a wider audience.
+
+For BM@N experiment, a new event visualization solution was developed based on VisionForge, a modern open-source visualization system. An important part of the solution is integration of the system with experiment's software framework BmnRoot, which is a CERN ROOT-based environment.
+
+
+Figs. 4, 5 illustrate how the Web interface of a new system looks like. The user can conveniently move and rotate the camera, zoom-in and zoom-out the scene as needed, browse the scene graph (object tree). For every object it is possible to change display properties such as color, opacity and visibility. Note that the full BM@N geometry model includes more than 400,000 primitives so rendering them all requires significant resources. To optimize the resource usage, so-called “prototypes” were implemented in VisionForge model for three-dimensional objects. For a single prototype its geometry is rendered only once and reused for multiple objects, which helps to significantly reduce memory usage.
+
+
+ Fig. 4
+
+
+
+
+ Fig. 5
+
+
+
+### Condition Database Services
+
+The Unified Condition Database (shortly called UniConDa) of the BM@N experiment is one of the advanced information systems required to support automation of experimental data collecting and processing, providing a central storage for experiment metadata being necessary for event data processing, including session and run information, detector and subsystem parameters, and descriptions of simulated event files.
+
+The condition database has been earlier implemented based on the PostgreSQL database management system in accordance with a well-designed scheme. An ecosystem of its supplementary services is constantly being improved to increase overall degree of database integration and convenience of the use by collaboration members.
+
+For the UniConDa ecosystem, the REST API, a modern HTTP-based interface for integration with both ROOT-based and non-ROOT software systems of the experiment, was developed.
+
+Additionally, Smart Data Parser, a part of the BM@N information system based on the Unified Condition Database, was created to solve the task of the parameter data migration from accumulated files of ASCII text, CSV or XML formats to the Unified Condition Database of the BM@N experiment (Fig. 6).
+
+
+ Fig. 6
+
+
+
+### Monitoring Service
+
+The software infrastructure of the BM@N experiment contains a set of various information systems that are essential for the work with experimental or simulated data on all processing stages, including the collection, storage, intermediate processing and physics analysis. In case one of such systems stops functioning, the work with BM@N data by collaboration members gets either impossible or, at least, much less productive. Due to this fact, the timely detection of possible failures in the systems due to software or hardware failures is fairly important.
+
+The developed Monitoring Service is used to check availability and health status of information systems. This includes measuring, storing, visualizing and sending alert notifications on monitored parameters, such as CPU, memory and disk utilization, DBMS functioning parameters, response times of databases and API endpoints, ping round-trip times, and so on. The current implementation of the BM@N monitoring service is illustrated in Figs. 7, 8. Note that a related task of building highly available information services is currently a work in progress.
+
+
+ Fig. 7
+
+
+
+
+ Fig. 8
+
+
+
+
+### Slow Control System Viewer
+
+Web interface for slow control system of BM@N was developed. It visualizes various sensor data graphs based on parameters and time intervals provided by user as shown in Fig. 9.
+
+
+ Fig. 9
+
+
+
+### References
+
+1. K. Gertsenberger, P. Klimai, M. Zelenyi. Auxiliary Services for the Condition Database of the BM@N Experiment at NICA // Phys.Part.Nucl.Lett. 20 (2023) 1217. https://www.doi.org/10.1134/S1547477123050291
+2. E. Alexandrov, I. Alexandrov, A. Chebotov, A. Degtyarev, I. Filozova, K. Gertsenberger, P. Klimai, A. Yakovlev. Implementation of the Event Metadata System for physics analysis in the NICA experiments // Journal of Physics: Conference Series 2438 (2023) 012046. https://www.doi.org/10.1088/1742-6596/2438/1/012046
+2. A. Degtyarev, K. Gertsenberger, P. Klimai. Usage of Apache Cassandra for Prototyping the Event Metadata System of the NICA Experiments // Phys.Part.Nucl.Lett. 19 (2022) 5, 562-565. https://doi.org/10.1134/S1547477122050144
+3. A. Chebotov, K. Gertsenberger, P. Klimai, A. Moshkin. Information System Based on the Condition Database for the NICA Experiments, User WEB Application, and Related Services // Phys.Part.Nucl.Lett. 19 (2022) 5, 558-561. https://doi.org/10.1134/S1547477122050132
+4. E. Alexandrov, I. Alexandrov, A. Degtyarev, K. Gertsenberger, I. Filozova, P. Klimai, A. Nozik, A. Yakovlev. Design of the Event Metadata System for the Experiments at NICA // Phys.Part.Nucl.Lett. 18 (2021) 5, 603-616. https://doi.org/10.1134/S1547477121050034
+5. K. Gertsenberger, I. Alexandrov, I. Filozova, E. Alexandrov, A. Moshkin, A. Chebotov, M. Mineev, D. Pryahina, G. Shestakova, A. Yakovlev, A. Nozik, P. Klimai. Development of Information Systems for Online and Offline Data Processing in the NICA Experiments // Phys.Part.Nucl. 52 (2021) 4, 801-807. https://doi.org/10.1134/S1063779621040250
+6. K.V. Gertsenberger, A.I. Chebotov, P.A. Klimai, I.N. Alexandrov, E.I. Alexandrov, I.A. Filozova, A.A. Moshkin. Implementation of the Condition Database for the Experiments of the NICA Complex // CEUR Workshop Proceedings, Vol. 3041 (2021) 128-132. https://doi.org/10.54546/MLIT.2021.53.54.001
+7. E.I. Alexandrov, I.N. Alexandrov, A.G. Degtyarev, I.A. Filozova, K.V. Gertsenberger, P.A. Klimai, A.V. Yakovlev. Development of the Event Metadata System for the NICA experiments // CEUR Workshop Proceedings, Vol. 3041 (2021) 439-444. https://doi.org/10.54546/MLIT.2021.80.18.001
+8. M.N.Kapishin et al, The Report on Project “Studies of Baryonic Matter at the Nuclotron (BM@N)”, https://bmn.jinr.ru/wp-content/uploads/2022/10/BMNproject_2021cor2.pdf
diff --git a/data/home/content/projects/bmn[info].md b/data/home/content/projects/bmn[info].md
new file mode 100644
index 0000000..28d36a6
--- /dev/null
+++ b/data/home/content/projects/bmn[info].md
@@ -0,0 +1,5 @@
+---
+language: en
+---
+
+Our team works on development of information systems and services for BM@N (Baryonic Matter at Nuclotron) experiment, part of NICA (Nuclotron-based Ion Collider fAсility) megaproject (located in Dubna, Russia). These works are performed together with scientists from JINR and other institutions.
\ No newline at end of file
diff --git a/data/home/content/projects/controls[info].md b/data/home/content/projects/controls[info].md
index 0ef95ad..6d15c0d 100644
--- a/data/home/content/projects/controls[info].md
+++ b/data/home/content/projects/controls[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
Controls.kt is a data acquisition framework (work in progress). It is based on DataForge, a software framework for automated data processing.
[Repository and documentation](https://github.com/mipt-npm/controls.kt)
\ No newline at end of file
diff --git a/data/home/content/projects/dataforge.md b/data/home/content/projects/dataforge.md
index 5dfc6d8..4d4d3a8 100644
--- a/data/home/content/projects/dataforge.md
+++ b/data/home/content/projects/dataforge.md
@@ -21,7 +21,7 @@ A presentation on application of (old version of) DataForge to Troitsk nu-mass a
## Questions and Answers
-In this section we will try to cover DataForge main ideas in the form of questions and answers.
+In this section, we will try to cover DataForge main ideas in the form of questions and answers.
### General
**Q**: I have a lot of data to analyze. The analysis process is complicated, requires a lot of stages and data flow is not always obvious. To top it the data size is huge, so I don't want to perform operation I don't need (calculate something I won't need or calculate something twice). And yes, I need it to be performed in parallel and probably on remote computer. By the way, I am sick and tired of scripts that modify other scripts that control scripts. Could you help me?
diff --git a/data/home/content/projects/dataforge[info].md b/data/home/content/projects/dataforge[info].md
index 4e24075..7692f12 100644
--- a/data/home/content/projects/dataforge[info].md
+++ b/data/home/content/projects/dataforge[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
A metadata processing workflow manipulation framework.
[Repository and documentation](https://github.com/mipt-npm/dataforge-core)
\ No newline at end of file
diff --git a/data/home/content/projects/kmath[info].md b/data/home/content/projects/kmath[info].md
index 67b5edb..a78ddb2 100644
--- a/data/home/content/projects/kmath[info].md
+++ b/data/home/content/projects/kmath[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
An experimental Kotlin library for mathematical operations, built on the principle of context-oriented programming using mathematical abstractions.
[Repository and documentation](https://github.com/mipt-npm/kmath)
diff --git a/data/home/content/projects/muography[info].md b/data/home/content/projects/muography[info].md
index dcfb377..f4262d1 100644
--- a/data/home/content/projects/muography[info].md
+++ b/data/home/content/projects/muography[info].md
@@ -1 +1,5 @@
+---
+language: en
+---
+
Geological gas storage monitoring and leaks prevention using muons from atmosphere.
\ No newline at end of file
diff --git a/data/home/content/projects/plotly[info].md b/data/home/content/projects/plotly[info].md
index d14815f..122ecd4 100644
--- a/data/home/content/projects/plotly[info].md
+++ b/data/home/content/projects/plotly[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
A kotlin visualization library wrapping popular [Plotly](https://plotly.com/javascript/) library.
[Repository and documentation](https://github.com/mipt-npm/plotly.kt)
\ No newline at end of file
diff --git a/data/home/content/projects/visionforge[info].md b/data/home/content/projects/visionforge[info].md
index bc79dce..8a1c00c 100644
--- a/data/home/content/projects/visionforge[info].md
+++ b/data/home/content/projects/visionforge[info].md
@@ -1,3 +1,7 @@
+---
+language: en
+---
+
A visualization framework written in Kotlin-multiplatform
[Repository and documentation](https://github.com/mipt-npm/visionforge)
\ No newline at end of file
diff --git a/data/home/content/wip.md b/data/home/content/wip.md
index f501aea..4dcf577 100644
--- a/data/home/content/wip.md
+++ b/data/home/content/wip.md
@@ -4,4 +4,4 @@ title: WIP
language: en
---
-This page is work in progress.
+This page is a work in progress.
diff --git a/data/home/images/projects/bmn/ems-arch.png b/data/home/images/projects/bmn/ems-arch.png
new file mode 100644
index 0000000..60c127f
Binary files /dev/null and b/data/home/images/projects/bmn/ems-arch.png differ
diff --git a/data/home/images/projects/bmn/ems-web.png b/data/home/images/projects/bmn/ems-web.png
new file mode 100644
index 0000000..a4ba44b
Binary files /dev/null and b/data/home/images/projects/bmn/ems-web.png differ
diff --git a/data/home/images/projects/bmn/mon-arch.png b/data/home/images/projects/bmn/mon-arch.png
new file mode 100644
index 0000000..8931954
Binary files /dev/null and b/data/home/images/projects/bmn/mon-arch.png differ
diff --git a/data/home/images/projects/bmn/mon-db.png b/data/home/images/projects/bmn/mon-db.png
new file mode 100644
index 0000000..0496621
Binary files /dev/null and b/data/home/images/projects/bmn/mon-db.png differ
diff --git a/data/home/images/projects/bmn/nica.png b/data/home/images/projects/bmn/nica.png
new file mode 100644
index 0000000..5c2f6eb
Binary files /dev/null and b/data/home/images/projects/bmn/nica.png differ
diff --git a/data/home/images/projects/bmn/scs.png b/data/home/images/projects/bmn/scs.png
new file mode 100644
index 0000000..27fecfd
Binary files /dev/null and b/data/home/images/projects/bmn/scs.png differ
diff --git a/data/home/images/projects/bmn/sdp.png b/data/home/images/projects/bmn/sdp.png
new file mode 100644
index 0000000..4af0041
Binary files /dev/null and b/data/home/images/projects/bmn/sdp.png differ
diff --git a/data/home/images/projects/bmn/vis-1.png b/data/home/images/projects/bmn/vis-1.png
new file mode 100644
index 0000000..2f36420
Binary files /dev/null and b/data/home/images/projects/bmn/vis-1.png differ
diff --git a/data/home/images/projects/bmn/vis-2.png b/data/home/images/projects/bmn/vis-2.png
new file mode 100644
index 0000000..817bc1c
Binary files /dev/null and b/data/home/images/projects/bmn/vis-2.png differ
diff --git a/data/magprog/assets/js/main.js b/data/magprog/assets/js/main.js
index 7e4aecb..71bec9c 100644
--- a/data/magprog/assets/js/main.js
+++ b/data/magprog/assets/js/main.js
@@ -48,7 +48,7 @@
if ($sidebar.length > 0) {
//adding exclusion for home link
- var $sidebar_a = $sidebar.find('a').not('.spc-home');
+ let $sidebar_a = $sidebar.find('a').not('.spc-home');
$sidebar_a
.addClass('scrolly')
@@ -71,7 +71,7 @@
})
.each(function () {
- var $this = $(this),
+ let $this = $(this),
id = $this.attr('href'),
$section = $(id);
@@ -81,7 +81,7 @@
// Scrollex.
$section.scrollex({
- mode: 'middle',
+ // mode: 'middle',
top: '-20vh',
bottom: '-20vh',
initialize: function () {
@@ -133,7 +133,7 @@
// Spotlights.
$('.spotlights > section')
.scrollex({
- mode: 'middle',
+ // mode: 'middle',
top: '-10vh',
bottom: '-10vh',
initialize: function () {
@@ -151,7 +151,7 @@
})
.each(function () {
- var $this = $(this),
+ let $this = $(this),
$image = $this.find('.image'),
$img = $image.find('img'),
x;
@@ -171,7 +171,7 @@
// Features.
$('.features')
.scrollex({
- mode: 'middle',
+ // mode: 'middle',
top: '-20vh',
bottom: '-20vh',
initialize: function () {
@@ -187,20 +187,22 @@
}
});
+
})(jQuery);
-//From https://www.w3schools.com/howto/howto_js_collapsible.asp
-const coll = document.getElementsByClassName("collapsible");
-let i;
-for (i = 0; i < coll.length; i++) {
- coll[i].addEventListener("click", function() {
- this.classList.toggle("active");
- const content = this.nextElementSibling;
- if (content.style.maxHeight){
+//From https://www.w3schools.com/howto/howto_js_collapsible.asp
+let collapsibles = document.getElementsByClassName("collapsible");
+
+Array.from(collapsibles).forEach(item => {
+ item.addEventListener("click", function () {
+ this.classList.toggle("collapsible-expanded");
+ let target = item.attributes.getNamedItem("data-target").value;
+ let content = document.getElementById(target);
+ if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
-}
\ No newline at end of file
+})
\ No newline at end of file
diff --git a/data/magprog/assets/js/util.js b/data/magprog/assets/js/util.js
index ecf7b37..9842bde 100644
--- a/data/magprog/assets/js/util.js
+++ b/data/magprog/assets/js/util.js
@@ -1,587 +1,587 @@
-(function($) {
+(function ($) {
- /**
- * Generate an indented list of links from a nav. Meant for use with panel().
- * @return {jQuery} jQuery object.
- */
- $.fn.navList = function() {
+ /**
+ * Generate an indented list of links from a nav. Meant for use with panel().
+ * @return {jQuery} jQuery object.
+ */
+ $.fn.navList = function () {
- var $this = $(this);
- $a = $this.find('a'),
- b = [];
+ const $this = $(this);
+ $a = $this.find('a'),
+ b = [];
- $a.each(function() {
+ $a.each(function () {
- var $this = $(this),
- indent = Math.max(0, $this.parents('li').length - 1),
- href = $this.attr('href'),
- target = $this.attr('target');
+ const $this = $(this),
+ indent = Math.max(0, $this.parents('li').length - 1),
+ href = $this.attr('href'),
+ target = $this.attr('target');
- b.push(
- '' +
- '' +
- $this.text() +
- ''
- );
+ b.push(
+ '' +
+ '' +
+ $this.text() +
+ ''
+ );
- });
+ });
- return b.join('');
+ return b.join('');
- };
+ };
- /**
- * Panel-ify an element.
- * @param {object} userConfig User config.
- * @return {jQuery} jQuery object.
- */
- $.fn.panel = function(userConfig) {
+ /**
+ * Panel-ify an element.
+ * @param {object} userConfig User config.
+ * @return {jQuery} jQuery object.
+ */
+ $.fn.panel = function (userConfig) {
- // No elements?
- if (this.length == 0)
- return $this;
+ let $this = $(this);
+// No elements?
+ if (this.length === 0)
+ return $this;
- // Multiple elements?
- if (this.length > 1) {
+ // Multiple elements?
+ if (this.length > 1) {
- for (var i=0; i < this.length; i++)
- $(this[i]).panel(userConfig);
+ for (let i = 0; i < this.length; i++)
+ $(this[i]).panel(userConfig);
- return $this;
+ return $this;
- }
+ }
- // Vars.
- var $this = $(this),
- $body = $('body'),
- $window = $(window),
- id = $this.attr('id'),
- config;
+ // Vars.
+ let $body = $('body'),
+ $window = $(window),
+ id = $this.attr('id'),
+ config;
- // Config.
- config = $.extend({
+ // Config.
+ config = $.extend({
- // Delay.
- delay: 0,
+ // Delay.
+ delay: 0,
- // Hide panel on link click.
- hideOnClick: false,
+ // Hide panel on link click.
+ hideOnClick: false,
- // Hide panel on escape keypress.
- hideOnEscape: false,
+ // Hide panel on escape keypress.
+ hideOnEscape: false,
- // Hide panel on swipe.
- hideOnSwipe: false,
+ // Hide panel on swipe.
+ hideOnSwipe: false,
- // Reset scroll position on hide.
- resetScroll: false,
+ // Reset scroll position on hide.
+ resetScroll: false,
- // Reset forms on hide.
- resetForms: false,
+ // Reset forms on hide.
+ resetForms: false,
- // Side of viewport the panel will appear.
- side: null,
+ // Side of viewport the panel will appear.
+ side: null,
- // Target element for "class".
- target: $this,
+ // Target element for "class".
+ target: $this,
- // Class to toggle.
- visibleClass: 'visible'
+ // Class to toggle.
+ visibleClass: 'visible'
- }, userConfig);
+ }, userConfig);
- // Expand "target" if it's not a jQuery object already.
- if (typeof config.target != 'jQuery')
- config.target = $(config.target);
+ // Expand "target" if it's not a jQuery object already.
+ if (typeof config.target != 'jQuery')
+ config.target = $(config.target);
- // Panel.
+ // Panel.
- // Methods.
- $this._hide = function(event) {
+ // Methods.
+ $this._hide = function (event) {
- // Already hidden? Bail.
- if (!config.target.hasClass(config.visibleClass))
- return;
+ // Already hidden? Bail.
+ if (!config.target.hasClass(config.visibleClass))
+ return;
- // If an event was provided, cancel it.
- if (event) {
+ // If an event was provided, cancel it.
+ if (event) {
- event.preventDefault();
- event.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
- }
+ }
- // Hide.
- config.target.removeClass(config.visibleClass);
+ // Hide.
+ config.target.removeClass(config.visibleClass);
- // Post-hide stuff.
- window.setTimeout(function() {
+ // Post-hide stuff.
+ window.setTimeout(function () {
- // Reset scroll position.
- if (config.resetScroll)
- $this.scrollTop(0);
+ // Reset scroll position.
+ if (config.resetScroll)
+ $this.scrollTop(0);
- // Reset forms.
- if (config.resetForms)
- $this.find('form').each(function() {
- this.reset();
- });
+ // Reset forms.
+ if (config.resetForms)
+ $this.find('form').each(function () {
+ this.reset();
+ });
- }, config.delay);
+ }, config.delay);
- };
+ };
- // Vendor fixes.
- $this
- .css('-ms-overflow-style', '-ms-autohiding-scrollbar')
- .css('-webkit-overflow-scrolling', 'touch');
+ // Vendor fixes.
+ $this
+ .css('-ms-overflow-style', '-ms-autohiding-scrollbar')
+ .css('-webkit-overflow-scrolling', 'touch');
- // Hide on click.
- if (config.hideOnClick) {
+ // Hide on click.
+ if (config.hideOnClick) {
- $this.find('a')
- .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
+ $this.find('a')
+ .css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
- $this
- .on('click', 'a', function(event) {
+ $this
+ .on('click', 'a', function (event) {
- var $a = $(this),
- href = $a.attr('href'),
- target = $a.attr('target');
+ const $a = $(this),
+ href = $a.attr('href'),
+ target = $a.attr('target');
- if (!href || href == '#' || href == '' || href == '#' + id)
- return;
+ if (!href || href === '#' || href === '' || href === '#' + id)
+ return;
- // Cancel original event.
- event.preventDefault();
- event.stopPropagation();
+ // Cancel original event.
+ event.preventDefault();
+ event.stopPropagation();
- // Hide panel.
- $this._hide();
+ // Hide panel.
+ $this._hide();
- // Redirect to href.
- window.setTimeout(function() {
+ // Redirect to href.
+ window.setTimeout(function () {
- if (target == '_blank')
- window.open(href);
- else
- window.location.href = href;
+ if (target === '_blank')
+ window.open(href);
+ else
+ window.location.href = href;
- }, config.delay + 10);
+ }, config.delay + 10);
- });
+ });
- }
+ }
- // Event: Touch stuff.
- $this.on('touchstart', function(event) {
+ // Event: Touch stuff.
+ $this.on('touchstart', function (event) {
- $this.touchPosX = event.originalEvent.touches[0].pageX;
- $this.touchPosY = event.originalEvent.touches[0].pageY;
+ $this.touchPosX = event.originalEvent.touches[0].pageX;
+ $this.touchPosY = event.originalEvent.touches[0].pageY;
- })
+ })
- $this.on('touchmove', function(event) {
+ $this.on('touchmove', function (event) {
- if ($this.touchPosX === null
- || $this.touchPosY === null)
- return;
+ if ($this.touchPosX === null
+ || $this.touchPosY === null)
+ return;
- var diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
- diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
- th = $this.outerHeight(),
- ts = ($this.get(0).scrollHeight - $this.scrollTop());
+ const diffX = $this.touchPosX - event.originalEvent.touches[0].pageX,
+ diffY = $this.touchPosY - event.originalEvent.touches[0].pageY,
+ th = $this.outerHeight(),
+ ts = ($this.get(0).scrollHeight - $this.scrollTop());
- // Hide on swipe?
- if (config.hideOnSwipe) {
+ // Hide on swipe?
+ if (config.hideOnSwipe) {
- var result = false,
- boundary = 20,
- delta = 50;
+ let result = false;
+ const boundary = 20,
+ delta = 50;
- switch (config.side) {
+ switch (config.side) {
- case 'left':
- result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
- break;
+ case 'left':
+ result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
+ break;
- case 'right':
- result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
- break;
+ case 'right':
+ result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
+ break;
- case 'top':
- result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
- break;
+ case 'top':
+ result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY > delta);
+ break;
- case 'bottom':
- result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
- break;
+ case 'bottom':
+ result = (diffX < boundary && diffX > (-1 * boundary)) && (diffY < (-1 * delta));
+ break;
- default:
- break;
+ default:
+ break;
- }
+ }
- if (result) {
+ if (result) {
- $this.touchPosX = null;
- $this.touchPosY = null;
- $this._hide();
+ $this.touchPosX = null;
+ $this.touchPosY = null;
+ $this._hide();
- return false;
+ return false;
- }
+ }
- }
+ }
- // Prevent vertical scrolling past the top or bottom.
- if (($this.scrollTop() < 0 && diffY < 0)
- || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
+ // Prevent vertical scrolling past the top or bottom.
+ if (($this.scrollTop() < 0 && diffY < 0)
+ || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
- event.preventDefault();
- event.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
- }
+ }
- });
+ });
- // Event: Prevent certain events inside the panel from bubbling.
- $this.on('click touchend touchstart touchmove', function(event) {
- event.stopPropagation();
- });
+ // Event: Prevent certain events inside the panel from bubbling.
+ $this.on('click touchend touchstart touchmove', function (event) {
+ event.stopPropagation();
+ });
- // Event: Hide panel if a child anchor tag pointing to its ID is clicked.
- $this.on('click', 'a[href="#' + id + '"]', function(event) {
+ // Event: Hide panel if a child anchor tag pointing to its ID is clicked.
+ $this.on('click', 'a[href="#' + id + '"]', function (event) {
- event.preventDefault();
- event.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
- config.target.removeClass(config.visibleClass);
+ config.target.removeClass(config.visibleClass);
- });
+ });
- // Body.
+ // Body.
- // Event: Hide panel on body click/tap.
- $body.on('click touchend', function(event) {
- $this._hide(event);
- });
+ // Event: Hide panel on body click/tap.
+ $body.on('click touchend', function (event) {
+ $this._hide(event);
+ });
- // Event: Toggle.
- $body.on('click', 'a[href="#' + id + '"]', function(event) {
+ // Event: Toggle.
+ $body.on('click', 'a[href="#' + id + '"]', function (event) {
- event.preventDefault();
- event.stopPropagation();
+ event.preventDefault();
+ event.stopPropagation();
- config.target.toggleClass(config.visibleClass);
+ config.target.toggleClass(config.visibleClass);
- });
+ });
- // Window.
+ // Window.
- // Event: Hide on ESC.
- if (config.hideOnEscape)
- $window.on('keydown', function(event) {
+ // Event: Hide on ESC.
+ if (config.hideOnEscape)
+ $window.on('keydown', function (event) {
- if (event.keyCode == 27)
- $this._hide(event);
+ if (event.keyCode === 27)
+ $this._hide(event);
- });
+ });
- return $this;
+ return $this;
- };
+ };
- /**
- * Apply "placeholder" attribute polyfill to one or more forms.
- * @return {jQuery} jQuery object.
- */
- $.fn.placeholder = function() {
+ /**
+ * Apply "placeholder" attribute polyfill to one or more forms.
+ * @return {jQuery} jQuery object.
+ */
+ $.fn.placeholder = function () {
- // Browser natively supports placeholders? Bail.
- if (typeof (document.createElement('input')).placeholder != 'undefined')
- return $(this);
+ // Browser natively supports placeholders? Bail.
+ if (typeof (document.createElement('input')).placeholder != 'undefined')
+ return $(this);
- // No elements?
- if (this.length == 0)
- return $this;
+ // No elements?
+ if (this.length === 0)
+ return $this;
- // Multiple elements?
- if (this.length > 1) {
+ // Multiple elements?
+ if (this.length > 1) {
- for (var i=0; i < this.length; i++)
- $(this[i]).placeholder();
+ for (let i = 0; i < this.length; i++)
+ $(this[i]).placeholder();
- return $this;
+ return $this;
- }
+ }
- // Vars.
- var $this = $(this);
+ // Vars.
+ var $this = $(this);
- // Text, TextArea.
- $this.find('input[type=text],textarea')
- .each(function() {
+ // Text, TextArea.
+ $this.find('input[type=text],textarea')
+ .each(function () {
- var i = $(this);
+ const i = $(this);
- if (i.val() == ''
- || i.val() == i.attr('placeholder'))
- i
- .addClass('polyfill-placeholder')
- .val(i.attr('placeholder'));
+ if (i.val() === ''
+ || i.val() === i.attr('placeholder'))
+ i
+ .addClass('polyfill-placeholder')
+ .val(i.attr('placeholder'));
- })
- .on('blur', function() {
+ })
+ .on('blur', function () {
- var i = $(this);
+ const i = $(this);
- if (i.attr('name').match(/-polyfill-field$/))
- return;
+ if (i.attr('name').match(/-polyfill-field$/))
+ return;
- if (i.val() == '')
- i
- .addClass('polyfill-placeholder')
- .val(i.attr('placeholder'));
+ if (i.val() === '')
+ i
+ .addClass('polyfill-placeholder')
+ .val(i.attr('placeholder'));
- })
- .on('focus', function() {
+ })
+ .on('focus', function () {
- var i = $(this);
+ const i = $(this);
- if (i.attr('name').match(/-polyfill-field$/))
- return;
+ if (i.attr('name').match(/-polyfill-field$/))
+ return;
- if (i.val() == i.attr('placeholder'))
- i
- .removeClass('polyfill-placeholder')
- .val('');
+ if (i.val() === i.attr('placeholder'))
+ i
+ .removeClass('polyfill-placeholder')
+ .val('');
- });
+ });
- // Password.
- $this.find('input[type=password]')
- .each(function() {
+ // Password.
+ $this.find('input[type=password]')
+ .each(function () {
- var i = $(this);
- var x = $(
- $('