diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..35b1b0675 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,52 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "enter program name, for example ${workspaceFolder}/a.out", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "enter program name, for example ${workspaceFolder}/a.out", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + } + + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..08d9005bb --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc build active file", + "command": "/usr/bin/gcc", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/website/README.md b/website/README.md new file mode 100644 index 000000000..0ade9634b --- /dev/null +++ b/website/README.md @@ -0,0 +1,56 @@ +# EPANET Website + +This is the official website for the EPANET project, built with HTML, CSS, and JavaScript. + +## Structure + +``` +website/ +├── index.html # Homepage +├── documentation.html # API Documentation +├── examples.html # Code Examples +├── css/ +│ └── style.css # Main stylesheet +├── js/ +│ └── main.js # JavaScript functionality +└── img/ + ├── Network.png # Network diagram + ├── DataFlow.png # Data flow diagram + ├── DistributionSystem.png + └── Example2.png +``` + +## Features + +- **Responsive Design**: Works on desktop, tablet, and mobile devices +- **Modern UI**: Clean, professional design with water-themed colors +- **Documentation**: Complete API reference and function categories +- **Examples**: Practical code examples in C +- **Mobile Navigation**: Hamburger menu for mobile devices + +## Viewing the Website + +To view the website locally, you can use any web server. For example: + +```bash +# Using Python +cd website +python -m http.server 8000 + +# Using Node.js +npx serve . + +# Using VS Code +# Open index.html with Live Server extension +``` + +Then open http://localhost:8000 in your browser. + +## About EPANET + +EPANET is an industry-standard program for modeling the hydraulic and water quality behavior of water distribution system pipe networks. It is maintained by the Open Water Analytics (OWA) community. + +For more information, visit: +- [GitHub Repository](https://github.com/OpenWaterAnalytics/EPANET) +- [API Documentation](http://wateranalytics.org/EPANET/) +- [Building Guide](BUILDING.md) \ No newline at end of file diff --git a/website/css/style.css b/website/css/style.css new file mode 100644 index 000000000..33334f90e --- /dev/null +++ b/website/css/style.css @@ -0,0 +1,548 @@ +/* EPANET Website Styles */ + +:root { + --primary-color: #0ea5e9; + --primary-dark: #0284c7; + --secondary-color: #64748b; + --accent-color: #0d9488; + --text-dark: #1e293b; + --text-light: #64748b; + --bg-light: #f8fafc; + --bg-white: #ffffff; + --border-color: #e2e8f0; + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --transition: all 0.3s ease; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + line-height: 1.6; + color: var(--text-dark); + background-color: var(--bg-white); +} + +.container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +/* Header & Navigation */ +.header { + background: var(--bg-white); + box-shadow: var(--shadow); + position: sticky; + top: 0; + z-index: 100; +} + +.navbar { + padding: 1rem 0; +} + +.navbar .container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + display: flex; + align-items: center; + gap: 10px; + font-size: 1.5rem; + font-weight: 700; + color: var(--primary-color); +} + +.logo i { + font-size: 1.8rem; +} + +.nav-menu { + display: flex; + list-style: none; + gap: 2rem; +} + +.nav-menu a { + text-decoration: none; + color: var(--text-dark); + font-weight: 500; + transition: var(--transition); + padding: 0.5rem 1rem; + border-radius: 0.5rem; +} + +.nav-menu a:hover, +.nav-menu a.active { + color: var(--primary-color); + background: var(--bg-light); +} + +.hamburger { + display: none; + flex-direction: column; + cursor: pointer; +} + +.bar { + width: 25px; + height: 3px; + background: var(--text-dark); + margin: 3px 0; + transition: var(--transition); + border-radius: 2px; +} + +/* Hero Section */ +.hero { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); + color: white; + padding: 4rem 0; + min-height: 80vh; + display: flex; + align-items: center; +} + +.hero .container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; +} + +.hero h1 { + font-size: 3.5rem; + font-weight: 700; + margin-bottom: 1rem; +} + +.hero-subtitle { + font-size: 1.25rem; + margin-bottom: 2rem; + opacity: 0.9; +} + +.hero-buttons { + display: flex; + gap: 1rem; +} + +.btn { + padding: 0.75rem 2rem; + border-radius: 0.5rem; + text-decoration: none; + font-weight: 600; + transition: var(--transition); + display: inline-block; + border: none; + cursor: pointer; +} + +.btn-primary { + background: var(--bg-white); + color: var(--primary-color); +} + +.btn-primary:hover { + background: var(--bg-light); + transform: translateY(-2px); + box-shadow: var(--shadow); +} + +.btn-secondary { + background: transparent; + color: var(--bg-white); + border: 2px solid var(--bg-white); +} + +.btn-secondary:hover { + background: var(--bg-white); + color: var(--primary-color); +} + +.btn-outline { + background: transparent; + color: var(--primary-color); + border: 2px solid var(--primary-color); +} + +.btn-outline:hover { + background: var(--primary-color); + color: var(--bg-white); +} + +.hero-image img { + width: 100%; + max-width: 500px; + border-radius: 1rem; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); +} + +/* Features Section */ +.features { + padding: 5rem 0; + background: var(--bg-light); +} + +.features h2 { + text-align: center; + font-size: 2.5rem; + margin-bottom: 3rem; + color: var(--text-dark); +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.feature-card { + background: var(--bg-white); + padding: 2rem; + border-radius: 1rem; + text-align: center; + box-shadow: var(--shadow); + transition: var(--transition); +} + +.feature-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); +} + +.feature-card i { + font-size: 2.5rem; + color: var(--primary-color); + margin-bottom: 1rem; +} + +.feature-card h3 { + font-size: 1.25rem; + margin-bottom: 1rem; + color: var(--text-dark); +} + +/* About Section */ +.about { + padding: 5rem 0; +} + +.about-content { + max-width: 800px; + margin: 0 auto; + text-align: center; +} + +.about h2 { + font-size: 2.5rem; + margin-bottom: 2rem; + color: var(--text-dark); +} + +.about p { + font-size: 1.1rem; + margin-bottom: 1.5rem; + color: var(--text-light); +} + +/* Quick Start Section */ +.quick-start { + padding: 5rem 0; + background: var(--bg-light); +} + +.quick-start h2 { + text-align: center; + font-size: 2.5rem; + margin-bottom: 2rem; + color: var(--text-dark); +} + +.code-block { + background: #1e293b; + border-radius: 0.75rem; + padding: 2rem; + overflow-x: auto; + margin-bottom: 1.5rem; +} + +.code-block pre { + color: #e2e8f0; + font-family: 'Fira Code', 'Consolas', monospace; + font-size: 0.95rem; + line-height: 1.5; +} + +.code-block code { + font-family: inherit; +} + +.quick-start-text { + text-align: center; + color: var(--text-light); +} + +.quick-start-text a { + color: var(--primary-color); + text-decoration: none; + font-weight: 600; +} + +/* CTA Section */ +.cta { + padding: 4rem 0; + background: linear-gradient(135deg, var(--accent-color) 0%, #0f766e 100%); + color: white; + text-align: center; +} + +.cta h2 { + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.cta p { + font-size: 1.1rem; + margin-bottom: 2rem; + opacity: 0.9; +} + +.cta-buttons { + display: flex; + justify-content: center; + gap: 1rem; +} + +.cta .btn-primary, +.cta .btn-secondary { + background: var(--bg-white); + color: var(--accent-color); +} + +.cta .btn-secondary { + background: transparent; + color: var(--bg-white); + border: 2px solid var(--bg-white); +} + +/* Footer */ +.footer { + background: var(--text-dark); + color: var(--bg-white); + padding: 3rem 0 1.5rem; +} + +.footer-content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 2rem; + margin-bottom: 2rem; +} + +.footer-section h3 { + font-size: 1.25rem; + margin-bottom: 1rem; + color: var(--primary-color); +} + +.footer-section ul { + list-style: none; +} + +.footer-section ul li { + margin-bottom: 0.5rem; +} + +.footer-section a { + color: var(--bg-white); + text-decoration: none; + transition: var(--transition); +} + +.footer-section a:hover { + color: var(--primary-color); +} + +.footer-bottom { + text-align: center; + padding-top: 1.5rem; + border-top: 1px solid var(--border-color); +} + +/* Documentation Page */ +.docs-hero { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); + color: white; + padding: 3rem 0; + text-align: center; +} + +.docs-hero h1 { + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.docs-content { + padding: 4rem 0; +} + +.docs-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.doc-card { + background: var(--bg-white); + padding: 2rem; + border-radius: 1rem; + box-shadow: var(--shadow); + transition: var(--transition); +} + +.doc-card:hover { + transform: translateY(-3px); +} + +.doc-card i { + font-size: 2rem; + color: var(--primary-color); + margin-bottom: 1rem; +} + +.doc-card h3 { + margin-bottom: 1rem; + color: var(--text-dark); +} + +.doc-card ul { + list-style: none; +} + +.doc-card ul li { + margin-bottom: 0.5rem; +} + +.doc-card a { + color: var(--primary-color); + text-decoration: none; +} + +.doc-card a:hover { + text-decoration: underline; +} + +/* Examples Page */ +.examples-hero { + background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); + color: white; + padding: 3rem 0; + text-align: center; +} + +.examples-content { + padding: 4rem 0; +} + +.example-card { + background: var(--bg-white); + border-radius: 1rem; + box-shadow: var(--shadow); + margin-bottom: 2rem; + overflow: hidden; +} + +.example-header { + background: var(--primary-color); + color: white; + padding: 1.5rem; +} + +.example-header h3 { + margin: 0; +} + +.example-body { + padding: 1.5rem; +} + +.example-body pre { + background: var(--bg-light); + padding: 1rem; + border-radius: 0.5rem; + overflow-x: auto; + margin: 1rem 0; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .hamburger { + display: flex; + } + + .nav-menu { + position: fixed; + left: -100%; + top: 70px; + flex-direction: column; + background: var(--bg-white); + width: 100%; + text-align: center; + transition: var(--transition); + box-shadow: var(--shadow); + padding: 2rem 0; + gap: 0; + } + + .nav-menu.active { + left: 0; + } + + .hero .container { + grid-template-columns: 1fr; + text-align: center; + } + + .hero h1 { + font-size: 2.5rem; + } + + .hero-buttons { + justify-content: center; + } + + .features h2, + .about h2, + .quick-start h2, + .cta h2 { + font-size: 2rem; + } +} + +/* Animation */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.feature-card, +.doc-card, +.example-card { + animation: fadeIn 0.6s ease-out; +} \ No newline at end of file diff --git a/website/documentation.html b/website/documentation.html new file mode 100644 index 000000000..a68091b4f --- /dev/null +++ b/website/documentation.html @@ -0,0 +1,188 @@ + + + + + + Documentation - EPANET + + + + + + +
+ +
+ + +
+
+

