diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a51474e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# stopshotspotter
+
diff --git a/android-chrome-192x192.png b/android-chrome-192x192.png
new file mode 100644
index 0000000..e168448
Binary files /dev/null and b/android-chrome-192x192.png differ
diff --git a/android-chrome-512x512.png b/android-chrome-512x512.png
new file mode 100644
index 0000000..c3decd6
Binary files /dev/null and b/android-chrome-512x512.png differ
diff --git a/css/main.css b/css/main.css
new file mode 100644
index 0000000..103608e
--- /dev/null
+++ b/css/main.css
@@ -0,0 +1,567 @@
+html,
+body {
+ margin: 0;
+ padding: 0;
+ font-family: "Source Sans Pro", sans-serif;
+ text-rendering: optimizeLegibility;
+ color: #222;
+ /* overflow-x: hidden; */
+}
+h4 {
+ font-size: 22px;
+ margin-bottom: 28px;
+}
+p {
+ font-size: 22px;
+}
+a,
+a:active {
+ text-decoration: none;
+ color: #693933;
+}
+
+header h2 {
+ font-size: 15px;
+ font-weight: 900;
+ letter-spacing: 0.15em;
+ margin-top: 70px;
+}
+header a h2 {
+ color: #222;
+}
+header a h2 span {
+ transition: all 400ms cubic-bezier(0.68, -0.1, 0.265, 1.55);
+}
+header a:hover h2 span {
+ color: #fff;
+ margin-left: 10px;
+}
+header .white-block {
+ margin-top: 20px;
+ width: 46px;
+ height: 6px;
+ background: #fff;
+}
+
+header .title {
+ margin-top: 80px;
+}
+header h1 {
+ font-size: 96px;
+ font-weight: 900;
+ letter-spacing: 0.12em;
+ margin: 0;
+ margin-left: -6px;
+}
+header h1 span {
+ display: inline-block;
+ font-size: 20px;
+ margin-top: 18px;
+ vertical-align: top;
+ letter-spacing: 0;
+}
+header .title a {
+ display: inline-block;
+ margin-top: 6px;
+ margin-right: -70px;
+ background: #222;
+ padding: 26px 80px;
+ color: #fff;
+ border-radius: 12px;
+ font-size: 26px;
+ letter-spacing: 0.2em;
+ transition: all 300ms cubic-bezier(0.68, -0.1, 0.265, 1.55);
+}
+header .title a:hover {
+ margin-top: 12px;
+ margin-right: -80px;
+ padding: 20px 90px;
+}
+header h3 {
+ font-size: 21px;
+}
+
+
+
+.main-title{
+ width: 36vw;
+}
+
+/*----------------------------------
+ Background Grid
+------------------------------------*/
+
+.grid {
+ position: fixed;
+ width: 100%;
+}
+.grid-line {
+ height: 100vh;
+ border-left: 1px solid #ccc;
+}
+
+.fixed-top {
+ position: absolute;
+ width: 600px;
+ height: 600px;
+ z-index: -1;
+ margin-top: 0;
+ margin-left: 100px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+
+/*----------------------------------
+ Main
+------------------------------------*/
+
+/* .main {
+ /* overflow: hidden; */
+
+.section {
+ position: relative;
+}
+section.surveillance {
+ margin-top: 100px;
+ z-index: -30;
+}
+
+.absolute {
+ position: absolute;
+ width: 90vw;
+ height: auto;
+ margin-top: 100px;
+}
+.absolute.above {
+ z-index: 2;
+}
+.absolute h2 {
+ font-size: 140px;
+ font-weight: 900;
+ letter-spacing: -0.04em;
+ color: #f0f0f0;
+ z-index: -1;
+}
+.lg-green {
+ width: 160px;
+ height: 160px;
+ margin-left: 80px;
+ margin-top: 200px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+
+}
+.sm-green {
+ width: 60px;
+ height: 60px;
+ margin-left: -30px;
+ margin-top: 400px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+.sm-purple {
+ width: 60px;
+ height: 60px;
+ margin-left: -30px;
+ margin-top: 400px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+.xs-green {
+ width: 60px;
+ height: 60px;
+ margin-left: -30px;
+ margin-top: 400px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+.md-green {
+ width: 120px;
+ height: 120px;
+ margin-left: -360px;
+ margin-top: 10px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+.lg-purple {
+ width: 80px;
+ height: 80px;
+ margin-left: 57px;
+ margin-top: 60px;
+ background-image: url("../images/stopshotspotter-NO.svg");
+}
+
+.data {
+ margin-top: 400px;
+ margin-left: 50vw;
+ font-size: 48px;
+ font-weight: 600;
+ color: #693933;
+}
+.temp {
+ margin-top: 25px;
+ margin-rigt: 300px;
+ text-align: right;
+ font-size: 280px;
+ font-weight: 900;
+ letter-spacing: -0.07em;
+ color: #B53422;
+}
+
+.text-editor {
+ background: #222;
+ padding: 60px 50px;
+ border-radius: 12px;
+}
+code {
+ text-align: left;
+ padding: 0;
+ font-size: 22px;
+ background: none;
+ border-radius: 0;
+ color: #f4462f;
+}
+
+.md-facebook {
+ width: 60px;
+ height: 60px;
+ background: #693933;
+ border-radius: 12px;
+ margin-left: 68px;
+ margin-top: 600px;
+}
+.md-twitter {
+ width: 120px;
+ height: 120px;
+ background: #f4462f;
+ border-radius: 12px;
+ margin-left: -60px;
+ margin-top: 60px;
+}
+.md-dixonandmoe {
+ width: 120px;
+ height: 120px;
+ background: #693933;
+ border-radius: 12px;
+ margin-left: -60px;
+ margin-top: 760px;
+}
+.bt-green {
+ width: 60px;
+ height: 60px;
+ background: #f4462f;
+ border-radius: 12px;
+ margin-left: -30px;
+ margin-top: 700px;
+}
+.bt-purple {
+ width: 80px;
+ height: 80px;
+ background: #693933;
+ border-radius: 12px;
+ margin-left: 57px;
+ margin-top: 600px;
+}
+
+.share {
+ position: relative;
+ top: 200px;
+}
+.share a {
+ font-size: 34px;
+}
+.share a span {
+ transition: all 400ms cubic-bezier(0.68, -0.1, 0.265, 1.55);
+}
+.share a:hover span {
+ margin-left: 10px;
+}
+
+a.dam,
+a.dam:hover,
+a.dam:active,
+a.dam:visited {
+ color: #222;
+}
+
+span.comment {
+ color: rgba(255, 255, 255, 0.4);
+}
+span.purple {
+ color: #693933;
+}
+span.green {
+ color: #a0f100;
+}
+span.white {
+ color: #fff;
+}
+span.orange {
+ color: orange;
+}
+
+.card-deck{
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ width: 90vw;
+ height: inherit;
+ align-items: flex-start;
+ overflow: hidden;
+ /*justify-content: space-around;*/
+ width: 100%;
+ justify-content: center;
+}
+
+.card{
+ width: 280px;
+ margin: 1vw;
+ background-color: #FFF;
+ border-radius: 12px;
+}
+
+.card-img-top{
+ width: 100%;
+ border-radius: 12px 12px 0 0 ;
+}
+
+.card-body{
+ margin: 1.5rem;
+}
+
+.card-title{
+ font-size: 18px;
+}
+
+.card-footer{
+ margin: 1.5rem;
+}
+
+.contact > img{
+
+ width: 80px;
+ height: 80px;
+}
+
+.contact:hover{
+
+ filter: invert(0.5);
+}
+
+.openletter{
+ background: #f4462f;
+ padding: .75rem;
+ border-radius: 8px;
+ color: #FFF;
+ display: block;
+ width: 250px;
+ text-align: center;
+}
+
+/* -------------------------------------------------------------------------- */
+/* =====================options-menu and documentation======================= */
+/* -------------------------------------------------------------------------- */
+h2.heading {
+ font-size: 50px;
+}
+h3.heading {
+ font-size: 27px;
+ font-weight: bolder;
+}
+
+.documentation {
+ margin-top: 8rem;
+}
+.options {
+ border-radius: 12px;
+ border: 1px solid #ccc;
+ background-color: white;
+ padding: 1.5rem;
+ margin-top: 2rem;
+ height: auto;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+}
+.options a {
+}
+.options-text {
+ font-size: medium;
+ text-transform: uppercase;
+ letter-spacing: 0.1rem;
+ margin: 1rem 0;
+}
+.options-text--margin {
+ margin-top: 3rem;
+}
+.options-title {
+ font-size: 24px;
+ padding: 1rem 0;
+ color: #222;
+}
+.options-cont{
+ width: 75%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+.sticky {
+ position: sticky;
+ top: 0;
+ z-index: 4;
+}
+/* Style the active class (and buttons on mouse-over) */
+.active {
+ color: #693933;
+}
+.option:hover {
+ color: #f4462f;
+}
+html {
+ scroll-behavior: smooth;
+}
+
+.noisy {
+ background-image: url(data:image/png;base64,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);
+}
+
+
+@keyframes fuzz /* Safari and Chrome */ {
+ 0% {
+ opacity: 0;
+ }
+ 40% {
+ opacity: 0;
+ }
+ 100% {
+ /*-webkit-transform: scaleY(1);*/
+ opacity: 1;
+ }
+}
+
+@keyframes fuzz2 /* Safari and Chrome */ {
+ 0% {
+ opacity: 0;
+ }
+ 48% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fuzz3 /* Safari and Chrome */ {
+ 0% {
+ opacity: 0;
+ }
+ 54% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes stretch /* Safari and Chrome */ {
+ 0% {
+ -webkit-transform: scaleX(0);
+ opacity: 0;
+ }
+ 50% {
+ -webkit-transform: scaleX(0);
+ opacity: 1;
+ }
+ 60% {
+ -webkit-transform: scaleX(0);
+ }
+ 100% {
+ -webkit-transform: scaleX(1);
+ }
+}
+
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 20px, 0);
+ }
+ 30% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+/*----------------------------------
+ Medium devices: iPad vertical
+------------------------------------*/
+
+@media (max-width: 991px) {
+
+}
+
+/*----------------------------------
+ Small devices: iPhone 6+ and below
+------------------------------------*/
+
+@media (max-width: 767px) {
+ .sticky {
+ display: none;
+ }
+ .section {
+ position: relative;
+}
+.fixed-top {
+ position: absolute;
+ width: 300px;
+ height: 300px;
+}
+.main-title > h1{
+ font-size: 52px;
+}
+.absolute h2 {
+ font-size: 60px;
+}
+.temp{
+ font-size: 200px;
+
+}
+.data{
+ font-size: 32px;
+
+}
+}
+
+/*----------------------------------
+ iPhone 6 & 6+ specific text bump
+------------------------------------*/
+
+@media (max-width: 991px) and (max-height: 736px) and (min-height: 481px) {
+ .sticky {
+ display: none;
+ }
+ p {
+ text-align: justify;
+ }
+.fixed-top {
+ position: absolute;
+ width: 300px;
+ height: 300px;
+}
+.main-title > h1{
+ font-size: 52px;
+}
+.absolute h2 {
+ font-size: 60px;
+}
+.temp{
+ font-size: 200px;
+}
+.data{
+ font-size: 32px;
+
+}
+
+}
+
+
+
+
+
diff --git a/favicon-16x16.png b/favicon-16x16.png
new file mode 100644
index 0000000..bf83906
Binary files /dev/null and b/favicon-16x16.png differ
diff --git a/favicon-32x32.png b/favicon-32x32.png
new file mode 100644
index 0000000..7fc58fc
Binary files /dev/null and b/favicon-32x32.png differ
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..50baca1
Binary files /dev/null and b/favicon.ico differ
diff --git a/images/ShotSpotterLogo.svg b/images/ShotSpotterLogo.svg
new file mode 100644
index 0000000..a260945
--- /dev/null
+++ b/images/ShotSpotterLogo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/news-CPDContract.jpg b/images/news-CPDContract.jpg
new file mode 100644
index 0000000..bed57ae
Binary files /dev/null and b/images/news-CPDContract.jpg differ
diff --git a/images/social.jpg b/images/social.jpg
new file mode 100644
index 0000000..d96835f
Binary files /dev/null and b/images/social.jpg differ
diff --git a/images/social_instagram.svg b/images/social_instagram.svg
new file mode 100644
index 0000000..5d7bcbf
--- /dev/null
+++ b/images/social_instagram.svg
@@ -0,0 +1 @@
+3Arts-social_instagram
\ No newline at end of file
diff --git a/images/social_twitter.svg b/images/social_twitter.svg
new file mode 100644
index 0000000..79350f2
--- /dev/null
+++ b/images/social_twitter.svg
@@ -0,0 +1 @@
+3Arts-social_twitter
\ No newline at end of file
diff --git a/images/stop.png b/images/stop.png
new file mode 100644
index 0000000..209cf7b
Binary files /dev/null and b/images/stop.png differ
diff --git a/images/stopshotspotter-FULL.svg b/images/stopshotspotter-FULL.svg
new file mode 100644
index 0000000..0f4c096
--- /dev/null
+++ b/images/stopshotspotter-FULL.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/stopshotspotter-NO.png b/images/stopshotspotter-NO.png
new file mode 100644
index 0000000..65e7f67
Binary files /dev/null and b/images/stopshotspotter-NO.png differ
diff --git a/images/stopshotspotter-NO.svg b/images/stopshotspotter-NO.svg
new file mode 100644
index 0000000..a91bf69
--- /dev/null
+++ b/images/stopshotspotter-NO.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/stopshotspotter-P.svg b/images/stopshotspotter-P.svg
new file mode 100644
index 0000000..0999550
--- /dev/null
+++ b/images/stopshotspotter-P.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/stopshotspotter-S.svg b/images/stopshotspotter-S.svg
new file mode 100644
index 0000000..05ab5d8
--- /dev/null
+++ b/images/stopshotspotter-S.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/stopshotspotter-T.svg b/images/stopshotspotter-T.svg
new file mode 100644
index 0000000..59873ff
--- /dev/null
+++ b/images/stopshotspotter-T.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/stopshotspotter-locations.svg b/images/stopshotspotter-locations.svg
new file mode 100644
index 0000000..cbd65fb
--- /dev/null
+++ b/images/stopshotspotter-locations.svg
@@ -0,0 +1,954 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5846885
--- /dev/null
+++ b/index.html
@@ -0,0 +1,445 @@
+
+
+
+
+
+
+
+
+ #STOPSHOTSPOTTER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
STOP SHOT SPOTTER
+
+
+
+
+
+
+
+
+
+
+ Stop ShotSpotter.
+ Invest in Communities
+
+
+
+
+
+
+
+
+
+
+ Racist Data
+ Over-Policing
+ Algorithmic Bias
+
+
+
+
+
+
+
+ ShotSpotter profits from gun violence
+
+
+
+
+
+
+
+
+
#STOPSHOTSPOTTER
+
+
+
+
+
+
+
+
Who We Are
+
+
We represent a broad coalition of local and national organizations and communities who are deeply concerned about ShotSpotter’s harmful impacts on the Black, brown, and poor people that the company surveils. We are united to #StopShotSpotter and build community-led responses to address the root causes of gun violence.
+
+
+
+
+
+
+
+
+
Sign our Open Letter
+
+
+
+
+
+
+
+
+
Our Demands
+
+ Join us as we demand cities immediately cancel their ShotSpotter contracts and redirect funds to social services which have been proven to address gun violence. City governments considering using ShotSpotter should reject their dubious marketing claims and embrace the science, which is clear that what most effectively addresses gun violence is a public health approach, not spending millions on flashy new law enforcement surveillance tools. It's time for Shotspotter to do the right thing- stop selling products that perpetuate injustices and start listening to communities calling for real solutions to gun violence.
+
+
+
+ News
+
+
+
+
+
+
+
Opinion: Adopting ‘Shotspotter’ gunfire detection system would be neither effective nor equitable
+
+
+
+
+
+
+
+
ShotSpotter Generates Thousands Of Alerts In Dayton, But Officers Find Few Crimes
+
+
+
+
+
+
+
+
Inside the controversial US gunshot-detection firm
+
+
+
+
+
+
+
+
St. Louis technology detects lots of gunfire, but calls often lead to a dead end
+
+
+
+
+
+
+
+
ShotSpotter held in contempt of court
+
+
+
+
+
+
+
+
Lawsuit Alleges Chicago Police Made False Arrests Based on Faulty ShotSpotter Alerts
+
+
+
+
+
+
+
+
Controversial ShotSpotter technology to be used by police along 3 square miles in east, south Durham
+
+
+
+
+
+
+
+
Report: ShotSpotter wastes officers time, provides little help in court, targets overpoliced communities
+
+
+
+
+
+
+
+
NYC, Chicago Waste Millions on Gunshot Detection Technology, Report Says
+
+
+
+
+
+
+
+
In Chicago, Controversy Mounts Over the Use of Gunshot Detection Sensors
+
+
+
+
+
+
+
+
More Cities Are Moving to Drop Automated Gunshot-Detection Tech
+
+
+
+
+
+
+
+
CPD Extends ShotSpotter Contract With No Public Notice
+
+
+
+
+
+
+
+
How AI-powered tech landed man in jail with scant evidence
+
+
+
+
+
+
+
+
How ShotSpotter fights criticism and leverages federal cash to win police contracts
+
+
+
+
+
+
+
+
Opinion: San Diegans deserve a new surveillance ordinance. Until then, we’re all in the dark.
+
+
+
+
+
+
+
+
Cancel the NYPD’s ShotSpotter contract
+
+
+
+
+
+
+
+
The Shots Heard Round the City
+
+
+
+
+
+
+
+
Oakland, CA to reconsider 'totally unproven' ShotSpotter tech
+
+
+
+
+
+
+
+
San Diego City Council Review of Renewal of SpotShotter System Pulled From Agenda
+
+
+
+
+
+
+
+
Chicago watchdog harshly criticizes ShotSpotter system
+
+
+
+
+
+
+
+
Nashville police want $800K for a gunshot alert system. But it’s unclear if the technology is effective.
+
+
+
+
+
+
+
+
Cleveland police say ShotSpotter is helping to reduce gun violence, but critics question its effectiveness
+
+
+
+
+
+
+
+
Too much Big Brother? Mobile gunshot detection initiative stirs government surveillance concerns
+
+
+
+
+
+
+
+
Viewpoints: Buffalo should not spend federal dollars on ShotSpotter
+
+
+
+
+
+
+
+
City of Buffalo 2023 budget approved, ShotSpotter police technology removed
+
+
+
+
+
+
+
+
Hall Monitor: Soft on Crime
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/option.js b/option.js
new file mode 100644
index 0000000..b2e4008
--- /dev/null
+++ b/option.js
@@ -0,0 +1,17 @@
+// ============================================================
+// To activate the active class on the links
+// ============================================================
+// Get the options menu element
+var optnCard = document.getElementById("optnCard");
+
+// Get all links with class="option" inside the menu
+var optns = optnCard.getElementsByClassName("option");
+
+// Loop through the links and add the active class to the current/clicked link
+for (var i = 0; i < optns.length; i++) {
+ optns[i].addEventListener("click", function() {
+ var current = document.getElementsByClassName("active");
+ current[0].className = current[0].className.replace(" active", "");
+ this.className += " active";
+ });
+}
diff --git a/rellax.js b/rellax.js
new file mode 100644
index 0000000..cd70557
--- /dev/null
+++ b/rellax.js
@@ -0,0 +1,497 @@
+
+// ------------------------------------------
+// Rellax.js
+// Buttery smooth parallax library
+// Copyright (c) 2016 Moe Amaya (@moeamaya)
+// MIT license
+//
+// Thanks to Paraxify.js and Jaime Cabllero
+// for parallax concepts
+// ------------------------------------------
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define([], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like environments that support module.exports,
+ // like Node.
+ module.exports = factory();
+ } else {
+ // Browser globals (root is window)
+ root.Rellax = factory();
+ }
+}(typeof window !== "undefined" ? window : global, function () {
+ var Rellax = function(el, options){
+ "use strict";
+
+ var self = Object.create(Rellax.prototype);
+
+ var posY = 0;
+ var screenY = 0;
+ var posX = 0;
+ var screenX = 0;
+ var blocks = [];
+ var pause = true;
+
+ // check what requestAnimationFrame to use, and if
+ // it's not supported, use the onscroll event
+ var loop = window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ function(callback){ return setTimeout(callback, 1000 / 60); };
+
+ // store the id for later use
+ var loopId = null;
+
+ // Test via a getter in the options object to see if the passive property is accessed
+ var supportsPassive = false;
+ try {
+ var opts = Object.defineProperty({}, 'passive', {
+ get: function() {
+ supportsPassive = true;
+ }
+ });
+ window.addEventListener("testPassive", null, opts);
+ window.removeEventListener("testPassive", null, opts);
+ } catch (e) {}
+
+ // check what cancelAnimation method to use
+ var clearLoop = window.cancelAnimationFrame || window.mozCancelAnimationFrame || clearTimeout;
+
+ // check which transform property to use
+ var transformProp = window.transformProp || (function(){
+ var testEl = document.createElement('div');
+ if (testEl.style.transform === null) {
+ var vendors = ['Webkit', 'Moz', 'ms'];
+ for (var vendor in vendors) {
+ if (testEl.style[ vendors[vendor] + 'Transform' ] !== undefined) {
+ return vendors[vendor] + 'Transform';
+ }
+ }
+ }
+ return 'transform';
+ })();
+
+ // Default Settings
+ self.options = {
+ speed: -2,
+ verticalSpeed: null,
+ horizontalSpeed: null,
+ breakpoints: [576, 768, 1201],
+ center: false,
+ wrapper: null,
+ relativeToWrapper: false,
+ round: true,
+ vertical: true,
+ horizontal: false,
+ verticalScrollAxis: "y",
+ horizontalScrollAxis: "x",
+ callback: function() {},
+ };
+
+ // User defined options (might have more in the future)
+ if (options){
+ Object.keys(options).forEach(function(key){
+ self.options[key] = options[key];
+ });
+ }
+
+ function validateCustomBreakpoints () {
+ if (self.options.breakpoints.length === 3 && Array.isArray(self.options.breakpoints)) {
+ var isAscending = true;
+ var isNumerical = true;
+ var lastVal;
+ self.options.breakpoints.forEach(function (i) {
+ if (typeof i !== 'number') isNumerical = false;
+ if (lastVal !== null) {
+ if (i < lastVal) isAscending = false;
+ }
+ lastVal = i;
+ });
+ if (isAscending && isNumerical) return;
+ }
+ // revert defaults if set incorrectly
+ self.options.breakpoints = [576, 768, 1201];
+ console.warn("Rellax: You must pass an array of 3 numbers in ascending order to the breakpoints option. Defaults reverted");
+ }
+
+ if (options && options.breakpoints) {
+ validateCustomBreakpoints();
+ }
+
+ // By default, rellax class
+ if (!el) {
+ el = '.rellax';
+ }
+
+ // check if el is a className or a node
+ var elements = typeof el === 'string' ? document.querySelectorAll(el) : [el];
+
+ // Now query selector
+ if (elements.length > 0) {
+ self.elems = elements;
+ }
+
+ // The elements don't exist
+ else {
+ console.warn("Rellax: The elements you're trying to select don't exist.");
+ return;
+ }
+
+ // Has a wrapper and it exists
+ if (self.options.wrapper) {
+ if (!self.options.wrapper.nodeType) {
+ var wrapper = document.querySelector(self.options.wrapper);
+
+ if (wrapper) {
+ self.options.wrapper = wrapper;
+ } else {
+ console.warn("Rellax: The wrapper you're trying to use doesn't exist.");
+ return;
+ }
+ }
+ }
+
+ // set a placeholder for the current breakpoint
+ var currentBreakpoint;
+
+ // helper to determine current breakpoint
+ var getCurrentBreakpoint = function (w) {
+ var bp = self.options.breakpoints;
+ if (w < bp[0]) return 'xs';
+ if (w >= bp[0] && w < bp[1]) return 'sm';
+ if (w >= bp[1] && w < bp[2]) return 'md';
+ return 'lg';
+ };
+
+ // Get and cache initial position of all elements
+ var cacheBlocks = function() {
+ for (var i = 0; i < self.elems.length; i++){
+ var block = createBlock(self.elems[i]);
+ blocks.push(block);
+ }
+ };
+
+
+ // Let's kick this script off
+ // Build array for cached element values
+ var init = function() {
+ for (var i = 0; i < blocks.length; i++){
+ self.elems[i].style.cssText = blocks[i].style;
+ }
+
+ blocks = [];
+
+ screenY = window.innerHeight;
+ screenX = window.innerWidth;
+ currentBreakpoint = getCurrentBreakpoint(screenX);
+
+ setPosition();
+
+ cacheBlocks();
+
+ animate();
+
+ // If paused, unpause and set listener for window resizing events
+ if (pause) {
+ window.addEventListener('resize', init);
+ pause = false;
+ // Start the loop
+ update();
+ }
+ };
+
+ // We want to cache the parallax blocks'
+ // values: base, top, height, speed
+ // el: is dom object, return: el cache values
+ var createBlock = function(el) {
+ var dataPercentage = el.getAttribute( 'data-rellax-percentage' );
+ var dataSpeed = el.getAttribute( 'data-rellax-speed' );
+ var dataXsSpeed = el.getAttribute( 'data-rellax-xs-speed' );
+ var dataMobileSpeed = el.getAttribute( 'data-rellax-mobile-speed' );
+ var dataTabletSpeed = el.getAttribute( 'data-rellax-tablet-speed' );
+ var dataDesktopSpeed = el.getAttribute( 'data-rellax-desktop-speed' );
+ var dataVerticalSpeed = el.getAttribute('data-rellax-vertical-speed');
+ var dataHorizontalSpeed = el.getAttribute('data-rellax-horizontal-speed');
+ var dataVericalScrollAxis = el.getAttribute('data-rellax-vertical-scroll-axis');
+ var dataHorizontalScrollAxis = el.getAttribute('data-rellax-horizontal-scroll-axis');
+ var dataZindex = el.getAttribute( 'data-rellax-zindex' ) || 0;
+ var dataMin = el.getAttribute( 'data-rellax-min' );
+ var dataMax = el.getAttribute( 'data-rellax-max' );
+ var dataMinX = el.getAttribute('data-rellax-min-x');
+ var dataMaxX = el.getAttribute('data-rellax-max-x');
+ var dataMinY = el.getAttribute('data-rellax-min-y');
+ var dataMaxY = el.getAttribute('data-rellax-max-y');
+ var mapBreakpoints;
+ var breakpoints = true;
+
+ if (!dataXsSpeed && !dataMobileSpeed && !dataTabletSpeed && !dataDesktopSpeed) {
+ breakpoints = false;
+ } else {
+ mapBreakpoints = {
+ 'xs': dataXsSpeed,
+ 'sm': dataMobileSpeed,
+ 'md': dataTabletSpeed,
+ 'lg': dataDesktopSpeed
+ };
+ }
+
+ // initializing at scrollY = 0 (top of browser), scrollX = 0 (left of browser)
+ // ensures elements are positioned based on HTML layout.
+ //
+ // If the element has the percentage attribute, the posY and posX needs to be
+ // the current scroll position's value, so that the elements are still positioned based on HTML layout
+ var wrapperPosY = self.options.wrapper ? self.options.wrapper.scrollTop : (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ // If the option relativeToWrapper is true, use the wrappers offset to top, subtracted from the current page scroll.
+ if (self.options.relativeToWrapper) {
+ var scrollPosY = (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ wrapperPosY = scrollPosY - self.options.wrapper.offsetTop;
+ }
+ var posY = self.options.vertical ? ( dataPercentage || self.options.center ? wrapperPosY : 0 ) : 0;
+ var posX = self.options.horizontal ? ( dataPercentage || self.options.center ? self.options.wrapper ? self.options.wrapper.scrollLeft : (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft) : 0 ) : 0;
+
+ var blockTop = posY + el.getBoundingClientRect().top;
+ var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight;
+
+ var blockLeft = posX + el.getBoundingClientRect().left;
+ var blockWidth = el.clientWidth || el.offsetWidth || el.scrollWidth;
+
+ // apparently parallax equation everyone uses
+ var percentageY = dataPercentage ? dataPercentage : (posY - blockTop + screenY) / (blockHeight + screenY);
+ var percentageX = dataPercentage ? dataPercentage : (posX - blockLeft + screenX) / (blockWidth + screenX);
+ if(self.options.center){ percentageX = 0.5; percentageY = 0.5; }
+
+ // Optional individual block speed as data attr, otherwise global speed
+ var speed = (breakpoints && mapBreakpoints[currentBreakpoint] !== null) ? Number(mapBreakpoints[currentBreakpoint]) : (dataSpeed ? dataSpeed : self.options.speed);
+ var verticalSpeed = dataVerticalSpeed ? dataVerticalSpeed : self.options.verticalSpeed;
+ var horizontalSpeed = dataHorizontalSpeed ? dataHorizontalSpeed : self.options.horizontalSpeed;
+
+ // Optional individual block movement axis direction as data attr, otherwise global movement direction
+ var verticalScrollAxis = dataVericalScrollAxis ? dataVericalScrollAxis : self.options.verticalScrollAxis;
+ var horizontalScrollAxis = dataHorizontalScrollAxis ? dataHorizontalScrollAxis : self.options.horizontalScrollAxis;
+
+ var bases = updatePosition(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed);
+
+ // ~~Store non-translate3d transforms~~
+ // Store inline styles and extract transforms
+ var style = el.style.cssText;
+ var transform = '';
+
+ // Check if there's an inline styled transform
+ var searchResult = /transform\s*:/i.exec(style);
+ if (searchResult) {
+ // Get the index of the transform
+ var index = searchResult.index;
+
+ // Trim the style to the transform point and get the following semi-colon index
+ var trimmedStyle = style.slice(index);
+ var delimiter = trimmedStyle.indexOf(';');
+
+ // Remove "transform" string and save the attribute
+ if (delimiter) {
+ transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g,'');
+ } else {
+ transform = " " + trimmedStyle.slice(11).replace(/\s/g,'');
+ }
+ }
+
+ return {
+ baseX: bases.x,
+ baseY: bases.y,
+ top: blockTop,
+ left: blockLeft,
+ height: blockHeight,
+ width: blockWidth,
+ speed: speed,
+ verticalSpeed: verticalSpeed,
+ horizontalSpeed: horizontalSpeed,
+ verticalScrollAxis: verticalScrollAxis,
+ horizontalScrollAxis: horizontalScrollAxis,
+ style: style,
+ transform: transform,
+ zindex: dataZindex,
+ min: dataMin,
+ max: dataMax,
+ minX: dataMinX,
+ maxX: dataMaxX,
+ minY: dataMinY,
+ maxY: dataMaxY
+ };
+ };
+
+ // set scroll position (posY, posX)
+ // side effect method is not ideal, but okay for now
+ // returns true if the scroll changed, false if nothing happened
+ var setPosition = function() {
+ var oldY = posY;
+ var oldX = posX;
+
+ posY = self.options.wrapper ? self.options.wrapper.scrollTop : (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset;
+ posX = self.options.wrapper ? self.options.wrapper.scrollLeft : (document.documentElement || document.body.parentNode || document.body).scrollLeft || window.pageXOffset;
+ // If option relativeToWrapper is true, use relative wrapper value instead.
+ if (self.options.relativeToWrapper) {
+ var scrollPosY = (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset;
+ posY = scrollPosY - self.options.wrapper.offsetTop;
+ }
+
+
+ if (oldY != posY && self.options.vertical) {
+ // scroll changed, return true
+ return true;
+ }
+
+ if (oldX != posX && self.options.horizontal) {
+ // scroll changed, return true
+ return true;
+ }
+
+ // scroll did not change
+ return false;
+ };
+
+ // Ahh a pure function, gets new transform value
+ // based on scrollPosition and speed
+ // Allow for decimal pixel values
+ var updatePosition = function(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed) {
+ var result = {};
+ var valueX = ((horizontalSpeed ? horizontalSpeed : speed) * (100 * (1 - percentageX)));
+ var valueY = ((verticalSpeed ? verticalSpeed : speed) * (100 * (1 - percentageY)));
+
+ result.x = self.options.round ? Math.round(valueX) : Math.round(valueX * 100) / 100;
+ result.y = self.options.round ? Math.round(valueY) : Math.round(valueY * 100) / 100;
+
+ return result;
+ };
+
+ // Remove event listeners and loop again
+ var deferredUpdate = function() {
+ window.removeEventListener('resize', deferredUpdate);
+ window.removeEventListener('orientationchange', deferredUpdate);
+ (self.options.wrapper ? self.options.wrapper : window).removeEventListener('scroll', deferredUpdate);
+ (self.options.wrapper ? self.options.wrapper : document).removeEventListener('touchmove', deferredUpdate);
+
+ // loop again
+ loopId = loop(update);
+ };
+
+ // Loop
+ var update = function() {
+ if (setPosition() && pause === false) {
+ animate();
+
+ // loop again
+ loopId = loop(update);
+ } else {
+ loopId = null;
+
+ // Don't animate until we get a position updating event
+ window.addEventListener('resize', deferredUpdate);
+ window.addEventListener('orientationchange', deferredUpdate);
+ (self.options.wrapper ? self.options.wrapper : window).addEventListener('scroll', deferredUpdate, supportsPassive ? { passive: true } : false);
+ (self.options.wrapper ? self.options.wrapper : document).addEventListener('touchmove', deferredUpdate, supportsPassive ? { passive: true } : false);
+ }
+ };
+
+ // Transform3d on parallax element
+ var animate = function() {
+ var positions;
+ for (var i = 0; i < self.elems.length; i++){
+ // Determine relevant movement directions
+ var verticalScrollAxis = blocks[i].verticalScrollAxis.toLowerCase();
+ var horizontalScrollAxis = blocks[i].horizontalScrollAxis.toLowerCase();
+ var verticalScrollX = verticalScrollAxis.indexOf("x") != -1 ? posY : 0;
+ var verticalScrollY = verticalScrollAxis.indexOf("y") != -1 ? posY : 0;
+ var horizontalScrollX = horizontalScrollAxis.indexOf("x") != -1 ? posX : 0;
+ var horizontalScrollY = horizontalScrollAxis.indexOf("y") != -1 ? posX : 0;
+
+ var percentageY = ((verticalScrollY + horizontalScrollY - blocks[i].top + screenY) / (blocks[i].height + screenY));
+ var percentageX = ((verticalScrollX + horizontalScrollX - blocks[i].left + screenX) / (blocks[i].width + screenX));
+
+ // Subtracting initialize value, so element stays in same spot as HTML
+ positions = updatePosition(percentageX, percentageY, blocks[i].speed, blocks[i].verticalSpeed, blocks[i].horizontalSpeed);
+ var positionY = positions.y - blocks[i].baseY;
+ var positionX = positions.x - blocks[i].baseX;
+
+ // The next two "if" blocks go like this:
+ // Check if a limit is defined (first "min", then "max");
+ // Check if we need to change the Y or the X
+ // (Currently working only if just one of the axes is enabled)
+ // Then, check if the new position is inside the allowed limit
+ // If so, use new position. If not, set position to limit.
+
+ // Check if a min limit is defined
+ if (blocks[i].min !== null) {
+ if (self.options.vertical && !self.options.horizontal) {
+ positionY = positionY <= blocks[i].min ? blocks[i].min : positionY;
+ }
+ if (self.options.horizontal && !self.options.vertical) {
+ positionX = positionX <= blocks[i].min ? blocks[i].min : positionX;
+ }
+ }
+
+ // Check if directional min limits are defined
+ if (blocks[i].minY != null) {
+ positionY = positionY <= blocks[i].minY ? blocks[i].minY : positionY;
+ }
+ if (blocks[i].minX != null) {
+ positionX = positionX <= blocks[i].minX ? blocks[i].minX : positionX;
+ }
+
+ // Check if a max limit is defined
+ if (blocks[i].max !== null) {
+ if (self.options.vertical && !self.options.horizontal) {
+ positionY = positionY >= blocks[i].max ? blocks[i].max : positionY;
+ }
+ if (self.options.horizontal && !self.options.vertical) {
+ positionX = positionX >= blocks[i].max ? blocks[i].max : positionX;
+ }
+ }
+
+ // Check if directional max limits are defined
+ if (blocks[i].maxY != null) {
+ positionY = positionY >= blocks[i].maxY ? blocks[i].maxY : positionY;
+ }
+ if (blocks[i].maxX != null) {
+ positionX = positionX >= blocks[i].maxX ? blocks[i].maxX : positionX;
+ }
+
+ var zindex = blocks[i].zindex;
+
+ // Move that element
+ // (Set the new translation and append initial inline transforms.)
+ var translate = 'translate3d(' + (self.options.horizontal ? positionX : '0') + 'px,' + (self.options.vertical ? positionY : '0') + 'px,' + zindex + 'px) ' + blocks[i].transform;
+ self.elems[i].style[transformProp] = translate;
+ }
+ self.options.callback(positions);
+ };
+
+ self.destroy = function() {
+ for (var i = 0; i < self.elems.length; i++){
+ self.elems[i].style.cssText = blocks[i].style;
+ }
+
+ // Remove resize event listener if not pause, and pause
+ if (!pause) {
+ window.removeEventListener('resize', init);
+ pause = true;
+ }
+
+ // Clear the animation loop to prevent possible memory leak
+ clearLoop(loopId);
+ loopId = null;
+ };
+
+ // Init
+ init();
+
+ // Allow to recalculate the initial values whenever we want
+ self.refresh = init;
+
+ return self;
+ };
+ return Rellax;
+}));
diff --git a/site.webmanifest b/site.webmanifest
new file mode 100644
index 0000000..3a0197d
--- /dev/null
+++ b/site.webmanifest
@@ -0,0 +1 @@
+{"Stop ShotSpotter":"","STOPSHOTSPOTTER":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
\ No newline at end of file