Documentation

+

Complete guide to using EPANET and its Programmer's Toolkit API

+
+
+ + +
+
+
+ +
+ +

API Reference

+ +
+ + +
+ +

Installation

+ +
+ + +
+ +

Input File Format

+ +
+ + + + + +
+ +

Node Functions

+ +
+ + +
+ +

Link Functions

+ +
+
+ + +
+

API Function Categories

+
+
+ +

Project Management

+

EN_createproject, EN_deleteproject, EN_open, EN_saveinpfile, EN_close

+
+
+ +

Hydraulic Analysis

+

EN_solveH, EN_openH, EN_initH, EN_runH, EN_nextH, EN_closeH

+
+
+ +

Water Quality

+

EN_solveQ, EN_openQ, EN_initQ, EN_runQ, EN_nextQ, EN_closeQ

+
+
+ +

Reporting

+

EN_report, EN_setreport, EN_writeline, EN_geterror

+
+
+ +

Options & Settings

+

EN_getoption, EN_setoption, EN_gettimeparam, EN_settimeparam

+
+
+ +

Network Elements

+

EN_addnode, EN_addlink, EN_getnodevalue, EN_setnodevalue

+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/website/examples.html b/website/examples.html new file mode 100644 index 000000000..51be6893e --- /dev/null +++ b/website/examples.html @@ -0,0 +1,314 @@ + + + + + + Examples - EPANET + + + + + + +
+ +
+ + +
+
+

Examples

+

Learn how to use EPANET with practical code examples and network models

+
+
+ + +
+
+ +
+
+

Basic Hydraulic Analysis

+
+
+

Run a complete hydraulic simulation with EPANET Toolkit:

+
#include 
+#include 
+
+int main() {
+    EN_Project ph;
+    int errcode;
+    
+    // Create a new EPANET project
+    errcode = EN_createproject(&ph);
+    if (errcode != 0) {
+        printf("Error creating project: %d\n", errcode);
+        return 1;
+    }
+    
+    // Open an existing network file
+    errcode = EN_open(ph, "Net1.inp", "Net1.rpt", "Net1.out");
+    if (errcode != 0) {
+        printf("Error opening file: %d\n", errcode);
+        EN_deleteproject(ph);
+        return 1;
+    }
+    
+    // Run hydraulic analysis
+    errcode = EN_solveH(ph);
+    if (errcode != 0) {
+        printf("Error in hydraulic analysis: %d\n", errcode);
+    }
+    
+    // Generate report
+    EN_report(ph);
+    
+    // Clean up
+    EN_deleteproject(ph);
+    return 0;
+}
+
+
+ + +
+
+

Water Quality Analysis

+
+
+

Run a water quality simulation with chlorine decay analysis:

+
#include 
+
+int main() {
+    EN_Project ph;
+    
+    // Create and open project
+    EN_createproject(&ph);
+    EN_open(ph, "Net1.inp", "quality.rpt", "quality.out");
+    
+    // Run hydraulic analysis first
+    EN_solveH(ph);
+    
+    // Run water quality analysis
+    EN_solveQ(ph);
+    
+    // Generate report
+    EN_report(ph);
+    
+    // Clean up
+    EN_deleteproject(ph);
+    return 0;
+}
+
+
+ + +
+
+

Step-by-Step Hydraulic Analysis

+
+
+

Access intermediate hydraulic results during simulation:

+
#include 
+
+int main() {
+    EN_Project ph;
+    long t, tstep;
+    double pressure, flow;
+    int nodeIndex, linkIndex;
+    
+    // Create and open project
+    EN_createproject(&ph);
+    EN_open(ph, "Net1.inp", "Net1.rpt", "");
+    
+    // Open hydraulic solver
+    EN_openH(ph);
+    EN_initH(ph, EN_SAVE);  // Save intermediate results
+    
+    // Run extended period simulation
+    do {
+        EN_runH(ph, &t);
+        
+        // Get node pressure at current time
+        nodeIndex = 1;  // Node 1
+        EN_getnodevalue(ph, nodeIndex, EN_PRESSURE, &pressure);
+        
+        // Get link flow at current time
+        linkIndex = 1;  // Link 1
+        EN_getlinkvalue(ph, linkIndex, EN_FLOW, &flow);
+        
+        printf("Time: %ld, Pressure: %.2f, Flow: %.2f\n", t, pressure, flow);
+        
+        EN_nextH(ph, &tstep);
+    } while (tstep > 0);
+    
+    EN_closeH(ph);
+    EN_deleteproject(ph);
+    return 0;
+}
+
+
+ + +
+
+

Network Modification

+
+
+

Modify network elements programmatically:

+
#include 
+
+int main() {
+    EN_Project ph;
+    int nodeIndex;
+    double newDemand;
+    
+    // Create and open project
+    EN_createproject(&ph);
+    EN_open(ph, "Net1.inp", "Net1.rpt", "");
+    
+    // Get node index by ID
+    EN_getnodeindex(ph, "11", &nodeIndex);
+    
+    // Modify node demand
+    newDemand = 200.0;  // GPM
+    EN_setnodevalue(ph, nodeIndex, EN_BASEDEMAND, newDemand);
+    
+    // Save modified network
+    EN_saveinpfile(ph, "modified_network.inp");
+    
+    // Run analysis on modified network
+    EN_solveH(ph);
+    EN_solveQ(ph);
+    EN_report(ph);
+    
+    EN_deleteproject(ph);
+    return 0;
+}
+
+
+ + +
+
+

Building Network from Scratch

+
+
+

Create a water network programmatically without an input file:

+
#include 
+
+int main() {
+    EN_Project ph;
+    int j1, j2, p1, t1, r1;
+    
+    // Create a new project
+    EN_createproject(&ph);
+    
+    // Initialize project with units and headloss formula
+    EN_init(ph, "report.rpt", "output.out", 
+            EN_GPM, EN_HW);  // GPM, Hazen-Williams
+    
+    // Add nodes
+    EN_addnode(ph, "J1", EN_JUNCTION, &j1);
+    EN_addnode(ph, "J2", EN_JUNCTION, &j2);
+    EN_addnode(ph, "Tank1", EN_TANK, &t1);
+    EN_addnode(ph, "Reservoir1", EN_RESERVOIR, &r1);
+    
+    // Set node properties
+    EN_setjuncdata(ph, j1, 100.0, 100.0, "");  // Elev, Demand
+    EN_setjuncdata(ph, j2, 95.0, 100.0, "");
+    
+    // Add links (pipes)
+    EN_addlink(ph, "P1", EN_PIPE, "J1", "J2", &p1);
+    
+    // Set link properties
+    EN_setlinkvalue(ph, p1, EN_LENGTH, 1000.0);
+    EN_setlinkvalue(ph, p1, EN_DIAMETER, 12.0);
+    EN_setlinkvalue(ph, p1, EN_ROUGHNESS, 0.1);
+    
+    // Run analysis
+    EN_solveH(ph);
+    EN_report(ph);
+    
+    EN_deleteproject(ph);
+    return 0;
+}
+
+
+ + +
+
+

Example Networks

+
+
+

Pre-built example networks are available in the example-networks directory:

+
    +
  • Net1.inp - Simple network with chlorine decay analysis
  • +
  • Net2.inp - Network with multiple tanks and pumps
  • +
  • Net3.inp - Complex network for advanced analysis
  • +
+

These networks demonstrate various features of EPANET including:

+
    +
  • Junctions, tanks, and reservoirs
  • +
  • Pipes, pumps, and valves
  • +
  • Demand patterns and time controls
  • +
  • Water quality analysis
  • +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/website/img/DataFlow.png b/website/img/DataFlow.png new file mode 100644 index 000000000..a499f45e4 Binary files /dev/null and b/website/img/DataFlow.png differ diff --git a/website/img/DistributionSystem.png b/website/img/DistributionSystem.png new file mode 100644 index 000000000..4804e7e3d Binary files /dev/null and b/website/img/DistributionSystem.png differ diff --git a/website/img/Example2.png b/website/img/Example2.png new file mode 100644 index 000000000..bff8f4e40 Binary files /dev/null and b/website/img/Example2.png differ diff --git a/website/img/Network.png b/website/img/Network.png new file mode 100644 index 000000000..3a6df9ec3 Binary files /dev/null and b/website/img/Network.png differ diff --git a/website/index.html b/website/index.html new file mode 100644 index 000000000..4a77c3cac --- /dev/null +++ b/website/index.html @@ -0,0 +1,171 @@ + + + + + + EPANET - Water Distribution System Modeling + + + + + + +
+ +
+ + +
+
+
+

EPANET

+

Industry-Standard Software for Modeling Hydraulic and Water Quality Behavior of Water Distribution Systems

+ +
+
+ Water Distribution Network +
+
+
+ + +
+
+

Key Features

+
+
+ +

Hydraulic Analysis

+

Model steady-state and extended period simulations of water flow, pressure, and tank levels.

+
+
+ +

Water Quality

+

Analyze the fate and transport of chemical constituents and trace the source of contamination.

+
+
+ +

Toolkit API

+

Programmatic access to EPANET's engine for custom applications in C/C++.

+
+
+ +

Network Modeling

+

Model complex water distribution networks with pumps, valves, tanks, and pipes.

+
+
+ +

Scenario Analysis

+

Evaluate system performance under various operating conditions and demand patterns.

+
+
+ +

Open Source

+

Community-driven development with contributions from researchers and practitioners worldwide.

+
+
+
+
+ + +
+
+
+

About EPANET

+

EPANET is an industry-standard program for modeling the hydraulic and water quality behavior of water distribution system pipe networks. Originally developed by the U.S. Environmental Protection Agency (USEPA), it is now maintained by the Open Water Analytics (OWA) community.

+

The EPANET Programmer's Toolkit is a library of functions (or API) written in C that allows programmers to customize the use of EPANET's solution engine for their own applications.

+ Learn More +
+
+
+ + +
+
+

Quick Start

+
+
// C Example - Simple EPANET Analysis
+#include 
+
+int main() {
+    EN_Project ph;
+    EN_createproject(&ph);
+    EN_open(ph, "network.inp", "network.rpt", "network.out");
+    EN_solveH(ph);  // Run hydraulic analysis
+    EN_solveQ(ph);  // Run water quality analysis
+    EN_report(ph);  // Generate report
+    EN_deleteproject(ph);
+    return 0;
+}
+
+

Use the Toolkit API to integrate EPANET into your applications. See the Documentation for more details.

+
+
+ + +
+
+

Join the Community

+

Contribute to EPANET development, report issues, or get help from the community.

+ +
+
+ + + + + + + \ No newline at end of file diff --git a/website/js/main.js b/website/js/main.js new file mode 100644 index 000000000..39e13f65f --- /dev/null +++ b/website/js/main.js @@ -0,0 +1,70 @@ +// EPANET Website - Main JavaScript + +// Mobile Navigation Toggle +const hamburger = document.querySelector('.hamburger'); +const navMenu = document.querySelector('.nav-menu'); + +if (hamburger && navMenu) { + hamburger.addEventListener('click', () => { + navMenu.classList.toggle('active'); + hamburger.classList.toggle('active'); + }); + + // Close mobile menu when clicking a link + document.querySelectorAll('.nav-menu a').forEach(link => { + link.addEventListener('click', () => { + navMenu.classList.remove('active'); + hamburger.classList.remove('active'); + }); + }); +} + +// Smooth scrolling for anchor links +document.querySelectorAll('a[href^="#]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); +}); + +// Add active class to current navigation item +const currentLocation = window.location.pathname; +const navLinks = document.querySelectorAll('.nav-menu a'); + +navLinks.forEach(link => { + if (link.getAttribute('href') === currentLocation.split('/').pop()) { + link.classList.add('active'); + } +}); + +// Animation on scroll +const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' +}; + +const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + }); +}, observerOptions); + +// Observe all cards for animation +document.querySelectorAll('.feature-card, .doc-card, .example-card').forEach(card => { + card.style.opacity = '0'; + card.style.transform = 'translateY(20px)'; + card.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + observer.observe(card); +}); + +// Console welcome message +console.log('EPANET Website - Open Water Analytics'); \ No newline at end of file