diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..e01fe513 --- /dev/null +++ b/.classpath @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index b5e6a76d..00000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -# Github language stats file -external/* linguist-vendored -lib/* linguist-vendored -*.css linguist-vendored -*.js linguist-vendored \ No newline at end of file diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index cc2fb2a5..47284c32 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,5 @@ -# Configuration and dependencies +/screenlog.0* /hal.conf /hal.db* -/lib/zutil-* -/recordings/ - -# Runtime files -/screenlog.0* -/OZW_Log.txt - -# Build and Ide files -build -.gradle -.idea \ No newline at end of file +/build/ +/lib/Zutil.jar diff --git a/.project b/.project new file mode 100644 index 00000000..2656b0ff --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + hal + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Hal.iml b/Hal.iml new file mode 100755 index 00000000..5f300ff3 --- /dev/null +++ b/Hal.iml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index c82929e5..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,35 +0,0 @@ -// Jenkinsfile (Pipeline Script) -node { - // Configure environment - env.JAVA_HOME = tool name: 'jdk-11' - env.REPO_URL = "repo.koc.se/hal.git" //scm.getUserRemoteConfigs()[0].getUrl() - env.BUILD_NAME = "BUILD-${env.BUILD_ID}" - - - checkout scm - - stage('Build') { - sh './gradlew clean' - sh './gradlew build' - } - - stage('Test') { - try { - sh './gradlew test' - } finally { - junit testResults: '**/build/test-results/test/*.xml' - } - } - - stage('Package') { - sh './gradlew distZip' - archiveArtifacts artifacts: 'build/distributions/Hal.zip', fingerprint: true - - // Tag artifact - withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'f8e5f6c6-4adb-4ab2-bb5d-1c8535dff491', - usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { - sh "git tag ${env.BUILD_NAME}" - sh "git push 'https://${USERNAME}:${PASSWORD}@${env.REPO_URL}' ${env.BUILD_NAME}" - } - } -} diff --git a/LICENSE.txt b/LICENSE.txt old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 5a054d6b..4e797208 --- a/README.md +++ b/README.md @@ -1,56 +1,39 @@ # Hal Hal is a home automation hub with sensor statistics with the functionality to -share that data between friends. It has been developed to be very extensible so future +share that data between friends. It is developed to be very extensible so future Sensors and other input devices can be supported. - -Features: -- **Map**, Set up a house map with sensors and events mapped on a floorplan -- **Triggers and Actions**, IFTTT type functionality -- **Power;Challenge**, Sync power or sensor usage between friends to challenge each other to lower the power usage -- **[Google Assistant Integration](plugins/hal-assistant-google/READNME.md)** - + Currently supported devices: - **Network Scanner**, IP scanner to detect devices on local network - **NUT**, Linux UPS daemon - **Tellstick**, Supported devices: - - NexaSelfLearning - - Oregon0x1A2D + - NexaSelfLearning + - Oregon0x1A2D - **Raspberry Pi**, GPIO connected sensors -- **[Zigbee](plugins/hal-zigbee/README.md)** - - Temperature Sensors - - Humidity Sensors - - Pressure Sensors - - OnnOff Devices -- **Google Assistant** -- **MQTT Devices** - -Under development (Not ready to be used yet) -- **Z-Wave** -The project is currently in alpha state, and as such things will change and break continuously. +The project is currently in alpha state, and as such things will change and break. ### Screenshots -![Week Graph](screenshot_01.jpg) +![](screenshot_01.jpg) -![Home Map](screenshot_02.jpg) +![](screenshot_02.jpg) -![Sensor Overview](screenshot_03.jpg) +![](screenshot_03.jpg) -![Event Overview](screenshot_04.jpg) +![](screenshot_04.jpg) ## Installing To run the Hal server you first need to clone the git repository and then run the -gradle command to build and run the server: +ant command to build and run: ``` -./gradlew run +ant run ``` -Check `hal.conf.example` for available configuration options. -By default, HAL server will be listening to http://localhost:8080. +Check `hal.conf.example` for available configuration options. ## Running the tests @@ -58,34 +41,7 @@ The current test coverage is greatly lacking, but to run the available JUnit test-cases run: ``` -./gradlew test -``` - -## Architecture - -``` - HalAbstractControlerManager - | - | HalAbstractController - | | - | | HalAbstractDevice - | | | - .-----------. .------------. .--------. - | | | | | | - | | | | ----> | Device | - | | | | | | - | | ----> | Controller | '--------' - | | | | .--------. - | | | | | | - | Manager | | | ----> | Device | - | | | | | | - | | '------------' '--------' - | | .------------. .--------. - | | | | | | - | | ----> | Controller | ----> | Device | - | | | | | | - '-----------' '------------' '--------' - +ant test ``` ## Authors diff --git a/arduino/ArduinoTellstickDuo/archtech.cpp b/arduino/ArduinoTellstickDuo/archtech.cpp index 491ce88d..74662a3b 100644 --- a/arduino/ArduinoTellstickDuo/archtech.cpp +++ b/arduino/ArduinoTellstickDuo/archtech.cpp @@ -59,28 +59,28 @@ bool parseArctechSelfLearning(uint8_t* bufStartP, uint8_t* bufEndP) { //start uint64_t data = 0; bool dimValuePresent; uint8_t b1,b2,b3,b4; - + //parse preamp b1 = *bufStartP; stepBufferPointer(&bufStartP); b2 = *bufStartP; stepBufferPointer(&bufStartP); - if (!IS_PREAMP(b1, b2)){ + if(!IS_PREAMP(b1, b2)){ return false; } //parse data - + uint16_t dataBitsInBuffer = (calculateBufferPointerDistance(bufStartP, bufEndP)-2) / 4; //each bit is representd by 4 high/low if (dataBitsInBuffer == 32) { dimValuePresent = false; - } else if (dataBitsInBuffer == 36){ + }else if(dataBitsInBuffer == 36){ dimValuePresent = true; } else { return false; } - + for (uint8_t i = 0; i < dataBitsInBuffer; ++i) { b1 = *bufStartP; //no of high stepBufferPointer(&bufStartP); @@ -103,7 +103,7 @@ bool parseArctechSelfLearning(uint8_t* bufStartP, uint8_t* bufEndP) { //start } //data parsed - send event over serial - + Serial.print(F("+Wclass:command;protocol:arctech;model:selflearning;data:0x")); uint8_t hexToSend = (dimValuePresent ? 9 : 8); for (int8_t i = hexToSend - 1; i >= 0; --i) { diff --git a/arduino/ArduinoTellstickDuo/rf.cpp b/arduino/ArduinoTellstickDuo/rf.cpp index c97cc0d7..0d5a31a7 100644 --- a/arduino/ArduinoTellstickDuo/rf.cpp +++ b/arduino/ArduinoTellstickDuo/rf.cpp @@ -17,29 +17,29 @@ void parseRadioRXBuffer() { bool parse = false; while (bufferReadP != bufferWriteP) { //stop if the read pointer is pointing to where the writing is currently performed uint8_t sampleCount = *bufferReadP; - + if ( (((uintptr_t)bufferReadP) & 0x1) == 1 ) { //buffer pointer is odd (stores highs) //Serial.print("high:"); Serial.println(sampleCount); if (prevValue >= SILENCE_LENGTH) { startDataP = bufferReadP; //some new data must start here since this is the first "high" after a silent period } - + //stream data to stream parsers parseOregonStream(HIGH, sampleCount); - + } else { //buffer pointer is even (stores lows) //Serial.print("low:"); Serial.println(sampleCount); if (sampleCount >= SILENCE_LENGTH) { //evaluate if it is time to parse the curernt data endDataP = bufferReadP; //this is a silient period and must be the end of a data - if (startDataP != 0){ + if(startDataP != 0){ parse = true; break; } } - + //stream data to stream parsers parseOregonStream(LOW, sampleCount); - + } //step the read pointer one step @@ -74,11 +74,11 @@ void parseRadioRXBuffer() { //Let all available parsers parse the data set now. parseArctechSelfLearning(startDataP, endDataP); //TODO: add more parsers here - + //reset the data pointers since the data have been parsed at this point startDataP = 0; endDataP = 0; - + }; //end radioTask void sendTCodedData(uint8_t* data, uint8_t T_long, uint8_t* timings, uint8_t repeat, uint8_t pause) { @@ -88,7 +88,7 @@ void sendTCodedData(uint8_t* data, uint8_t T_long, uint8_t* timings, uint8_t rep for (int i = 0; i < T_long; ++i) { uint8_t timeIndex = (data[i / 4] >> (6 - (2 * (i % 4)))) & 0x03; if (timings[timeIndex] > 0 || i == T_long - 1) { - if (nextPinState){ + if(nextPinState){ TX_PIN_HIGH(); }else{ TX_PIN_LOW(); @@ -111,7 +111,7 @@ void sendSCodedData(uint8_t* data, uint8_t pulseCount, uint8_t repeat, uint8_t p bool nextPinState = HIGH; for (int i = 0; i < pulseCount; ++i) { if (data[i] > 0 || i == pulseCount - 1) { - if (nextPinState){ + if(nextPinState){ TX_PIN_HIGH(); }else{ TX_PIN_LOW(); diff --git a/arduino/HalMultiSensor/HalConfiguration.h b/arduino/HalMultiSensor/HalConfiguration.h old mode 100644 new mode 100755 index afab67fa..374981c5 --- a/arduino/HalMultiSensor/HalConfiguration.h +++ b/arduino/HalMultiSensor/HalConfiguration.h @@ -6,25 +6,24 @@ #define TIMER_MILLISECOND 60000 // poling in minutes #define INDICATOR_PIN 13 // diode -#define TX_PIN 11 -#define DEVICE_BASE_ID 99 +#define DEVICE_BASE_ID 20 // POWER CONSUMPTION SENSOR //#define POWERCON_ENABLED // comment out to disable sensor #define POWERCON_SENSOR SensorPhotocell() -#define POWERCON_PROTOCOL ProtocolOregon(TX_PIN, DEVICE_BASE_ID + 0) +#define POWERCON_PROTOCOL ProtocolOregon(11, DEVICE_BASE_ID + 1) #define POWER_TIMER_MULTIPLIER 1 // TEMPERATURE SENSOR #define TEMPERATURE_ENABLED // comment out to disable sensor #define TEMPERATURE_SENSOR SensorDHT(DHT11, 10) -#define TEMPERATURE_PROTOCOL ProtocolOregon(TX_PIN, DEVICE_BASE_ID + 1) +#define TEMPERATURE_PROTOCOL ProtocolOregon(11, DEVICE_BASE_ID + 2) #define TEMPERATURE_TIMER_MULTIPLIER 10 // LIGHT SENSOR -//#define LIGHT_ENABLED // comment out to disable sensor +#define LIGHT_ENABLED // comment out to disable sensor #define LIGHT_SENSOR SensorBH1750() -#define LIGHT_PROTOCOL ProtocolOregon(TX_PIN, DEVICE_BASE_ID + 2) +#define LIGHT_PROTOCOL ProtocolOregon(11, DEVICE_BASE_ID + 3) #define LIGHT_TIMER_MULTIPLIER 10 diff --git a/arduino/HalMultiSensor/HalInclude.h b/arduino/HalMultiSensor/HalInclude.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/HalInterfaces.h b/arduino/HalMultiSensor/HalInterfaces.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/HalMultiSensor.ino b/arduino/HalMultiSensor/HalMultiSensor.ino old mode 100644 new mode 100755 index 0b9f3b4b..c7bc6714 --- a/arduino/HalMultiSensor/HalMultiSensor.ino +++ b/arduino/HalMultiSensor/HalMultiSensor.ino @@ -10,15 +10,6 @@ the data to a central location. #include "Interrupt.h" -#ifndef POWERCON_ENABLED - #define POWER_TIMER_MULTIPLIER 1 -#endif -#ifndef TEMPERATURE_ENABLED - #define TEMPERATURE_TIMER_MULTIPLIER 1 -#endif -#ifndef LIGHT_ENABLED - #define LIGHT_TIMER_MULTIPLIER 1 -#endif #define TIMER_MULTIPLIER_MAX \ POWER_TIMER_MULTIPLIER * TEMPERATURE_TIMER_MULTIPLIER * LIGHT_TIMER_MULTIPLIER unsigned int timerMultiplier = 0; @@ -92,7 +83,7 @@ void loop() // Send power consumption #ifdef POWERCON_ENABLED - if (timerMultiplier % POWER_TIMER_MULTIPLIER == 0) + if(timerMultiplier % POWER_TIMER_MULTIPLIER == 0) { static PowerData powerData; powerSensor->read(powerData); // not needed, only here for future use @@ -103,7 +94,7 @@ void loop() // Handle temperature sensor #ifdef TEMPERATURE_ENABLED - if (timerMultiplier % TEMPERATURE_TIMER_MULTIPLIER == 0) + if(timerMultiplier % TEMPERATURE_TIMER_MULTIPLIER == 0) { static TemperatureData tempData; tempSensor->read(tempData); @@ -114,7 +105,7 @@ void loop() // Handle light sensor #ifdef LIGHT_ENABLED - if (timerMultiplier % LIGHT_TIMER_MULTIPLIER == 0) + if(timerMultiplier % LIGHT_TIMER_MULTIPLIER == 0) { static LightData lightData; lightSensor->read(lightData); diff --git a/arduino/HalMultiSensor/HalMultiSensorEnclosure.FCStd b/arduino/HalMultiSensor/HalMultiSensorEnclosure.FCStd old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/HalMultiSensorEnclosure_bottom.stl b/arduino/HalMultiSensor/HalMultiSensorEnclosure_bottom.stl old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/HalMultiSensorEnclosure_top.stl b/arduino/HalMultiSensor/HalMultiSensorEnclosure_top.stl old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/Interrupt.cpp b/arduino/HalMultiSensor/Interrupt.cpp old mode 100644 new mode 100755 index 009af7e7..4389e570 --- a/arduino/HalMultiSensor/Interrupt.cpp +++ b/arduino/HalMultiSensor/Interrupt.cpp @@ -36,14 +36,13 @@ void Interrupt::sleep() sleep_enable(); // enables the sleep bit in the mcucr register // so sleep is possible. just a safety pin - //power_adc_disable(); + power_adc_disable(); //power_spi_disable(); - //power_usart0_disable(); //power_timer0_disable(); //power_timer1_disable(); //power_timer2_disable(); //power_twi_disable(); - //power_all_disable(); + //power_all_disable() while( ! Interrupt::wakeUpNow) { @@ -53,7 +52,7 @@ void Interrupt::sleep() sleep_disable(); // first thing after waking from sleep: // disable sleep... - //power_adc_enable(); + power_adc_enable(); //power_spi_enable(); //power_usart0_enable(); //power_timer0_enable(); diff --git a/arduino/HalMultiSensor/Interrupt.h b/arduino/HalMultiSensor/Interrupt.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/ProtocolNexa.cpp b/arduino/HalMultiSensor/ProtocolNexa.cpp old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/ProtocolNexa.h b/arduino/HalMultiSensor/ProtocolNexa.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/ProtocolOregon.cpp b/arduino/HalMultiSensor/ProtocolOregon.cpp old mode 100644 new mode 100755 index 96f04174..04e91ca9 --- a/arduino/HalMultiSensor/ProtocolOregon.cpp +++ b/arduino/HalMultiSensor/ProtocolOregon.cpp @@ -83,14 +83,14 @@ inline void ProtocolOregon::setId(byte data[], byte id) */ inline void ProtocolOregon::setBatteryLevel(byte data[], bool level) { - if (!level) data[4] = 0x0C; + if(!level) data[4] = 0x0C; else data[4] = 0x00; } inline void ProtocolOregon::setTemperature(byte data[], float temp) { // Set temperature sign - if (temp < 0) + if(temp < 0) { data[6] = 0x08; temp *= -1; @@ -127,9 +127,9 @@ inline void ProtocolOregon::calculateAndSetChecksum(byte data[]) for(byte i = 0; i<8;i++) { sum += (data[i]&0xF0) >> 4; - sum += (data[i]&0x0F); + sum += (data[i]&0xF); } - data[8] = ((sum - 0x0A) & 0xFF); + data[8] = ((sum - 0xa) & 0xFF); } diff --git a/arduino/HalMultiSensor/ProtocolOregon.h b/arduino/HalMultiSensor/ProtocolOregon.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorBH1750.cpp b/arduino/HalMultiSensor/SensorBH1750.cpp old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorBH1750.h b/arduino/HalMultiSensor/SensorBH1750.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorDHT.cpp b/arduino/HalMultiSensor/SensorDHT.cpp old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorDHT.h b/arduino/HalMultiSensor/SensorDHT.h old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorPhotocell.cpp b/arduino/HalMultiSensor/SensorPhotocell.cpp old mode 100644 new mode 100755 diff --git a/arduino/HalMultiSensor/SensorPhotocell.h b/arduino/HalMultiSensor/SensorPhotocell.h old mode 100644 new mode 100755 diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 5f07163e..00000000 --- a/build.gradle +++ /dev/null @@ -1,86 +0,0 @@ -plugins { - id 'java' - id 'application' -} - -// ------------------------------------ -// Hal common configuration -// ------------------------------------ - -allprojects { - repositories { - mavenLocal() - mavenCentral() - } -} - -subprojects { - apply plugin: 'java-library' - - dependencies { - //implementation 'se.koc:zutil:1.0.314' - implementation 'se.koc:zutil:1.0.0-SNAPSHOT' - - testImplementation 'junit:junit:4.12' - testImplementation 'org.hamcrest:hamcrest-core:2.2' - } - - sourceSets { - main { - java { - srcDirs 'src' - } - // We do not want the resource folder to be included in the jar file - //resources { - // srcDir 'resource' - //} - } - test { - java { - srcDirs 'test' - } - } - } -} - -// ------------------------------------ -// Hal general configuration -// ------------------------------------ - -dependencies { - project.subprojects.each { subProject -> - runtimeOnly subProject - } -} - -distributions { - distTar.enabled = false - distZip.enabled = false - - main { - contents { - from 'hal.conf.example' - from 'logging.properties' - - from "${buildDir}/resources" - } - } -} - -task copyRecources(type: Copy) { - doFirst{ - System.out.println("Copying resource files...") - } - - project.subprojects.each { subProject -> - from "${subProject.projectDir}/resources" - } - - into("${buildDir}/resources") -} - -processResources.finalizedBy(copyRecources) - -application { - mainClass = 'se.hal.HalServer' -} \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100755 index 00000000..6278ccc5 --- /dev/null +++ b/build.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/Z-Stick_Gen5_Drivers.zip b/external/Z-Stick_Gen5_Drivers.zip deleted file mode 100644 index c5b3356e..00000000 Binary files a/external/Z-Stick_Gen5_Drivers.zip and /dev/null differ diff --git a/external/marytts-5.1.2/LICENSE.txt b/external/marytts-5.1.2/LICENSE.txt new file mode 100755 index 00000000..01dc74eb --- /dev/null +++ b/external/marytts-5.1.2/LICENSE.txt @@ -0,0 +1,74 @@ +MARY Software User Agreement +11 April 2011 + +MARY is licensed under the following terms. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . + + + + +Applicable Licenses + +MARY is built upon a number of other open source technologies and products. +Here is a list of those products with links to their licenses. + +hts_engine: the HMM-based speech synthesis code in MARY TTS is based on HTS, ported to Java by DFKI. The original HTS can be obtained from +http://hts-engine.sourceforge.net/ -- it is released under the New and +Simplified BSD License. + +freetts: MARY uses code from FreeTTS (http://freetts.sf.net) for various +processing modules and as the source of one method for waveform synthesis. +FreeTTS is licensed under the (BSD-style) FreeTTS license, see +doc/licenses/freetts-license.txt. + +JTok: The JTok tokenizer from http://heartofgold.dfki.de is distributed +under the GNU Lesser General Public License, see http://www.gnu.org or +doc/licenses/LGPL.txt. + +jsresources.jar: A few utility classes from http://www.jsresources.org +are distributed under the terms of the jsresources license, see +doc/licenses/jsresources-license.txt. + +log4j: MARY uses log4j (http://logging.apache.org/log4j) as a logging +mechanism. log4j is distributed under the Apache Software License, see +http://www.apache.org or doc/licenses/apache-software-license.txt + +JUnit: For unit testing of the java source, mary uses JUnit +(http://junit.org). JUnit is licensed under the Common Public License, see +http://junit.org or doc/licenses/CPL.txt. + +java-diff: A java diff implementation from http://www.incava.org/projects/java-diff for input-output-comparisons in the +Mary Expert Interface. java-diff is licensed under the GNU Lesser General +Public License, see http://www.gnu.org or doc/licenses/LGPL.txt. + +fast-md5: A fast md5 checksum implementation from http://www.twmacinta.com/myjava/fast_md5.php +used for computing checksums after downloading voices. fast-md5 is licensed under +the GNU Lesser General Public License, see http://www.gnu.org or doc/licenses/LGPL.txt. + +JavaOpenAIR: MARY can optionally be used as an OpenAIR component, +building on the JavaOpenAIR reference implementation from +http://www.mindmakers.org, which is licensed under the +(BSD-style) JavaOpenAIR license, see doc/licenses/JavaOpenAIR-license.txt +(files concerned: JavaOpenAIR.jar) + +mwdumper: A tool for extracting sets of pages from a MediaWiki dump file. +mwdumper is MIT-style like licensed, see http://www.mediawiki.org/wiki/Mwdumper +and for the license http://en.wikipedia.org/wiki/MIT_License. +(files concerned: mwdumper-2008-04-13.jar) + + +sgt: The Scientific Graphics Toolkit (sgt) is provided by the NOAA/PMEL/EPIC group (see http://www.epic.noaa.gov/java/sgt/) under the BSD-style EPIC license, see doc/licenses/epic-license.txt. + +IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS +AND CONDITIONS PRIOR TO USE OF THIS CONTENT. diff --git a/external/marytts-5.1.2/bin/marytts-client b/external/marytts-5.1.2/bin/marytts-client new file mode 100755 index 00000000..f20b9315 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-client @@ -0,0 +1,10 @@ +#!/bin/bash +########################################################################## +# MARY TTS client +########################################################################## + +# Set the Mary base installation directory in an environment variable: +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" + +java -showversion -ea -Dserver.host=localhost -Dserver.port=59125 -jar "$MARY_BASE/lib/marytts-client-5.1.2-jar-with-dependencies.jar" diff --git a/external/marytts-5.1.2/bin/marytts-client.bat b/external/marytts-5.1.2/bin/marytts-client.bat new file mode 100755 index 00000000..4a231d9f --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-client.bat @@ -0,0 +1,10 @@ +@echo off +set BINDIR=%~dp0 +call :RESOLVE "%BINDIR%\.." MARY_BASE + +java -showversion -ea -Dserver.host=localhost -Dserver.port=59125 -jar "%MARY_BASE%\lib\marytts-client-5.1.2-jar-with-dependencies.jar" +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/bin/marytts-component-installer b/external/marytts-5.1.2/bin/marytts-component-installer new file mode 100755 index 00000000..44cea75d --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-component-installer @@ -0,0 +1,5 @@ +#!/bin/sh +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" +java -showversion -ea -Dmary.base="$MARY_BASE" $* -cp "$MARY_BASE/lib/*" marytts.tools.install.InstallerGUI + diff --git a/external/marytts-5.1.2/bin/marytts-component-installer.bat b/external/marytts-5.1.2/bin/marytts-component-installer.bat new file mode 100755 index 00000000..8f4ff657 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-component-installer.bat @@ -0,0 +1,9 @@ +@echo off +set BINDIR=%~dp0 +call :RESOLVE "%BINDIR%\.." MARY_BASE +java -showversion -ea -Dmary.base="%MARY_BASE%" -cp ".;%MARY_BASE%\lib\*" marytts.tools.install.InstallerGUI +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/bin/marytts-server b/external/marytts-5.1.2/bin/marytts-server new file mode 100755 index 00000000..be3c0939 --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-server @@ -0,0 +1,11 @@ +#!/bin/bash +########################################################################## +# MARY TTS server +########################################################################## + +# Set the Mary base installation directory in an environment variable: +BINDIR="`dirname "$0"`" +export MARY_BASE="`(cd "$BINDIR"/.. ; pwd)`" + + +java -showversion -ea -Xms40m -Xmx1g -cp "$MARY_BASE/lib/*" -Dmary.base="$MARY_BASE" $* marytts.server.Mary diff --git a/external/marytts-5.1.2/bin/marytts-server.bat b/external/marytts-5.1.2/bin/marytts-server.bat new file mode 100755 index 00000000..f3cc2dad --- /dev/null +++ b/external/marytts-5.1.2/bin/marytts-server.bat @@ -0,0 +1,14 @@ +@echo off + +rem Set the Mary base installation directory in an environment variable: +set BINDIR=%~dp0 + +call :RESOLVE "%BINDIR%\.." MARY_BASE + +set CLASSPATH=".;%MARY_BASE%\lib\*" +java -showversion -ea -Xms40m -Xmx1g -cp %CLASSPATH% "-Dmary.base=%MARY_BASE%" marytts.server.Mary +goto :EOF + +:RESOLVE +set %2=%~f1 +goto :EOF diff --git a/external/marytts-5.1.2/doc/examples/client/MaryClient.py b/external/marytts-5.1.2/doc/examples/client/MaryClient.py new file mode 100755 index 00000000..46569873 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/MaryClient.py @@ -0,0 +1,367 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import socket, sys, types, getopt + + +languageNames = {'de':'German', + 'en':'English', + 'en_US':'US English', + 'tib':'Tibetan'} + +class MaryClient: + specificationVersion = "0.1" + + """Python implementation of a MARY TTS client""" + def __init__( self, host="cling.dfki.uni-sb.de", port=59125, profile=False, quiet=False ): + self.host = host + self.port = port + self.profile = profile + self.quiet = quiet + self.allVoices = None # array of Voice objects + self.voicesByLocaleMap = {} # Map locale strings to arrays of Voice objects + self.allDataTypes = None # array of DataType objects + self.inputDataTypes = None # array of DataType objects + self.outputDataTypes = None # array of DataType objects + self.serverExampleTexts = {} + self.voiceExampleTexts = {} + self.serverVersionInfo = u'' + + if not self.quiet: + sys.stderr.write( "MARY TTS Python Client %s\n" % ( self.specificationVersion ) ) + try: + info = self.getServerVersionInfo() + except: + sys.stderr.write( "Problem connecting to mary server at %s:%i\n" % ( self.host, self.port ) ) + raise + sys.stderr.write( "Connected to %s:%i, " % ( self.host, self.port ) ) + sys.stderr.write( info ) + sys.stderr.write( '\n' ) + + def __getServerInfo( self, request="", marySocket=None ): + """Get answer to request from mary server. Returns a list of unicode strings, + each representing a line without the line break. + """ + closeSocket = False + if marySocket is None: + closeSocket = True + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + assert isinstance(marySocket, socket.SocketType) + maryFile = marySocket.makefile( 'rwb', 1 ) # read-write, line-buffered + maryFile.write( unicode( request+"\n" ).encode( 'utf-8' ) ) + result = [] + while True: + got = unicode( maryFile.readline().strip(), 'utf-8' ) + # read until end of file or an empty line is read: + if not got: break + result.append(got) + if closeSocket: + marySocket.close() + return result + + def getServerVersionInfo( self ): + "Get version info from server. Returns a unicode string" + if self.serverVersionInfo == u'': + # need to get it from server + self.serverVersionInfo = u'\n'.join(self.__getServerInfo("MARY VERSION")) + return self.serverVersionInfo + + def getAllDataTypes(self, locale=None): + """Obtain a list of all data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of DataType objects + """ + if self.allDataTypes is None: + self.__fillDataTypes() + assert self.allDataTypes is not None and len( self.allDataTypes ) > 0 + if locale is None: + return self.allDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.allDataTypes if d.locale is None or d.locale == locale] + + def getInputDataTypes(self,locale=None): + """Obtain a list of input data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an arry of DataType objects + """ + if self.inputDataTypes is None: + self.__fillDataTypes() + assert self.inputDataTypes is not None and len( self.inputDataTypes ) > 0 + if locale is None: + return self.inputDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.inputDataTypes if d.locale is None or d.locale == locale] + + def getOutputDataTypes(self, locale=None): + """Obtain a list of output data types known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an arry of DataType objects + """ + if self.outputDataTypes is None: + self.__fillDataTypes() + assert self.outputDataTypes is not None and len( self.outputDataTypes ) > 0 + if locale is None: + return self.outputDataTypes + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + return [d for d in self.outputDataTypes if d.locale is None or d.locale == locale] + + + def __fillDataTypes( self ): + self.allDataTypes = [] + self.inputDataTypes = [] + self.outputDataTypes = [] + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + # Expect a variable number of lines of the kind + # RAWMARYXML INPUT OUTPUT + # TEXT_DE LOCALE=de INPUT + # AUDIO OUTPUT + typeStrings = self.__getServerInfo( "MARY LIST DATATYPES", marySocket ) + if not typeStrings or len(typeStrings) == 0: + raise IOError( "Could not get list of data types from Mary server" ) + marySocket.close() + for typeString in typeStrings: + parts = typeString.split() + if len( parts ) == 0: + continue + name = parts[0] + isInputType = False + isOutputType = False + locale = None + for part in parts[1:]: + if part[:7] == "LOCALE=": + locale = part[7:] + elif part == "INPUT": + isInputType = True + elif part == "OUTPUT": + isOutputType = True + dt = DataType( name, locale, isInputType, isOutputType ) + self.allDataTypes.append( dt ) + if dt.isInputType: + self.inputDataTypes.append( dt ) + if dt.isOutputType: + self.outputDataTypes.append( dt ) + + def getVoices( self, locale=None ): + """Obtain a list of voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + if self.allVoices is None: + self.__fillVoices() + assert self.allVoices is not None and len( self.allVoices ) > 0 + if locale is None: + return self.allVoices + else: + assert isinstance(locale, types.UnicodeType), "Unexpected type for locale: '%s'" % (type(locale)) + if self.voicesByLocaleMap.has_key(locale): + return self.voicesByLocaleMap[locale] + else: + raise Exception("No voices for locale '%s'" % (locale)) + + def __fillVoices( self ): + self.allVoices = [] + self.voicesByLocaleMap = {} + marySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + marySocket.connect( ( self.host, self.port ) ) + # Expect a variable number of lines of the kind + # de7 de female + # us2 en male + # dfki-stadium-emo de male limited + voiceStrings = self.__getServerInfo( "MARY LIST VOICES", marySocket ) + if not voiceStrings or len(voiceStrings) == 0: + raise IOError( "Could not get list of voices from Mary server" ) + marySocket.close() + for voiceString in voiceStrings: + parts = voiceString.split() + if len( parts ) < 3: + continue + name = parts[0] + locale = parts[1] + gender = parts[2] + domain = None + if len( parts ) > 3: + domain = parts[3] + voice = Voice( name, locale, gender, domain ) + self.allVoices.append( voice ) + localeVoices = None + if self.voicesByLocaleMap.has_key( locale ): + localeVoices = self.voicesByLocaleMap[locale] + else: + localeVoices = [] + self.voicesByLocaleMap[locale] = localeVoices + localeVoices.append( voice ) + + def getGeneralDomainVoices( self, locale=None ): + """Obtain a list of general domain voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + return [v for v in self.getVoices( locale ) if not v.isLimitedDomain] + + def getLimitedDomainVoices( self, locale=None ): + """Obtain a list of limited domain voices known to the server. If the information is not + yet available, the server is queried. This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities. + Returns an array of Voice objects + """ + return [v for v in self.getVoices( locale ) if v.isLimitedDomain] + + def getAvailableLanguages(self): + """ Check available voices and return a list of tuples (abbrev, name) + representing the available languages -- e.g. [('en', 'English'),('de', 'German')]. + """ + if self.allVoices is None: + self.__fillVoices() + assert self.allVoices is not None and len( self.allVoices ) > 0 + languages = [] + for l in self.voicesByLocaleMap.keys(): + if languageNames.has_key(l): + languages.append((l,languageNames[l])) + else: + languages.append((l, l)) + return languages + + def getServerExampleText( self, dataType ): + """Request an example text for a given data type from the server. + dataType the string representation of the data type, + e.g. "RAWMARYXML". This is optional information + which is not required for the normal operation of the client, but + may help to avoid incompatibilities.""" + if not self.serverExampleTexts.has_key( dataType ): + exampleTexts = self.__getServerInfo( "MARY EXAMPLETEXT %s" % ( dataType ) ) + if not exampleTexts or len(exampleTexts) == 0: + raise IOError( "Could not get example text for type '%s' from Mary server" % (dataType)) + exampleText = u'\n'.join(exampleTexts) + self.serverExampleTexts[dataType] = exampleText + return self.serverExampleTexts[dataType] + + def process( self, input, inputType, outputType, audioType=None, defaultVoiceName=None, output=sys.stdout ): + assert type( input ) in types.StringTypes + assert type( inputType ) in types.StringTypes + assert type( outputType ) in types.StringTypes + assert audioType is None or type( audioType ) in types.StringTypes + assert defaultVoiceName is None or type( defaultVoiceName ) in types.StringTypes + assert callable( getattr( output, 'write' ) ) + if type( input ) != types.UnicodeType: + input = unicode( input, 'utf-8' ) + maryInfoSocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + maryInfoSocket.connect( ( self.host, self.port ) ) + assert type( maryInfoSocket ) is socket.SocketType + maryInfo = maryInfoSocket.makefile( 'rwb', 1 ) # read-write, line-buffered + maryInfo.write( unicode( "MARY IN=%s OUT=%s" % ( inputType, outputType ), 'utf-8' ) ) + if audioType: + maryInfo.write( unicode( " AUDIO=%s" % ( audioType ), 'utf-8' ) ) + if defaultVoiceName: + maryInfo.write( unicode( " VOICE=%s" % ( defaultVoiceName ), 'utf-8' ) ) + maryInfo.write( "\r\n" ) + # Receive a request ID: + id = maryInfo.readline() + maryDataSocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) + maryDataSocket.connect( ( self.host, self.port ) ) + assert type( maryDataSocket ) is socket.SocketType + maryDataSocket.sendall( id ) # includes newline + maryDataSocket.sendall( input.encode( 'utf-8' ) ) + maryDataSocket.shutdown( 1 ) # shutdown writing + # Set mary info socket to non-blocking, so we only read somthing + # if there is something to read: + maryInfoSocket.setblocking( 0 ) + while True: + try: + err = maryInfoSocket.recv( 8192 ) + if err: sys.stderr.write( err ) + except: + pass + got = maryDataSocket.recv( 8192 ) + if not got: break + output.write( got ) + maryInfoSocket.setblocking( 1 ) + while True: + err = maryInfoSocket.recv( 8192 ) + if not err: break + sys.stderr.write( err ) + + + +################ data representation classes ################## + +class DataType: + def __init__( self, name, locale=None, isInputType=False, isOutputType=False ): + self.name = name + self.locale = locale + self.isInputType = isInputType + self.isOutputType = isOutputType + + def isTextType( self ): + return self.name != "AUDIO" + +class Voice: + + def __init__( self, name, locale, gender, domain="general" ): + self.name = name + self.locale = locale + self.gender = gender + self.domain = domain + if not domain or domain == "general": + self.isLimitedDomain = False + else: + self.isLimitedDomain = True + + def __str__(self): + if languageNames.has_key(self.locale): + langName = languageNames[self.locale] + else: + langName = self.locale + if self.isLimitedDomain: + return "%s (%s, %s %s)" % (self.name, self.domain, langName, self.gender) + else: + return "%s (%s %s)" % (self.name, langName, self.gender) + +##################### Main ######################### + +if __name__ == '__main__': + + serverHost = "cling.dfki.uni-sb.de" + serverPort = 59125 + inputType = "TEXT" + outputType = "AUDIO" + audioType = "WAVE" + defaultVoice = None + inputEncoding = 'utf-8' + ( options, rest ) = getopt.getopt( sys.argv[1:], '', \ + ['server.host=', 'server.port=', 'input.type=', 'output.type=', \ + 'audio.type=', 'voice.default=', 'input.encoding='] ) + for ( option, value ) in options: + if option == '--server.host': serverHost = value + elif option == '--server.port': serverPort = int( value ) + elif option == '--input.type': inputType = value + elif option == '--output.type': outputType = value + elif option == '--audio.type': audioType = value + elif option == '--voice.default': defaultVoice = value + elif option == '--input.encoding': inputEncoding = value + if len( rest )>0: # have input file + inputFile = file( rest[0] ) + else: + inputFile = sys.stdin + input = unicode( ''.join( inputFile.readlines() ), inputEncoding ) + if len( rest )>1: # also have output file + outputFile = file( rest[1] ) + else: + outputFile = sys.stdout + + maryClient = MaryClient( serverHost, serverPort ) + maryClient.process( input, inputType, outputType, audioType, defaultVoice, outputFile ) diff --git a/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java b/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java new file mode 100755 index 00000000..a416ab3a --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/MaryClientUser.java @@ -0,0 +1,96 @@ +/** + * Copyright 2000-2006 DFKI GmbH. + * All Rights Reserved. Use is subject to license terms. + * + * Permission is hereby granted, free of charge, to use and distribute + * this software and its documentation without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of this work, and to + * permit persons to whom this work is furnished to do so, subject to + * the following conditions: + * + * 1. The code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Any modifications must be clearly marked as such. + * 3. Original authors' names are not deleted. + * 4. The authors' names are not used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE + * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +import marytts.client.MaryClient; +import marytts.util.data.audio.AudioPlayer; +import marytts.util.http.Address; + +import javax.sound.sampled.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.UnknownHostException; + +/** + * A demo class illustrating how to use the MaryClient class. + * This will connect to a MARY server, version 4.x. + * It requires maryclient.jar from MARY 4.0. + * This works transparently with MARY servers in both http and socket server mode. + * + * Compile this as follows: + * javac -cp maryclient.jar MaryClientUser.java + * + * And run as: + * java -cp .:maryclient.jar MaryClientUser + * + * @author marc + * + */ + +public class MaryClientUser { + + public static void main(String[] args) + throws IOException, UnknownHostException, UnsupportedAudioFileException, + InterruptedException + { + String serverHost = System.getProperty("server.host", "cling.dfki.uni-sb.de"); + int serverPort = Integer.getInteger("server.port", 59125).intValue(); + MaryClient mary = MaryClient.getMaryClient(new Address(serverHost, serverPort)); + String text = "Willkommen in der Welt der Sprachsynthese!"; + // If the given locale is not supported by the server, it returns + // an ambigous exception: "Problem processing the data." + String locale = "de"; // or US English (en-US), Telugu (te), Turkish (tr), ... + String inputType = "TEXT"; + String outputType = "AUDIO"; + String audioType = "WAVE"; + String defaultVoiceName = null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mary.process(text, inputType, outputType, locale, audioType, defaultVoiceName, baos); + // The byte array constitutes a full wave file, including the headers. + // And now, play the audio data: + AudioInputStream ais = AudioSystem.getAudioInputStream( + new ByteArrayInputStream(baos.toByteArray())); + LineListener lineListener = new LineListener() { + public void update(LineEvent event) { + if (event.getType() == LineEvent.Type.START) { + System.err.println("Audio started playing."); + } else if (event.getType() == LineEvent.Type.STOP) { + System.err.println("Audio stopped playing."); + } else if (event.getType() == LineEvent.Type.OPEN) { + System.err.println("Audio line opened."); + } else if (event.getType() == LineEvent.Type.CLOSE) { + System.err.println("Audio line closed."); + } + } + }; + + AudioPlayer ap = new AudioPlayer(ais, lineListener); + ap.start(); + } +} diff --git a/external/marytts-5.1.2/doc/examples/client/c++/Makefile b/external/marytts-5.1.2/doc/examples/client/c++/Makefile new file mode 100755 index 00000000..a609beda --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/Makefile @@ -0,0 +1,45 @@ +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## + +CC=g++ +CFLAGS=-Wall -w -O3 -g +ICUDIR=/usr/local/icu +ICULIBS=-Wl,-R,$(ICUDIR)/lib -L$(ICUDIR)/lib -licuuc -licui18n -ldl + +all: MaryDemo + +MaryDemo: MaryClient.o MaryDemo.o + $(CC) $(CFLAGS) *.o -o MaryDemo $(LIBS) + +%.o: %.cc + $(CC) $(CFLAGS) $(RFLAGS) -o $@ -c $< + +clean: + rm -rf *.o ./MaryDemo + diff --git a/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc b/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc new file mode 100755 index 00000000..7e920d95 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/MaryClient.cc @@ -0,0 +1,277 @@ +/** + * Copyright 2000-2006 DFKI GmbH. + * All Rights Reserved. Use is subject to license terms. + * + * Permission is hereby granted, free of charge, to use and distribute + * this software and its documentation without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of this work, and to + * permit persons to whom this work is furnished to do so, subject to + * the following conditions: + * + * 1. The code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * 2. Any modifications must be clearly marked as such. + * 3. Original authors' names are not deleted. + * 4. The authors' names are not used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE + * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ +#include +#include +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * A C++ implementation of a simple client to the MARY TTS system. + * result: an empty string serving as the container for the output. + * It will return text or audio data; text data will be encoded as UTF-8. + * inputText: the UTF-8 encoded text (or XML document) to send as a request + * maryInFormat: the input type of the data in inputText, e.g. TEXT + * maryOutFormat: the output type to produce, e.g. MBROLA, AUDIO + * locale: the language of the input, e.g. EN-US, DE + * audioType: for AUDIO output, the type of audio data to produce, + * e.g. WAVE or MP3. + * voice: the voice to be used, e.g. cmu-slt-hsmm, bits3. + * effects: the list of effects to be generated. + * return value: 0 on success, negative on failure. + */ +int +MaryClient::maryQuery( int server_port, + string server_host, + string& result, + string inputText, + string maryInFormat, + string maryOutFormat, + string locale, + string audioType, + string voice, + string effects ) { + + // prepare the request + string query = "MARY"; + query += " IN=" + maryInFormat; + query += " OUT=" + maryOutFormat; + query += " LOCALE=" + locale; // remove this line, if using an older version than MARY 4.0 + query += " AUDIO=" + audioType; + query += " VOICE=" + voice; + if (effects != "") { + query += " EFFECTS=" + effects; + } + query += "\012\015"; + + //cout << "Constructed query: " << query << endl; + + // declare connection stuff + struct sockaddr_in maryServer; + struct sockaddr_in maryClient; + struct hostent* hostInfo; + + // declare variables + int maryInfoSocket; + int maryDataSocket; + + // set configuration parameters + + // get host information + hostInfo = gethostbyname (server_host.c_str()); + + if (hostInfo == NULL) + { + return -2; + } + + + // create a tcp connection to the mary server + maryInfoSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryInfoSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryInfoSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryInfoSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + // send request to the Mary server + if (send (maryInfoSocket, query.c_str (), query.size (), 0) == -1) + { + return -2; + } + + + // receive the request id + char id [32] = ""; + + if (recv (maryInfoSocket, id, 32, 0) == -1) + { + return -2; + } + + //cout << "Read id: " << id << endl; + + // create a tcp connection to the mary server + maryDataSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryDataSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryDataSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryDataSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + + // send the request id to the Mary server + if (send (maryDataSocket, id, strlen (id), 0) == -1) + { + return -2; + } + + //cout << "Sending request: " << inputText << endl; + + // send the query to the Mary server + if (send (maryDataSocket, inputText.c_str (), inputText.size (), 0) == -1) + { + return -2; + } + + if (send (maryDataSocket, "\012\015", 2, 0) == -1) + { + return -2; + } + + + // shutdown data socket + shutdown (maryDataSocket, 1); + + + //cout << "Reading result" << endl; + + unsigned int total_bytes = 0; + int recv_bytes = 0; + char data [1024] = ""; + + result [0] = '\0'; + + // receive the request result + do + { + data [0] = '\0'; + + recv_bytes = recv (maryDataSocket, data, 1024, 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + //cout << "("</libwsock32.a +#include +#else +#include +#endif + +#include +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * A C++ implementation of a simple client to the MARY TTS system. + * result: an empty string serving as the container for the output. + * It will return text or audio data; text data will be encoded as UTF-8. + * inputText: the UTF-8 encoded text (or XML document) to send as a request + * maryInFormat: the input type of the data in inputText, e.g. TEXT + * maryOutFormat: the output type to produce, e.g. MBROLA, AUDIO + * locale: the language of the input, e.g. EN-US, DE + * audioType: for AUDIO output, the type of audio data to produce, + * e.g. WAVE or MP3. + * voice: the voice to be used, e.g. cmu-slt-hsmm, bits3. + * effects: the list of effects to be generated. + * return value: 0 on success, negative on failure. + */ +int +MaryClient::maryQuery( int server_port, + string server_host, + string& result, + string inputText, + string maryInFormat, + string maryOutFormat, + string locale, + string audioType, + string voice, + string effects ) { + + // prepare the request + string query = "MARY"; + query += " IN=" + maryInFormat; + query += " OUT=" + maryOutFormat; + query += " LOCALE=" + locale; // remove this line, if using an older version than MARY 4.0 + query += " AUDIO=" + audioType; + query += " VOICE=" + voice; + if (effects != "") { + query += " EFFECTS=" + effects; + } + query += "\012\015"; + + //cout << "Constructed query: " << query << endl; + + // declare connection stuff + struct sockaddr_in maryServer; + struct sockaddr_in maryClient; + struct hostent* hostInfo; + + // declare variables + int maryInfoSocket; + int maryDataSocket; + + // set configuration parameters + + // get host information + hostInfo = gethostbyname (server_host.c_str()); + + if (hostInfo == NULL) + { + return -2; + } + + + // create a tcp connection to the mary server + maryInfoSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryInfoSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryInfoSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryInfoSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + // send request to the Mary server + if (send (maryInfoSocket, query.c_str (), query.size (), 0) == -1) + { + return -2; + } + + + // receive the request id + char id [32] = ""; + + if (recv (maryInfoSocket, id, 32, 0) == -1) + { + return -2; + } + + //cout << "Read id: " << id << endl; + + // create a tcp connection to the mary server + maryDataSocket = socket (AF_INET, SOCK_STREAM, 0); + + // verify that the socket could be opened successfully + if (maryDataSocket == -1) + { + return -2; + } + else + // autoflush stdout, bind and connect + { + maryClient.sin_family = AF_INET; + maryClient.sin_port = htons (0); + maryClient.sin_addr.s_addr = INADDR_ANY; + + int status = bind (maryDataSocket, (struct sockaddr*) &maryClient, sizeof (maryClient)); + + if (status != 0) + { + return -2; + } + + maryServer.sin_family = AF_INET; + maryServer.sin_port = htons (server_port); + memcpy ((char*) &maryServer.sin_addr.s_addr, hostInfo->h_addr_list [0], hostInfo->h_length); + + status = connect (maryDataSocket, (struct sockaddr*) &maryServer, sizeof (maryServer)); + + if (status != 0) + { + return -2; + } + } + + + // send the request id to the Mary server + if (send (maryDataSocket, id, strlen (id), 0) == -1) + { + return -2; + } + + //cout << "Sending request: " << inputText << endl; + + // send the query to the Mary server + if (send (maryDataSocket, inputText.c_str (), inputText.size (), 0) == -1) + { + return -2; + } + + if (send (maryDataSocket, "\012\015", 2, 0) == -1) + { + return -2; + } + + + // shutdown data socket + shutdown (maryDataSocket, 1); + + + //cout << "Reading result" << endl; + + unsigned int total_bytes = 0; + int recv_bytes = 0; + char data [1024] = ""; + + result [0] = '\0'; + + // receive the request result + do + { + data [0] = '\0'; + + recv_bytes = recv (maryDataSocket, data, 1024, 0); + + if (recv_bytes == -1) + { + return -2; + } + else if (recv_bytes > 0) + { + //cout << "("< +#include +#include + +#include "MaryClient.h" + +using namespace std; + +/** + * Demonstration code for using the MaryClient. + + Call this as: + * ./MaryDemo + * or + * ./MaryDemo > output.wav + */ +int main() { + int server_port = 59125; + string server_host = "localhost"; + string inputText = "Welcome to the world of speech synthesis!"; + string maryInFormat = "TEXT"; + string maryOutFormat = "AUDIO"; + //string maryOutFormat = "REALISED_DURATIONS"; + string locale = "en-US"; + string audioType = "WAV_FILE"; + string voice = "cmu-slt-hsmm"; + string effects; +// effects += "Volume(amount:5.0;)+"; +// effects += "TractScaler(amount:1.5;)+"; +// effects += "F0Scale(f0Scale:2.0;)+"; +// effects += "F0Add(f0Add:50.0;)+"; +// effects += "Rate(durScale:1.5;)+"; +// effects += "Robot(amount:100.0;)+"; +// effects += "Whisper(amount:100.0;)+"; +// effects += "Stadium(amount:100.0)+"; +// effects += "Chorus(delay1:466;amp1:0.54;delay2:600;amp2:-0.10;delay3:250;amp3:0.30)+"; +// effects += "FIRFilter(type:3;fc1:500.0;fc2:2000.0)+"; +// effects += "JetPilot"; + string result; + + MaryClient maryClient; + maryClient.maryQuery( server_port, server_host, result, inputText, maryInFormat, maryOutFormat, locale, audioType, voice, effects); + + if (maryOutFormat == "AUDIO") { + // write result into a file + const char *filename = "output.wav"; + ofstream file( filename ); + file << result; + + // play output + //system("play output.wav"); + } else { + cout << "RESULT: " << endl << result << endl; + } + + return 0; +} + diff --git a/external/marytts-5.1.2/doc/examples/client/c++/README.txt b/external/marytts-5.1.2/doc/examples/client/c++/README.txt new file mode 100755 index 00000000..e38af989 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/c++/README.txt @@ -0,0 +1,4 @@ +Start MARY as a socket server: + +maryserver -Dserver=socket +(or change entry 'server' in conf/marybase.config) diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient-http.py b/external/marytts-5.1.2/doc/examples/client/maryclient-http.py new file mode 100755 index 00000000..cf9782e8 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient-http.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +import httplib, urllib + +# A basic mary client in Python, +# kindly donated to the MARY TTS project +# by Hugh Sasse. Thanks Hugh! + +# A very basic Python class for accessing +# the MARY TTS system using the modern +# HTTP server. +# Warning, this is probably ghastly Python, +# most of my time of late has been with +# other languages, so I'm not up to date +# with all the stylistic conventions of +# modern Python. +# This does seem to work OK though. + +class maryclient: + """A basic handler for MARY-TTS HTTP clients + + At present, there is no checking for + allowed voices, locales, and so on. + Most of the useful parameters can be + accessed by get_ and set_ methods. + Relying on winsound, this is Windows + specific. + """ + def __init__(self): + """Set up useful defaults (for + people in England, anyway)""" + self.host = "127.0.0.1" + self.port = 59125 + self.input_type = "TEXT" + self.output_type = "AUDIO" + self.audio = "WAVE_FILE" + self.locale = "en_GB" + self.voice = "dfki-prudence-hsmm" + + def set_host(self, a_host): + """Set the host for the TTS server.""" + self.host = a_host + + def get_host(self): + """Get the host for the TTS server.""" + self.host + + def set_port(self, a_port): + """Set the port for the TTS server.""" + self.port = a_port + + def get_port(self): + """Get the port for the TTS server.""" + self.port + + def set_input_type(self, type): + """Set the type of input being + supplied to the TTS server + (such as 'TEXT').""" + self.input_type = type + + def get_input_type(self): + """Get the type of input being + supplied to the TTS server + (such as 'TEXT').""" + self.input_type + + def set_output_type(self, type): + """Set the type of input being + supplied to the TTS server + (such as 'AUDIO').""" + self.output_type = type + + def get_output_type(self): + """Get the type of input being + supplied to the TTS server + (such as "AUDIO").""" + self.output_type + + def set_locale(self, a_locale): + """Set the locale + (such as "en_GB").""" + self.locale = a_locale + + def get_locale(self): + """Get the locale + (such as "en_GB").""" + self.locale + + def set_audio(self, audio_type): + """Set the audio type for playback + (such as "WAVE_FILE").""" + self.audio = audio_type + + def get_audio(self): + """Get the audio type for playback + (such as "WAVE_FILE").""" + self.audio + + def set_voice(self, a_voice): + """Set the voice to speak with + (such as "dfki-prudence-hsmm").""" + self.voice = a_voice + + def get_voice(self): + """Get the voice to speak with + (such as "dfki-prudence-hsmm").""" + self.voice + + def generate(self, message): + """Given a message in message, + return a response in the appropriate + format.""" + raw_params = {"INPUT_TEXT": message, + "INPUT_TYPE": self.input_type, + "OUTPUT_TYPE": self.output_type, + "LOCALE": self.locale, + "AUDIO": self.audio, + "VOICE": self.voice, + } + params = urllib.urlencode(raw_params) + headers = {} + + # Open connection to self.host, self.port. + conn = httplib.HTTPConnection(self.host, self.port) + + # conn.set_debuglevel(5) + + conn.request("POST", "/process", params, headers) + response = conn.getresponse() + if response.status != 200: + print response.getheaders() + raise RuntimeError("{0}: {1}".format(response.status, + response.reason)) + return response.read() + +# If this is invoked as a program, just give +# a greeting to show it is working. +# The platform specific code is moved to this +# part so that this file may be imported without +# bringing platform specific code in. +if __name__ == "__main__": + + # For handling command line arguments: + import sys + import platform + + # check we are on Windows: + system = platform.system().lower() + if (system == "windows"): + + import winsound + + class Player: + def __init__(self): + pass + + def play(self, a_sound): + winsound.PlaySound(a_sound, winsound.SND_MEMORY) + + #if ("cygwin" in system): + else: + # Not sure how to do audio on cygwin, + # portably for python. So have a sound + # player class that doesn't play sounds. + # A null object, if you like. + class Player: + def __init__(self): + pass + + def play(self, a_sound): + print("Here I would play a sound if I knew how") + pass + + # Probably want to parse arguments to + # set the voice, etc., here + + client = maryclient() + client.set_audio("WAVE_FILE") # for example + + player = Player() + the_sound = client.generate("hello from Mary Text to Speech, with Python.") + if client.output_type == "AUDIO": + player.play(the_sound) + +# vi:set sw=4 et: diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.cgi b/external/marytts-5.1.2/doc/examples/client/maryclient.cgi new file mode 100755 index 00000000..876a846e --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.cgi @@ -0,0 +1,177 @@ +#!/usr/bin/perl -T +# -*- Mode: Perl -*- +# MARY Text-to-Speech System +# CGI Script implementing a simple mary client, +# can be used for web pages. +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## +# Author: Marc Schroeder + +use strict; +use IO::Socket; +use CGI; + +# variables getting their values from form: +my ($inputtext, $in, $out, $audiotype, $voice); + +# little helpers: +my ($var, $tmp); + +# contacting the mary server: +my ($host, $port, $maryInfoSocket, $maryDataSocket, $id); + +# helping with audio output: +my ($save_to_disk, $audiosubtype, $filename); + + +my $cgi = new CGI; +my @param = $cgi->param(); +$inputtext = $cgi->param('inputtext'); +$in = $cgi->param('in'); +$out = $cgi->param('out'); +$audiotype = $cgi->param('audiotype'); +$save_to_disk = $cgi->param('save_to_disk'); +$voice = $cgi->param('voice'); + +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); +$year += 1900; +printf STDERR "[%04i-%02i-%02i %02i:%02i:%02i] ", $year, $mon, $mday, $hour, $min, $sec; +print STDERR "Request from ",$cgi->remote_user(),"@",$cgi->remote_host(),": \n"; +print STDERR " in=",$in; +print STDERR " out=",$out; +print STDERR " audiotype=",$audiotype; +print STDERR " voice=",$voice; +print STDERR " save_to_disk=",$save_to_disk,"\n"; +print STDERR " inputtext: "; +print STDERR $inputtext,"\n"; + + +# Limit inputtext length to 5000 bytes: +if (length $inputtext > 5000) { + $inputtext = substr $inputtext, 0, 5000; +} + + +# set audio subtype +if ($out eq "AUDIO") { + if ($audiotype eq "AU") { + $audiosubtype = "basic"; + $filename = "mary.au"; + } elsif ($audiotype eq "AIFF") { + $audiosubtype = "x-aiff"; + $filename = "mary.aiff"; + } elsif ($audiotype eq "WAVE") { + $audiosubtype = "x-wav"; + $filename = "mary.wav"; + } elsif ($audiotype eq "MP3") { + $audiosubtype = "mp3"; + $filename = "mary.mp3"; + } else { + $audiosubtype = "x-wav"; + $filename = "mary.wav"; + } +} + +# announce data type on stdout +if ($save_to_disk) { + print "Content-Type: application/octet-stream"; +} else { + print "Content-Type: audio/$audiosubtype"; +} +print "\nContent-Disposition: filename=\"$filename\"\n\n"; + +# contact mary server +$host = "cling.dfki.uni-sb.de"; +$port = 59125; + +# create a tcp connection to the specified host and port +$maryInfoSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; + +# avoid buffering when writing to server: +$maryInfoSocket->autoflush(1); # so output gets there right away + +########## Write input to server: ########## +# formulate the request: +print $maryInfoSocket "MARY IN=$in OUT=$out AUDIO=$audiotype"; +if ($voice && $voice ne 'v') { print $maryInfoSocket " VOICE=$voice"; } +print $maryInfoSocket " LOG=\"REMOTE_HOST=$ENV{'REMOTE_HOST'}", + ", REMOTE_ADDR=$ENV{'REMOTE_ADDR'}\""; +print $maryInfoSocket "\015\012"; + +# receive a request ID: +$id = <$maryInfoSocket>; + +# open second socket for the data: +$maryDataSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; +# identify with request number: +print $maryDataSocket $id; # $id contains a newline character + +# copy $inputtext to mary data socket +print $maryDataSocket $inputtext; + +# mark end-of-request: +print $maryDataSocket "\015\012"; # that is a \n, actually +$maryDataSocket->shutdown(1); # we have stopped writing data + +########## Read output from server: ########## +# copy the data socket to standard output +if ($out ne "AUDIO") { # text output + my $line; + while (defined ($line = <$maryDataSocket>)) { + print STDOUT $line; + } +} else { # audio data output + my $nr; # number of bytes read + my $buf; # buffer to read into + my $outnr; # number of bytes written + while($nr = read($maryDataSocket, $buf, 8192)) { + # (read returns no. of bytes read, 0 at eof) + print STDOUT $buf + or die "Write error on stdout"; + } # while read something from socket +} # audio output + +### Read complaints from server: +my $line; +while (defined ($line = <$maryInfoSocket>)) { + print STDERR $line; +} + + + + + + + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.pl b/external/marytts-5.1.2/doc/examples/client/maryclient.pl new file mode 100755 index 00000000..8b3f5f80 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.pl @@ -0,0 +1,136 @@ +#!/usr/bin/env perl +# +# MARY Text-to-Speech System +# Minimal Socket client (for demonstration) +########################################################################## +# Copyright (C) 2000-2006 DFKI GmbH. +# All rights reserved. Use is subject to license terms. +# +# Permission is hereby granted, free of charge, to use and distribute +# this software and its documentation without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of this work, and to +# permit persons to whom this work is furnished to do so, subject to +# the following conditions: +# +# 1. The code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Any modifications must be clearly marked as such. +# 3. Original authors' names are not deleted. +# 4. The authors' names are not used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE +# CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +# THIS SOFTWARE. +########################################################################## +# Author: Marc Schroeder +# This is a minimal version of a socket client for the mary TtS system. +# It is intended to be used as a model for writing socket clients for +# particular applications. All input verification, command line options, +# and other luxury have been omitted. +# +# Usage: +# maryclient.pl infile.txt > outfile.wav +# +# Input/output formats and other options must be set in the perl code directly. +# See also Protocol.html for a description of the Protocol. +# + +use strict; +use IO::Socket; + +############################ +# Package-global variables # +############################ +# global settings: +my $maryInfoSocket; # handle to socket server +my $maryDataSocket; # handle to socket server +my $host; # string containing host address +my $port; # socket port on which we listen +my ($in, $out, $audiotype); # requested input / output format +my $voice; # default voice +my $id; # request ID + +###################################################################### +################################ main ################################ +###################################################################### + +STDOUT->autoflush(1); + +$host = "cling.dfki.uni-sb.de"; +$port = 59125; +$in = "TEXT_DE"; +$out = "AUDIO"; +$audiotype = "MP3"; +#$audiotype = "WAVE"; +#$voice = "male"; +$voice = "de3"; + +# create a tcp connection to the specified host and port +$maryInfoSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; + +# avoid buffering when writing to server: +$maryInfoSocket->autoflush(1); # so output gets there right away + +########## Write input to server: ########## +# formulate the request: +print $maryInfoSocket "MARY IN=$in OUT=$out AUDIO=$audiotype"; +if ($voice) { print $maryInfoSocket " VOICE=$voice"; } +print $maryInfoSocket "\015\012"; + +# receive a request ID: +$id = <$maryInfoSocket>; +chomp $id; chomp $id; + +# open second socket for the data: +$maryDataSocket = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port) + or die "can't connect to port $port on $host: $!"; +# identify with request number: +print $maryDataSocket $id, "\015\012"; + +# copy standard input and/or files given on the command line to the socket +while (defined (my $line = <>)) { + print $maryDataSocket $line; +} +# mark end-of-request: +print $maryDataSocket "\015\012"; # that is a \n, actually +shutdown($maryDataSocket, 1); # we have stopped writing data + +########## Read output from server: ########## +# copy the data socket to standard output +if ($out ne "AUDIO") { # text output + my $line; + while (defined ($line = <$maryDataSocket>)) { + print STDOUT $line; + } +} else { # audio data output + my $nr; # number of bytes read + my $buf; # buffer to read into + my $outnr; # number of bytes written + while($nr = read($maryDataSocket, $buf, 100000)) { + # (read returns no. of bytes read, 0 at eof) + print STDOUT $buf + or die "Write error on stdout"; + } # while read something from socket +} # audio output + +### Read complaints from server: +my $line; +while (defined ($line = <$maryInfoSocket>)) { + print STDERR $line; +} + + + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.rb b/external/marytts-5.1.2/doc/examples/client/maryclient.rb new file mode 100755 index 00000000..c4156cbb --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.rb @@ -0,0 +1,261 @@ +#!/usr/bin/env ruby +# +# A basic mary client in Ruby, +# kindly donated to the MARY TTS project +# by Hugh Sasse. Thanks Hugh! + + +# Ruby client for the MARY TTS HTTP server. +# This is for Windows only, and relies on +# the Win32-Sound gem to access the audio. +# +# + +require 'rubygems' +require 'net/http' +require 'uri' + +# A fairly minimal client class for the +# MARY TTS system. This uses the modern +# HTTP interface to access the server. +# At present, this doesn't wrap the methods +# which provide documentation or lists of +# voices or features. +class MaryClient + attr_accessor :host, :port + attr_accessor :input_type, :output_type + attr_accessor :locale, :audio, :voice + + # Set up the defaults for the MARY TTS + # server, which is assumed to be running + # on the local host, with British voices + # installed. These may be modified with + # the appropriate methods. + # host = 127.0.0.1) + # port = 59125 + # input_type = "TEXT" + # output_type = "AUDIO" + # audio = "WAVE_FILE" + # locale = "en_GB" + # voice = "dfki-prudence-hsmm" + def initialize + @host = "127.0.0.1" # The local machine + @port = 59125 + @input_type = "TEXT" + @output_type = "AUDIO" + @locale = "en_GB" + @audio = "WAVE_FILE" + @voice = "dfki-prudence-hsmm" + end + + # Process a text message, which with a + # new client, will return the audio. + # This is so that platform dependent parts + # are kept separate. + def generate(message) + raw_params = {"INPUT_TEXT" => message, + "INPUT_TYPE" => @input_type, + "OUTPUT_TYPE" => @output_type, + "LOCALE" => @locale, + "AUDIO" => @audio, + "VOICE" => @voice, + } + res = Net::HTTP.post_form(URI.parse("http://#{@host}:#{@port}/process"), raw_params) + res.value # Throw an exception on failure + #puts res.body + return res.body + end +end + + +# If this invoked as a program with no +# argumens, just give a greeting to show +# that it is working. If arguments are +# supplied, process options to work out +# what to do with the arguments. +if __FILE__ == $0 + + # These files are only loaded when this is + # invoked as a program. + require 'rbconfig' + require 'getoptlong' + + # PLATFORM SPECIFIC CODE. + # Needs more work [!] + case Config::CONFIG['host_os'] + when /darwin/i + raise NotImplementedError.new("Don't know how to play audio on a Mac") + when /linux/i + raise NotImplementedError.new("Far too many ways to play audio on Linux, you'll need to choose something") + when /sunos|solaris/i + raise NotImplementedError.new("Have not played audio on Suns for too long to implement this.") + when /java/i + raise NotImplementedError.new("Don't know how to play audio from Java ") + when /win32|cygwin|mingw32/i + # The various things that can use the Win32 + # sound gem + require 'win32/sound' + # Create a player class that will play the + # sound that the Mary TTS system returns + class Player + + # Play the audio passed in. + # Possibly this should receive the audio + # type so we can check that we can play it, + # but at the moment that is the + # responsibility of the user. + def self.play(sound) + Win32::Sound.play(sound, Win32::Sound::MEMORY) + end + end + else + raise NotImplementedError.new("Haven't thought how to support this OS yet") + end + + + client = nil + split = "" + + if ARGV.size.zero? + client = MaryClient.new() + sound = client.generate("Hello from Mary Text to Speech with Ruby.") + Player.play(sound) + else + args_mode = :words + stdout_mode = :absorb + opts = GetoptLong::new( + ["--audio", "-a", GetoptLong::REQUIRED_ARGUMENT], + ["--echo", "-e", GetoptLong::NO_ARGUMENT], + ["--help", "-h", GetoptLong::NO_ARGUMENT], + ["--host", "-H", GetoptLong::REQUIRED_ARGUMENT], + ["--input-type", "-i", GetoptLong::REQUIRED_ARGUMENT], + ["--locale", "-l", GetoptLong::REQUIRED_ARGUMENT], + ["--read", "-r", GetoptLong::NO_ARGUMENT], + + ["--split", "-s", GetoptLong::REQUIRED_ARGUMENT], + ["--output-type", "-o", GetoptLong::REQUIRED_ARGUMENT], + ["--port", "-P", GetoptLong::REQUIRED_ARGUMENT], + ["--tee", "-t", GetoptLong::NO_ARGUMENT], + ["--voice", "-v", GetoptLong::REQUIRED_ARGUMENT] + ) + + opts.each do |opt, arg| + unless ["--help", "-h"].include?(opt) + # skip if we are only getting help + client ||= MaryClient.new() + end + case opt + when "--help", "-h" + puts <<-EOHELP +Usage: #{$0} [options] [arguments] +--audio -a + Audio format. Defualt: WAVE_FILE +--echo -e + Act as an echo command and send output + arguments to the synthesizer only (not + to standard output. + Turns off --read|-r +--help -h + Print this help, then exit. +--host -H + The host which is the server. + Default: 127.0.0.1 +--input-type -i + The type of the input supplied to the + TTS system. Default: TEXT +--locale -l + The locale of the input. Default: en_GB +--output-type -o + The output type from the TTS system. + Default: AUDIO +--port -P + The port for the TTS server + Default: 59125 +--read -r + Read the files passed as arguments. + Turns off --echo|-e +--split -s (lines|paragraphs) + When reading files, split the input + into lines or paragraphs. Paragraphs + mean reading up to the next double + newline. Note, the argument is literally + "lines" or "paragraphs" (or some + abbreviation of those) without the + quotes. + Default is paragraphs. +--tee -t + Act as tee: send the output to the TTS + system, and to standard output. +--voice -v + The voice to use. + Default: dfki-prudence-hsmm + EOHELP + exit(0) + when "--audio", "-a" + client.audio = arg + when "--echo", "-e" + args_mode = :words + when "--host", "-H" + client.host = arg + when "--input-type", "-i" + client.input_type = arg + when "--locale", "-l" + client.locale = arg + when "--output-type", "-o" + client.output_type = arg + when "--port", "-P" + client.port = arg.to_i + when "--read", "-r" + args_mode = :files + when "--split", "-s" + case arg + when /^p/i + split = "" + when /^l/i + split = $/ + end + when "--tee", "-t" + stdout_mode = :emit + when "--voice", "-v" + client.voice = arg + end + end + + client ||= MaryClient.new() + case args_mode + when :words + input_text = ARGV.join(" ") + unless input_text =~ /\A\s*\Z/m + sound = client.generate(input_text) + if client.output_type == "AUDIO" + Player.play(sound) + end + end + if stdout_mode == :emit + puts input_text + end + when :files + # Slurp in paragraphs so sentences + # don't get broken in stupid places. + $/ = split # paragraph mode + ARGF.each do |paragraph| + begin + unless paragraph =~ /\A\s*\Z/m + sound = client.generate(paragraph) + if client.output_type == "AUDIO" + # and client.audio == "WAVE_FILE" + Player.play(sound) + end + end + rescue Exception => e + puts "got error #{e} while trying to say #{paragraph.inspect}" + raise + end + if stdout_mode == :emit + puts paragraph + end # end if + end # end ARGF.each + end # end case + end # if ARGV.size.zero? +end + diff --git a/external/marytts-5.1.2/doc/examples/client/maryclient.tcl b/external/marytts-5.1.2/doc/examples/client/maryclient.tcl new file mode 100755 index 00000000..3a358235 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/maryclient.tcl @@ -0,0 +1,705 @@ +# Tcl/Tk MARY TTS client. + +# This has been tested on Windows, and because +# of the use of sound there will be portability +# issues. However, there should be enough here +# for a reasonable start at a client, for any +# platform that supports Tcl/Tk. The platform +# specific code has, as far as possible, been +# isolated in the part of the code that detects +# whether this is being run as a program. + +# Notes: +# More work will need to be done with this, +# in order to make the code clean. It should +# probably be wrapped in a package, to solve +# any namespace issues. There are a lot of +# global variables. It seems that some of +# these are necessary for the menus to work. +# Handling of temporary files could be improved. + +# TODO: +# Create modifier sliders, for the effects. +# Extend the query proc to make use of them. +# Turn the Help menu into something more useful. +# Debug the actions for the Edit menu. +# Provide a means of getting example inputs +# from the server. +# Provide a means of re-loading all the +# dynamically collected information when the +# server is changed from the menu. This means +# that we need to delete the existing menu +# entries in order to add them correctly. +# How do we ensure temporary files are removed +# in the event of a problem? if {catch {}} ...? +# Maybe leaving them around is diagnostic info? +# Make that an option? +# Add error handling code for network and disk +# failures likely to beset such clients. +# Add sensible defaults for things the user must +# always set at startup, but these will be +# platform spacific. Always default to Audio +# output for example, or is it possible that +# people have no voices installed? + + +# This is a GUI, so: +package require Tk + +# We are communicating with the Mary server +# with HTTP. +package require http + +# Use the local machine in preference to the +# one in Germany. +set mary_tts_default_host "127.0.0.1" +set mary_tts_default_port 59125 + +# Actual host and port, and global old +# copies to allow revert on cancel in the +# dialogues. Apparently upvar #0 is the +# norm for that sort of thing [Tcl Wiki] +set mary_tts_host $mary_tts_default_host +set old_mary_tts_host $mary_tts_host +set mary_tts_port $mary_tts_default_port +set old_mary_tts_port $mary_tts_port + +# Informational URLs +set informational_urls [ list \ +version datatypes voices \ +audioformats audioeffects ] + +####### + +# Obtain a static page from the server, i.e. +# no parameters are needed to get it. +proc get_page { relative_url } { + global mary_tts_host mary_tts_port + set url http://$mary_tts_host:$mary_tts_port/$relative_url + set result [::http::geturl $url] + return [::http::data $result] +} + +proc list_of_lines {str} { + return [ split $str "\n" ] +} + + +# We will need to collect this information +# when we have the server and port chosen. +proc get_audioeffects {} { + return [list_of_lines [get_page audioeffects] ] +} + +proc get_audioformats {} { + return [list_of_lines [get_page audioformats] ] +} + +proc get_datatypes {} { + return [ list_of_lines [get_page datatypes] ] +} + + +proc get_voices {} { + return [list_of_lines [get_page voices] ] +} + +# Handling post queries. + +# Submit the query to the server, using the +# http POST method. +proc make_query {url encoded_params} { + set http [::http::geturl $url -query $encoded_params] + set result [::http::data $http] + return $result +} + +# Get the text from the input text area +proc get_input_text {} { + return [.io.inp.input_area get 1.0 end] +} + +# Get the text from the output text area +proc get_output_text {} { + return [.io.out.output_area get 1.0 end] +} + +# Collect the audio data from the server. +proc collect_audio_data {text_to_process} { + global mary_tts_host mary_tts_port + global inputtype outputtype locales + global audioformat voice + set url "http://$mary_tts_host:$mary_tts_port/process" + # ::http::formatQuery converts a list of + # key value pairs into the correct format + # for http POST. + set params [::http::formatQuery INPUT_TEXT $text_to_process INPUT_TYPE $inputtype OUTPUT_TYPE $outputtype LOCALE $locales($voice) AUDIO $audioformat VOICE $voice ] + set result [make_query $url $params] + return $result +} + +# Pushes the query to the server and gets +# the results back, displaying or playing +# them. +proc generate_output {text_to_process} { + global outputtype + set result [collect_audio_data $text_to_process] + if {$outputtype eq "AUDIO"} { + # call the platform dependent implementation. + play $result + } else { + clear_output + add_message $result + } + # Return the result so we can save it if + # the user requires it. + return $result +} + + +# These next procs are for handling the +# lists of data one gets back from the server +# which possibly have several words per line, +# separated by spaces. + +# If the first word of each listed line is +# significant, extract the list of first words. +proc collect_first_words_of_phrase_list {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + return $words +} + + +# If the second word of each listed line is +# significant, extract the list of second words. +proc collect_second_words_of_phrase_list {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + set word [ lindex [split $data " "] 1 ] + lappend words $word + } + return $words +} + + +# The list of datatypes must be separated into +# input data types and output data types so that +# interactions with the server make sense. +# This handles the inputs. +proc collect_first_words_of_input_types {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + if {[ string match -nocase "*input*" $data ]} { + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + } + return $words +} + + +# The list of datatypes must be separated into +# input data types and output data types so that +# interactions with the server make sense. +# This handles the outputs. +proc collect_first_words_of_output_types {a_list} { + for {set i 0} {$i < [llength $a_list]} {incr i} { + set data [lindex $a_list $i ] + if {[string match -nocase "*output*" $data]} { + set word [ lindex [split $data " "] 0 ] + lappend words $word + } + } + return $words +} + +# setup all the variables to hold voices, +# audio options, etc., based on what the +# server can do. +proc setup_globals {} { + global audioeffects audioformats voices + global inputtypes outputtypes audioformat voice + global inputtype outputtype locales + + set audioeffects [get_audioeffects] + set audioformats [get_audioformats] + set audioformat [lindex $audioformats 0 ] + set datatypes_data [get_datatypes] + set inputtypes [collect_first_words_of_input_types $datatypes_data] + set inputtype [lindex $inputtypes 0] + set outputtypes [collect_first_words_of_output_types $datatypes_data] + set outputtype [lindex $outputtypes 0] + set voices_data [get_voices] + set voices [collect_first_words_of_phrase_list $voices_data] + set locales_list [collect_second_words_of_phrase_list $voices_data ] + for {set i 0} {$i < [llength $voices]} {incr i} { + set locales([lindex $voices $i]) [lindex $locales_list $i] + } + set voice [lindex $voices 0] +} + +# A general procedure for filling in the +# elements of a listbox from a list. +# At present this is unused, but it could +# be useful later. [It took a while to +# figure out so I'm not ready to kill it +# with YAGNI.] +proc add_listbox_items {a_var a_widget} { + upvar $a_var var + foreach item $var { + $a_widget insert end $item + } +} + +# Create the menubuttons along the top. +# Usual File, Edit and Help menus plus +# those to set attributes. +proc create_menubuttons {} { + set buttons [ list file File edit Edit \ + server "Server" \ + inputtype "Input type" outputtype "Output type" \ + voice Voice \ + audioformat "Audio format" \ + textstyle "Text style" help Help ] + + set count 1 + foreach { menu_tag string_tag} $buttons { + menubutton .menus.$menu_tag -text $string_tag \ + -menu .menus.${menu_tag}.menu -underline 0 -font ClientFont + menu .menus.${menu_tag}.menu -tearoff true + grid .menus.$menu_tag -in .menus -row 1 -column $count -sticky w + incr count + } +} + +# Get the contents of a text file for reading +# or loading into a text widget, etc. +proc text_file_contents {what_for} { + set a_file [tk_getOpenFile -title $what_for ] + set the_text "" + + if {$a_file != ""} { + set a_stream [open $a_file r ] + set the_text [read $a_stream] + close $a_stream + } + + return $the_text +} + + +# Save the_text to a text file specified +# by the user, for the given reason (what_for). +# At the moment there is no error handling +# for this (disk full, write protected, etc). +proc save_text_file {the_text what_for} { + set a_file [tk_getSaveFile -title $what_for -parent .] + if {$a_file != ""} { + set a_stream [open $a_file w ] + puts $a_stream $the_text + close $a_stream + } +} + +# Save the_data to a binary file specified +# by the user, for the given reason (what_for), +# a text string. +# At the moment there is no error handling +# for this (disk full, write protected, etc). +proc save_binary_file {the_data what_for} { + set a_file [tk_getSaveFile -title $what_for -parent .] + if {$a_file != ""} { + set a_stream [open $a_file w ] + fconfigure $a_stream -translation binary + puts -nonewline $a_stream $the_data + close $a_stream + } +} + +# Create the menu for File operations +proc create_menu_file {} { + set fmenu .menus.file.menu + $fmenu add command -label "New" \ + -font ClientFont -command { + .io.inp.input_area delete 1.0 end + } + # Replace the contents of the input text + # widget by the data from the open file. + # YAGNI, but is there any reason + # to allow inserting a file, rather than + # replacing the text with file contents? + # + $fmenu add command -label "Open" \ + -font ClientFont -command { + set the_text [text_file_contents "File to load"] + if {$the_text != ""} { + .io.inp.input_area delete 1.0 end + .io.inp.input_area insert end $the_text + } + } + + $fmenu add command -label "Read" \ + -font ClientFont -command { + generate_output [text_file_contents "File to read"] + } + # How to make these disabled for now? + $fmenu add command -label "Save Input" \ + -font ClientFont -command { + set the_text [get_input_text] + save_text_file $the_text "Save Input" + } + $fmenu add command -label "Save Output" \ + -font ClientFont -command { + set the_text [get_output_text] + save_text_file $the_text "Save Output" + } +} + +# Create the menu for edit operations +proc create_menu_edit {} { + set emenu .menus.edit.menu + $emenu add command -label "Select All from Input Area" \ + -font ClientFont -command { + # This code says copy the selection as well. + # May be wrong for some platforms, but is + # it more useful? + .io.inp.input_area tag add sel 1.0 end + event generate .io.inp.input_area <> +} + $emenu add command -label "Select All from Output Area" \ + -font ClientFont -command { + # This code says copy the selection as well. + # May be wrong for some platforms, but is + # it more useful? + .io.out.output_area tag add sel 1.0 end + event generate .io.out.output_area <> +} + $emenu add command -label "Copy from Input Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.inp.input_area <> + } + $emenu add command -label "Copy from Output Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.out.output_area <> + } + $emenu add command -label "Paste into Input Area" \ + -font ClientFont -command { + # this appears not to work. FIXME + event generate .io.inp.input_area <> + } + $emenu add command \ + -font ClientFont -label "Insert example text into Input Area"\ + -command { + } + # Add specific editing commands here later. + # For example, we would like to be able to + # add whole tags to the XML based formats, + # wrap matching tags around selected text. + # Also we need to find out what happens with + # copy cut and paste, given that X Windows + # is different from MS Windows. + # Allow example text to be inserted. + # However, my thinking is that this should not + # overwrite as it is in the Java application, + # because this rubs out edits when switching + # voices, and this can be annoying when + # exploring the system. +} + +# Set the server properties, mostly just +# host and port. Maybe later protocol will +# be possible for https connections? +proc create_menu_server {} { + set smenu .menus.server.menu + $smenu add command -label "host" -font ClientFont -command { + create_entry_dialog "MARY TTS server name" "hostname/IP Address" mary_tts_host + } + $smenu add command -label "port" -font ClientFont -command { + create_entry_dialog "MARY TTS server port" "pott number" mary_tts_port + } +} + +# setup the fonts for the various areas on the dipslay. +proc setup_font {family size} { + foreach win {.io .controls .entry.dialogue } { + font configure ClientFont -family $family -size $size + } +} + +# Create the menu for changing the text size. +proc create_menu_textstyle {} { + set tmenu .menus.textstyle.menu + + $tmenu add cascade -label "Courier" -underline 0 -menu \ + $tmenu.courier -font ClientFont + $tmenu add cascade -label "Times" -underline 0 -menu \ + $tmenu.times -font ClientFont + $tmenu add cascade -label "Helvetica" -underline 0 -menu \ + $tmenu.helvetica -font ClientFont + foreach {name family} [list $tmenu.courier Courier \ + $tmenu.times Times $tmenu.helvetica Helvetica ] { + set m1 [menu $name] + foreach pts {6 7 8 9 10 12 14 16 18 20 24 28 32 36} { + $m1 add command -label "$pts" -font ClientFont\ + -command [list setup_font $family $pts ] + } + } +} + + + +# Create the menu for Help +proc create_menu_help {} { + # This is all pretty much "wet paint" + # Is there enough to merit separate menus? + set hmenu .menus.help.menu + $hmenu add command -label "Introduction" -font ClientFont\ + -command { + tk_messageBox -message "This is a basic Tcl/Tk +client for the MARY TTS system. Most of the options +are reached through the menus on the top. Some +facilities are presently lacking. + +Most of the interface should be self-explanatory. +In the File menu, Read will read a given file aloud +(or at least take it as input for the present +form of processing), whereas Open will load it +into the input area. Save input and Save output +refer to the contents of the text windows. The +save button next to the play button will save +the output to a file; this is assumed to be a +text file, unless the output is audio, in which +case it is a binary file. + +The Edit menu has cut and paste facilities, +but these don't seem to work reliably. The +default key bindings for text areas should +be useable. + +You will need to set the input and output types +and the audio format before pressing play. +Code does not yet exist to figure out sensible +defaults for your platform. + +This does not have support for the effects, yet. + +Contributions from developers welcome." -type ok + } + $hmenu add command -label "About" -command {} -font ClientFont +} + +# We need to create menus for the available +# voices and audio formats, etc. +# When we have the data for these menus from +# the server, create them by using the global +# lists of information. +proc create_radio_menu_from_list {what} { + global $what + set plural "${what}s" + upvar 1 $plural var + foreach item $var { + .menus.${what}.menu add radiobutton -label $item -variable $what \ + -value $item -font ClientFont + } +} + +proc reset_entry_and_var {a_variable} { + upvar #0 $a_variable var + upvar #0 old_$a_variable old_var + set var $old_var + destroy .entry_dialogue +} +# Create the toplevel for choosing a host +# or port, something taken from an entry. +proc create_entry_dialog {a_message a_label a_variable} { + upvar #0 $a_variable var + upvar #0 old_$a_variable old_var + toplevel .entry_dialogue + label .entry_dialogue.the_message -text $a_message \ + -font ClientFont + label .entry_dialogue.the_label -text $a_label -font ClientFont + entry .entry_dialogue.the_entry -textvariable $a_variable \ + -font ClientFont + button .entry_dialogue.ok -text "OK" -font ClientFont -command { + destroy .entry_dialogue + } + button .entry_dialogue.cancel -text "Cancel" -font ClientFont \ + -command "reset_entry_and_var $a_variable" + + grid .entry_dialogue.the_message -row 1 -column 1 + grid .entry_dialogue.the_label -row 2 -column 1 + grid .entry_dialogue.the_entry -row 2 -column 2 + grid .entry_dialogue.ok -row 3 -column 1 + grid .entry_dialogue.cancel -row 3 -column 2 +} + +# Add a message to the end of the output +# text widget. +proc add_message {a_message} { + .io.out.output_area configure -state normal + .io.out.output_area insert end $a_message + .io.out.output_area configure -state disabled +} + + +# Clear the text in the output text widget. +proc clear_output {} { + .io.out.output_area configure -state normal + .io.out.output_area delete 1.0 end + .io.out.output_area configure -state disabled +} + +# Sound generation is platform dependent. +# This provides an "abstract" function to +# be overridden by the platform dependent +# code. In this case it alerts the user +# in the output window that nothing is going +# to happen. +proc play {sound} { + add_message \ + "play sound not implemented on this platform apparently" +} + +# Graphical stuff. + +# In order to be able to scale the font, define a font. +font create ClientFont -family [font actual TkDefaultFont -family] \ + -size [font actual TkDefaultFont -size] + +frame .menus +create_menubuttons +create_menu_file +create_menu_edit +create_menu_server +create_menu_textstyle +create_menu_help +# Fill in the other menus at runtime. + +# .io communicates text with the user, +# through an input and output window. +frame .io +frame .io.inp +frame .io.out +# .controls will hold the play button and +# the effects controls. +frame .controls + +# Draw the controls in .io +label .io.inp.input_label -text "Input Area" -font ClientFont +text .io.inp.input_area -height 10 -width 40 \ +-xscrollcommand ".io.inp.input_x set" \ +-yscrollcommand ".io.inp.input_y set" -font ClientFont +scrollbar .io.inp.input_x -orient horizontal \ +-command ".io.inp.input_area xview" +scrollbar .io.inp.input_y -orient vertical \ +-command ".io.inp.input_area yview" + +label .io.out.output_label -text "Output Area" -font ClientFont +text .io.out.output_area -height 10 -width 40 -state disabled \ +-xscrollcommand ".io.out.output_x set" \ +-yscrollcommand ".io.out.output_y set" -font ClientFont +scrollbar .io.out.output_x -orient horizontal \ +-command ".io.out.output_area xview" +scrollbar .io.out.output_y -orient vertical \ +-command ".io.out.output_area yview" + +grid .io.inp -in .io -row 1 -column 1 +grid .io.out -in .io -row 1 -column 2 +grid .io.inp.input_label -in .io.inp -row 1 -column 1 +grid .io.inp.input_area -in .io.inp -row 2 -column 1 +grid .io.inp.input_y -in .io.inp -row 2 -column 2 -sticky ns +grid .io.inp.input_x -in .io.inp -row 3 -column 1 -sticky ew + +grid .io.out.output_label -in .io.out -row 1 -column 1 +grid .io.out.output_area -in .io.out -row 2 -column 1 +grid .io.out.output_y -in .io.out -row 2 -column 2 -sticky ns +grid .io.out.output_x -in .io.out -row 3 -column 1 -sticky ew + +button .controls.play -text "play" -font ClientFont -command { + generate_output [get_input_text] +} +grid .controls.play -in .controls -row 1 -column 1 + +button .controls.save -text "save" -font ClientFont -command { + global outputtype + set input_text [get_input_text] + if { $outputtype eq "AUDIO" } { + save_binary_file [collect_audio_data $input_text ] "Save audio file" + } else { + save_text_file [collect_audio_data $input_text ] "Save output to file" + } +} + +grid .controls.save -in .controls -row 1 -column 2 + +pack .menus .io .controls -in . -side top + + + +# Detect whether this is the main program +# This test was taken from the Tcl Wiki, and +# seems to work OK. + +if {[info exists argv0] && [file tail [info script]] eq [file tail $argv0]} { + + # Try to find the temporary files directory. + catch { set tmpdir "/tmp" } + catch { set tmpdir $::env(TRASH_FOLDER) } + catch { set tmpdir $::env(TMP) } + catch { set tmpdir $::env(TEMP) } + # This needs better handling of + # possible alternatives + # This is needed for Windows sound only. + + # Do the platform dependent things. + if {$tcl_platform(platform) eq "windows"} { + package require twapi + + proc play {sound} { + global tmpdir + # Write sound to a temporary file + set sndfile [file join $tmpdir "MARYTTS_sound.[pid].wav" ] + set stream [open $sndfile w] + # Make sure the file is binary: + fconfigure $stream -translation binary + puts -nonewline $stream $sound + close $stream + # Play the file. + ::twapi::play_sound $sndfile + # Remove the file. + file delete $sndfile + } + } + # Put other platforms here. + + # Setup the globals with reference to the + # server, which is assumed to be working. + # Since we have options to alter this with + # menu items, there probably needs to be + # some way to reload all this. But we need + # to know how to delete the existing menu + # entries to do that. + setup_globals + create_radio_menu_from_list inputtype + create_radio_menu_from_list outputtype + create_radio_menu_from_list voice + create_radio_menu_from_list audioformat + + # Note, at the moment voices holds locales, + # gender, and voice type + + # At the moment this is just diagnostic: + ## add_message [ join $voices "\n" ] + # it tells us we have a basically working + # system and the list of voices has been + # picked up and manipulated correctly. + # So it is commented out now. +} + + diff --git a/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html b/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html new file mode 100755 index 00000000..e788cf50 --- /dev/null +++ b/external/marytts-5.1.2/doc/examples/client/texttospeechdemo.html @@ -0,0 +1,81 @@ + + + + + + - - - - -
- - \ No newline at end of file diff --git a/hal-core/resources/web/api/openapi.json b/hal-core/resources/web/api/openapi.json deleted file mode 100644 index a3dbf042..00000000 --- a/hal-core/resources/web/api/openapi.json +++ /dev/null @@ -1,313 +0,0 @@ -{ - "components": { - "schemas": { - "alertClass": { - "type": "object", - "properties": { - "id": {"type": "integer"}, - "level": {"type": "string"}, - "ttl": {"type": "integer"}, - "title": {"type": "string"}, - "description": {"type": "string"} - } - }, - - "eventClass": { - "type": "object", - "properties": { - "data": { - "type": "object", - "$ref": "#/components/schemas/dataClass" - }, - "dataType": {"type": "string"}, - "name": {"type": "string"}, - "id": {"type": "integer"}, - "map": { - "type": "object", - "$ref": "#/components/schemas/mapClass" - }, - "user": {"type": "string"}, - "config": { - "type": "object", - "$ref": "#/components/schemas/configClass" - }, - "configType": {"type": "string"} - } - }, - - "roomClass": { - "type": "object", - "properties": { - "id": {"type": "integer"}, - "name": {"type": "string"}, - "map": { - "type": "object", - "$ref": "#/components/schemas/mapClass" - } - } - }, - - "sensorClass": { - "type": "object", - "properties": { - "data": { - "type": "object", - "$ref": "#/components/schemas/dataClass" - }, - "name": {"type": "string"}, - "id": {"type": "integer"}, - "map": { - "type": "object", - "$ref": "#/components/schemas/mapClass" - }, - "user": {"type": "string"}, - "config": { - "type": "object", - "$ref": "#/components/schemas/configClass" - }, - "aggregate": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "number" - } - }, - "timestamps": { - "type": "array", - "items": { - "type": "integer" - } - } - } - } - } - }, - - "configClass": { - "type": "object", - "properties": { - } - }, - - "dataClass": { - "type": "object", - "properties": { - "valueStr": {"type": "string"}, - "value": {"type": "number"}, - "timestamp": {"type": "integer"} - } - }, - - "mapClass": { - "type": "object", - "properties": { - "x": {"type": "number"}, - "y": {"type": "number"}, - "width": {"type": "number"}, - "height": {"type": "number"} - } - }, - } - }, - - "servers": [ - { - "description": "Hal Server", - "url": "/api" - } - ], - - "openapi": "3.0.1", - - "paths": { - "/alert": { - "get": { - "responses": { - "200": { - "description": "A successful response.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/components/schemas/alertClass" - } - } - } - } - } - }, - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "poll", - "peek", - "dismiss" - ] - }, - "in": "query", - "name": "action", - "required": true - }, - { - "schema": { - "type": "integer" - }, - "in": "query", - "name": "id", - "required": false - } - ] - } - }, - - "/event": { - "get": { - "responses": { - "200": { - "description": "A successful response.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/components/schemas/eventClass" - } - } - } - } - } - }, - "parameters": [ - { - "schema": { - "type": "integer" - }, - "in": "query", - "name": "id", - "required": false - }, - { - "schema": { - "type": "string" - }, - "in": "query", - "name": "configType", - "required": false - }, - { - "schema": { - "type": "string" - }, - "in": "query", - "name": "dataType", - "required": false - } - ] - } - }, - - "/room": { - "get": { - "responses": { - "200": { - "description": "A successful response.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/components/schemas/roomClass" - } - } - } - } - } - }, - "parameters": [ - { - "schema": { - "type": "integer" - }, - "in": "query", - "name": "id", - "required": false - } - ] - } - }, - - "/sensor": { - "get": { - "responses": { - "200": { - "description": "A successful response.", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "$ref": "#/components/schemas/sensorClass" - } - } - } - } - } - }, - "parameters": [ - { - "schema": { - "type": "integer" - }, - "in": "query", - "name": "id", - "required": false - }, - { - "schema": { - "type": "string" - }, - "in": "query", - "name": "configType", - "required": false - }, - { - "schema": { - "type": "string" - }, - "in": "query", - "name": "dataType", - "required": false - }, - { - "schema": { - "type": "string", - "enum": [ - "min", - "hour", - "day", - "week" - ] - }, - "in": "query", - "name": "aggregation", - "required": false - } - ] - } - } - }, - "info": { - "description": "This API allows developers and external tools to interface to Hal data and trigger different actions.", - "title": "Hal REST API", - "version": "" - } -} \ No newline at end of file diff --git a/hal-core/resources/web/css/hal.css b/hal-core/resources/web/css/hal.css deleted file mode 100644 index 030b1863..00000000 --- a/hal-core/resources/web/css/hal.css +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Base structure - */ - -/* Move down content because we have a fixed navbar that is 50px tall */ -body { - padding-top: 50px; -} - - -/* - * Global add-ons - */ - -.sub-header { - padding-bottom: 10px; - border-bottom: 1px solid #eee; -} - -/* - * Top navigation - * Hide default border to remove 1px line. - */ -.navbar-fixed-top { - border: 0; -} - -/* - * Sidebar - */ - -/* Hide for mobile, show later */ -.sidebar { - display: none; -} -@media (min-width: 768px) { - .sidebar { - position: fixed; - top: 51px; - bottom: 0; - left: 0; - z-index: 1000; - display: block; - padding: 20px; - overflow-x: hidden; - overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ - background-color: #f5f5f5; - border-right: 1px solid #eee; - } -} - -/* Sidebar navigation */ -.nav-sidebar { - margin-right: -21px; /* 20px padding + 1px border */ - margin-bottom: 20px; - margin-left: -20px; -} -.nav-sidebar > li > a { - padding-right: 20px; - padding-left: 20px; -} -.nav-sidebar > .active > a, -.nav-sidebar > .active > a:hover, -.nav-sidebar > .active > a:focus { - color: #fff; - background-color: #428bca; -} - - -/* - * Main content - */ - -.main { - padding: 20px; -} -@media (min-width: 768px) { - .main { - padding-right: 40px; - padding-left: 40px; - } -} -.main .page-header { - margin-top: 0; -} - -.vertical-space { - height: 70px; -} -.text-vert-middle { - vertical-align: middle !important; -} - -.table-borderless tbody tr td { - border: none; -} - -.drop-shadow { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) -} - -.disabled { - background-color: lightgray; - opacity: .6; -} - -/* - * Placeholder dashboard ideas - */ - -.placeholders { - margin-bottom: 150px; - text-align: center; -} -.placeholders h4 { - margin-bottom: 0; -} -.placeholder { - margin-bottom: 20px; -} -.placeholder img { - display: inline-block; - border-radius: 50%; -} - - -/* - * c3.js charts overrides - */ -.c3 line, .c3 path { - stroke: #ccc; -} -.c3-line { - stroke-width: 2px; -} - - -.anim-spin { - animation: spin 2s infinite linear; -} -@keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(359deg); - } -} - -/* - * Animations - */ - -.pulse-border { - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { - stroke-width: 3; - opacity: 1; - } - - 10% { - stroke-width: 5; - opacity: 0.2; - } - 15% { - stroke-width: 5; - opacity: 0.2; - } - - 50% { - stroke-width: 3; - opacity: 1; - } - - 100% { - stroke-width: 3; - opacity: 1; - } -} - -/* - * Switch checkbox CSS, originally from: - * https://community.tadabase.io/t/convert-checkbox-to-toggle-switch/2566 - */ - -input[type="checkbox"].switch { - /* Backgrpund properties */ - appearance: none; - background-color: #e1e1e1; /* unchecked background color */ - border-radius: 72px; - border-style: none; - flex-shrink: 0; - position: relative; - cursor: pointer; - margin: 0 !important; - width: 40px !important; - height: 20px !important; - border: 1px solid #ccc; -} -input[type="checkbox"].switch, -input[type="checkbox"].switch::after { - transition: all 100ms ease-out; -} -input[type="checkbox"].switch::after { - /* Ball properties */ - background-color: #fff; /* Color of ball */ - border-radius: 50%; - content: ""; - height: 15px !important; - width: 15px !important; - left: 3px !important; - top: 2px !important; - position: absolute; -} -/* Properties for a checked state */ -input[type="checkbox"].switch:checked { - background-color: #d9534f; /* Color of background */ -} -input[type="checkbox"].switch:checked::after { - background-color: #fff; /* Color of ball */ - left: 20px !important; -} - -/* - * Slider styling - */ - -input[type="range"] { - accent-color: #d9534f; -} diff --git a/hal-core/resources/web/css/lib/bootstrap-switch.min.css b/hal-core/resources/web/css/lib/bootstrap-switch.min.css deleted file mode 100644 index 77006595..00000000 --- a/hal-core/resources/web/css/lib/bootstrap-switch.min.css +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches. - * - * @version v3.3.4 - * @homepage https://bttstrp.github.io/bootstrap-switch - * @author Mattia Larentis (http://larentis.eu) - * @license Apache-2.0 - */ - -.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid #ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:table-cell;vertical-align:middle;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-handle-on{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary{color:#fff;background:#337ab7}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch span::before{content:"\200b"}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type=radio],.bootstrap-switch input[type=checkbox]{position:absolute!important;top:0;left:0;margin:0;z-index:-1;opacity:0;filter:alpha(opacity=0);visibility:hidden}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.3333333}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-indeterminate,.bootstrap-switch.bootstrap-switch-readonly{cursor:default!important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default!important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;-o-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-radius:0 3px 3px 0}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-radius:3px 0 0 3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px} \ No newline at end of file diff --git a/hal-core/resources/web/css/lib/bootstrap.min.css b/hal-core/resources/web/css/lib/bootstrap.min.css deleted file mode 100644 index 2b8fc88d..00000000 --- a/hal-core/resources/web/css/lib/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../../fonts/glyphicons-halflings-regular.eot);src:url(../../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} -/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/hal-core/resources/web/css/lib/svg.select.css b/hal-core/resources/web/css/lib/svg.select.css deleted file mode 100644 index 18888ab3..00000000 --- a/hal-core/resources/web/css/lib/svg.select.css +++ /dev/null @@ -1,44 +0,0 @@ -.svg_select_points_lt{ - cursor: nw-resize; -} -.svg_select_points_rt{ - cursor: ne-resize; -} -.svg_select_points_rb{ - cursor: se-resize; -} -.svg_select_points_lb{ - cursor: sw-resize; -} -.svg_select_points_t{ - cursor: n-resize; -} -.svg_select_points_r{ - cursor: e-resize; -} -.svg_select_points_b{ - cursor: s-resize; -} -.svg_select_points_l{ - cursor: w-resize; -} - -.svg_select_points_rot{ - stroke-width:1; - stroke:black; - fill: #F9FFED; -} - -.svg_select_points_point{ - cursor: move; -} - -.svg_select_boundingRect{ - stroke-width:1; - fill:gray; - stroke-dasharray:10 10; - stroke:black; - stroke-opacity:0.8; - fill-opacity:0.1; - pointer-events:none; /* This ons is needed if you want to deselect or drag the shape*/ -} \ No newline at end of file diff --git a/hal-core/resources/web/css/lib/svg.select.min.css b/hal-core/resources/web/css/lib/svg.select.min.css deleted file mode 100644 index d8345130..00000000 --- a/hal-core/resources/web/css/lib/svg.select.min.css +++ /dev/null @@ -1 +0,0 @@ -.svg_select_points_lt{cursor:nw-resize}.svg_select_points_rt{cursor:ne-resize}.svg_select_points_rb{cursor:se-resize}.svg_select_points_lb{cursor:sw-resize}.svg_select_points_t{cursor:n-resize}.svg_select_points_r{cursor:e-resize}.svg_select_points_b{cursor:s-resize}.svg_select_points_l{cursor:w-resize}.svg_select_points_rot{stroke-width:1;stroke:#000;fill:#f9ffed}.svg_select_points_point{cursor:move}.svg_select_boundingRect{stroke-width:1;fill:gray;stroke-dasharray:10 10;stroke:#000;stroke-opacity:.8;fill-opacity:.1;pointer-events:none} \ No newline at end of file diff --git a/hal-core/resources/web/favicon.ico b/hal-core/resources/web/favicon.ico deleted file mode 100644 index e9d61583..00000000 Binary files a/hal-core/resources/web/favicon.ico and /dev/null differ diff --git a/hal-core/resources/web/img/favicon.svg b/hal-core/resources/web/img/favicon.svg deleted file mode 100644 index bb6996a5..00000000 --- a/hal-core/resources/web/img/favicon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/hal-core/resources/web/js/hal.js b/hal-core/resources/web/js/hal.js deleted file mode 100644 index e731b2e7..00000000 --- a/hal-core/resources/web/js/hal.js +++ /dev/null @@ -1,214 +0,0 @@ -// -------------------------------------------------------- -// Autostart -// -------------------------------------------------------- - -"use strict"; - -$(function(){ - $(".toggle-switch").bootstrapSwitch({inverse: true, size: "mini"}); - - $(".timestamp").relTimestamp(); -}); - -// -------------------------------------------------------- -// JQuery helper functions -// -------------------------------------------------------- - -// $.attr() # returns all attributes of an element -(function(old) { - $.fn.attr = function() { - if(arguments.length === 0) { - if(this.length === 0) { - return null; - } - - let obj = {}; - $.each(this[0].attributes, function() { - if(this.specified) { - obj[this.name] = this.value; - } - }); - return obj; - } - - return old.apply(this, arguments); - }; -})($.fn.attr); - -// -------------------------------------------------------- -// Timestamps -// -------------------------------------------------------- - -// Converts all timestamps to human readable time and date -$.fn.relTimestamp = function() { - return this.each(function() { - let timestamp = parseInt($(this).text()); - - $(this).text(getRelTimestamp(timestamp)); - return this; - }); -}; - -// Converts all timestamps to human readable time and date -function getRelTimestamp(timestamp) { - if (timestamp == null) - return ""; - - let timestampNow = Date.now(); - let timeDiff = timestampNow - timestamp; - - if(timeDiff < 10 * 60 * 1000) // less than 10 min - return moment(timestamp).fromNow(); - else if(timeDiff < 24 * 60 * 60 * 1000) // less than 24 hours - return moment(timestamp).fromNow() + " ("+moment(timestamp).format("HH:mm")+")"; - else - return moment(timestamp).format("YYYY-MM-DD HH:mm"); -} - -// -------------------------------------------------------- -// Chart functions -// -------------------------------------------------------- - -function createChart(elementId, url, updateTime=-1){ - let tickConf = {count: 20}; - if (updateTime < 60*60*1000) - tickConf['format'] = '%H:%M'; - else if (updateTime < 24*60*60*1000) - tickConf['format'] = '%Y-%m-%d %H:%M'; - else - tickConf['format'] = '%Y-%m-%d'; - - - var chart = c3.generate({ - bindto: elementId, - data: {json: []}, // set empty data, data will be loaded later - axis : { - x : { - type : 'timeseries', - label: 'Timestamp', - tick: tickConf, - }, - y: { - label: 'Power (kWh)', - min: 0, - }, - y2: { - show: true, - label: 'Temperature (C)', - min: 0, - } - }, - grid: { - y: {show: true} - }, - point: { - show: false - } - }); - - updateChart(chart, url, updateTime); - $(window).focus(function(e) { - updateChart(chart, url); - }); -} -function updateChart(chart, url, updateTime=-1){ - console.log('Updating chart: ' + chart.element.id); - - $.getJSON(url, function(json) { - chart.load(getChartData(json)); - }); - - if (updateTime > 0) { - setTimeout(function() { - updateChart(chart, url, updateTime); - }, updateTime); - } -} -function getChartData(json){ - let dataXaxis = {}; - let dataYaxis = {}; - let data = []; - let labels = []; - - json.forEach(function(sensor, i) { - var index = 'data' + i; - labels[index] = sensor.user + ': ' + sensor.name; - dataXaxis[index] = 'data' + i + 'x'; - data.push([index + 'x'].concat(sensor.aggregate.timestamps)); - data.push([index].concat(sensor.aggregate.data)); - - if (sensor.type == 'PowerConsumptionSensorData') - dataYaxis[index] = 'y'; - else //if (sensor.type == "TemperatureSensorData") - dataYaxis[index] = 'y2'; - }); - - return { - xs: dataXaxis, - columns: data, - names: labels, - type: 'spline', - axes: dataYaxis, - unload: true, - }; -} - -// -------------------------------------------------------- -// Dynamic forms -// -------------------------------------------------------- - -var dynamicConf = {}; - -function initDynamicModalForm(modalId, formTemplateId = null, templateID = null){ - // read in all configurations into global variable (to skip naming issues) - if (formTemplateId != null) { - dynamicConf[formTemplateId] = []; - $("#" + templateID + " div").each(function(){ - dynamicConf[formTemplateId][$(this).prop("id")] = $(this).html(); - }); - - // Update dynamic inputs - $("#" + modalId + " select[name=type]").change(function(){ - $("#" + modalId + " #" + formTemplateId).html(dynamicConf[formTemplateId][$(this).val()]); - }); - } - - // click event - $("#" + modalId).on('show.bs.modal', function (event) { - let button = $(event.relatedTarget); - let modal = $(this); - - modal.find(" input, select").val('').change(); // Reset all inputs - - // Set dynamic form data - $.each(button.attr(), function(fieldName, value) { - if(fieldName.startsWith("data-")) { - fieldName = fieldName.substring(5); // remove prefix data- - - // Case-insensitive search - var input = modal.find("input, select").filter(function() { - if (this.name.toLowerCase() == fieldName) { - if (this.type == "hidden" && modal.find("input[type=checkbox][name=" + fieldName + "]").length > 0) - return false; // Workaround for the default(false) boolean input - return true; - } - return false; - }); - - if (input.length > 0) { - if (input.prop("type") == "checkbox") { // special handling for checkboxes - input.prop("value", "true"); - input.prop("checked", value == "true"); - - if (modal.find("input[type=hidden][name=" + fieldName + "]") == null) { - // Add default false value as a unchecked checkbox is not included in the post - input.parent().prepend(""); - } - } else { - input.val(value).change(); - } - } - } - }); - }); -} \ No newline at end of file diff --git a/hal-core/resources/web/js/hal_alert.js b/hal-core/resources/web/js/hal_alert.js deleted file mode 100644 index e2e9baa7..00000000 --- a/hal-core/resources/web/js/hal_alert.js +++ /dev/null @@ -1,86 +0,0 @@ -// -------------------------------------------------------- -// Autostart -// -------------------------------------------------------- - -"use strict"; - -var alertDivId = "alert-container" -var alertTemplate = { - ERROR: ` -
- - -   - -
`, - WARNING: ` -
- - -   - -
`, - SUCCESS: ` -
- - -   - -
`, - INFO: ` -
- - -   - -
` -} - -$(function(){ - updateAlerts(); - - setInterval(function() { - updateAlerts(); - }, 3000); // 3 sec -}); - -function updateAlerts() { - fetch('/api/alert?action=poll') - .then(response => response.json()) - .then(data => { - data.forEach(alert => { - var alertElement = $("#alert-id-" + alert.id); - - if (alertElement.length <= 0) { - alertElement = $(alertTemplate[alert.level]); - $("#" + alertDivId).append(alertElement); - - alertElement.attr("id", "alert-id-" + alert.id); - alertElement.data("alert-id", alert.id); - alertElement.find(".close").click(dismissEvent); - } - - alertElement.find(".alert-title").html(alert.title); - alertElement.find(".alert-description").html(alert.description); - alertElement.find(".timestamp").relTimestamp(); - }); - }); -} - -function dismissEvent(e) { - dismissAlert($(e.target).parent().parent().data("alert-id")); -} -function dismissAlert(id) { - fetch('/api/alert?action=dismiss&id=' + id) - .then(response => response.json()) - .then(data => { - }); -} \ No newline at end of file diff --git a/hal-core/resources/web/js/hal_map.js b/hal-core/resources/web/js/hal_map.js deleted file mode 100644 index d05f22a4..00000000 --- a/hal-core/resources/web/js/hal_map.js +++ /dev/null @@ -1,306 +0,0 @@ -"use strict"; - -var svg; -var data = { - rooms: [], - sensors: [], - events: [] -}; -var editModeEnabled = false; - -$(function(){ - // ------------------------------------------ - // Setup map - // ------------------------------------------ - - svg = SVG('map'); - - // Initialize events - - $("#button-edit").click(function() { - editMode(true); - }); - $("#button-save").click(function() { - saveMap(); - editMode(false); - fetchData(drawMap); - }); - $("#button-cancel").click(function() { - editMode(false); - fetchData(drawMap); - }); - - // Initialize background image uploader - - $("#button-bg-edit").click(function() { - // Reset modal - $('#bg-file-input').parent().show(); - if ($("#file_input").prop("jFiler") != null) - $("#file_input").prop("jFiler").reset(); - $('#bg-file-progress').parent().hide(); - $('#bgUploadModal').modal('show'); - }); - $('#bg-file-input').filer({ - limit: 1, - extensions: ['jpg','png','svg','gif'], - maxSize: 3, // in MB - uploadFile: { - url: "", - type: 'POST', - enctype: 'multipart/form-data', - beforeSend: function(){ - $('#bg-file-input').parent().hide(); - $('#bg-file-progress').parent().show(); - }, - success: function(data, el){ - $('#bgUploadModal').modal('hide'); - drawMap(); - }, - error: function(el){ - $("#bg-file-progress").addClass("progress-bar-danger"); - }, - onProgress: function(t){ - $("#bg-file-progress").css("width", t + "%"); - }, - } - }); - - // ------------------------------------------ - // Start draw loop - // ------------------------------------------ - - fetchData(drawMap); - - setInterval(function() { - if (editModeEnabled == false) { - fetchData(drawMap); - } - }, 3000); // 3 sec - //}, 10000); // 10 sec - -}); - -// ---------------------------------------------- -// Events -// ---------------------------------------------- - -function editMode(enable){ - if (editModeEnabled == enable) { - return; - } - - editModeEnabled = enable; - - if (editModeEnabled) { - $('.edit-mode').show(); - $('.view-mode').hide(); - $('#map').css('border-color', '#6eb16e'); - - svg.select('.draggable').draggable(true); - //svg.select('.resizable').on('click', selectEvent, false); - svg.select('.resizable').selectize({ - points: ['rt', 'lb', 'rb'], // Add selection points on the corners - rotationPoint: false - }).resize() - } else { - $('.edit-mode').hide(); - $('.view-mode').show(); - $('#map').css('border-color', ''); - - svg.select('.draggable').draggable(false); - svg.select('resizable').selectize(false); - } -} - -function beforeDragEvent(e) { - if (editModeEnabled == false) { - e.preventDefault(); // Prevent drag - } -} - -function selectEvent(e) { - if (editModeEnabled == true) { - e.target.selectize({ - points: ['rt', 'lb', 'rb'], // Add selection points on the corners - rotationPoint: false - }).resize(); - } -} - -// -------------------------------------- -// Draw -// -------------------------------------- - -function drawMap() { - // Reset map - svg.clear(); - - // Background - - if (svg.select(".bg-image").length() <= 0) { - var bgImage = svg.image("?bgimage").addClass("bg-image") - .x(0) - .y(0) - .width("100%") - .height("100%"); - } - - // Rooms - - if (data.rooms != null) { - $.each(data.rooms, function(i, room) { - svg.select("#room-" + room.id).remove(); - - var group = svg.group(); - - group.text(room.name).move(5, 5).fill('#999'); - var rect = group.rect(room.map.width, room.map.height); - setAlertStyle(rect, (room.alert == null ? null : room.alert.level)); - rect.addClass("resizable"); - - group.addClass("room") - .attr("id", "room-" + room.id) - .attr("room-id", room.id) - .x(room.map.x) - .y(room.map.y) - .addClass("draggable"); - }); - } - - // Sensors - - if (data.sensors != null) { - $.each(data.sensors, function(i, sensor) { - svg.select("#sensor-" + sensor.id).remove(); - - var group = svg.group(); - group.element('title').words(sensor.name); - - group.text(sensor.data.valueStr).move(45, 15).fill('#999'); - group.image("/img/temperature.svg").size(50, 50); - - group.addClass("sensor") - .attr("id", "sensor-" + sensor.id) - .attr("sensor-id", sensor.id) - .x(sensor.map.x) - .y(sensor.map.y) - .addClass("draggable"); - }); - } - - // Events - - if (data.events != null) { - $.each(data.events, function(i, event) { - svg.select("#event-" + event.id).remove(); - - var group = svg.group(); - group.element('title').words(event.name); - - var img = "/img/lightbulb_off.svg"; - if (event.data.valueStr == "ON") - img = "/img/lightbulb_on.svg"; - group.image(img).size(50, 50); - - group.addClass("event") - .attr("id", "event-" + event.id) - .attr("event-id", event.id) - .x(event.map.x) - .y(event.map.y) - .addClass("draggable"); - }); - } -} - -// ---------------------------------------------- -// Load and Store data -// ---------------------------------------------- - -async function fetchData(callback) { - await fetch('/api/room') - .then(response => response.json()) - .then(json => { - data.rooms = json; - }) - - await fetch('/api/sensor') - .then(response => response.json()) - .then(json => { - data.sensors = json; - }) - - await fetch('/api/event') - .then(response => response.json()) - .then(json => { - data.events = json; - }) - - callback(); -} - -function saveMap(){ - svg.select(".room").each(function(){ - saveDevice(this, "room", "room-id"); - }); - svg.select(".sensor").each(function(){ - saveDevice(this, "sensor", "sensor-id"); - }); - svg.select(".event").each(function(){ - saveDevice(this, "event", "event-id"); - }); -} -function saveDevice(element, type, id) { - var data = { - action: "save", - id: element.attr(id), - type: type, - x: element.x(), - y: element.y() - }; - - var resizable = element.select(".resizable"); - if (resizable.length() > 0) { - data.width = resizable.get(0).width(); - data.height = resizable.get(0).height(); - } - - $.ajax({ - async: false, - dataType: "json", - url: "/api/map?", - data: data - }); -} - -// ---------------------------------------------- -// Colors -// ---------------------------------------------- - -function setAlertStyle(target, level=null) { - target.addClass("pulse-border"); - target.fill('none'); - - switch(level) { - case "ERROR": - target.stroke({opacity: 1, color: '#f00'}); - break; - case "WARNING": - target.stroke({opacity: 1, color: '#ffa500'}); - break; - case "SUCCESS": - target.stroke({opacity: 1, color: '#90EE90'}); - break; - case "INFO": - target.stroke({opacity: 1, color: '#87CEFA'}); - break; - - default: - target.removeClass("pulse-border"); - target.stroke({ - color: '#000', - opacity: 0.6, - width: 3 - });; - break; - } -} \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE b/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE deleted file mode 100644 index bc6fc511..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Javi Aguilar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE.txt b/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE.txt deleted file mode 100644 index bc6fc511..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-colorpicker.LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Javi Aguilar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/hal-core/resources/web/js/lib/bootstrap-colorpicker.js b/hal-core/resources/web/js/lib/bootstrap-colorpicker.js deleted file mode 100644 index e2fd15da..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-colorpicker.js +++ /dev/null @@ -1,6252 +0,0 @@ -/*! - * Bootstrap Colorpicker - Bootstrap Colorpicker is a modular color picker plugin for Bootstrap 4. - * @package bootstrap-colorpicker - * @version v3.2.0 - * @license MIT - * @link https://itsjavi.com/bootstrap-colorpicker/ - * @link https://github.com/itsjavi/bootstrap-colorpicker.git - */ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("jquery")); - else if(typeof define === 'function' && define.amd) - define("bootstrap-colorpicker", ["jquery"], factory); - else if(typeof exports === 'object') - exports["bootstrap-colorpicker"] = factory(require("jquery")); - else - root["bootstrap-colorpicker"] = factory(root["jQuery"]); -})(window, function(__WEBPACK_EXTERNAL_MODULE__0__) { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 7); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports) { - -module.exports = __WEBPACK_EXTERNAL_MODULE__0__; - -/***/ }), -/* 1 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Colorpicker extension class. - */ -var Extension = function () { - /** - * @param {Colorpicker} colorpicker - * @param {Object} options - */ - function Extension(colorpicker) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, Extension); - - /** - * The colorpicker instance - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * Extension options - * - * @type {Object} - */ - this.options = options; - - if (!(this.colorpicker.element && this.colorpicker.element.length)) { - throw new Error('Extension: this.colorpicker.element is not valid'); - } - - this.colorpicker.element.on('colorpickerCreate.colorpicker-ext', _jquery2.default.proxy(this.onCreate, this)); - this.colorpicker.element.on('colorpickerDestroy.colorpicker-ext', _jquery2.default.proxy(this.onDestroy, this)); - this.colorpicker.element.on('colorpickerUpdate.colorpicker-ext', _jquery2.default.proxy(this.onUpdate, this)); - this.colorpicker.element.on('colorpickerChange.colorpicker-ext', _jquery2.default.proxy(this.onChange, this)); - this.colorpicker.element.on('colorpickerInvalid.colorpicker-ext', _jquery2.default.proxy(this.onInvalid, this)); - this.colorpicker.element.on('colorpickerShow.colorpicker-ext', _jquery2.default.proxy(this.onShow, this)); - this.colorpicker.element.on('colorpickerHide.colorpicker-ext', _jquery2.default.proxy(this.onHide, this)); - this.colorpicker.element.on('colorpickerEnable.colorpicker-ext', _jquery2.default.proxy(this.onEnable, this)); - this.colorpicker.element.on('colorpickerDisable.colorpicker-ext', _jquery2.default.proxy(this.onDisable, this)); - } - - /** - * Function called every time a new color needs to be created. - * Return false to skip this resolver and continue with other extensions' ones - * or return anything else to consider the color resolved. - * - * @param {ColorItem|String|*} color - * @param {boolean} realColor if true, the color should resolve into a real (not named) color code - * @return {ColorItem|String|*} - */ - - - _createClass(Extension, [{ - key: 'resolveColor', - value: function resolveColor(color) { - var realColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - return false; - } - - /** - * Method called after the colorpicker is created - * - * @listens Colorpicker#colorpickerCreate - * @param {Event} event - */ - - }, { - key: 'onCreate', - value: function onCreate(event) {} - // to be extended - - - /** - * Method called after the colorpicker is destroyed - * - * @listens Colorpicker#colorpickerDestroy - * @param {Event} event - */ - - }, { - key: 'onDestroy', - value: function onDestroy(event) { - this.colorpicker.element.off('.colorpicker-ext'); - } - - /** - * Method called after the colorpicker is updated - * - * @listens Colorpicker#colorpickerUpdate - * @param {Event} event - */ - - }, { - key: 'onUpdate', - value: function onUpdate(event) {} - // to be extended - - - /** - * Method called after the colorpicker color is changed - * - * @listens Colorpicker#colorpickerChange - * @param {Event} event - */ - - }, { - key: 'onChange', - value: function onChange(event) {} - // to be extended - - - /** - * Method called when the colorpicker color is invalid - * - * @listens Colorpicker#colorpickerInvalid - * @param {Event} event - */ - - }, { - key: 'onInvalid', - value: function onInvalid(event) {} - // to be extended - - - /** - * Method called after the colorpicker is hidden - * - * @listens Colorpicker#colorpickerHide - * @param {Event} event - */ - - }, { - key: 'onHide', - value: function onHide(event) {} - // to be extended - - - /** - * Method called after the colorpicker is shown - * - * @listens Colorpicker#colorpickerShow - * @param {Event} event - */ - - }, { - key: 'onShow', - value: function onShow(event) {} - // to be extended - - - /** - * Method called after the colorpicker is disabled - * - * @listens Colorpicker#colorpickerDisable - * @param {Event} event - */ - - }, { - key: 'onDisable', - value: function onDisable(event) {} - // to be extended - - - /** - * Method called after the colorpicker is enabled - * - * @listens Colorpicker#colorpickerEnable - * @param {Event} event - */ - - }, { - key: 'onEnable', - value: function onEnable(event) { - // to be extended - } - }]); - - return Extension; -}(); - -exports.default = Extension; -module.exports = exports.default; - -/***/ }), -/* 2 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.ColorItem = exports.HSVAColor = undefined; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** - * Color manipulation class, specific for Bootstrap Colorpicker - */ - - -var _color = __webpack_require__(16); - -var _color2 = _interopRequireDefault(_color); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * HSVA color data class, containing the hue, saturation, value and alpha - * information. - */ -var HSVAColor = function () { - /** - * @param {number|int} h - * @param {number|int} s - * @param {number|int} v - * @param {number|int} a - */ - function HSVAColor(h, s, v, a) { - _classCallCheck(this, HSVAColor); - - this.h = isNaN(h) ? 0 : h; - this.s = isNaN(s) ? 0 : s; - this.v = isNaN(v) ? 0 : v; - this.a = isNaN(h) ? 1 : a; - } - - _createClass(HSVAColor, [{ - key: 'toString', - value: function toString() { - return this.h + ', ' + this.s + '%, ' + this.v + '%, ' + this.a; - } - }]); - - return HSVAColor; -}(); - -/** - * HSVA color manipulation - */ - - -var ColorItem = function () { - _createClass(ColorItem, [{ - key: 'api', - - - /** - * Applies a method of the QixColor API and returns a new Color object or - * the return value of the method call. - * - * If no argument is provided, the internal QixColor object is returned. - * - * @param {String} fn QixColor function name - * @param args QixColor function arguments - * @example let darkerColor = color.api('darken', 0.25); - * @example let luminosity = color.api('luminosity'); - * @example color = color.api('negate'); - * @example let qColor = color.api().negate(); - * @returns {ColorItem|QixColor|*} - */ - value: function api(fn) { - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - if (arguments.length === 0) { - return this._color; - } - - var result = this._color[fn].apply(this._color, args); - - if (!(result instanceof _color2.default)) { - // return result of the method call - return result; - } - - return new ColorItem(result, this.format); - } - - /** - * Returns the original ColorItem constructor data, - * plus a 'valid' flag to know if it's valid or not. - * - * @returns {{color: *, format: String, valid: boolean}} - */ - - }, { - key: 'original', - get: function get() { - return this._original; - } - - /** - * @param {ColorItem|HSVAColor|QixColor|String|*|null} color Color data - * @param {String|null} format Color model to convert to by default. Supported: 'rgb', 'hsl', 'hex'. - */ - - }], [{ - key: 'HSVAColor', - - - /** - * Returns the HSVAColor class - * - * @static - * @example let colorData = new ColorItem.HSVAColor(360, 100, 100, 1); - * @returns {HSVAColor} - */ - get: function get() { - return HSVAColor; - } - }]); - - function ColorItem() { - var color = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - - _classCallCheck(this, ColorItem); - - this.replace(color, format); - } - - /** - * Replaces the internal QixColor object with a new one. - * This also replaces the internal original color data. - * - * @param {ColorItem|HSVAColor|QixColor|String|*|null} color Color data to be parsed (if needed) - * @param {String|null} format Color model to convert to by default. Supported: 'rgb', 'hsl', 'hex'. - * @example color.replace('rgb(255,0,0)', 'hsl'); - * @example color.replace(hsvaColorData); - */ - - - _createClass(ColorItem, [{ - key: 'replace', - value: function replace(color) { - var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - - format = ColorItem.sanitizeFormat(format); - - /** - * @type {{color: *, format: String}} - * @private - */ - this._original = { - color: color, - format: format, - valid: true - }; - /** - * @type {QixColor} - * @private - */ - this._color = ColorItem.parse(color); - - if (this._color === null) { - this._color = (0, _color2.default)(); - this._original.valid = false; - return; - } - - /** - * @type {*|string} - * @private - */ - this._format = format ? format : ColorItem.isHex(color) ? 'hex' : this._color.model; - } - - /** - * Parses the color returning a Qix Color object or null if cannot be - * parsed. - * - * @param {ColorItem|HSVAColor|QixColor|String|*|null} color Color data - * @example let qColor = ColorItem.parse('rgb(255,0,0)'); - * @static - * @returns {QixColor|null} - */ - - }, { - key: 'isValid', - - - /** - * Returns true if the color is valid, false if not. - * - * @returns {boolean} - */ - value: function isValid() { - return this._original.valid === true; - } - - /** - * Hue value from 0 to 360 - * - * @returns {int} - */ - - }, { - key: 'setHueRatio', - - - /** - * Sets the hue ratio, where 1.0 is 0, 0.5 is 180 and 0.0 is 360. - * - * @ignore - * @param {number} h Ratio from 1.0 to 0.0 - */ - value: function setHueRatio(h) { - this.hue = (1 - h) * 360; - } - - /** - * Sets the saturation value - * - * @param {int} value Integer from 0 to 100 - */ - - }, { - key: 'setSaturationRatio', - - - /** - * Sets the saturation ratio, where 1.0 is 100 and 0.0 is 0. - * - * @ignore - * @param {number} s Ratio from 0.0 to 1.0 - */ - value: function setSaturationRatio(s) { - this.saturation = s * 100; - } - - /** - * Sets the 'value' channel value - * - * @param {int} value Integer from 0 to 100 - */ - - }, { - key: 'setValueRatio', - - - /** - * Sets the value ratio, where 1.0 is 0 and 0.0 is 100. - * - * @ignore - * @param {number} v Ratio from 1.0 to 0.0 - */ - value: function setValueRatio(v) { - this.value = (1 - v) * 100; - } - - /** - * Sets the alpha value. It will be rounded to 2 decimals. - * - * @param {int} value Float from 0.0 to 1.0 - */ - - }, { - key: 'setAlphaRatio', - - - /** - * Sets the alpha ratio, where 1.0 is 0.0 and 0.0 is 1.0. - * - * @ignore - * @param {number} a Ratio from 1.0 to 0.0 - */ - value: function setAlphaRatio(a) { - this.alpha = 1 - a; - } - - /** - * Sets the default color format - * - * @param {String} value Supported: 'rgb', 'hsl', 'hex' - */ - - }, { - key: 'isDesaturated', - - - /** - * Returns true if the saturation value is zero, false otherwise - * - * @returns {boolean} - */ - value: function isDesaturated() { - return this.saturation === 0; - } - - /** - * Returns true if the alpha value is zero, false otherwise - * - * @returns {boolean} - */ - - }, { - key: 'isTransparent', - value: function isTransparent() { - return this.alpha === 0; - } - - /** - * Returns true if the alpha value is numeric and less than 1, false otherwise - * - * @returns {boolean} - */ - - }, { - key: 'hasTransparency', - value: function hasTransparency() { - return this.hasAlpha() && this.alpha < 1; - } - - /** - * Returns true if the alpha value is numeric, false otherwise - * - * @returns {boolean} - */ - - }, { - key: 'hasAlpha', - value: function hasAlpha() { - return !isNaN(this.alpha); - } - - /** - * Returns a new HSVAColor object, based on the current color - * - * @returns {HSVAColor} - */ - - }, { - key: 'toObject', - value: function toObject() { - return new HSVAColor(this.hue, this.saturation, this.value, this.alpha); - } - - /** - * Alias of toObject() - * - * @returns {HSVAColor} - */ - - }, { - key: 'toHsva', - value: function toHsva() { - return this.toObject(); - } - - /** - * Returns a new HSVAColor object with the ratio values (from 0.0 to 1.0), - * based on the current color. - * - * @ignore - * @returns {HSVAColor} - */ - - }, { - key: 'toHsvaRatio', - value: function toHsvaRatio() { - return new HSVAColor(this.hue / 360, this.saturation / 100, this.value / 100, this.alpha); - } - - /** - * Converts the current color to its string representation, - * using the internal format of this instance. - * - * @returns {String} - */ - - }, { - key: 'toString', - value: function toString() { - return this.string(); - } - - /** - * Converts the current color to its string representation, - * using the given format. - * - * @param {String|null} format Format to convert to. If empty or null, the internal format will be used. - * @returns {String} - */ - - }, { - key: 'string', - value: function string() { - var format = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - - format = ColorItem.sanitizeFormat(format ? format : this.format); - - if (!format) { - return this._color.round().string(); - } - - if (this._color[format] === undefined) { - throw new Error('Unsupported color format: \'' + format + '\''); - } - - var str = this._color[format](); - - return str.round ? str.round().string() : str; - } - - /** - * Returns true if the given color values equals this one, false otherwise. - * The format is not compared. - * If any of the colors is invalid, the result will be false. - * - * @param {ColorItem|HSVAColor|QixColor|String|*|null} color Color data - * - * @returns {boolean} - */ - - }, { - key: 'equals', - value: function equals(color) { - color = color instanceof ColorItem ? color : new ColorItem(color); - - if (!color.isValid() || !this.isValid()) { - return false; - } - - return this.hue === color.hue && this.saturation === color.saturation && this.value === color.value && this.alpha === color.alpha; - } - - /** - * Creates a copy of this instance - * - * @returns {ColorItem} - */ - - }, { - key: 'getClone', - value: function getClone() { - return new ColorItem(this._color, this.format); - } - - /** - * Creates a copy of this instance, only copying the hue value, - * and setting the others to its max value. - * - * @returns {ColorItem} - */ - - }, { - key: 'getCloneHueOnly', - value: function getCloneHueOnly() { - return new ColorItem([this.hue, 100, 100, 1], this.format); - } - - /** - * Creates a copy of this instance setting the alpha to the max. - * - * @returns {ColorItem} - */ - - }, { - key: 'getCloneOpaque', - value: function getCloneOpaque() { - return new ColorItem(this._color.alpha(1), this.format); - } - - /** - * Converts the color to a RGB string - * - * @returns {String} - */ - - }, { - key: 'toRgbString', - value: function toRgbString() { - return this.string('rgb'); - } - - /** - * Converts the color to a Hexadecimal string - * - * @returns {String} - */ - - }, { - key: 'toHexString', - value: function toHexString() { - return this.string('hex'); - } - - /** - * Converts the color to a HSL string - * - * @returns {String} - */ - - }, { - key: 'toHslString', - value: function toHslString() { - return this.string('hsl'); - } - - /** - * Returns true if the color is dark, false otherwhise. - * This is useful to decide a text color. - * - * @returns {boolean} - */ - - }, { - key: 'isDark', - value: function isDark() { - return this._color.isDark(); - } - - /** - * Returns true if the color is light, false otherwhise. - * This is useful to decide a text color. - * - * @returns {boolean} - */ - - }, { - key: 'isLight', - value: function isLight() { - return this._color.isLight(); - } - - /** - * Generates a list of colors using the given hue-based formula or the given array of hue values. - * Hue formulas can be extended using ColorItem.colorFormulas static property. - * - * @param {String|Number[]} formula Examples: 'complementary', 'triad', 'tetrad', 'splitcomplement', [180, 270] - * @example let colors = color.generate('triad'); - * @example let colors = color.generate([45, 80, 112, 200]); - * @returns {ColorItem[]} - */ - - }, { - key: 'generate', - value: function generate(formula) { - var hues = []; - - if (Array.isArray(formula)) { - hues = formula; - } else if (!ColorItem.colorFormulas.hasOwnProperty(formula)) { - throw new Error('No color formula found with the name \'' + formula + '\'.'); - } else { - hues = ColorItem.colorFormulas[formula]; - } - - var colors = [], - mainColor = this._color, - format = this.format; - - hues.forEach(function (hue) { - var levels = [hue ? (mainColor.hue() + hue) % 360 : mainColor.hue(), mainColor.saturationv(), mainColor.value(), mainColor.alpha()]; - - colors.push(new ColorItem(levels, format)); - }); - - return colors; - } - }, { - key: 'hue', - get: function get() { - return this._color.hue(); - } - - /** - * Saturation value from 0 to 100 - * - * @returns {int} - */ - , - - - /** - * Sets the hue value - * - * @param {int} value Integer from 0 to 360 - */ - set: function set(value) { - this._color = this._color.hue(value); - } - }, { - key: 'saturation', - get: function get() { - return this._color.saturationv(); - } - - /** - * Value channel value from 0 to 100 - * - * @returns {int} - */ - , - set: function set(value) { - this._color = this._color.saturationv(value); - } - }, { - key: 'value', - get: function get() { - return this._color.value(); - } - - /** - * Alpha value from 0.0 to 1.0 - * - * @returns {number} - */ - , - set: function set(value) { - this._color = this._color.value(value); - } - }, { - key: 'alpha', - get: function get() { - var a = this._color.alpha(); - - return isNaN(a) ? 1 : a; - } - - /** - * Default color format to convert to when calling toString() or string() - * - * @returns {String} 'rgb', 'hsl', 'hex' or '' - */ - , - set: function set(value) { - // 2 decimals max - this._color = this._color.alpha(Math.round(value * 100) / 100); - } - }, { - key: 'format', - get: function get() { - return this._format ? this._format : this._color.model; - }, - set: function set(value) { - this._format = ColorItem.sanitizeFormat(value); - } - }], [{ - key: 'parse', - value: function parse(color) { - if (color instanceof _color2.default) { - return color; - } - - if (color instanceof ColorItem) { - return color._color; - } - - var format = null; - - if (color instanceof HSVAColor) { - color = [color.h, color.s, color.v, isNaN(color.a) ? 1 : color.a]; - } else { - color = ColorItem.sanitizeString(color); - } - - if (color === null) { - return null; - } - - if (Array.isArray(color)) { - format = 'hsv'; - } - - try { - return (0, _color2.default)(color, format); - } catch (e) { - return null; - } - } - - /** - * Sanitizes a color string, adding missing hash to hexadecimal colors - * and converting 'transparent' to a color code. - * - * @param {String|*} str Color string - * @example let colorStr = ColorItem.sanitizeString('ffaa00'); - * @static - * @returns {String|*} - */ - - }, { - key: 'sanitizeString', - value: function sanitizeString(str) { - if (!(typeof str === 'string' || str instanceof String)) { - return str; - } - - if (str.match(/^[0-9a-f]{2,}$/i)) { - return '#' + str; - } - - if (str.toLowerCase() === 'transparent') { - return '#FFFFFF00'; - } - - return str; - } - - /** - * Detects if a value is a string and a color in hexadecimal format (in any variant). - * - * @param {String} str - * @example ColorItem.isHex('rgba(0,0,0)'); // false - * @example ColorItem.isHex('ffaa00'); // true - * @example ColorItem.isHex('#ffaa00'); // true - * @static - * @returns {boolean} - */ - - }, { - key: 'isHex', - value: function isHex(str) { - if (!(typeof str === 'string' || str instanceof String)) { - return false; - } - - return !!str.match(/^#?[0-9a-f]{2,}$/i); - } - - /** - * Sanitizes a color format to one supported by web browsers. - * Returns an empty string of the format can't be recognised. - * - * @param {String|*} format - * @example ColorItem.sanitizeFormat('rgba'); // 'rgb' - * @example ColorItem.isHex('hex8'); // 'hex' - * @example ColorItem.isHex('invalid'); // '' - * @static - * @returns {String} 'rgb', 'hsl', 'hex' or ''. - */ - - }, { - key: 'sanitizeFormat', - value: function sanitizeFormat(format) { - switch (format) { - case 'hex': - case 'hex3': - case 'hex4': - case 'hex6': - case 'hex8': - return 'hex'; - case 'rgb': - case 'rgba': - case 'keyword': - case 'name': - return 'rgb'; - case 'hsl': - case 'hsla': - case 'hsv': - case 'hsva': - case 'hwb': // HWB this is supported by Qix Color, but not by browsers - case 'hwba': - return 'hsl'; - default: - return ''; - } - } - }]); - - return ColorItem; -}(); - -/** - * List of hue-based color formulas used by ColorItem.prototype.generate() - * - * @static - * @type {{complementary: number[], triad: number[], tetrad: number[], splitcomplement: number[]}} - */ - - -ColorItem.colorFormulas = { - complementary: [180], - triad: [0, 120, 240], - tetrad: [0, 90, 180, 270], - splitcomplement: [0, 72, 216] -}; - -exports.default = ColorItem; -exports.HSVAColor = HSVAColor; -exports.ColorItem = ColorItem; - -/***/ }), -/* 3 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/** - * @module - */ - -// adjust these values accordingly to the sass vars - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var sassVars = { - 'bar_size_short': 16, - 'base_margin': 6, - 'columns': 6 -}; - -var sliderSize = sassVars.bar_size_short * sassVars.columns + sassVars.base_margin * (sassVars.columns - 1); - -/** - * Colorpicker default options - */ -exports.default = { - /** - * Custom class to be added to the `.colorpicker-element` element - * - * @type {String|null} - * @default null - */ - customClass: null, - /** - * Sets a initial color, ignoring the one from the element/input value or the data-color attribute. - * - * @type {(String|ColorItem|boolean)} - * @default false - */ - color: false, - /** - * Fallback color to use when the given color is invalid. - * If false, the latest valid color will be used as a fallback. - * - * @type {String|ColorItem|boolean} - * @default false - */ - fallbackColor: false, - /** - * Forces an specific color format. If 'auto', it will be automatically detected the first time only, - * but if null it will be always recalculated. - * - * Note that the ending 'a' of the format meaning "alpha" has currently no effect, meaning that rgb is the same as - * rgba excepting if the alpha channel is disabled (see useAlpha). - * - * @type {('rgb'|'hex'|'hsl'|'auto'|null)} - * @default 'auto' - */ - format: 'auto', - /** - * Horizontal mode layout. - * - * If true, the hue and alpha channel bars will be rendered horizontally, above the saturation selector. - * - * @type {boolean} - * @default false - */ - horizontal: false, - /** - * Forces to show the colorpicker as an inline element. - * - * Note that if there is no container specified, the inline element - * will be added to the body, so you may want to set the container option. - * - * @type {boolean} - * @default false - */ - inline: false, - /** - * Container where the colorpicker is appended to in the DOM. - * - * If is a string (CSS selector), the colorpicker will be placed inside this container. - * If true, the `.colorpicker-element` element itself will be used as the container. - * If false, the document body is used as the container, unless it is a popover (in this case it is appended to the - * popover body instead). - * - * @type {String|boolean} - * @default false - */ - container: false, - /** - * Bootstrap Popover options. - * The trigger, content and html options are always ignored. - * - * @type {boolean} - * @default Object - */ - popover: { - animation: true, - placement: 'bottom', - fallbackPlacement: 'flip' - }, - /** - * If true, loads the 'debugger' extension automatically, which logs the events in the console - * @type {boolean} - * @default false - */ - debug: false, - /** - * Child CSS selector for the colorpicker input. - * - * @type {String} - * @default 'input' - */ - input: 'input', - /** - * Child CSS selector for the colorpicker addon. - * If it exists, the child element background will be changed on color change. - * - * @type {String} - * @default '.colorpicker-trigger, .colorpicker-input-addon' - */ - addon: '.colorpicker-input-addon', - /** - * If true, the input content will be replaced always with a valid color, - * if false, the invalid color will be left in the input, - * while the internal color object will still resolve into a valid one. - * - * @type {boolean} - * @default true - */ - autoInputFallback: true, - /** - * If true a hash will be prepended to hexadecimal colors. - * If false, the hash will be removed. - * This only affects the input values in hexadecimal format. - * - * @type {boolean} - * @default true - */ - useHashPrefix: true, - /** - * If true, the alpha channel bar will be displayed no matter what. - * - * If false, it will be always hidden and alpha channel will be disabled also programmatically, meaning that - * the selected or typed color will be always opaque. - * - * If null, the alpha channel will be automatically disabled/enabled depending if the initial color format supports - * alpha or not. - * - * @type {boolean} - * @default true - */ - useAlpha: true, - /** - * Colorpicker widget template - * @type {String} - * @example - * - *
- *
- *
- *
- *
- * - *
- *
- */ - template: '
\n
\n
\n
\n
\n \n
\n
', - /** - * - * Associative object with the extension class name and its config. - * Colorpicker comes with many bundled extensions: debugger, palette, preview and swatches (a superset of palette). - * - * @type {Object[]} - * @example - * extensions: [ - * { - * name: 'swatches' - * options: { - * colors: { - * 'primary': '#337ab7', - * 'success': '#5cb85c', - * 'info': '#5bc0de', - * 'warning': '#f0ad4e', - * 'danger': '#d9534f' - * }, - * namesAsValues: true - * } - * } - * ] - */ - extensions: [{ - name: 'preview', - options: { - showText: true - } - }], - /** - * Vertical sliders configuration - * @type {Object} - */ - sliders: { - saturation: { - selector: '.colorpicker-saturation', - maxLeft: sliderSize, - maxTop: sliderSize, - callLeft: 'setSaturationRatio', - callTop: 'setValueRatio' - }, - hue: { - selector: '.colorpicker-hue', - maxLeft: 0, - maxTop: sliderSize, - callLeft: false, - callTop: 'setHueRatio' - }, - alpha: { - selector: '.colorpicker-alpha', - childSelector: '.colorpicker-alpha-color', - maxLeft: 0, - maxTop: sliderSize, - callLeft: false, - callTop: 'setAlphaRatio' - } - }, - /** - * Horizontal sliders configuration - * @type {Object} - */ - slidersHorz: { - saturation: { - selector: '.colorpicker-saturation', - maxLeft: sliderSize, - maxTop: sliderSize, - callLeft: 'setSaturationRatio', - callTop: 'setValueRatio' - }, - hue: { - selector: '.colorpicker-hue', - maxLeft: sliderSize, - maxTop: 0, - callLeft: 'setHueRatio', - callTop: false - }, - alpha: { - selector: '.colorpicker-alpha', - childSelector: '.colorpicker-alpha-color', - maxLeft: sliderSize, - maxTop: 0, - callLeft: 'setAlphaRatio', - callTop: false - } - } -}; -module.exports = exports.default; - -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _Extension2 = __webpack_require__(1); - -var _Extension3 = _interopRequireDefault(_Extension2); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -var defaults = { - /** - * Key-value pairs defining a color alias and its CSS color representation. - * - * They can also be just an array of values. In that case, no special names are used, only the real colors. - * - * @type {Object|Array} - * @default null - * @example - * { - * 'black': '#000000', - * 'white': '#ffffff', - * 'red': '#FF0000', - * 'default': '#777777', - * 'primary': '#337ab7', - * 'success': '#5cb85c', - * 'info': '#5bc0de', - * 'warning': '#f0ad4e', - * 'danger': '#d9534f' - * } - * - * @example ['#f0ad4e', '#337ab7', '#5cb85c'] - */ - colors: null, - /** - * If true, when a color swatch is selected the name (alias) will be used as input value, - * otherwise the swatch real color value will be used. - * - * @type {boolean} - * @default true - */ - namesAsValues: true -}; - -/** - * Palette extension - * @ignore - */ - -var Palette = function (_Extension) { - _inherits(Palette, _Extension); - - _createClass(Palette, [{ - key: 'colors', - - - /** - * @returns {Object|Array} - */ - get: function get() { - return this.options.colors; - } - }]); - - function Palette(colorpicker) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, Palette); - - var _this = _possibleConstructorReturn(this, (Palette.__proto__ || Object.getPrototypeOf(Palette)).call(this, colorpicker, _jquery2.default.extend(true, {}, defaults, options))); - - if (!Array.isArray(_this.options.colors) && _typeof(_this.options.colors) !== 'object') { - _this.options.colors = null; - } - return _this; - } - - /** - * @returns {int} - */ - - - _createClass(Palette, [{ - key: 'getLength', - value: function getLength() { - if (!this.options.colors) { - return 0; - } - - if (Array.isArray(this.options.colors)) { - return this.options.colors.length; - } - - if (_typeof(this.options.colors) === 'object') { - return Object.keys(this.options.colors).length; - } - - return 0; - } - }, { - key: 'resolveColor', - value: function resolveColor(color) { - var realColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - if (this.getLength() <= 0) { - return false; - } - - // Array of colors - if (Array.isArray(this.options.colors)) { - if (this.options.colors.indexOf(color) >= 0) { - return color; - } - if (this.options.colors.indexOf(color.toUpperCase()) >= 0) { - return color.toUpperCase(); - } - if (this.options.colors.indexOf(color.toLowerCase()) >= 0) { - return color.toLowerCase(); - } - return false; - } - - if (_typeof(this.options.colors) !== 'object') { - return false; - } - - // Map of objects - if (!this.options.namesAsValues || realColor) { - return this.getValue(color, false); - } - return this.getName(color, this.getName('#' + color)); - } - - /** - * Given a color value, returns the corresponding color name or defaultValue. - * - * @param {String} value - * @param {*} defaultValue - * @returns {*} - */ - - }, { - key: 'getName', - value: function getName(value) { - var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - if (!(typeof value === 'string') || !this.options.colors) { - return defaultValue; - } - for (var name in this.options.colors) { - if (!this.options.colors.hasOwnProperty(name)) { - continue; - } - if (this.options.colors[name].toLowerCase() === value.toLowerCase()) { - return name; - } - } - return defaultValue; - } - - /** - * Given a color name, returns the corresponding color value or defaultValue. - * - * @param {String} name - * @param {*} defaultValue - * @returns {*} - */ - - }, { - key: 'getValue', - value: function getValue(name) { - var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - - if (!(typeof name === 'string') || !this.options.colors) { - return defaultValue; - } - if (this.options.colors.hasOwnProperty(name)) { - return this.options.colors[name]; - } - return defaultValue; - } - }]); - - return Palette; -}(_Extension3.default); - -exports.default = Palette; -module.exports = exports.default; - -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; - - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - -/* MIT license */ -var cssKeywords = __webpack_require__(5); - -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) - -var reverseKeywords = {}; -for (var key in cssKeywords) { - if (cssKeywords.hasOwnProperty(key)) { - reverseKeywords[cssKeywords[key]] = key; - } -} - -var convert = module.exports = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} -}; - -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } - - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } - - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } - - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); - } -} - -convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; - - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } - - h = Math.min(h * 60, 360); - - if (h < 0) { - h += 360; - } - - l = (min + max) / 2; - - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); - } - - return [h, s * 100, l * 100]; -}; - -convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; - - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; - - if (diff === 0) { - h = s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); - - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - } - - return [ - h * 360, - s * 100, - v * 100 - ]; -}; - -convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); - - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); - - return [h, w * 100, b * 100]; -}; - -convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; - - return [c * 100, m * 100, y * 100, k * 100]; -}; - -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ -function comparativeDistance(x, y) { - return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) - ); -} - -convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } - - var currentClosestDistance = Infinity; - var currentClosestKeyword; - - for (var keyword in cssKeywords) { - if (cssKeywords.hasOwnProperty(keyword)) { - var value = cssKeywords[keyword]; - - // Compute comparative distance - var distance = comparativeDistance(rgb, value); - - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } - } - } - - return currentClosestKeyword; -}; - -convert.keyword.rgb = function (keyword) { - return cssKeywords[keyword]; -}; - -convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - - return [x * 100, y * 100, z * 100]; -}; - -convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; - - if (s === 0) { - val = l * 255; - return [val, val, val]; - } - - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } - - t1 = 2 * l - t2; - - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } - if (t3 > 1) { - t3--; - } - - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; - } - - rgb[i] = val * 255; - } - - return rgb; -}; - -convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; - - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); - - return [h, sv * 100, v * 100]; -}; - -convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; - - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); - v *= 255; - - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -}; - -convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; - - l = (2 - s) * v; - lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; - - return [h, sl * 100, l * 100]; -}; - -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } - - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; - - if ((i & 0x01) !== 0) { - f = 1 - f; - } - - n = wh + f * (v - wh); // linear interpolation - - var r; - var g; - var b; - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } - - return [r * 255, g * 255, b * 255]; -}; - -convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; - - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; - - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - - // assume sRGB - r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r * 12.92; - - g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g * 12.92; - - b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b * 12.92; - - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); - - return [r * 255, g * 255, b * 255]; -}; - -convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - - x /= 95.047; - y /= 100; - z /= 108.883; - - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - - return [l, a, b]; -}; - -convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; - - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; - - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; - - x *= 95.047; - y *= 100; - z *= 108.883; - - return [x, y, z]; -}; - -convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; - - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; - - if (h < 0) { - h += 360; - } - - c = Math.sqrt(a * a + b * b); - - return [l, c, h]; -}; - -convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; - - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); - - return [l, a, b]; -}; - -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization - - value = Math.round(value / 50); - - if (value === 0) { - return 30; - } - - var ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); - - if (value === 2) { - ansi += 60; - } - - return ansi; -}; - -convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; - -convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - - // we use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; - } - - if (r > 248) { - return 231; - } - - return Math.round(((r - 8) / 247) * 24) + 232; - } - - var ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); - - return ansi; -}; - -convert.ansi16.rgb = function (args) { - var color = args % 10; - - // handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; - } - - color = color / 10.5 * 255; - - return [color, color, color]; - } - - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; - - return [r, g, b]; -}; - -convert.ansi256.rgb = function (args) { - // handle greyscale - if (args >= 232) { - var c = (args - 232) * 10 + 8; - return [c, c, c]; - } - - args -= 16; - - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; - - return [r, g, b]; -}; - -convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; - } - - var colorString = match[0]; - - if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { - return char + char; - }).join(''); - } - - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; - - return [r, g, b]; -}; - -convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; - - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } - - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma + 4; - } - - hue /= 6; - hue %= 1; - - return [hue * 360, chroma * 100, grayscale * 100]; -}; - -convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; - - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } - - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } - - return [hsl[0], c * 100, f * 100]; -}; - -convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; - - var c = s * v; - var f = 0; - - if (c < 1.0) { - f = (v - c) / (1 - c); - } - - return [hsv[0], c * 100, f * 100]; -}; - -convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } - - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; - - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; - } - - mg = (1.0 - c) * g; - - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; - -convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var v = c + g * (1.0 - c); - var f = 0; - - if (v > 0.0) { - f = c / v; - } - - return [hcg[0], f * 100, v * 100]; -}; - -convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; - - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); - } - - return [hcg[0], s * 100, l * 100]; -}; - -convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; -}; - -convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; - - if (c < 1) { - g = (v - c) / (1 - c); - } - - return [hwb[0], c * 100, g * 100]; -}; - -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; - -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; - -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; - -convert.gray.hsl = convert.gray.hsv = function (args) { - return [0, 0, args[0]]; -}; - -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; - -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; - -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; - -convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; - - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; - -convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; -}; - - -/***/ }), -/* 7 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; - -var _Colorpicker = __webpack_require__(8); - -var _Colorpicker2 = _interopRequireDefault(_Colorpicker); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var plugin = 'colorpicker'; - -_jquery2.default[plugin] = _Colorpicker2.default; - -// Colorpicker jQuery Plugin API -_jquery2.default.fn[plugin] = function (option) { - var fnArgs = Array.prototype.slice.call(arguments, 1), - isSingleElement = this.length === 1, - returnValue = null; - - var $elements = this.each(function () { - var $this = (0, _jquery2.default)(this), - inst = $this.data(plugin), - options = (typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' ? option : {}; - - // Create instance if does not exist - if (!inst) { - inst = new _Colorpicker2.default(this, options); - $this.data(plugin, inst); - } - - if (!isSingleElement) { - return; - } - - returnValue = $this; - - if (typeof option === 'string') { - if (option === 'colorpicker') { - // Return colorpicker instance: e.g. .colorpicker('colorpicker') - returnValue = inst; - } else if (_jquery2.default.isFunction(inst[option])) { - // Return method call return value: e.g. .colorpicker('isEnabled') - returnValue = inst[option].apply(inst, fnArgs); - } else { - // Return property value: e.g. .colorpicker('element') - returnValue = inst[option]; - } - } - }); - - return isSingleElement ? returnValue : $elements; -}; - -_jquery2.default.fn[plugin].constructor = _Colorpicker2.default; - -/***/ }), -/* 8 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _Extension = __webpack_require__(1); - -var _Extension2 = _interopRequireDefault(_Extension); - -var _options = __webpack_require__(3); - -var _options2 = _interopRequireDefault(_options); - -var _extensions = __webpack_require__(9); - -var _extensions2 = _interopRequireDefault(_extensions); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -var _SliderHandler = __webpack_require__(13); - -var _SliderHandler2 = _interopRequireDefault(_SliderHandler); - -var _PopupHandler = __webpack_require__(14); - -var _PopupHandler2 = _interopRequireDefault(_PopupHandler); - -var _InputHandler = __webpack_require__(15); - -var _InputHandler2 = _interopRequireDefault(_InputHandler); - -var _ColorHandler = __webpack_require__(22); - -var _ColorHandler2 = _interopRequireDefault(_ColorHandler); - -var _PickerHandler = __webpack_require__(23); - -var _PickerHandler2 = _interopRequireDefault(_PickerHandler); - -var _AddonHandler = __webpack_require__(24); - -var _AddonHandler2 = _interopRequireDefault(_AddonHandler); - -var _ColorItem = __webpack_require__(2); - -var _ColorItem2 = _interopRequireDefault(_ColorItem); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var colorPickerIdCounter = 0; - -var root = typeof self !== 'undefined' ? self : undefined; // window - -/** - * Colorpicker widget class - */ - -var Colorpicker = function () { - _createClass(Colorpicker, [{ - key: 'color', - - - /** - * Internal color object - * - * @type {Color|null} - */ - get: function get() { - return this.colorHandler.color; - } - - /** - * Internal color format - * - * @type {String|null} - */ - - }, { - key: 'format', - get: function get() { - return this.colorHandler.format; - } - - /** - * Getter of the picker element - * - * @returns {jQuery|HTMLElement} - */ - - }, { - key: 'picker', - get: function get() { - return this.pickerHandler.picker; - } - - /** - * @fires Colorpicker#colorpickerCreate - * @param {Object|String} element - * @param {Object} options - * @constructor - */ - - }], [{ - key: 'Color', - - /** - * Color class - * - * @static - * @type {Color} - */ - get: function get() { - return _ColorItem2.default; - } - - /** - * Extension class - * - * @static - * @type {Extension} - */ - - }, { - key: 'Extension', - get: function get() { - return _Extension2.default; - } - }]); - - function Colorpicker(element, options) { - _classCallCheck(this, Colorpicker); - - colorPickerIdCounter += 1; - /** - * The colorpicker instance number - * @type {number} - */ - this.id = colorPickerIdCounter; - - /** - * Latest colorpicker event - * - * @type {{name: String, e: *}} - */ - this.lastEvent = { - alias: null, - e: null - }; - - /** - * The element that the colorpicker is bound to - * - * @type {*|jQuery} - */ - this.element = (0, _jquery2.default)(element).addClass('colorpicker-element').attr('data-colorpicker-id', this.id); - - /** - * @type {defaults} - */ - this.options = _jquery2.default.extend(true, {}, _options2.default, options, this.element.data()); - - /** - * @type {boolean} - * @private - */ - this.disabled = false; - - /** - * Extensions added to this instance - * - * @type {Extension[]} - */ - this.extensions = []; - - /** - * The element where the - * @type {*|jQuery} - */ - this.container = this.options.container === true || this.options.container !== true && this.options.inline === true ? this.element : this.options.container; - - this.container = this.container !== false ? (0, _jquery2.default)(this.container) : false; - - /** - * @type {InputHandler} - */ - this.inputHandler = new _InputHandler2.default(this); - /** - * @type {ColorHandler} - */ - this.colorHandler = new _ColorHandler2.default(this); - /** - * @type {SliderHandler} - */ - this.sliderHandler = new _SliderHandler2.default(this); - /** - * @type {PopupHandler} - */ - this.popupHandler = new _PopupHandler2.default(this, root); - /** - * @type {PickerHandler} - */ - this.pickerHandler = new _PickerHandler2.default(this); - /** - * @type {AddonHandler} - */ - this.addonHandler = new _AddonHandler2.default(this); - - this.init(); - - // Emit a create event - (0, _jquery2.default)(_jquery2.default.proxy(function () { - /** - * (Colorpicker) When the Colorpicker instance has been created and the DOM is ready. - * - * @event Colorpicker#colorpickerCreate - */ - this.trigger('colorpickerCreate'); - }, this)); - } - - /** - * Initializes the plugin - * @private - */ - - - _createClass(Colorpicker, [{ - key: 'init', - value: function init() { - // Init addon - this.addonHandler.bind(); - - // Init input - this.inputHandler.bind(); - - // Init extensions (before initializing the color) - this.initExtensions(); - - // Init color - this.colorHandler.bind(); - - // Init picker - this.pickerHandler.bind(); - - // Init sliders and popup - this.sliderHandler.bind(); - this.popupHandler.bind(); - - // Inject into the DOM (this may make it visible) - this.pickerHandler.attach(); - - // Update all components - this.update(); - - if (this.inputHandler.isDisabled()) { - this.disable(); - } - } - - /** - * Initializes the plugin extensions - * @private - */ - - }, { - key: 'initExtensions', - value: function initExtensions() { - var _this = this; - - if (!Array.isArray(this.options.extensions)) { - this.options.extensions = []; - } - - if (this.options.debug) { - this.options.extensions.push({ name: 'debugger' }); - } - - // Register and instantiate extensions - this.options.extensions.forEach(function (ext) { - _this.registerExtension(Colorpicker.extensions[ext.name.toLowerCase()], ext.options || {}); - }); - } - - /** - * Creates and registers the given extension - * - * @param {Extension} ExtensionClass The extension class to instantiate - * @param {Object} [config] Extension configuration - * @returns {Extension} - */ - - }, { - key: 'registerExtension', - value: function registerExtension(ExtensionClass) { - var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - var ext = new ExtensionClass(this, config); - - this.extensions.push(ext); - return ext; - } - - /** - * Destroys the current instance - * - * @fires Colorpicker#colorpickerDestroy - */ - - }, { - key: 'destroy', - value: function destroy() { - var color = this.color; - - this.sliderHandler.unbind(); - this.inputHandler.unbind(); - this.popupHandler.unbind(); - this.colorHandler.unbind(); - this.addonHandler.unbind(); - this.pickerHandler.unbind(); - - this.element.removeClass('colorpicker-element').removeData('colorpicker', 'color').off('.colorpicker'); - - /** - * (Colorpicker) When the instance is destroyed with all events unbound. - * - * @event Colorpicker#colorpickerDestroy - */ - this.trigger('colorpickerDestroy', color); - } - - /** - * Shows the colorpicker widget if hidden. - * If the colorpicker is disabled this call will be ignored. - * - * @fires Colorpicker#colorpickerShow - * @param {Event} [e] - */ - - }, { - key: 'show', - value: function show(e) { - this.popupHandler.show(e); - } - - /** - * Hides the colorpicker widget. - * - * @fires Colorpicker#colorpickerHide - * @param {Event} [e] - */ - - }, { - key: 'hide', - value: function hide(e) { - this.popupHandler.hide(e); - } - - /** - * Toggles the colorpicker between visible and hidden. - * - * @fires Colorpicker#colorpickerShow - * @fires Colorpicker#colorpickerHide - * @param {Event} [e] - */ - - }, { - key: 'toggle', - value: function toggle(e) { - this.popupHandler.toggle(e); - } - - /** - * Returns the current color value as string - * - * @param {String|*} [defaultValue] - * @returns {String|*} - */ - - }, { - key: 'getValue', - value: function getValue() { - var defaultValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - - var val = this.colorHandler.color; - - val = val instanceof _ColorItem2.default ? val : defaultValue; - - if (val instanceof _ColorItem2.default) { - return val.string(this.format); - } - - return val; - } - - /** - * Sets the color manually - * - * @fires Colorpicker#colorpickerChange - * @param {String|Color} val - */ - - }, { - key: 'setValue', - value: function setValue(val) { - if (this.isDisabled()) { - return; - } - var ch = this.colorHandler; - - if (ch.hasColor() && !!val && ch.color.equals(val) || !ch.hasColor() && !val) { - // same color or still empty - return; - } - - ch.color = val ? ch.createColor(val, this.options.autoInputFallback) : null; - - /** - * (Colorpicker) When the color is set programmatically with setValue(). - * - * @event Colorpicker#colorpickerChange - */ - this.trigger('colorpickerChange', ch.color, val); - - // force update if color has changed to empty - this.update(); - } - - /** - * Updates the UI and the input color according to the internal color. - * - * @fires Colorpicker#colorpickerUpdate - */ - - }, { - key: 'update', - value: function update() { - if (this.colorHandler.hasColor()) { - this.inputHandler.update(); - } else { - this.colorHandler.assureColor(); - } - - this.addonHandler.update(); - this.pickerHandler.update(); - - /** - * (Colorpicker) Fired when the widget is updated. - * - * @event Colorpicker#colorpickerUpdate - */ - this.trigger('colorpickerUpdate'); - } - - /** - * Enables the widget and the input if any - * - * @fires Colorpicker#colorpickerEnable - * @returns {boolean} - */ - - }, { - key: 'enable', - value: function enable() { - this.inputHandler.enable(); - this.disabled = false; - this.picker.removeClass('colorpicker-disabled'); - - /** - * (Colorpicker) When the widget has been enabled. - * - * @event Colorpicker#colorpickerEnable - */ - this.trigger('colorpickerEnable'); - return true; - } - - /** - * Disables the widget and the input if any - * - * @fires Colorpicker#colorpickerDisable - * @returns {boolean} - */ - - }, { - key: 'disable', - value: function disable() { - this.inputHandler.disable(); - this.disabled = true; - this.picker.addClass('colorpicker-disabled'); - - /** - * (Colorpicker) When the widget has been disabled. - * - * @event Colorpicker#colorpickerDisable - */ - this.trigger('colorpickerDisable'); - return true; - } - - /** - * Returns true if this instance is enabled - * @returns {boolean} - */ - - }, { - key: 'isEnabled', - value: function isEnabled() { - return !this.isDisabled(); - } - - /** - * Returns true if this instance is disabled - * @returns {boolean} - */ - - }, { - key: 'isDisabled', - value: function isDisabled() { - return this.disabled === true; - } - - /** - * Triggers a Colorpicker event. - * - * @param eventName - * @param color - * @param value - */ - - }, { - key: 'trigger', - value: function trigger(eventName) { - var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - var value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; - - this.element.trigger({ - type: eventName, - colorpicker: this, - color: color ? color : this.color, - value: value ? value : this.getValue() - }); - } - }]); - - return Colorpicker; -}(); - -/** - * Colorpicker extension classes, indexed by extension name - * - * @static - * @type {Object} a map between the extension name and its class - */ - - -Colorpicker.extensions = _extensions2.default; - -exports.default = Colorpicker; -module.exports = exports.default; - -/***/ }), -/* 9 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.Palette = exports.Swatches = exports.Preview = exports.Debugger = undefined; - -var _Debugger = __webpack_require__(10); - -var _Debugger2 = _interopRequireDefault(_Debugger); - -var _Preview = __webpack_require__(11); - -var _Preview2 = _interopRequireDefault(_Preview); - -var _Swatches = __webpack_require__(12); - -var _Swatches2 = _interopRequireDefault(_Swatches); - -var _Palette = __webpack_require__(4); - -var _Palette2 = _interopRequireDefault(_Palette); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -exports.Debugger = _Debugger2.default; -exports.Preview = _Preview2.default; -exports.Swatches = _Swatches2.default; -exports.Palette = _Palette2.default; -exports.default = { - 'debugger': _Debugger2.default, - 'preview': _Preview2.default, - 'swatches': _Swatches2.default, - 'palette': _Palette2.default -}; - -/***/ }), -/* 10 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; - -var _Extension2 = __webpack_require__(1); - -var _Extension3 = _interopRequireDefault(_Extension2); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** - * Debugger extension class - * @alias DebuggerExtension - * @ignore - */ -var Debugger = function (_Extension) { - _inherits(Debugger, _Extension); - - function Debugger(colorpicker) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, Debugger); - - /** - * @type {number} - */ - var _this = _possibleConstructorReturn(this, (Debugger.__proto__ || Object.getPrototypeOf(Debugger)).call(this, colorpicker, options)); - - _this.eventCounter = 0; - if (_this.colorpicker.inputHandler.hasInput()) { - _this.colorpicker.inputHandler.input.on('change.colorpicker-ext', _jquery2.default.proxy(_this.onChangeInput, _this)); - } - return _this; - } - - /** - * @fires DebuggerExtension#colorpickerDebug - * @param {string} eventName - * @param {*} args - */ - - - _createClass(Debugger, [{ - key: 'log', - value: function log(eventName) { - var _console; - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - this.eventCounter += 1; - - var logMessage = '#' + this.eventCounter + ': Colorpicker#' + this.colorpicker.id + ' [' + eventName + ']'; - - (_console = console).debug.apply(_console, [logMessage].concat(args)); - - /** - * Whenever the debugger logs an event, this other event is emitted. - * - * @event DebuggerExtension#colorpickerDebug - * @type {object} The event object - * @property {Colorpicker} colorpicker The Colorpicker instance - * @property {ColorItem} color The color instance - * @property {{debugger: DebuggerExtension, eventName: String, logArgs: Array, logMessage: String}} debug - * The debug info - */ - this.colorpicker.element.trigger({ - type: 'colorpickerDebug', - colorpicker: this.colorpicker, - color: this.color, - value: null, - debug: { - debugger: this, - eventName: eventName, - logArgs: args, - logMessage: logMessage - } - }); - } - }, { - key: 'resolveColor', - value: function resolveColor(color) { - var realColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - this.log('resolveColor()', color, realColor); - return false; - } - }, { - key: 'onCreate', - value: function onCreate(event) { - this.log('colorpickerCreate'); - return _get(Debugger.prototype.__proto__ || Object.getPrototypeOf(Debugger.prototype), 'onCreate', this).call(this, event); - } - }, { - key: 'onDestroy', - value: function onDestroy(event) { - this.log('colorpickerDestroy'); - this.eventCounter = 0; - - if (this.colorpicker.inputHandler.hasInput()) { - this.colorpicker.inputHandler.input.off('.colorpicker-ext'); - } - - return _get(Debugger.prototype.__proto__ || Object.getPrototypeOf(Debugger.prototype), 'onDestroy', this).call(this, event); - } - }, { - key: 'onUpdate', - value: function onUpdate(event) { - this.log('colorpickerUpdate'); - } - - /** - * @listens Colorpicker#change - * @param {Event} event - */ - - }, { - key: 'onChangeInput', - value: function onChangeInput(event) { - this.log('input:change.colorpicker', event.value, event.color); - } - }, { - key: 'onChange', - value: function onChange(event) { - this.log('colorpickerChange', event.value, event.color); - } - }, { - key: 'onInvalid', - value: function onInvalid(event) { - this.log('colorpickerInvalid', event.value, event.color); - } - }, { - key: 'onHide', - value: function onHide(event) { - this.log('colorpickerHide'); - this.eventCounter = 0; - } - }, { - key: 'onShow', - value: function onShow(event) { - this.log('colorpickerShow'); - } - }, { - key: 'onDisable', - value: function onDisable(event) { - this.log('colorpickerDisable'); - } - }, { - key: 'onEnable', - value: function onEnable(event) { - this.log('colorpickerEnable'); - } - }]); - - return Debugger; -}(_Extension3.default); - -exports.default = Debugger; -module.exports = exports.default; - -/***/ }), -/* 11 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; - -var _Extension2 = __webpack_require__(1); - -var _Extension3 = _interopRequireDefault(_Extension2); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** - * Color preview extension - * @ignore - */ -var Preview = function (_Extension) { - _inherits(Preview, _Extension); - - function Preview(colorpicker) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, Preview); - - var _this = _possibleConstructorReturn(this, (Preview.__proto__ || Object.getPrototypeOf(Preview)).call(this, colorpicker, _jquery2.default.extend(true, {}, { - template: '
', - showText: true, - format: colorpicker.format - }, options))); - - _this.element = (0, _jquery2.default)(_this.options.template); - _this.elementInner = _this.element.find('div'); - return _this; - } - - _createClass(Preview, [{ - key: 'onCreate', - value: function onCreate(event) { - _get(Preview.prototype.__proto__ || Object.getPrototypeOf(Preview.prototype), 'onCreate', this).call(this, event); - this.colorpicker.picker.append(this.element); - } - }, { - key: 'onUpdate', - value: function onUpdate(event) { - _get(Preview.prototype.__proto__ || Object.getPrototypeOf(Preview.prototype), 'onUpdate', this).call(this, event); - - if (!event.color) { - this.elementInner.css('backgroundColor', null).css('color', null).html(''); - return; - } - - this.elementInner.css('backgroundColor', event.color.toRgbString()); - - if (this.options.showText) { - this.elementInner.html(event.color.string(this.options.format || this.colorpicker.format)); - - if (event.color.isDark() && event.color.alpha > 0.5) { - this.elementInner.css('color', 'white'); - } else { - this.elementInner.css('color', 'black'); - } - } - } - }]); - - return Preview; -}(_Extension3.default); - -exports.default = Preview; -module.exports = exports.default; - -/***/ }), -/* 12 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; - -var _Palette2 = __webpack_require__(4); - -var _Palette3 = _interopRequireDefault(_Palette2); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -var defaults = { - barTemplate: '
\n
\n
', - swatchTemplate: '' -}; - -/** - * Color swatches extension - * @ignore - */ - -var Swatches = function (_Palette) { - _inherits(Swatches, _Palette); - - function Swatches(colorpicker) { - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, Swatches); - - var _this = _possibleConstructorReturn(this, (Swatches.__proto__ || Object.getPrototypeOf(Swatches)).call(this, colorpicker, _jquery2.default.extend(true, {}, defaults, options))); - - _this.element = null; - return _this; - } - - _createClass(Swatches, [{ - key: 'isEnabled', - value: function isEnabled() { - return this.getLength() > 0; - } - }, { - key: 'onCreate', - value: function onCreate(event) { - _get(Swatches.prototype.__proto__ || Object.getPrototypeOf(Swatches.prototype), 'onCreate', this).call(this, event); - - if (!this.isEnabled()) { - return; - } - - this.element = (0, _jquery2.default)(this.options.barTemplate); - this.load(); - this.colorpicker.picker.append(this.element); - } - }, { - key: 'load', - value: function load() { - var _this2 = this; - - var colorpicker = this.colorpicker, - swatchContainer = this.element.find('.colorpicker-swatches--inner'), - isAliased = this.options.namesAsValues === true && !Array.isArray(this.colors); - - swatchContainer.empty(); - - _jquery2.default.each(this.colors, function (name, value) { - var $swatch = (0, _jquery2.default)(_this2.options.swatchTemplate).attr('data-name', name).attr('data-value', value).attr('title', isAliased ? name + ': ' + value : value).on('mousedown.colorpicker touchstart.colorpicker', function (e) { - var $sw = (0, _jquery2.default)(this); - - // e.preventDefault(); - - colorpicker.setValue(isAliased ? $sw.attr('data-name') : $sw.attr('data-value')); - }); - - $swatch.find('.colorpicker-swatch--inner').css('background-color', value); - - swatchContainer.append($swatch); - }); - - swatchContainer.append((0, _jquery2.default)('')); - } - }]); - - return Swatches; -}(_Palette3.default); - -exports.default = Swatches; -module.exports = exports.default; - -/***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Class that handles all configured sliders on mouse or touch events. - * @ignore - */ -var SliderHandler = function () { - /** - * @param {Colorpicker} colorpicker - */ - function SliderHandler(colorpicker) { - _classCallCheck(this, SliderHandler); - - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * @type {*|String} - * @private - */ - this.currentSlider = null; - /** - * @type {{left: number, top: number}} - * @private - */ - this.mousePointer = { - left: 0, - top: 0 - }; - - /** - * @type {Function} - */ - this.onMove = _jquery2.default.proxy(this.defaultOnMove, this); - } - - /** - * This function is called every time a slider guide is moved - * The scope of "this" is the SliderHandler object. - * - * @param {int} top - * @param {int} left - */ - - - _createClass(SliderHandler, [{ - key: 'defaultOnMove', - value: function defaultOnMove(top, left) { - if (!this.currentSlider) { - return; - } - - var slider = this.currentSlider, - cp = this.colorpicker, - ch = cp.colorHandler; - - // Create a color object - var color = !ch.hasColor() ? ch.getFallbackColor() : ch.color.getClone(); - - // Adjust the guide position - slider.guideStyle.left = left + 'px'; - slider.guideStyle.top = top + 'px'; - - // Adjust the color - if (slider.callLeft) { - color[slider.callLeft](left / slider.maxLeft); - } - if (slider.callTop) { - color[slider.callTop](top / slider.maxTop); - } - - // Set the new color - cp.setValue(color); - cp.popupHandler.focus(); - } - - /** - * Binds the colorpicker sliders to the mouse/touch events - */ - - }, { - key: 'bind', - value: function bind() { - var sliders = this.colorpicker.options.horizontal ? this.colorpicker.options.slidersHorz : this.colorpicker.options.sliders; - - var sliderClasses = []; - - for (var sliderName in sliders) { - if (!sliders.hasOwnProperty(sliderName)) { - continue; - } - - sliderClasses.push(sliders[sliderName].selector); - } - - this.colorpicker.picker.find(sliderClasses.join(', ')).on('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.pressed, this)); - } - - /** - * Unbinds any event bound by this handler - */ - - }, { - key: 'unbind', - value: function unbind() { - (0, _jquery2.default)(this.colorpicker.picker).off({ - 'mousemove.colorpicker': _jquery2.default.proxy(this.moved, this), - 'touchmove.colorpicker': _jquery2.default.proxy(this.moved, this), - 'mouseup.colorpicker': _jquery2.default.proxy(this.released, this), - 'touchend.colorpicker': _jquery2.default.proxy(this.released, this) - }); - } - - /** - * Function triggered when clicking in one of the color adjustment bars - * - * @private - * @fires Colorpicker#mousemove - * @param {Event} e - */ - - }, { - key: 'pressed', - value: function pressed(e) { - if (this.colorpicker.isDisabled()) { - return; - } - this.colorpicker.lastEvent.alias = 'pressed'; - this.colorpicker.lastEvent.e = e; - - if (!e.pageX && !e.pageY && e.originalEvent && e.originalEvent.touches) { - e.pageX = e.originalEvent.touches[0].pageX; - e.pageY = e.originalEvent.touches[0].pageY; - } - // e.stopPropagation(); - // e.preventDefault(); - - var target = (0, _jquery2.default)(e.target); - - // detect the slider and set the limits and callbacks - var zone = target.closest('div'); - - var sliders = this.colorpicker.options.horizontal ? this.colorpicker.options.slidersHorz : this.colorpicker.options.sliders; - - if (zone.is('.colorpicker')) { - return; - } - - this.currentSlider = null; - - for (var sliderName in sliders) { - if (!sliders.hasOwnProperty(sliderName)) { - continue; - } - - var slider = sliders[sliderName]; - - if (zone.is(slider.selector)) { - this.currentSlider = _jquery2.default.extend({}, slider, { name: sliderName }); - break; - } else if (slider.childSelector !== undefined && zone.is(slider.childSelector)) { - this.currentSlider = _jquery2.default.extend({}, slider, { name: sliderName }); - zone = zone.parent(); // zone.parents(slider.selector).first() ? - break; - } - } - - var guide = zone.find('.colorpicker-guide').get(0); - - if (this.currentSlider === null || guide === null) { - return; - } - - var offset = zone.offset(); - - // reference to guide's style - this.currentSlider.guideStyle = guide.style; - this.currentSlider.left = e.pageX - offset.left; - this.currentSlider.top = e.pageY - offset.top; - this.mousePointer = { - left: e.pageX, - top: e.pageY - }; - - // TODO: fix moving outside the picker makes the guides to keep moving. The event needs to be bound to the window. - /** - * (window.document) Triggered on mousedown for the document object, - * so the color adjustment guide is moved to the clicked position. - * - * @event Colorpicker#mousemove - */ - (0, _jquery2.default)(this.colorpicker.picker).on({ - 'mousemove.colorpicker': _jquery2.default.proxy(this.moved, this), - 'touchmove.colorpicker': _jquery2.default.proxy(this.moved, this), - 'mouseup.colorpicker': _jquery2.default.proxy(this.released, this), - 'touchend.colorpicker': _jquery2.default.proxy(this.released, this) - }).trigger('mousemove'); - } - - /** - * Function triggered when dragging a guide inside one of the color adjustment bars. - * - * @private - * @param {Event} e - */ - - }, { - key: 'moved', - value: function moved(e) { - this.colorpicker.lastEvent.alias = 'moved'; - this.colorpicker.lastEvent.e = e; - - if (!e.pageX && !e.pageY && e.originalEvent && e.originalEvent.touches) { - e.pageX = e.originalEvent.touches[0].pageX; - e.pageY = e.originalEvent.touches[0].pageY; - } - - // e.stopPropagation(); - e.preventDefault(); // prevents scrolling on mobile - - var left = Math.max(0, Math.min(this.currentSlider.maxLeft, this.currentSlider.left + ((e.pageX || this.mousePointer.left) - this.mousePointer.left))); - - var top = Math.max(0, Math.min(this.currentSlider.maxTop, this.currentSlider.top + ((e.pageY || this.mousePointer.top) - this.mousePointer.top))); - - this.onMove(top, left); - } - - /** - * Function triggered when releasing the click in one of the color adjustment bars. - * - * @private - * @param {Event} e - */ - - }, { - key: 'released', - value: function released(e) { - this.colorpicker.lastEvent.alias = 'released'; - this.colorpicker.lastEvent.e = e; - - // e.stopPropagation(); - // e.preventDefault(); - - (0, _jquery2.default)(this.colorpicker.picker).off({ - 'mousemove.colorpicker': this.moved, - 'touchmove.colorpicker': this.moved, - 'mouseup.colorpicker': this.released, - 'touchend.colorpicker': this.released - }); - } - }]); - - return SliderHandler; -}(); - -exports.default = SliderHandler; -module.exports = exports.default; - -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -var _options = __webpack_require__(3); - -var _options2 = _interopRequireDefault(_options); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Handles everything related to the UI of the colorpicker popup: show, hide, position,... - * @ignore - */ -var PopupHandler = function () { - /** - * @param {Colorpicker} colorpicker - * @param {Window} root - */ - function PopupHandler(colorpicker, root) { - _classCallCheck(this, PopupHandler); - - /** - * @type {Window} - */ - this.root = root; - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * @type {jQuery} - */ - this.popoverTarget = null; - /** - * @type {jQuery} - */ - this.popoverTip = null; - - /** - * If true, the latest click was inside the popover - * @type {boolean} - */ - this.clicking = false; - /** - * @type {boolean} - */ - this.hidding = false; - /** - * @type {boolean} - */ - this.showing = false; - } - - /** - * @private - * @returns {jQuery|false} - */ - - - _createClass(PopupHandler, [{ - key: 'bind', - - - /** - * Binds the different colorpicker elements to the focus/mouse/touch events so it reacts in order to show or - * hide the colorpicker popup accordingly. It also adds the proper classes. - */ - value: function bind() { - var cp = this.colorpicker; - - if (cp.options.inline) { - cp.picker.addClass('colorpicker-inline colorpicker-visible'); - return; // no need to bind show/hide events for inline elements - } - - cp.picker.addClass('colorpicker-popup colorpicker-hidden'); - - // there is no input or addon - if (!this.hasInput && !this.hasAddon) { - return; - } - - // create Bootstrap 4 popover - if (cp.options.popover) { - this.createPopover(); - } - - // bind addon show/hide events - if (this.hasAddon) { - // enable focus on addons - if (!this.addon.attr('tabindex')) { - this.addon.attr('tabindex', 0); - } - - this.addon.on({ - 'mousedown.colorpicker touchstart.colorpicker': _jquery2.default.proxy(this.toggle, this) - }); - - this.addon.on({ - 'focus.colorpicker': _jquery2.default.proxy(this.show, this) - }); - - this.addon.on({ - 'focusout.colorpicker': _jquery2.default.proxy(this.hide, this) - }); - } - - // bind input show/hide events - if (this.hasInput && !this.hasAddon) { - this.input.on({ - 'mousedown.colorpicker touchstart.colorpicker': _jquery2.default.proxy(this.show, this), - 'focus.colorpicker': _jquery2.default.proxy(this.show, this) - }); - - this.input.on({ - 'focusout.colorpicker': _jquery2.default.proxy(this.hide, this) - }); - } - - // reposition popup on window resize - (0, _jquery2.default)(this.root).on('resize.colorpicker', _jquery2.default.proxy(this.reposition, this)); - } - - /** - * Unbinds any event bound by this handler - */ - - }, { - key: 'unbind', - value: function unbind() { - if (this.hasInput) { - this.input.off({ - 'mousedown.colorpicker touchstart.colorpicker': _jquery2.default.proxy(this.show, this), - 'focus.colorpicker': _jquery2.default.proxy(this.show, this) - }); - this.input.off({ - 'focusout.colorpicker': _jquery2.default.proxy(this.hide, this) - }); - } - - if (this.hasAddon) { - this.addon.off({ - 'mousedown.colorpicker touchstart.colorpicker': _jquery2.default.proxy(this.toggle, this) - }); - this.addon.off({ - 'focus.colorpicker': _jquery2.default.proxy(this.show, this) - }); - this.addon.off({ - 'focusout.colorpicker': _jquery2.default.proxy(this.hide, this) - }); - } - - if (this.popoverTarget) { - this.popoverTarget.popover('dispose'); - } - - (0, _jquery2.default)(this.root).off('resize.colorpicker', _jquery2.default.proxy(this.reposition, this)); - (0, _jquery2.default)(this.root.document).off('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.hide, this)); - (0, _jquery2.default)(this.root.document).off('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.onClickingInside, this)); - } - }, { - key: 'isClickingInside', - value: function isClickingInside(e) { - if (!e) { - return false; - } - - return this.isOrIsInside(this.popoverTip, e.currentTarget) || this.isOrIsInside(this.popoverTip, e.target) || this.isOrIsInside(this.colorpicker.picker, e.currentTarget) || this.isOrIsInside(this.colorpicker.picker, e.target); - } - }, { - key: 'isOrIsInside', - value: function isOrIsInside(container, element) { - if (!container || !element) { - return false; - } - - element = (0, _jquery2.default)(element); - - return element.is(container) || container.find(element).length > 0; - } - }, { - key: 'onClickingInside', - value: function onClickingInside(e) { - this.clicking = this.isClickingInside(e); - } - }, { - key: 'createPopover', - value: function createPopover() { - var cp = this.colorpicker; - - this.popoverTarget = this.hasAddon ? this.addon : this.input; - - cp.picker.addClass('colorpicker-bs-popover-content'); - - this.popoverTarget.popover(_jquery2.default.extend(true, {}, _options2.default.popover, cp.options.popover, { trigger: 'manual', content: cp.picker, html: true })); - - this.popoverTip = (0, _jquery2.default)(this.popoverTarget.popover('getTipElement').data('bs.popover').tip); - this.popoverTip.addClass('colorpicker-bs-popover'); - - this.popoverTarget.on('shown.bs.popover', _jquery2.default.proxy(this.fireShow, this)); - this.popoverTarget.on('hidden.bs.popover', _jquery2.default.proxy(this.fireHide, this)); - } - - /** - * If the widget is not inside a container or inline, rearranges its position relative to its element offset. - * - * @param {Event} [e] - * @private - */ - - }, { - key: 'reposition', - value: function reposition(e) { - if (this.popoverTarget && this.isVisible()) { - this.popoverTarget.popover('update'); - } - } - - /** - * Toggles the colorpicker between visible or hidden - * - * @fires Colorpicker#colorpickerShow - * @fires Colorpicker#colorpickerHide - * @param {Event} [e] - */ - - }, { - key: 'toggle', - value: function toggle(e) { - if (this.isVisible()) { - this.hide(e); - } else { - this.show(e); - } - } - - /** - * Shows the colorpicker widget if hidden. - * - * @fires Colorpicker#colorpickerShow - * @param {Event} [e] - */ - - }, { - key: 'show', - value: function show(e) { - if (this.isVisible() || this.showing || this.hidding) { - return; - } - - this.showing = true; - this.hidding = false; - this.clicking = false; - - var cp = this.colorpicker; - - cp.lastEvent.alias = 'show'; - cp.lastEvent.e = e; - - // Prevent showing browser native HTML5 colorpicker - if (e && (!this.hasInput || this.input.attr('type') === 'color') && e && e.preventDefault) { - e.stopPropagation(); - e.preventDefault(); - } - - // If it's a popover, add event to the document to hide the picker when clicking outside of it - if (this.isPopover) { - (0, _jquery2.default)(this.root).on('resize.colorpicker', _jquery2.default.proxy(this.reposition, this)); - } - - // add visible class before popover is shown - cp.picker.addClass('colorpicker-visible').removeClass('colorpicker-hidden'); - - if (this.popoverTarget) { - this.popoverTarget.popover('show'); - } else { - this.fireShow(); - } - } - }, { - key: 'fireShow', - value: function fireShow() { - this.hidding = false; - this.showing = false; - - if (this.isPopover) { - // Add event to hide on outside click - (0, _jquery2.default)(this.root.document).on('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.hide, this)); - (0, _jquery2.default)(this.root.document).on('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.onClickingInside, this)); - } - - /** - * (Colorpicker) When show() is called and the widget can be shown. - * - * @event Colorpicker#colorpickerShow - */ - this.colorpicker.trigger('colorpickerShow'); - } - - /** - * Hides the colorpicker widget. - * Hide is prevented when it is triggered by an event whose target element has been clicked/touched. - * - * @fires Colorpicker#colorpickerHide - * @param {Event} [e] - */ - - }, { - key: 'hide', - value: function hide(e) { - if (this.isHidden() || this.showing || this.hidding) { - return; - } - - var cp = this.colorpicker, - clicking = this.clicking || this.isClickingInside(e); - - this.hidding = true; - this.showing = false; - this.clicking = false; - - cp.lastEvent.alias = 'hide'; - cp.lastEvent.e = e; - - // TODO: fix having to click twice outside when losing focus and last 2 clicks where inside the colorpicker - - // Prevent hide if triggered by an event and an element inside the colorpicker has been clicked/touched - if (clicking) { - this.hidding = false; - return; - } - - if (this.popoverTarget) { - this.popoverTarget.popover('hide'); - } else { - this.fireHide(); - } - } - }, { - key: 'fireHide', - value: function fireHide() { - this.hidding = false; - this.showing = false; - - var cp = this.colorpicker; - - // add hidden class after popover is hidden - cp.picker.addClass('colorpicker-hidden').removeClass('colorpicker-visible'); - - // Unbind window and document events, since there is no need to keep them while the popup is hidden - (0, _jquery2.default)(this.root).off('resize.colorpicker', _jquery2.default.proxy(this.reposition, this)); - (0, _jquery2.default)(this.root.document).off('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.hide, this)); - (0, _jquery2.default)(this.root.document).off('mousedown.colorpicker touchstart.colorpicker', _jquery2.default.proxy(this.onClickingInside, this)); - - /** - * (Colorpicker) When hide() is called and the widget can be hidden. - * - * @event Colorpicker#colorpickerHide - */ - cp.trigger('colorpickerHide'); - } - }, { - key: 'focus', - value: function focus() { - if (this.hasAddon) { - return this.addon.focus(); - } - if (this.hasInput) { - return this.input.focus(); - } - return false; - } - - /** - * Returns true if the colorpicker element has the colorpicker-visible class and not the colorpicker-hidden one. - * False otherwise. - * - * @returns {boolean} - */ - - }, { - key: 'isVisible', - value: function isVisible() { - return this.colorpicker.picker.hasClass('colorpicker-visible') && !this.colorpicker.picker.hasClass('colorpicker-hidden'); - } - - /** - * Returns true if the colorpicker element has the colorpicker-hidden class and not the colorpicker-visible one. - * False otherwise. - * - * @returns {boolean} - */ - - }, { - key: 'isHidden', - value: function isHidden() { - return this.colorpicker.picker.hasClass('colorpicker-hidden') && !this.colorpicker.picker.hasClass('colorpicker-visible'); - } - }, { - key: 'input', - get: function get() { - return this.colorpicker.inputHandler.input; - } - - /** - * @private - * @returns {boolean} - */ - - }, { - key: 'hasInput', - get: function get() { - return this.colorpicker.inputHandler.hasInput(); - } - - /** - * @private - * @returns {jQuery|false} - */ - - }, { - key: 'addon', - get: function get() { - return this.colorpicker.addonHandler.addon; - } - - /** - * @private - * @returns {boolean} - */ - - }, { - key: 'hasAddon', - get: function get() { - return this.colorpicker.addonHandler.hasAddon(); - } - - /** - * @private - * @returns {boolean} - */ - - }, { - key: 'isPopover', - get: function get() { - return !this.colorpicker.options.inline && !!this.popoverTip; - } - }]); - - return PopupHandler; -}(); - -exports.default = PopupHandler; -module.exports = exports.default; - -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -var _ColorItem = __webpack_require__(2); - -var _ColorItem2 = _interopRequireDefault(_ColorItem); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Handles everything related to the colorpicker input - * @ignore - */ -var InputHandler = function () { - /** - * @param {Colorpicker} colorpicker - */ - function InputHandler(colorpicker) { - _classCallCheck(this, InputHandler); - - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * @type {jQuery|false} - */ - this.input = this.colorpicker.element.is('input') ? this.colorpicker.element : this.colorpicker.options.input ? this.colorpicker.element.find(this.colorpicker.options.input) : false; - - if (this.input && this.input.length === 0) { - this.input = false; - } - - this._initValue(); - } - - _createClass(InputHandler, [{ - key: 'bind', - value: function bind() { - if (!this.hasInput()) { - return; - } - this.input.on({ - 'keyup.colorpicker': _jquery2.default.proxy(this.onkeyup, this) - }); - this.input.on({ - 'change.colorpicker': _jquery2.default.proxy(this.onchange, this) - }); - } - }, { - key: 'unbind', - value: function unbind() { - if (!this.hasInput()) { - return; - } - this.input.off('.colorpicker'); - } - }, { - key: '_initValue', - value: function _initValue() { - if (!this.hasInput()) { - return; - } - - var val = ''; - - [ - // candidates: - this.input.val(), this.input.data('color'), this.input.attr('data-color')].map(function (item) { - if (item && val === '') { - val = item; - } - }); - - if (val instanceof _ColorItem2.default) { - val = this.getFormattedColor(val.string(this.colorpicker.format)); - } else if (!(typeof val === 'string' || val instanceof String)) { - val = ''; - } - - this.input.prop('value', val); - } - - /** - * Returns the color string from the input value. - * If there is no input the return value is false. - * - * @returns {String|boolean} - */ - - }, { - key: 'getValue', - value: function getValue() { - if (!this.hasInput()) { - return false; - } - - return this.input.val(); - } - - /** - * If the input element is present, it updates the value with the current color object color string. - * If the value is changed, this method fires a "change" event on the input element. - * - * @param {String} val - * - * @fires Colorpicker#change - */ - - }, { - key: 'setValue', - value: function setValue(val) { - if (!this.hasInput()) { - return; - } - - var inputVal = this.input.prop('value'); - - val = val ? val : ''; - - if (val === (inputVal ? inputVal : '')) { - // No need to set value or trigger any event if nothing changed - return; - } - - this.input.prop('value', val); - - /** - * (Input) Triggered on the input element when a new color is selected. - * - * @event Colorpicker#change - */ - this.input.trigger({ - type: 'change', - colorpicker: this.colorpicker, - color: this.colorpicker.color, - value: val - }); - } - - /** - * Returns the formatted color string, with the formatting options applied - * (e.g. useHashPrefix) - * - * @param {String|null} val - * - * @returns {String} - */ - - }, { - key: 'getFormattedColor', - value: function getFormattedColor() { - var val = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - - val = val ? val : this.colorpicker.colorHandler.getColorString(); - - if (!val) { - return ''; - } - - val = this.colorpicker.colorHandler.resolveColorDelegate(val, false); - - if (this.colorpicker.options.useHashPrefix === false) { - val = val.replace(/^#/g, ''); - } - - return val; - } - - /** - * Returns true if the widget has an associated input element, false otherwise - * @returns {boolean} - */ - - }, { - key: 'hasInput', - value: function hasInput() { - return this.input !== false; - } - - /** - * Returns true if the input exists and is disabled - * @returns {boolean} - */ - - }, { - key: 'isEnabled', - value: function isEnabled() { - return this.hasInput() && !this.isDisabled(); - } - - /** - * Returns true if the input exists and is disabled - * @returns {boolean} - */ - - }, { - key: 'isDisabled', - value: function isDisabled() { - return this.hasInput() && this.input.prop('disabled') === true; - } - - /** - * Disables the input if any - * - * @fires Colorpicker#colorpickerDisable - * @returns {boolean} - */ - - }, { - key: 'disable', - value: function disable() { - if (this.hasInput()) { - this.input.prop('disabled', true); - } - } - - /** - * Enables the input if any - * - * @fires Colorpicker#colorpickerEnable - * @returns {boolean} - */ - - }, { - key: 'enable', - value: function enable() { - if (this.hasInput()) { - this.input.prop('disabled', false); - } - } - - /** - * Calls setValue with the current internal color value - * - * @fires Colorpicker#change - */ - - }, { - key: 'update', - value: function update() { - if (!this.hasInput()) { - return; - } - - if (this.colorpicker.options.autoInputFallback === false && this.colorpicker.colorHandler.isInvalidColor()) { - // prevent update if color is invalid, autoInputFallback is disabled and the last event is keyup. - return; - } - - this.setValue(this.getFormattedColor()); - } - - /** - * Function triggered when the input has changed, so the colorpicker gets updated. - * - * @private - * @param {Event} e - * @returns {boolean} - */ - - }, { - key: 'onchange', - value: function onchange(e) { - this.colorpicker.lastEvent.alias = 'input.change'; - this.colorpicker.lastEvent.e = e; - - var val = this.getValue(); - - if (val !== e.value) { - this.colorpicker.setValue(val); - } - } - - /** - * Function triggered after a keyboard key has been released. - * - * @private - * @param {Event} e - * @returns {boolean} - */ - - }, { - key: 'onkeyup', - value: function onkeyup(e) { - this.colorpicker.lastEvent.alias = 'input.keyup'; - this.colorpicker.lastEvent.e = e; - - var val = this.getValue(); - - if (val !== e.value) { - this.colorpicker.setValue(val); - } - } - }]); - - return InputHandler; -}(); - -exports.default = InputHandler; -module.exports = exports.default; - -/***/ }), -/* 16 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var colorString = __webpack_require__(17); -var convert = __webpack_require__(20); - -var _slice = [].slice; - -var skippedModels = [ - // to be honest, I don't really feel like keyword belongs in color convert, but eh. - 'keyword', - - // gray conflicts with some method names, and has its own method defined. - 'gray', - - // shouldn't really be in color-convert either... - 'hex' -]; - -var hashedModelKeys = {}; -Object.keys(convert).forEach(function (model) { - hashedModelKeys[_slice.call(convert[model].labels).sort().join('')] = model; -}); - -var limiters = {}; - -function Color(obj, model) { - if (!(this instanceof Color)) { - return new Color(obj, model); - } - - if (model && model in skippedModels) { - model = null; - } - - if (model && !(model in convert)) { - throw new Error('Unknown model: ' + model); - } - - var i; - var channels; - - if (obj == null) { // eslint-disable-line no-eq-null,eqeqeq - this.model = 'rgb'; - this.color = [0, 0, 0]; - this.valpha = 1; - } else if (obj instanceof Color) { - this.model = obj.model; - this.color = obj.color.slice(); - this.valpha = obj.valpha; - } else if (typeof obj === 'string') { - var result = colorString.get(obj); - if (result === null) { - throw new Error('Unable to parse color from string: ' + obj); - } - - this.model = result.model; - channels = convert[this.model].channels; - this.color = result.value.slice(0, channels); - this.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1; - } else if (obj.length) { - this.model = model || 'rgb'; - channels = convert[this.model].channels; - var newArr = _slice.call(obj, 0, channels); - this.color = zeroArray(newArr, channels); - this.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1; - } else if (typeof obj === 'number') { - // this is always RGB - can be converted later on. - obj &= 0xFFFFFF; - this.model = 'rgb'; - this.color = [ - (obj >> 16) & 0xFF, - (obj >> 8) & 0xFF, - obj & 0xFF - ]; - this.valpha = 1; - } else { - this.valpha = 1; - - var keys = Object.keys(obj); - if ('alpha' in obj) { - keys.splice(keys.indexOf('alpha'), 1); - this.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0; - } - - var hashedKeys = keys.sort().join(''); - if (!(hashedKeys in hashedModelKeys)) { - throw new Error('Unable to parse color from object: ' + JSON.stringify(obj)); - } - - this.model = hashedModelKeys[hashedKeys]; - - var labels = convert[this.model].labels; - var color = []; - for (i = 0; i < labels.length; i++) { - color.push(obj[labels[i]]); - } - - this.color = zeroArray(color); - } - - // perform limitations (clamping, etc.) - if (limiters[this.model]) { - channels = convert[this.model].channels; - for (i = 0; i < channels; i++) { - var limit = limiters[this.model][i]; - if (limit) { - this.color[i] = limit(this.color[i]); - } - } - } - - this.valpha = Math.max(0, Math.min(1, this.valpha)); - - if (Object.freeze) { - Object.freeze(this); - } -} - -Color.prototype = { - toString: function () { - return this.string(); - }, - - toJSON: function () { - return this[this.model](); - }, - - string: function (places) { - var self = this.model in colorString.to ? this : this.rgb(); - self = self.round(typeof places === 'number' ? places : 1); - var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha); - return colorString.to[self.model](args); - }, - - percentString: function (places) { - var self = this.rgb().round(typeof places === 'number' ? places : 1); - var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha); - return colorString.to.rgb.percent(args); - }, - - array: function () { - return this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha); - }, - - object: function () { - var result = {}; - var channels = convert[this.model].channels; - var labels = convert[this.model].labels; - - for (var i = 0; i < channels; i++) { - result[labels[i]] = this.color[i]; - } - - if (this.valpha !== 1) { - result.alpha = this.valpha; - } - - return result; - }, - - unitArray: function () { - var rgb = this.rgb().color; - rgb[0] /= 255; - rgb[1] /= 255; - rgb[2] /= 255; - - if (this.valpha !== 1) { - rgb.push(this.valpha); - } - - return rgb; - }, - - unitObject: function () { - var rgb = this.rgb().object(); - rgb.r /= 255; - rgb.g /= 255; - rgb.b /= 255; - - if (this.valpha !== 1) { - rgb.alpha = this.valpha; - } - - return rgb; - }, - - round: function (places) { - places = Math.max(places || 0, 0); - return new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model); - }, - - alpha: function (val) { - if (arguments.length) { - return new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model); - } - - return this.valpha; - }, - - // rgb - red: getset('rgb', 0, maxfn(255)), - green: getset('rgb', 1, maxfn(255)), - blue: getset('rgb', 2, maxfn(255)), - - hue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style - - saturationl: getset('hsl', 1, maxfn(100)), - lightness: getset('hsl', 2, maxfn(100)), - - saturationv: getset('hsv', 1, maxfn(100)), - value: getset('hsv', 2, maxfn(100)), - - chroma: getset('hcg', 1, maxfn(100)), - gray: getset('hcg', 2, maxfn(100)), - - white: getset('hwb', 1, maxfn(100)), - wblack: getset('hwb', 2, maxfn(100)), - - cyan: getset('cmyk', 0, maxfn(100)), - magenta: getset('cmyk', 1, maxfn(100)), - yellow: getset('cmyk', 2, maxfn(100)), - black: getset('cmyk', 3, maxfn(100)), - - x: getset('xyz', 0, maxfn(100)), - y: getset('xyz', 1, maxfn(100)), - z: getset('xyz', 2, maxfn(100)), - - l: getset('lab', 0, maxfn(100)), - a: getset('lab', 1), - b: getset('lab', 2), - - keyword: function (val) { - if (arguments.length) { - return new Color(val); - } - - return convert[this.model].keyword(this.color); - }, - - hex: function (val) { - if (arguments.length) { - return new Color(val); - } - - return colorString.to.hex(this.rgb().round().color); - }, - - rgbNumber: function () { - var rgb = this.rgb().color; - return ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF); - }, - - luminosity: function () { - // http://www.w3.org/TR/WCAG20/#relativeluminancedef - var rgb = this.rgb().color; - - var lum = []; - for (var i = 0; i < rgb.length; i++) { - var chan = rgb[i] / 255; - lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); - } - - return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; - }, - - contrast: function (color2) { - // http://www.w3.org/TR/WCAG20/#contrast-ratiodef - var lum1 = this.luminosity(); - var lum2 = color2.luminosity(); - - if (lum1 > lum2) { - return (lum1 + 0.05) / (lum2 + 0.05); - } - - return (lum2 + 0.05) / (lum1 + 0.05); - }, - - level: function (color2) { - var contrastRatio = this.contrast(color2); - if (contrastRatio >= 7.1) { - return 'AAA'; - } - - return (contrastRatio >= 4.5) ? 'AA' : ''; - }, - - isDark: function () { - // YIQ equation from http://24ways.org/2010/calculating-color-contrast - var rgb = this.rgb().color; - var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; - return yiq < 128; - }, - - isLight: function () { - return !this.isDark(); - }, - - negate: function () { - var rgb = this.rgb(); - for (var i = 0; i < 3; i++) { - rgb.color[i] = 255 - rgb.color[i]; - } - return rgb; - }, - - lighten: function (ratio) { - var hsl = this.hsl(); - hsl.color[2] += hsl.color[2] * ratio; - return hsl; - }, - - darken: function (ratio) { - var hsl = this.hsl(); - hsl.color[2] -= hsl.color[2] * ratio; - return hsl; - }, - - saturate: function (ratio) { - var hsl = this.hsl(); - hsl.color[1] += hsl.color[1] * ratio; - return hsl; - }, - - desaturate: function (ratio) { - var hsl = this.hsl(); - hsl.color[1] -= hsl.color[1] * ratio; - return hsl; - }, - - whiten: function (ratio) { - var hwb = this.hwb(); - hwb.color[1] += hwb.color[1] * ratio; - return hwb; - }, - - blacken: function (ratio) { - var hwb = this.hwb(); - hwb.color[2] += hwb.color[2] * ratio; - return hwb; - }, - - grayscale: function () { - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - var rgb = this.rgb().color; - var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; - return Color.rgb(val, val, val); - }, - - fade: function (ratio) { - return this.alpha(this.valpha - (this.valpha * ratio)); - }, - - opaquer: function (ratio) { - return this.alpha(this.valpha + (this.valpha * ratio)); - }, - - rotate: function (degrees) { - var hsl = this.hsl(); - var hue = hsl.color[0]; - hue = (hue + degrees) % 360; - hue = hue < 0 ? 360 + hue : hue; - hsl.color[0] = hue; - return hsl; - }, - - mix: function (mixinColor, weight) { - // ported from sass implementation in C - // https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 - if (!mixinColor || !mixinColor.rgb) { - throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor); - } - var color1 = mixinColor.rgb(); - var color2 = this.rgb(); - var p = weight === undefined ? 0.5 : weight; - - var w = 2 * p - 1; - var a = color1.alpha() - color2.alpha(); - - var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - return Color.rgb( - w1 * color1.red() + w2 * color2.red(), - w1 * color1.green() + w2 * color2.green(), - w1 * color1.blue() + w2 * color2.blue(), - color1.alpha() * p + color2.alpha() * (1 - p)); - } -}; - -// model conversion methods and static constructors -Object.keys(convert).forEach(function (model) { - if (skippedModels.indexOf(model) !== -1) { - return; - } - - var channels = convert[model].channels; - - // conversion methods - Color.prototype[model] = function () { - if (this.model === model) { - return new Color(this); - } - - if (arguments.length) { - return new Color(arguments, model); - } - - var newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha; - return new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha), model); - }; - - // 'static' construction methods - Color[model] = function (color) { - if (typeof color === 'number') { - color = zeroArray(_slice.call(arguments), channels); - } - return new Color(color, model); - }; -}); - -function roundTo(num, places) { - return Number(num.toFixed(places)); -} - -function roundToPlace(places) { - return function (num) { - return roundTo(num, places); - }; -} - -function getset(model, channel, modifier) { - model = Array.isArray(model) ? model : [model]; - - model.forEach(function (m) { - (limiters[m] || (limiters[m] = []))[channel] = modifier; - }); - - model = model[0]; - - return function (val) { - var result; - - if (arguments.length) { - if (modifier) { - val = modifier(val); - } - - result = this[model](); - result.color[channel] = val; - return result; - } - - result = this[model]().color[channel]; - if (modifier) { - result = modifier(result); - } - - return result; - }; -} - -function maxfn(max) { - return function (v) { - return Math.max(0, Math.min(max, v)); - }; -} - -function assertArray(val) { - return Array.isArray(val) ? val : [val]; -} - -function zeroArray(arr, length) { - for (var i = 0; i < length; i++) { - if (typeof arr[i] !== 'number') { - arr[i] = 0; - } - } - - return arr; -} - -module.exports = Color; - - -/***/ }), -/* 17 */ -/***/ (function(module, exports, __webpack_require__) { - -/* MIT license */ -var colorNames = __webpack_require__(5); -var swizzle = __webpack_require__(18); - -var reverseNames = {}; - -// create a list of reverse color names -for (var name in colorNames) { - if (colorNames.hasOwnProperty(name)) { - reverseNames[colorNames[name]] = name; - } -} - -var cs = module.exports = { - to: {}, - get: {} -}; - -cs.get = function (string) { - var prefix = string.substring(0, 3).toLowerCase(); - var val; - var model; - switch (prefix) { - case 'hsl': - val = cs.get.hsl(string); - model = 'hsl'; - break; - case 'hwb': - val = cs.get.hwb(string); - model = 'hwb'; - break; - default: - val = cs.get.rgb(string); - model = 'rgb'; - break; - } - - if (!val) { - return null; - } - - return {model: model, value: val}; -}; - -cs.get.rgb = function (string) { - if (!string) { - return null; - } - - var abbr = /^#([a-f0-9]{3,4})$/i; - var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i; - var rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; - var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; - var keyword = /(\D+)/; - - var rgb = [0, 0, 0, 1]; - var match; - var i; - var hexAlpha; - - if (match = string.match(hex)) { - hexAlpha = match[2]; - match = match[1]; - - for (i = 0; i < 3; i++) { - // https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19 - var i2 = i * 2; - rgb[i] = parseInt(match.slice(i2, i2 + 2), 16); - } - - if (hexAlpha) { - rgb[3] = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100; - } - } else if (match = string.match(abbr)) { - match = match[1]; - hexAlpha = match[3]; - - for (i = 0; i < 3; i++) { - rgb[i] = parseInt(match[i] + match[i], 16); - } - - if (hexAlpha) { - rgb[3] = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100; - } - } else if (match = string.match(rgba)) { - for (i = 0; i < 3; i++) { - rgb[i] = parseInt(match[i + 1], 0); - } - - if (match[4]) { - rgb[3] = parseFloat(match[4]); - } - } else if (match = string.match(per)) { - for (i = 0; i < 3; i++) { - rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); - } - - if (match[4]) { - rgb[3] = parseFloat(match[4]); - } - } else if (match = string.match(keyword)) { - if (match[1] === 'transparent') { - return [0, 0, 0, 0]; - } - - rgb = colorNames[match[1]]; - - if (!rgb) { - return null; - } - - rgb[3] = 1; - - return rgb; - } else { - return null; - } - - for (i = 0; i < 3; i++) { - rgb[i] = clamp(rgb[i], 0, 255); - } - rgb[3] = clamp(rgb[3], 0, 1); - - return rgb; -}; - -cs.get.hsl = function (string) { - if (!string) { - return null; - } - - var hsl = /^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; - var match = string.match(hsl); - - if (match) { - var alpha = parseFloat(match[4]); - var h = (parseFloat(match[1]) + 360) % 360; - var s = clamp(parseFloat(match[2]), 0, 100); - var l = clamp(parseFloat(match[3]), 0, 100); - var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1); - - return [h, s, l, a]; - } - - return null; -}; - -cs.get.hwb = function (string) { - if (!string) { - return null; - } - - var hwb = /^hwb\(\s*([+-]?\d*[\.]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/; - var match = string.match(hwb); - - if (match) { - var alpha = parseFloat(match[4]); - var h = ((parseFloat(match[1]) % 360) + 360) % 360; - var w = clamp(parseFloat(match[2]), 0, 100); - var b = clamp(parseFloat(match[3]), 0, 100); - var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, w, b, a]; - } - - return null; -}; - -cs.to.hex = function () { - var rgba = swizzle(arguments); - - return ( - '#' + - hexDouble(rgba[0]) + - hexDouble(rgba[1]) + - hexDouble(rgba[2]) + - (rgba[3] < 1 - ? (hexDouble(Math.round(rgba[3] * 255))) - : '') - ); -}; - -cs.to.rgb = function () { - var rgba = swizzle(arguments); - - return rgba.length < 4 || rgba[3] === 1 - ? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')' - : 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')'; -}; - -cs.to.rgb.percent = function () { - var rgba = swizzle(arguments); - - var r = Math.round(rgba[0] / 255 * 100); - var g = Math.round(rgba[1] / 255 * 100); - var b = Math.round(rgba[2] / 255 * 100); - - return rgba.length < 4 || rgba[3] === 1 - ? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)' - : 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')'; -}; - -cs.to.hsl = function () { - var hsla = swizzle(arguments); - return hsla.length < 4 || hsla[3] === 1 - ? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)' - : 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')'; -}; - -// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax -// (hwb have alpha optional & 1 is default value) -cs.to.hwb = function () { - var hwba = swizzle(arguments); - - var a = ''; - if (hwba.length >= 4 && hwba[3] !== 1) { - a = ', ' + hwba[3]; - } - - return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')'; -}; - -cs.to.keyword = function (rgb) { - return reverseNames[rgb.slice(0, 3)]; -}; - -// helpers -function clamp(num, min, max) { - return Math.min(Math.max(min, num), max); -} - -function hexDouble(num) { - var str = num.toString(16).toUpperCase(); - return (str.length < 2) ? '0' + str : str; -} - - -/***/ }), -/* 18 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isArrayish = __webpack_require__(19); - -var concat = Array.prototype.concat; -var slice = Array.prototype.slice; - -var swizzle = module.exports = function swizzle(args) { - var results = []; - - for (var i = 0, len = args.length; i < len; i++) { - var arg = args[i]; - - if (isArrayish(arg)) { - // http://jsperf.com/javascript-array-concat-vs-push/98 - results = concat.call(results, slice.call(arg)); - } else { - results.push(arg); - } - } - - return results; -}; - -swizzle.wrap = function (fn) { - return function () { - return fn(swizzle(arguments)); - }; -}; - - -/***/ }), -/* 19 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = function isArrayish(obj) { - if (!obj) { - return false; - } - - return obj instanceof Array || Array.isArray(obj) || - (obj.length >= 0 && obj.splice instanceof Function); -}; - - -/***/ }), -/* 20 */ -/***/ (function(module, exports, __webpack_require__) { - -var conversions = __webpack_require__(6); -var route = __webpack_require__(21); - -var convert = {}; - -var models = Object.keys(conversions); - -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - return fn(args); - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - var result = fn(args); - - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } - - return result; - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -models.forEach(function (fromModel) { - convert[fromModel] = {}; - - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - - var routes = route(fromModel); - var routeModels = Object.keys(routes); - - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; - - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); - -module.exports = convert; - - -/***/ }), -/* 21 */ -/***/ (function(module, exports, __webpack_require__) { - -var conversions = __webpack_require__(6); - -/* - this function routes a model to all other models. - - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). - - conversions that are not possible simply are not included. -*/ - -function buildGraph() { - var graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); - - for (var len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; - } - - return graph; -} - -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop - - graph[fromModel].distance = 0; - - while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); - - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; - - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } - - return graph; -} - -function link(from, to) { - return function (args) { - return to(from(args)); - }; -} - -function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; - - var cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; - } - - fn.conversion = path; - return fn; -} - -module.exports = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; - - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; - - if (node.parent === null) { - // no possible conversion, or this node is the source model. - continue; - } - - conversion[toModel] = wrapConversion(toModel, graph); - } - - return conversion; -}; - - - -/***/ }), -/* 22 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -var _ColorItem = __webpack_require__(2); - -var _ColorItem2 = _interopRequireDefault(_ColorItem); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Handles everything related to the colorpicker color - * @ignore - */ -var ColorHandler = function () { - /** - * @param {Colorpicker} colorpicker - */ - function ColorHandler(colorpicker) { - _classCallCheck(this, ColorHandler); - - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - } - - /** - * @returns {*|String|ColorItem} - */ - - - _createClass(ColorHandler, [{ - key: 'bind', - value: function bind() { - // if the color option is set - if (this.colorpicker.options.color) { - this.color = this.createColor(this.colorpicker.options.color); - return; - } - - // if element[color] is empty and the input has a value - if (!this.color && !!this.colorpicker.inputHandler.getValue()) { - this.color = this.createColor(this.colorpicker.inputHandler.getValue(), this.colorpicker.options.autoInputFallback); - } - } - }, { - key: 'unbind', - value: function unbind() { - this.colorpicker.element.removeData('color'); - } - - /** - * Returns the color string from the input value or the 'data-color' attribute of the input or element. - * If empty, it returns the defaultValue parameter. - * - * @returns {String|*} - */ - - }, { - key: 'getColorString', - value: function getColorString() { - if (!this.hasColor()) { - return ''; - } - - return this.color.string(this.format); - } - - /** - * Sets the color value - * - * @param {String|ColorItem} val - */ - - }, { - key: 'setColorString', - value: function setColorString(val) { - var color = val ? this.createColor(val) : null; - - this.color = color ? color : null; - } - - /** - * Creates a new color using the widget instance options (fallbackColor, format). - * - * @fires Colorpicker#colorpickerInvalid - * @param {*} val - * @param {boolean} fallbackOnInvalid - * @returns {ColorItem} - */ - - }, { - key: 'createColor', - value: function createColor(val) { - var fallbackOnInvalid = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - var color = new _ColorItem2.default(this.resolveColorDelegate(val), this.format); - - if (!color.isValid()) { - if (fallbackOnInvalid) { - color = this.getFallbackColor(); - } - - /** - * (Colorpicker) Fired when the color is invalid and the fallback color is going to be used. - * - * @event Colorpicker#colorpickerInvalid - */ - this.colorpicker.trigger('colorpickerInvalid', color, val); - } - - if (!this.isAlphaEnabled()) { - // Alpha is disabled - color.alpha = 1; - } - - return color; - } - }, { - key: 'getFallbackColor', - value: function getFallbackColor() { - if (this.fallback && this.fallback === this.color) { - return this.color; - } - - var fallback = this.resolveColorDelegate(this.fallback); - - var color = new _ColorItem2.default(fallback, this.format); - - if (!color.isValid()) { - console.warn('The fallback color is invalid. Falling back to the previous color or black if any.'); - return this.color ? this.color : new _ColorItem2.default('#000000', this.format); - } - - return color; - } - - /** - * @returns {ColorItem} - */ - - }, { - key: 'assureColor', - value: function assureColor() { - if (!this.hasColor()) { - this.color = this.getFallbackColor(); - } - - return this.color; - } - - /** - * Delegates the color resolution to the colorpicker extensions. - * - * @param {String|*} color - * @param {boolean} realColor if true, the color should resolve into a real (not named) color code - * @returns {ColorItem|String|*|null} - */ - - }, { - key: 'resolveColorDelegate', - value: function resolveColorDelegate(color) { - var realColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - - var extResolvedColor = false; - - _jquery2.default.each(this.colorpicker.extensions, function (name, ext) { - if (extResolvedColor !== false) { - // skip if resolved - return; - } - extResolvedColor = ext.resolveColor(color, realColor); - }); - - return extResolvedColor ? extResolvedColor : color; - } - - /** - * Checks if there is a color object, that it is valid and it is not a fallback - * @returns {boolean} - */ - - }, { - key: 'isInvalidColor', - value: function isInvalidColor() { - return !this.hasColor() || !this.color.isValid(); - } - - /** - * Returns true if the useAlpha option is exactly true, false otherwise - * @returns {boolean} - */ - - }, { - key: 'isAlphaEnabled', - value: function isAlphaEnabled() { - return this.colorpicker.options.useAlpha !== false; - } - - /** - * Returns true if the current color object is an instance of Color, false otherwise. - * @returns {boolean} - */ - - }, { - key: 'hasColor', - value: function hasColor() { - return this.color instanceof _ColorItem2.default; - } - }, { - key: 'fallback', - get: function get() { - return this.colorpicker.options.fallbackColor ? this.colorpicker.options.fallbackColor : this.hasColor() ? this.color : null; - } - - /** - * @returns {String|null} - */ - - }, { - key: 'format', - get: function get() { - if (this.colorpicker.options.format) { - return this.colorpicker.options.format; - } - - if (this.hasColor() && this.color.hasTransparency() && this.color.format.match(/^hex/)) { - return this.isAlphaEnabled() ? 'rgba' : 'hex'; - } - - if (this.hasColor()) { - return this.color.format; - } - - return 'rgb'; - } - - /** - * Internal color getter - * - * @type {ColorItem|null} - */ - - }, { - key: 'color', - get: function get() { - return this.colorpicker.element.data('color'); - } - - /** - * Internal color setter - * - * @ignore - * @param {ColorItem|null} value - */ - , - set: function set(value) { - this.colorpicker.element.data('color', value); - - if (value instanceof _ColorItem2.default && this.colorpicker.options.format === 'auto') { - // If format is 'auto', use the first parsed one from now on - this.colorpicker.options.format = this.color.format; - } - } - }]); - - return ColorHandler; -}(); - -exports.default = ColorHandler; -module.exports = exports.default; - -/***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -var _jquery = __webpack_require__(0); - -var _jquery2 = _interopRequireDefault(_jquery); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -/** - * Handles everything related to the colorpicker UI - * @ignore - */ -var PickerHandler = function () { - /** - * @param {Colorpicker} colorpicker - */ - function PickerHandler(colorpicker) { - _classCallCheck(this, PickerHandler); - - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * @type {jQuery} - */ - this.picker = null; - } - - _createClass(PickerHandler, [{ - key: 'bind', - value: function bind() { - /** - * @type {jQuery|HTMLElement} - */ - var picker = this.picker = (0, _jquery2.default)(this.options.template); - - if (this.options.customClass) { - picker.addClass(this.options.customClass); - } - - if (this.options.horizontal) { - picker.addClass('colorpicker-horizontal'); - } - - if (this._supportsAlphaBar()) { - this.options.useAlpha = true; - picker.addClass('colorpicker-with-alpha'); - } else { - this.options.useAlpha = false; - } - } - }, { - key: 'attach', - value: function attach() { - // Inject the colorpicker element into the DOM - var pickerParent = this.colorpicker.container ? this.colorpicker.container : null; - - if (pickerParent) { - this.picker.appendTo(pickerParent); - } - } - }, { - key: 'unbind', - value: function unbind() { - this.picker.remove(); - } - }, { - key: '_supportsAlphaBar', - value: function _supportsAlphaBar() { - return (this.options.useAlpha || this.colorpicker.colorHandler.hasColor() && this.color.hasTransparency()) && this.options.useAlpha !== false && (!this.options.format || this.options.format && !this.options.format.match(/^hex([36])?$/i)); - } - - /** - * Changes the color adjustment bars using the current color object information. - */ - - }, { - key: 'update', - value: function update() { - if (!this.colorpicker.colorHandler.hasColor()) { - return; - } - - var vertical = this.options.horizontal !== true, - slider = vertical ? this.options.sliders : this.options.slidersHorz; - - var saturationGuide = this.picker.find('.colorpicker-saturation .colorpicker-guide'), - hueGuide = this.picker.find('.colorpicker-hue .colorpicker-guide'), - alphaGuide = this.picker.find('.colorpicker-alpha .colorpicker-guide'); - - var hsva = this.color.toHsvaRatio(); - - // Set guides position - if (hueGuide.length) { - hueGuide.css(vertical ? 'top' : 'left', (vertical ? slider.hue.maxTop : slider.hue.maxLeft) * (1 - hsva.h)); - } - if (alphaGuide.length) { - alphaGuide.css(vertical ? 'top' : 'left', (vertical ? slider.alpha.maxTop : slider.alpha.maxLeft) * (1 - hsva.a)); - } - if (saturationGuide.length) { - saturationGuide.css({ - 'top': slider.saturation.maxTop - hsva.v * slider.saturation.maxTop, - 'left': hsva.s * slider.saturation.maxLeft - }); - } - - // Set saturation hue background - this.picker.find('.colorpicker-saturation').css('backgroundColor', this.color.getCloneHueOnly().toHexString()); // we only need hue - - // Set alpha color gradient - var hexColor = this.color.toHexString(); - - var alphaBg = ''; - - if (this.options.horizontal) { - alphaBg = 'linear-gradient(to right, ' + hexColor + ' 0%, transparent 100%)'; - } else { - alphaBg = 'linear-gradient(to bottom, ' + hexColor + ' 0%, transparent 100%)'; - } - - this.picker.find('.colorpicker-alpha-color').css('background', alphaBg); - } - }, { - key: 'options', - get: function get() { - return this.colorpicker.options; - } - }, { - key: 'color', - get: function get() { - return this.colorpicker.colorHandler.color; - } - }]); - - return PickerHandler; -}(); - -exports.default = PickerHandler; -module.exports = exports.default; - -/***/ }), -/* 24 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -/** - * Handles everything related to the colorpicker addon - * @ignore - */ - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var AddonHandler = function () { - /** - * @param {Colorpicker} colorpicker - */ - function AddonHandler(colorpicker) { - _classCallCheck(this, AddonHandler); - - /** - * @type {Colorpicker} - */ - this.colorpicker = colorpicker; - /** - * @type {jQuery} - */ - this.addon = null; - } - - _createClass(AddonHandler, [{ - key: 'hasAddon', - value: function hasAddon() { - return !!this.addon; - } - }, { - key: 'bind', - value: function bind() { - /** - * @type {*|jQuery} - */ - this.addon = this.colorpicker.options.addon ? this.colorpicker.element.find(this.colorpicker.options.addon) : null; - - if (this.addon && this.addon.length === 0) { - // not found - this.addon = null; - } - } - }, { - key: 'unbind', - value: function unbind() { - if (this.hasAddon()) { - this.addon.off('.colorpicker'); - } - } - - /** - * If the addon element is present, its background color is updated - */ - - }, { - key: 'update', - value: function update() { - if (!this.colorpicker.colorHandler.hasColor() || !this.hasAddon()) { - return; - } - - var colorStr = this.colorpicker.colorHandler.getColorString(); - - var styles = { 'background': colorStr }; - - var icn = this.addon.find('i').eq(0); - - if (icn.length > 0) { - icn.css(styles); - } else { - this.addon.css(styles); - } - } - }]); - - return AddonHandler; -}(); - -exports.default = AddonHandler; -module.exports = exports.default; - -/***/ }) -/******/ ]); -}); -//# sourceMappingURL=bootstrap-colorpicker.js.map \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/bootstrap-colorpicker.min.js b/hal-core/resources/web/js/lib/bootstrap-colorpicker.min.js deleted file mode 100644 index bfdbed0c..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-colorpicker.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * Bootstrap Colorpicker - Bootstrap Colorpicker is a modular color picker plugin for Bootstrap 4. - * @package bootstrap-colorpicker - * @version v3.2.0 - * @license MIT - * @link https://itsjavi.com/bootstrap-colorpicker/ - * @link https://github.com/itsjavi/bootstrap-colorpicker.git - */ -(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory(require("jquery"));else if(typeof define==="function"&&define.amd)define("bootstrap-colorpicker",["jquery"],factory);else if(typeof exports==="object")exports["bootstrap-colorpicker"]=factory(require("jquery"));else root["bootstrap-colorpicker"]=factory(root["jQuery"])})(window,function(__WEBPACK_EXTERNAL_MODULE__0__){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!=="undefined"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"})}Object.defineProperty(exports,"__esModule",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value==="object"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,"default",{enumerable:true,value});if(mode&2&&typeof value!="string")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module["default"]}:function getModuleExports(){return module};__webpack_require__.d(getter,"a",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=7)}([function(module,exports){module.exports=__WEBPACK_EXTERNAL_MODULE__0__},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,Extension);this.colorpicker=colorpicker;this.options=options;if(!(this.colorpicker.element&&this.colorpicker.element.length)){throw new Error("Extension: this.colorpicker.element is not valid")}this.colorpicker.element.on("colorpickerCreate.colorpicker-ext",_jquery2.default.proxy(this.onCreate,this));this.colorpicker.element.on("colorpickerDestroy.colorpicker-ext",_jquery2.default.proxy(this.onDestroy,this));this.colorpicker.element.on("colorpickerUpdate.colorpicker-ext",_jquery2.default.proxy(this.onUpdate,this));this.colorpicker.element.on("colorpickerChange.colorpicker-ext",_jquery2.default.proxy(this.onChange,this));this.colorpicker.element.on("colorpickerInvalid.colorpicker-ext",_jquery2.default.proxy(this.onInvalid,this));this.colorpicker.element.on("colorpickerShow.colorpicker-ext",_jquery2.default.proxy(this.onShow,this));this.colorpicker.element.on("colorpickerHide.colorpicker-ext",_jquery2.default.proxy(this.onHide,this));this.colorpicker.element.on("colorpickerEnable.colorpicker-ext",_jquery2.default.proxy(this.onEnable,this));this.colorpicker.element.on("colorpickerDisable.colorpicker-ext",_jquery2.default.proxy(this.onDisable,this))}_createClass(Extension,[{key:"resolveColor",value:function resolveColor(color){var realColor=arguments.length>1&&arguments[1]!==undefined?arguments[1]:true;return false}},{key:"onCreate",value:function onCreate(event){}},{key:"onDestroy",value:function onDestroy(event){this.colorpicker.element.off(".colorpicker-ext")}},{key:"onUpdate",value:function onUpdate(event){}},{key:"onChange",value:function onChange(event){}},{key:"onInvalid",value:function onInvalid(event){}},{key:"onHide",value:function onHide(event){}},{key:"onShow",value:function onShow(event){}},{key:"onDisable",value:function onDisable(event){}},{key:"onEnable",value:function onEnable(event){}}]);return Extension}();exports.default=Extension;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.ColorItem=exports.HSVAColor=undefined;var _createClass=function(){function defineProperties(target,props){for(var i=0;i1?_len-1:0),_key=1;_key<_len;_key++){args[_key-1]=arguments[_key]}if(arguments.length===0){return this._color}var result=this._color[fn].apply(this._color,args);if(!(result instanceof _color2.default)){return result}return new ColorItem(result,this.format)}},{key:"original",get:function get(){return this._original}}],[{key:"HSVAColor",get:function get(){return HSVAColor}}]);function ColorItem(){var color=arguments.length>0&&arguments[0]!==undefined?arguments[0]:null;var format=arguments.length>1&&arguments[1]!==undefined?arguments[1]:null;_classCallCheck(this,ColorItem);this.replace(color,format)}_createClass(ColorItem,[{key:"replace",value:function replace(color){var format=arguments.length>1&&arguments[1]!==undefined?arguments[1]:null;format=ColorItem.sanitizeFormat(format);this._original={color,format,valid:true};this._color=ColorItem.parse(color);if(this._color===null){this._color=(0,_color2.default)();this._original.valid=false;return}this._format=format?format:ColorItem.isHex(color)?"hex":this._color.model}},{key:"isValid",value:function isValid(){return this._original.valid===true}},{key:"setHueRatio",value:function setHueRatio(h){this.hue=(1-h)*360}},{key:"setSaturationRatio",value:function setSaturationRatio(s){this.saturation=s*100}},{key:"setValueRatio",value:function setValueRatio(v){this.value=(1-v)*100}},{key:"setAlphaRatio",value:function setAlphaRatio(a){this.alpha=1-a}},{key:"isDesaturated",value:function isDesaturated(){return this.saturation===0}},{key:"isTransparent",value:function isTransparent(){return this.alpha===0}},{key:"hasTransparency",value:function hasTransparency(){return this.hasAlpha()&&this.alpha<1}},{key:"hasAlpha",value:function hasAlpha(){return!isNaN(this.alpha)}},{key:"toObject",value:function toObject(){return new HSVAColor(this.hue,this.saturation,this.value,this.alpha)}},{key:"toHsva",value:function toHsva(){return this.toObject()}},{key:"toHsvaRatio",value:function toHsvaRatio(){return new HSVAColor(this.hue/360,this.saturation/100,this.value/100,this.alpha)}},{key:"toString",value:function toString(){return this.string()}},{key:"string",value:function string(){var format=arguments.length>0&&arguments[0]!==undefined?arguments[0]:null;format=ColorItem.sanitizeFormat(format?format:this.format);if(!format){return this._color.round().string()}if(this._color[format]===undefined){throw new Error("Unsupported color format: '"+format+"'")}var str=this._color[format]();return str.round?str.round().string():str}},{key:"equals",value:function equals(color){color=color instanceof ColorItem?color:new ColorItem(color);if(!color.isValid()||!this.isValid()){return false}return this.hue===color.hue&&this.saturation===color.saturation&&this.value===color.value&&this.alpha===color.alpha}},{key:"getClone",value:function getClone(){return new ColorItem(this._color,this.format)}},{key:"getCloneHueOnly",value:function getCloneHueOnly(){return new ColorItem([this.hue,100,100,1],this.format)}},{key:"getCloneOpaque",value:function getCloneOpaque(){return new ColorItem(this._color.alpha(1),this.format)}},{key:"toRgbString",value:function toRgbString(){return this.string("rgb")}},{key:"toHexString",value:function toHexString(){return this.string("hex")}},{key:"toHslString",value:function toHslString(){return this.string("hsl")}},{key:"isDark",value:function isDark(){return this._color.isDark()}},{key:"isLight",value:function isLight(){return this._color.isLight()}},{key:"generate",value:function generate(formula){var hues=[];if(Array.isArray(formula)){hues=formula}else if(!ColorItem.colorFormulas.hasOwnProperty(formula)){throw new Error("No color formula found with the name '"+formula+"'.")}else{hues=ColorItem.colorFormulas[formula]}var colors=[],mainColor=this._color,format=this.format;hues.forEach(function(hue){var levels=[hue?(mainColor.hue()+hue)%360:mainColor.hue(),mainColor.saturationv(),mainColor.value(),mainColor.alpha()];colors.push(new ColorItem(levels,format))});return colors}},{key:"hue",get:function get(){return this._color.hue()},set:function set(value){this._color=this._color.hue(value)}},{key:"saturation",get:function get(){return this._color.saturationv()},set:function set(value){this._color=this._color.saturationv(value)}},{key:"value",get:function get(){return this._color.value()},set:function set(value){this._color=this._color.value(value)}},{key:"alpha",get:function get(){var a=this._color.alpha();return isNaN(a)?1:a},set:function set(value){this._color=this._color.alpha(Math.round(value*100)/100)}},{key:"format",get:function get(){return this._format?this._format:this._color.model},set:function set(value){this._format=ColorItem.sanitizeFormat(value)}}],[{key:"parse",value:function parse(color){if(color instanceof _color2.default){return color}if(color instanceof ColorItem){return color._color}var format=null;if(color instanceof HSVAColor){color=[color.h,color.s,color.v,isNaN(color.a)?1:color.a]}else{color=ColorItem.sanitizeString(color)}if(color===null){return null}if(Array.isArray(color)){format="hsv"}try{return(0,_color2.default)(color,format)}catch(e){return null}}},{key:"sanitizeString",value:function sanitizeString(str){if(!(typeof str==="string"||str instanceof String)){return str}if(str.match(/^[0-9a-f]{2,}$/i)){return"#"+str}if(str.toLowerCase()==="transparent"){return"#FFFFFF00"}return str}},{key:"isHex",value:function isHex(str){if(!(typeof str==="string"||str instanceof String)){return false}return!!str.match(/^#?[0-9a-f]{2,}$/i)}},{key:"sanitizeFormat",value:function sanitizeFormat(format){switch(format){case"hex":case"hex3":case"hex4":case"hex6":case"hex8":return"hex";case"rgb":case"rgba":case"keyword":case"name":return"rgb";case"hsl":case"hsla":case"hsv":case"hsva":case"hwb":case"hwba":return"hsl";default:return""}}}]);return ColorItem}();ColorItem.colorFormulas={complementary:[180],triad:[0,120,240],tetrad:[0,90,180,270],splitcomplement:[0,72,216]};exports.default=ColorItem;exports.HSVAColor=HSVAColor;exports.ColorItem=ColorItem},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var sassVars={bar_size_short:16,base_margin:6,columns:6};var sliderSize=sassVars.bar_size_short*sassVars.columns+sassVars.base_margin*(sassVars.columns-1);exports.default={customClass:null,color:false,fallbackColor:false,format:"auto",horizontal:false,inline:false,container:false,popover:{animation:true,placement:"bottom",fallbackPlacement:"flip"},debug:false,input:"input",addon:".colorpicker-input-addon",autoInputFallback:true,useHashPrefix:true,useAlpha:true,template:'
\n
\n
\n
\n
\n \n
\n
',extensions:[{name:"preview",options:{showText:true}}],sliders:{saturation:{selector:".colorpicker-saturation",maxLeft:sliderSize,maxTop:sliderSize,callLeft:"setSaturationRatio",callTop:"setValueRatio"},hue:{selector:".colorpicker-hue",maxLeft:0,maxTop:sliderSize,callLeft:false,callTop:"setHueRatio"},alpha:{selector:".colorpicker-alpha",childSelector:".colorpicker-alpha-color",maxLeft:0,maxTop:sliderSize,callLeft:false,callTop:"setAlphaRatio"}},slidersHorz:{saturation:{selector:".colorpicker-saturation",maxLeft:sliderSize,maxTop:sliderSize,callLeft:"setSaturationRatio",callTop:"setValueRatio"},hue:{selector:".colorpicker-hue",maxLeft:sliderSize,maxTop:0,callLeft:"setHueRatio",callTop:false},alpha:{selector:".colorpicker-alpha",childSelector:".colorpicker-alpha-color",maxLeft:sliderSize,maxTop:0,callLeft:"setAlphaRatio",callTop:false}}};module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj};var _createClass=function(){function defineProperties(target,props){for(var i=0;i1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,Palette);var _this=_possibleConstructorReturn(this,(Palette.__proto__||Object.getPrototypeOf(Palette)).call(this,colorpicker,_jquery2.default.extend(true,{},defaults,options)));if(!Array.isArray(_this.options.colors)&&_typeof(_this.options.colors)!=="object"){_this.options.colors=null}return _this}_createClass(Palette,[{key:"getLength",value:function getLength(){if(!this.options.colors){return 0}if(Array.isArray(this.options.colors)){return this.options.colors.length}if(_typeof(this.options.colors)==="object"){return Object.keys(this.options.colors).length}return 0}},{key:"resolveColor",value:function resolveColor(color){var realColor=arguments.length>1&&arguments[1]!==undefined?arguments[1]:true;if(this.getLength()<=0){return false}if(Array.isArray(this.options.colors)){if(this.options.colors.indexOf(color)>=0){return color}if(this.options.colors.indexOf(color.toUpperCase())>=0){return color.toUpperCase()}if(this.options.colors.indexOf(color.toLowerCase())>=0){return color.toLowerCase()}return false}if(_typeof(this.options.colors)!=="object"){return false}if(!this.options.namesAsValues||realColor){return this.getValue(color,false)}return this.getName(color,this.getName("#"+color))}},{key:"getName",value:function getName(value){var defaultValue=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;if(!(typeof value==="string")||!this.options.colors){return defaultValue}for(var name in this.options.colors){if(!this.options.colors.hasOwnProperty(name)){continue}if(this.options.colors[name].toLowerCase()===value.toLowerCase()){return name}}return defaultValue}},{key:"getValue",value:function getValue(name){var defaultValue=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;if(!(typeof name==="string")||!this.options.colors){return defaultValue}if(this.options.colors.hasOwnProperty(name)){return this.options.colors[name]}return defaultValue}}]);return Palette}(_Extension3.default);exports.default=Palette;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";module.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},function(module,exports,__webpack_require__){var cssKeywords=__webpack_require__(5);var reverseKeywords={};for(var key in cssKeywords){if(cssKeywords.hasOwnProperty(key)){reverseKeywords[cssKeywords[key]]=key}}var convert=module.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var model in convert){if(convert.hasOwnProperty(model)){if(!("channels"in convert[model])){throw new Error("missing channels property: "+model)}if(!("labels"in convert[model])){throw new Error("missing channel labels property: "+model)}if(convert[model].labels.length!==convert[model].channels){throw new Error("channel and label counts mismatch: "+model)}var channels=convert[model].channels;var labels=convert[model].labels;delete convert[model].channels;delete convert[model].labels;Object.defineProperty(convert[model],"channels",{value:channels});Object.defineProperty(convert[model],"labels",{value:labels})}}convert.rgb.hsl=function(rgb){var r=rgb[0]/255;var g=rgb[1]/255;var b=rgb[2]/255;var min=Math.min(r,g,b);var max=Math.max(r,g,b);var delta=max-min;var h;var s;var l;if(max===min){h=0}else if(r===max){h=(g-b)/delta}else if(g===max){h=2+(b-r)/delta}else if(b===max){h=4+(r-g)/delta}h=Math.min(h*60,360);if(h<0){h+=360}l=(min+max)/2;if(max===min){s=0}else if(l<=.5){s=delta/(max+min)}else{s=delta/(2-max-min)}return[h,s*100,l*100]};convert.rgb.hsv=function(rgb){var rdif;var gdif;var bdif;var h;var s;var r=rgb[0]/255;var g=rgb[1]/255;var b=rgb[2]/255;var v=Math.max(r,g,b);var diff=v-Math.min(r,g,b);var diffc=function(c){return(v-c)/6/diff+1/2};if(diff===0){h=s=0}else{s=diff/v;rdif=diffc(r);gdif=diffc(g);bdif=diffc(b);if(r===v){h=bdif-gdif}else if(g===v){h=1/3+rdif-bdif}else if(b===v){h=2/3+gdif-rdif}if(h<0){h+=1}else if(h>1){h-=1}}return[h*360,s*100,v*100]};convert.rgb.hwb=function(rgb){var r=rgb[0];var g=rgb[1];var b=rgb[2];var h=convert.rgb.hsl(rgb)[0];var w=1/255*Math.min(r,Math.min(g,b));b=1-1/255*Math.max(r,Math.max(g,b));return[h,w*100,b*100]};convert.rgb.cmyk=function(rgb){var r=rgb[0]/255;var g=rgb[1]/255;var b=rgb[2]/255;var c;var m;var y;var k;k=Math.min(1-r,1-g,1-b);c=(1-r-k)/(1-k)||0;m=(1-g-k)/(1-k)||0;y=(1-b-k)/(1-k)||0;return[c*100,m*100,y*100,k*100]};function comparativeDistance(x,y){return Math.pow(x[0]-y[0],2)+Math.pow(x[1]-y[1],2)+Math.pow(x[2]-y[2],2)}convert.rgb.keyword=function(rgb){var reversed=reverseKeywords[rgb];if(reversed){return reversed}var currentClosestDistance=Infinity;var currentClosestKeyword;for(var keyword in cssKeywords){if(cssKeywords.hasOwnProperty(keyword)){var value=cssKeywords[keyword];var distance=comparativeDistance(rgb,value);if(distance.04045?Math.pow((r+.055)/1.055,2.4):r/12.92;g=g>.04045?Math.pow((g+.055)/1.055,2.4):g/12.92;b=b>.04045?Math.pow((b+.055)/1.055,2.4):b/12.92;var x=r*.4124+g*.3576+b*.1805;var y=r*.2126+g*.7152+b*.0722;var z=r*.0193+g*.1192+b*.9505;return[x*100,y*100,z*100]};convert.rgb.lab=function(rgb){var xyz=convert.rgb.xyz(rgb);var x=xyz[0];var y=xyz[1];var z=xyz[2];var l;var a;var b;x/=95.047;y/=100;z/=108.883;x=x>.008856?Math.pow(x,1/3):7.787*x+16/116;y=y>.008856?Math.pow(y,1/3):7.787*y+16/116;z=z>.008856?Math.pow(z,1/3):7.787*z+16/116;l=116*y-16;a=500*(x-y);b=200*(y-z);return[l,a,b]};convert.hsl.rgb=function(hsl){var h=hsl[0]/360;var s=hsl[1]/100;var l=hsl[2]/100;var t1;var t2;var t3;var rgb;var val;if(s===0){val=l*255;return[val,val,val]}if(l<.5){t2=l*(1+s)}else{t2=l+s-l*s}t1=2*l-t2;rgb=[0,0,0];for(var i=0;i<3;i++){t3=h+1/3*-(i-1);if(t3<0){t3++}if(t3>1){t3--}if(6*t3<1){val=t1+(t2-t1)*6*t3}else if(2*t3<1){val=t2}else if(3*t3<2){val=t1+(t2-t1)*(2/3-t3)*6}else{val=t1}rgb[i]=val*255}return rgb};convert.hsl.hsv=function(hsl){var h=hsl[0];var s=hsl[1]/100;var l=hsl[2]/100;var smin=s;var lmin=Math.max(l,.01);var sv;var v;l*=2;s*=l<=1?l:2-l;smin*=lmin<=1?lmin:2-lmin;v=(l+s)/2;sv=l===0?2*smin/(lmin+smin):2*s/(l+s);return[h,sv*100,v*100]};convert.hsv.rgb=function(hsv){var h=hsv[0]/60;var s=hsv[1]/100;var v=hsv[2]/100;var hi=Math.floor(h)%6;var f=h-Math.floor(h);var p=255*v*(1-s);var q=255*v*(1-s*f);var t=255*v*(1-s*(1-f));v*=255;switch(hi){case 0:return[v,t,p];case 1:return[q,v,p];case 2:return[p,v,t];case 3:return[p,q,v];case 4:return[t,p,v];case 5:return[v,p,q]}};convert.hsv.hsl=function(hsv){var h=hsv[0];var s=hsv[1]/100;var v=hsv[2]/100;var vmin=Math.max(v,.01);var lmin;var sl;var l;l=(2-s)*v;lmin=(2-s)*vmin;sl=s*vmin;sl/=lmin<=1?lmin:2-lmin;sl=sl||0;l/=2;return[h,sl*100,l*100]};convert.hwb.rgb=function(hwb){var h=hwb[0]/360;var wh=hwb[1]/100;var bl=hwb[2]/100;var ratio=wh+bl;var i;var v;var f;var n;if(ratio>1){wh/=ratio;bl/=ratio}i=Math.floor(6*h);v=1-bl;f=6*h-i;if((i&1)!==0){f=1-f}n=wh+f*(v-wh);var r;var g;var b;switch(i){default:case 6:case 0:r=v;g=n;b=wh;break;case 1:r=n;g=v;b=wh;break;case 2:r=wh;g=v;b=n;break;case 3:r=wh;g=n;b=v;break;case 4:r=n;g=wh;b=v;break;case 5:r=v;g=wh;b=n;break}return[r*255,g*255,b*255]};convert.cmyk.rgb=function(cmyk){var c=cmyk[0]/100;var m=cmyk[1]/100;var y=cmyk[2]/100;var k=cmyk[3]/100;var r;var g;var b;r=1-Math.min(1,c*(1-k)+k);g=1-Math.min(1,m*(1-k)+k);b=1-Math.min(1,y*(1-k)+k);return[r*255,g*255,b*255]};convert.xyz.rgb=function(xyz){var x=xyz[0]/100;var y=xyz[1]/100;var z=xyz[2]/100;var r;var g;var b;r=x*3.2406+y*-1.5372+z*-.4986;g=x*-.9689+y*1.8758+z*.0415;b=x*.0557+y*-.204+z*1.057;r=r>.0031308?1.055*Math.pow(r,1/2.4)-.055:r*12.92;g=g>.0031308?1.055*Math.pow(g,1/2.4)-.055:g*12.92;b=b>.0031308?1.055*Math.pow(b,1/2.4)-.055:b*12.92;r=Math.min(Math.max(0,r),1);g=Math.min(Math.max(0,g),1);b=Math.min(Math.max(0,b),1);return[r*255,g*255,b*255]};convert.xyz.lab=function(xyz){var x=xyz[0];var y=xyz[1];var z=xyz[2];var l;var a;var b;x/=95.047;y/=100;z/=108.883;x=x>.008856?Math.pow(x,1/3):7.787*x+16/116;y=y>.008856?Math.pow(y,1/3):7.787*y+16/116;z=z>.008856?Math.pow(z,1/3):7.787*z+16/116;l=116*y-16;a=500*(x-y);b=200*(y-z);return[l,a,b]};convert.lab.xyz=function(lab){var l=lab[0];var a=lab[1];var b=lab[2];var x;var y;var z;y=(l+16)/116;x=a/500+y;z=y-b/200;var y2=Math.pow(y,3);var x2=Math.pow(x,3);var z2=Math.pow(z,3);y=y2>.008856?y2:(y-16/116)/7.787;x=x2>.008856?x2:(x-16/116)/7.787;z=z2>.008856?z2:(z-16/116)/7.787;x*=95.047;y*=100;z*=108.883;return[x,y,z]};convert.lab.lch=function(lab){var l=lab[0];var a=lab[1];var b=lab[2];var hr;var h;var c;hr=Math.atan2(b,a);h=hr*360/2/Math.PI;if(h<0){h+=360}c=Math.sqrt(a*a+b*b);return[l,c,h]};convert.lch.lab=function(lch){var l=lch[0];var c=lch[1];var h=lch[2];var a;var b;var hr;hr=h/360*2*Math.PI;a=c*Math.cos(hr);b=c*Math.sin(hr);return[l,a,b]};convert.rgb.ansi16=function(args){var r=args[0];var g=args[1];var b=args[2];var value=1 in arguments?arguments[1]:convert.rgb.hsv(args)[2];value=Math.round(value/50);if(value===0){return 30}var ansi=30+(Math.round(b/255)<<2|Math.round(g/255)<<1|Math.round(r/255));if(value===2){ansi+=60}return ansi};convert.hsv.ansi16=function(args){return convert.rgb.ansi16(convert.hsv.rgb(args),args[2])};convert.rgb.ansi256=function(args){var r=args[0];var g=args[1];var b=args[2];if(r===g&&g===b){if(r<8){return 16}if(r>248){return 231}return Math.round((r-8)/247*24)+232}var ansi=16+36*Math.round(r/255*5)+6*Math.round(g/255*5)+Math.round(b/255*5);return ansi};convert.ansi16.rgb=function(args){var color=args%10;if(color===0||color===7){if(args>50){color+=3.5}color=color/10.5*255;return[color,color,color]}var mult=(~~(args>50)+1)*.5;var r=(color&1)*mult*255;var g=(color>>1&1)*mult*255;var b=(color>>2&1)*mult*255;return[r,g,b]};convert.ansi256.rgb=function(args){if(args>=232){var c=(args-232)*10+8;return[c,c,c]}args-=16;var rem;var r=Math.floor(args/36)/5*255;var g=Math.floor((rem=args%36)/6)/5*255;var b=rem%6/5*255;return[r,g,b]};convert.rgb.hex=function(args){var integer=((Math.round(args[0])&255)<<16)+((Math.round(args[1])&255)<<8)+(Math.round(args[2])&255);var string=integer.toString(16).toUpperCase();return"000000".substring(string.length)+string};convert.hex.rgb=function(args){var match=args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!match){return[0,0,0]}var colorString=match[0];if(match[0].length===3){colorString=colorString.split("").map(function(char){return char+char}).join("")}var integer=parseInt(colorString,16);var r=integer>>16&255;var g=integer>>8&255;var b=integer&255;return[r,g,b]};convert.rgb.hcg=function(rgb){var r=rgb[0]/255;var g=rgb[1]/255;var b=rgb[2]/255;var max=Math.max(Math.max(r,g),b);var min=Math.min(Math.min(r,g),b);var chroma=max-min;var grayscale;var hue;if(chroma<1){grayscale=min/(1-chroma)}else{grayscale=0}if(chroma<=0){hue=0}else if(max===r){hue=(g-b)/chroma%6}else if(max===g){hue=2+(b-r)/chroma}else{hue=4+(r-g)/chroma+4}hue/=6;hue%=1;return[hue*360,chroma*100,grayscale*100]};convert.hsl.hcg=function(hsl){var s=hsl[1]/100;var l=hsl[2]/100;var c=1;var f=0;if(l<.5){c=2*s*l}else{c=2*s*(1-l)}if(c<1){f=(l-.5*c)/(1-c)}return[hsl[0],c*100,f*100]};convert.hsv.hcg=function(hsv){var s=hsv[1]/100;var v=hsv[2]/100;var c=s*v;var f=0;if(c<1){f=(v-c)/(1-c)}return[hsv[0],c*100,f*100]};convert.hcg.rgb=function(hcg){var h=hcg[0]/360;var c=hcg[1]/100;var g=hcg[2]/100;if(c===0){return[g*255,g*255,g*255]}var pure=[0,0,0];var hi=h%1*6;var v=hi%1;var w=1-v;var mg=0;switch(Math.floor(hi)){case 0:pure[0]=1;pure[1]=v;pure[2]=0;break;case 1:pure[0]=w;pure[1]=1;pure[2]=0;break;case 2:pure[0]=0;pure[1]=1;pure[2]=v;break;case 3:pure[0]=0;pure[1]=w;pure[2]=1;break;case 4:pure[0]=v;pure[1]=0;pure[2]=1;break;default:pure[0]=1;pure[1]=0;pure[2]=w}mg=(1-c)*g;return[(c*pure[0]+mg)*255,(c*pure[1]+mg)*255,(c*pure[2]+mg)*255]};convert.hcg.hsv=function(hcg){var c=hcg[1]/100;var g=hcg[2]/100;var v=c+g*(1-c);var f=0;if(v>0){f=c/v}return[hcg[0],f*100,v*100]};convert.hcg.hsl=function(hcg){var c=hcg[1]/100;var g=hcg[2]/100;var l=g*(1-c)+.5*c;var s=0;if(l>0&&l<.5){s=c/(2*l)}else if(l>=.5&&l<1){s=c/(2*(1-l))}return[hcg[0],s*100,l*100]};convert.hcg.hwb=function(hcg){var c=hcg[1]/100;var g=hcg[2]/100;var v=c+g*(1-c);return[hcg[0],(v-c)*100,(1-v)*100]};convert.hwb.hcg=function(hwb){var w=hwb[1]/100;var b=hwb[2]/100;var v=1-b;var c=v-w;var g=0;if(c<1){g=(v-c)/(1-c)}return[hwb[0],c*100,g*100]};convert.apple.rgb=function(apple){return[apple[0]/65535*255,apple[1]/65535*255,apple[2]/65535*255]};convert.rgb.apple=function(rgb){return[rgb[0]/255*65535,rgb[1]/255*65535,rgb[2]/255*65535]};convert.gray.rgb=function(args){return[args[0]/100*255,args[0]/100*255,args[0]/100*255]};convert.gray.hsl=convert.gray.hsv=function(args){return[0,0,args[0]]};convert.gray.hwb=function(gray){return[0,100,gray[0]]};convert.gray.cmyk=function(gray){return[0,0,0,gray[0]]};convert.gray.lab=function(gray){return[gray[0],0,0]};convert.gray.hex=function(gray){var val=Math.round(gray[0]/100*255)&255;var integer=(val<<16)+(val<<8)+val;var string=integer.toString(16).toUpperCase();return"000000".substring(string.length)+string};convert.rgb.gray=function(rgb){var val=(rgb[0]+rgb[1]+rgb[2])/3;return[val/255*100]}},function(module,exports,__webpack_require__){"use strict";var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj};var _Colorpicker=__webpack_require__(8);var _Colorpicker2=_interopRequireDefault(_Colorpicker);var _jquery=__webpack_require__(0);var _jquery2=_interopRequireDefault(_jquery);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}var plugin="colorpicker";_jquery2.default[plugin]=_Colorpicker2.default;_jquery2.default.fn[plugin]=function(option){var fnArgs=Array.prototype.slice.call(arguments,1),isSingleElement=this.length===1,returnValue=null;var $elements=this.each(function(){var $this=(0,_jquery2.default)(this),inst=$this.data(plugin),options=(typeof option==="undefined"?"undefined":_typeof(option))==="object"?option:{};if(!inst){inst=new _Colorpicker2.default(this,options);$this.data(plugin,inst)}if(!isSingleElement){return}returnValue=$this;if(typeof option==="string"){if(option==="colorpicker"){returnValue=inst}else if(_jquery2.default.isFunction(inst[option])){returnValue=inst[option].apply(inst,fnArgs)}else{returnValue=inst[option]}}});return isSingleElement?returnValue:$elements};_jquery2.default.fn[plugin].constructor=_Colorpicker2.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i1&&arguments[1]!==undefined?arguments[1]:{};var ext=new ExtensionClass(this,config);this.extensions.push(ext);return ext}},{key:"destroy",value:function destroy(){var color=this.color;this.sliderHandler.unbind();this.inputHandler.unbind();this.popupHandler.unbind();this.colorHandler.unbind();this.addonHandler.unbind();this.pickerHandler.unbind();this.element.removeClass("colorpicker-element").removeData("colorpicker","color").off(".colorpicker");this.trigger("colorpickerDestroy",color)}},{key:"show",value:function show(e){this.popupHandler.show(e)}},{key:"hide",value:function hide(e){this.popupHandler.hide(e)}},{key:"toggle",value:function toggle(e){this.popupHandler.toggle(e)}},{key:"getValue",value:function getValue(){var defaultValue=arguments.length>0&&arguments[0]!==undefined?arguments[0]:null;var val=this.colorHandler.color;val=val instanceof _ColorItem2.default?val:defaultValue;if(val instanceof _ColorItem2.default){return val.string(this.format)}return val}},{key:"setValue",value:function setValue(val){if(this.isDisabled()){return}var ch=this.colorHandler;if(ch.hasColor()&&!!val&&ch.color.equals(val)||!ch.hasColor()&&!val){return}ch.color=val?ch.createColor(val,this.options.autoInputFallback):null;this.trigger("colorpickerChange",ch.color,val);this.update()}},{key:"update",value:function update(){if(this.colorHandler.hasColor()){this.inputHandler.update()}else{this.colorHandler.assureColor()}this.addonHandler.update();this.pickerHandler.update();this.trigger("colorpickerUpdate")}},{key:"enable",value:function enable(){this.inputHandler.enable();this.disabled=false;this.picker.removeClass("colorpicker-disabled");this.trigger("colorpickerEnable");return true}},{key:"disable",value:function disable(){this.inputHandler.disable();this.disabled=true;this.picker.addClass("colorpicker-disabled");this.trigger("colorpickerDisable");return true}},{key:"isEnabled",value:function isEnabled(){return!this.isDisabled()}},{key:"isDisabled",value:function isDisabled(){return this.disabled===true}},{key:"trigger",value:function trigger(eventName){var color=arguments.length>1&&arguments[1]!==undefined?arguments[1]:null;var value=arguments.length>2&&arguments[2]!==undefined?arguments[2]:null;this.element.trigger({type:eventName,colorpicker:this,color:color?color:this.color,value:value?value:this.getValue()})}}]);return Colorpicker}();Colorpicker.extensions=_extensions2.default;exports.default=Colorpicker;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.Palette=exports.Swatches=exports.Preview=exports.Debugger=undefined;var _Debugger=__webpack_require__(10);var _Debugger2=_interopRequireDefault(_Debugger);var _Preview=__webpack_require__(11);var _Preview2=_interopRequireDefault(_Preview);var _Swatches=__webpack_require__(12);var _Swatches2=_interopRequireDefault(_Swatches);var _Palette=__webpack_require__(4);var _Palette2=_interopRequireDefault(_Palette);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}exports.Debugger=_Debugger2.default;exports.Preview=_Preview2.default;exports.Swatches=_Swatches2.default;exports.Palette=_Palette2.default;exports.default={debugger:_Debugger2.default,preview:_Preview2.default,swatches:_Swatches2.default,palette:_Palette2.default}},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,Debugger);var _this=_possibleConstructorReturn(this,(Debugger.__proto__||Object.getPrototypeOf(Debugger)).call(this,colorpicker,options));_this.eventCounter=0;if(_this.colorpicker.inputHandler.hasInput()){_this.colorpicker.inputHandler.input.on("change.colorpicker-ext",_jquery2.default.proxy(_this.onChangeInput,_this))}return _this}_createClass(Debugger,[{key:"log",value:function log(eventName){var _console;for(var _len=arguments.length,args=Array(_len>1?_len-1:0),_key=1;_key<_len;_key++){args[_key-1]=arguments[_key]}this.eventCounter+=1;var logMessage="#"+this.eventCounter+": Colorpicker#"+this.colorpicker.id+" ["+eventName+"]";(_console=console).debug.apply(_console,[logMessage].concat(args));this.colorpicker.element.trigger({type:"colorpickerDebug",colorpicker:this.colorpicker,color:this.color,value:null,debug:{debugger:this,eventName,logArgs:args,logMessage}})}},{key:"resolveColor",value:function resolveColor(color){var realColor=arguments.length>1&&arguments[1]!==undefined?arguments[1]:true;this.log("resolveColor()",color,realColor);return false}},{key:"onCreate",value:function onCreate(event){this.log("colorpickerCreate");return _get(Debugger.prototype.__proto__||Object.getPrototypeOf(Debugger.prototype),"onCreate",this).call(this,event)}},{key:"onDestroy",value:function onDestroy(event){this.log("colorpickerDestroy");this.eventCounter=0;if(this.colorpicker.inputHandler.hasInput()){this.colorpicker.inputHandler.input.off(".colorpicker-ext")}return _get(Debugger.prototype.__proto__||Object.getPrototypeOf(Debugger.prototype),"onDestroy",this).call(this,event)}},{key:"onUpdate",value:function onUpdate(event){this.log("colorpickerUpdate")}},{key:"onChangeInput",value:function onChangeInput(event){this.log("input:change.colorpicker",event.value,event.color)}},{key:"onChange",value:function onChange(event){this.log("colorpickerChange",event.value,event.color)}},{key:"onInvalid",value:function onInvalid(event){this.log("colorpickerInvalid",event.value,event.color)}},{key:"onHide",value:function onHide(event){this.log("colorpickerHide");this.eventCounter=0}},{key:"onShow",value:function onShow(event){this.log("colorpickerShow")}},{key:"onDisable",value:function onDisable(event){this.log("colorpickerDisable")}},{key:"onEnable",value:function onEnable(event){this.log("colorpickerEnable")}}]);return Debugger}(_Extension3.default);exports.default=Debugger;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,Preview);var _this=_possibleConstructorReturn(this,(Preview.__proto__||Object.getPrototypeOf(Preview)).call(this,colorpicker,_jquery2.default.extend(true,{},{template:'
',showText:true,format:colorpicker.format},options)));_this.element=(0,_jquery2.default)(_this.options.template);_this.elementInner=_this.element.find("div");return _this}_createClass(Preview,[{key:"onCreate",value:function onCreate(event){_get(Preview.prototype.__proto__||Object.getPrototypeOf(Preview.prototype),"onCreate",this).call(this,event);this.colorpicker.picker.append(this.element)}},{key:"onUpdate",value:function onUpdate(event){_get(Preview.prototype.__proto__||Object.getPrototypeOf(Preview.prototype),"onUpdate",this).call(this,event);if(!event.color){this.elementInner.css("backgroundColor",null).css("color",null).html("");return}this.elementInner.css("backgroundColor",event.color.toRgbString());if(this.options.showText){this.elementInner.html(event.color.string(this.options.format||this.colorpicker.format));if(event.color.isDark()&&event.color.alpha>.5){this.elementInner.css("color","white")}else{this.elementInner.css("color","black")}}}}]);return Preview}(_Extension3.default);exports.default=Preview;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i\n
\n
',swatchTemplate:''};var Swatches=function(_Palette){_inherits(Swatches,_Palette);function Swatches(colorpicker){var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};_classCallCheck(this,Swatches);var _this=_possibleConstructorReturn(this,(Swatches.__proto__||Object.getPrototypeOf(Swatches)).call(this,colorpicker,_jquery2.default.extend(true,{},defaults,options)));_this.element=null;return _this}_createClass(Swatches,[{key:"isEnabled",value:function isEnabled(){return this.getLength()>0}},{key:"onCreate",value:function onCreate(event){_get(Swatches.prototype.__proto__||Object.getPrototypeOf(Swatches.prototype),"onCreate",this).call(this,event);if(!this.isEnabled()){return}this.element=(0,_jquery2.default)(this.options.barTemplate);this.load();this.colorpicker.picker.append(this.element)}},{key:"load",value:function load(){var _this2=this;var colorpicker=this.colorpicker,swatchContainer=this.element.find(".colorpicker-swatches--inner"),isAliased=this.options.namesAsValues===true&&!Array.isArray(this.colors);swatchContainer.empty();_jquery2.default.each(this.colors,function(name,value){var $swatch=(0,_jquery2.default)(_this2.options.swatchTemplate).attr("data-name",name).attr("data-value",value).attr("title",isAliased?name+": "+value:value).on("mousedown.colorpicker touchstart.colorpicker",function(e){var $sw=(0,_jquery2.default)(this);colorpicker.setValue(isAliased?$sw.attr("data-name"):$sw.attr("data-value"))});$swatch.find(".colorpicker-swatch--inner").css("background-color",value);swatchContainer.append($swatch)});swatchContainer.append((0,_jquery2.default)(''))}}]);return Swatches}(_Palette3.default);exports.default=Swatches;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i0}},{key:"onClickingInside",value:function onClickingInside(e){this.clicking=this.isClickingInside(e)}},{key:"createPopover",value:function createPopover(){var cp=this.colorpicker;this.popoverTarget=this.hasAddon?this.addon:this.input;cp.picker.addClass("colorpicker-bs-popover-content");this.popoverTarget.popover(_jquery2.default.extend(true,{},_options2.default.popover,cp.options.popover,{trigger:"manual",content:cp.picker,html:true}));this.popoverTip=(0,_jquery2.default)(this.popoverTarget.popover("getTipElement").data("bs.popover").tip);this.popoverTip.addClass("colorpicker-bs-popover");this.popoverTarget.on("shown.bs.popover",_jquery2.default.proxy(this.fireShow,this));this.popoverTarget.on("hidden.bs.popover",_jquery2.default.proxy(this.fireHide,this))}},{key:"reposition",value:function reposition(e){if(this.popoverTarget&&this.isVisible()){this.popoverTarget.popover("update")}}},{key:"toggle",value:function toggle(e){if(this.isVisible()){this.hide(e)}else{this.show(e)}}},{key:"show",value:function show(e){if(this.isVisible()||this.showing||this.hidding){return}this.showing=true;this.hidding=false;this.clicking=false;var cp=this.colorpicker;cp.lastEvent.alias="show";cp.lastEvent.e=e;if(e&&(!this.hasInput||this.input.attr("type")==="color")&&e&&e.preventDefault){e.stopPropagation();e.preventDefault()}if(this.isPopover){(0,_jquery2.default)(this.root).on("resize.colorpicker",_jquery2.default.proxy(this.reposition,this))}cp.picker.addClass("colorpicker-visible").removeClass("colorpicker-hidden");if(this.popoverTarget){this.popoverTarget.popover("show")}else{this.fireShow()}}},{key:"fireShow",value:function fireShow(){this.hidding=false;this.showing=false;if(this.isPopover){(0,_jquery2.default)(this.root.document).on("mousedown.colorpicker touchstart.colorpicker",_jquery2.default.proxy(this.hide,this));(0,_jquery2.default)(this.root.document).on("mousedown.colorpicker touchstart.colorpicker",_jquery2.default.proxy(this.onClickingInside,this))}this.colorpicker.trigger("colorpickerShow")}},{key:"hide",value:function hide(e){if(this.isHidden()||this.showing||this.hidding){return}var cp=this.colorpicker,clicking=this.clicking||this.isClickingInside(e);this.hidding=true;this.showing=false;this.clicking=false;cp.lastEvent.alias="hide";cp.lastEvent.e=e;if(clicking){this.hidding=false;return}if(this.popoverTarget){this.popoverTarget.popover("hide")}else{this.fireHide()}}},{key:"fireHide",value:function fireHide(){this.hidding=false;this.showing=false;var cp=this.colorpicker;cp.picker.addClass("colorpicker-hidden").removeClass("colorpicker-visible");(0,_jquery2.default)(this.root).off("resize.colorpicker",_jquery2.default.proxy(this.reposition,this));(0,_jquery2.default)(this.root.document).off("mousedown.colorpicker touchstart.colorpicker",_jquery2.default.proxy(this.hide,this));(0,_jquery2.default)(this.root.document).off("mousedown.colorpicker touchstart.colorpicker",_jquery2.default.proxy(this.onClickingInside,this));cp.trigger("colorpickerHide")}},{key:"focus",value:function focus(){if(this.hasAddon){return this.addon.focus()}if(this.hasInput){return this.input.focus()}return false}},{key:"isVisible",value:function isVisible(){return this.colorpicker.picker.hasClass("colorpicker-visible")&&!this.colorpicker.picker.hasClass("colorpicker-hidden")}},{key:"isHidden",value:function isHidden(){return this.colorpicker.picker.hasClass("colorpicker-hidden")&&!this.colorpicker.picker.hasClass("colorpicker-visible")}},{key:"input",get:function get(){return this.colorpicker.inputHandler.input}},{key:"hasInput",get:function get(){return this.colorpicker.inputHandler.hasInput()}},{key:"addon",get:function get(){return this.colorpicker.addonHandler.addon}},{key:"hasAddon",get:function get(){return this.colorpicker.addonHandler.hasAddon()}},{key:"isPopover",get:function get(){return!this.colorpicker.options.inline&&!!this.popoverTip}}]);return PopupHandler}();exports.default=PopupHandler;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i0&&arguments[0]!==undefined?arguments[0]:null;val=val?val:this.colorpicker.colorHandler.getColorString();if(!val){return""}val=this.colorpicker.colorHandler.resolveColorDelegate(val,false);if(this.colorpicker.options.useHashPrefix===false){val=val.replace(/^#/g,"")}return val}},{key:"hasInput",value:function hasInput(){return this.input!==false}},{key:"isEnabled",value:function isEnabled(){return this.hasInput()&&!this.isDisabled()}},{key:"isDisabled",value:function isDisabled(){return this.hasInput()&&this.input.prop("disabled")===true}},{key:"disable",value:function disable(){if(this.hasInput()){this.input.prop("disabled",true)}}},{key:"enable",value:function enable(){if(this.hasInput()){this.input.prop("disabled",false)}}},{key:"update",value:function update(){if(!this.hasInput()){return}if(this.colorpicker.options.autoInputFallback===false&&this.colorpicker.colorHandler.isInvalidColor()){return}this.setValue(this.getFormattedColor())}},{key:"onchange",value:function onchange(e){this.colorpicker.lastEvent.alias="input.change";this.colorpicker.lastEvent.e=e;var val=this.getValue();if(val!==e.value){this.colorpicker.setValue(val)}}},{key:"onkeyup",value:function onkeyup(e){this.colorpicker.lastEvent.alias="input.keyup";this.colorpicker.lastEvent.e=e;var val=this.getValue();if(val!==e.value){this.colorpicker.setValue(val)}}}]);return InputHandler}();exports.default=InputHandler;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";var colorString=__webpack_require__(17);var convert=__webpack_require__(20);var _slice=[].slice;var skippedModels=["keyword","gray","hex"];var hashedModelKeys={};Object.keys(convert).forEach(function(model){hashedModelKeys[_slice.call(convert[model].labels).sort().join("")]=model});var limiters={};function Color(obj,model){if(!(this instanceof Color)){return new Color(obj,model)}if(model&&model in skippedModels){model=null}if(model&&!(model in convert)){throw new Error("Unknown model: "+model)}var i;var channels;if(obj==null){this.model="rgb";this.color=[0,0,0];this.valpha=1}else if(obj instanceof Color){this.model=obj.model;this.color=obj.color.slice();this.valpha=obj.valpha}else if(typeof obj==="string"){var result=colorString.get(obj);if(result===null){throw new Error("Unable to parse color from string: "+obj)}this.model=result.model;channels=convert[this.model].channels;this.color=result.value.slice(0,channels);this.valpha=typeof result.value[channels]==="number"?result.value[channels]:1}else if(obj.length){this.model=model||"rgb";channels=convert[this.model].channels;var newArr=_slice.call(obj,0,channels);this.color=zeroArray(newArr,channels);this.valpha=typeof obj[channels]==="number"?obj[channels]:1}else if(typeof obj==="number"){obj&=16777215;this.model="rgb";this.color=[obj>>16&255,obj>>8&255,obj&255];this.valpha=1}else{this.valpha=1;var keys=Object.keys(obj);if("alpha"in obj){keys.splice(keys.indexOf("alpha"),1);this.valpha=typeof obj.alpha==="number"?obj.alpha:0}var hashedKeys=keys.sort().join("");if(!(hashedKeys in hashedModelKeys)){throw new Error("Unable to parse color from object: "+JSON.stringify(obj))}this.model=hashedModelKeys[hashedKeys];var labels=convert[this.model].labels;var color=[];for(i=0;ilum2){return(lum1+.05)/(lum2+.05)}return(lum2+.05)/(lum1+.05)},level:function(color2){var contrastRatio=this.contrast(color2);if(contrastRatio>=7.1){return"AAA"}return contrastRatio>=4.5?"AA":""},isDark:function(){var rgb=this.rgb().color;var yiq=(rgb[0]*299+rgb[1]*587+rgb[2]*114)/1e3;return yiq<128},isLight:function(){return!this.isDark()},negate:function(){var rgb=this.rgb();for(var i=0;i<3;i++){rgb.color[i]=255-rgb.color[i]}return rgb},lighten:function(ratio){var hsl=this.hsl();hsl.color[2]+=hsl.color[2]*ratio;return hsl},darken:function(ratio){var hsl=this.hsl();hsl.color[2]-=hsl.color[2]*ratio;return hsl},saturate:function(ratio){var hsl=this.hsl();hsl.color[1]+=hsl.color[1]*ratio;return hsl},desaturate:function(ratio){var hsl=this.hsl();hsl.color[1]-=hsl.color[1]*ratio;return hsl},whiten:function(ratio){var hwb=this.hwb();hwb.color[1]+=hwb.color[1]*ratio;return hwb},blacken:function(ratio){var hwb=this.hwb();hwb.color[2]+=hwb.color[2]*ratio;return hwb},grayscale:function(){var rgb=this.rgb().color;var val=rgb[0]*.3+rgb[1]*.59+rgb[2]*.11;return Color.rgb(val,val,val)},fade:function(ratio){return this.alpha(this.valpha-this.valpha*ratio)},opaquer:function(ratio){return this.alpha(this.valpha+this.valpha*ratio)},rotate:function(degrees){var hsl=this.hsl();var hue=hsl.color[0];hue=(hue+degrees)%360;hue=hue<0?360+hue:hue;hsl.color[0]=hue;return hsl},mix:function(mixinColor,weight){if(!mixinColor||!mixinColor.rgb){throw new Error('Argument to "mix" was not a Color instance, but rather an instance of '+typeof mixinColor)}var color1=mixinColor.rgb();var color2=this.rgb();var p=weight===undefined?.5:weight;var w=2*p-1;var a=color1.alpha()-color2.alpha();var w1=((w*a===-1?w:(w+a)/(1+w*a))+1)/2;var w2=1-w1;return Color.rgb(w1*color1.red()+w2*color2.red(),w1*color1.green()+w2*color2.green(),w1*color1.blue()+w2*color2.blue(),color1.alpha()*p+color2.alpha()*(1-p))}};Object.keys(convert).forEach(function(model){if(skippedModels.indexOf(model)!==-1){return}var channels=convert[model].channels;Color.prototype[model]=function(){if(this.model===model){return new Color(this)}if(arguments.length){return new Color(arguments,model)}var newAlpha=typeof arguments[channels]==="number"?channels:this.valpha;return new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha),model)};Color[model]=function(color){if(typeof color==="number"){color=zeroArray(_slice.call(arguments),channels)}return new Color(color,model)}});function roundTo(num,places){return Number(num.toFixed(places))}function roundToPlace(places){return function(num){return roundTo(num,places)}}function getset(model,channel,modifier){model=Array.isArray(model)?model:[model];model.forEach(function(m){(limiters[m]||(limiters[m]=[]))[channel]=modifier});model=model[0];return function(val){var result;if(arguments.length){if(modifier){val=modifier(val)}result=this[model]();result.color[channel]=val;return result}result=this[model]().color[channel];if(modifier){result=modifier(result)}return result}}function maxfn(max){return function(v){return Math.max(0,Math.min(max,v))}}function assertArray(val){return Array.isArray(val)?val:[val]}function zeroArray(arr,length){for(var i=0;i=4&&hwba[3]!==1){a=", "+hwba[3]}return"hwb("+hwba[0]+", "+hwba[1]+"%, "+hwba[2]+"%"+a+")"};cs.to.keyword=function(rgb){return reverseNames[rgb.slice(0,3)]};function clamp(num,min,max){return Math.min(Math.max(min,num),max)}function hexDouble(num){var str=num.toString(16).toUpperCase();return str.length<2?"0"+str:str}},function(module,exports,__webpack_require__){"use strict";var isArrayish=__webpack_require__(19);var concat=Array.prototype.concat;var slice=Array.prototype.slice;var swizzle=module.exports=function swizzle(args){var results=[];for(var i=0,len=args.length;i=0&&obj.splice instanceof Function}},function(module,exports,__webpack_require__){var conversions=__webpack_require__(6);var route=__webpack_require__(21);var convert={};var models=Object.keys(conversions);function wrapRaw(fn){var wrappedFn=function(args){if(args===undefined||args===null){return args}if(arguments.length>1){args=Array.prototype.slice.call(arguments)}return fn(args)};if("conversion"in fn){wrappedFn.conversion=fn.conversion}return wrappedFn}function wrapRounded(fn){var wrappedFn=function(args){if(args===undefined||args===null){return args}if(arguments.length>1){args=Array.prototype.slice.call(arguments)}var result=fn(args);if(typeof result==="object"){for(var len=result.length,i=0;i1&&arguments[1]!==undefined?arguments[1]:true;var color=new _ColorItem2.default(this.resolveColorDelegate(val),this.format);if(!color.isValid()){if(fallbackOnInvalid){color=this.getFallbackColor()}this.colorpicker.trigger("colorpickerInvalid",color,val)}if(!this.isAlphaEnabled()){color.alpha=1}return color}},{key:"getFallbackColor",value:function getFallbackColor(){if(this.fallback&&this.fallback===this.color){return this.color}var fallback=this.resolveColorDelegate(this.fallback);var color=new _ColorItem2.default(fallback,this.format);if(!color.isValid()){console.warn("The fallback color is invalid. Falling back to the previous color or black if any.");return this.color?this.color:new _ColorItem2.default("#000000",this.format)}return color}},{key:"assureColor",value:function assureColor(){if(!this.hasColor()){this.color=this.getFallbackColor()}return this.color}},{key:"resolveColorDelegate",value:function resolveColorDelegate(color){var realColor=arguments.length>1&&arguments[1]!==undefined?arguments[1]:true;var extResolvedColor=false;_jquery2.default.each(this.colorpicker.extensions,function(name,ext){if(extResolvedColor!==false){return}extResolvedColor=ext.resolveColor(color,realColor)});return extResolvedColor?extResolvedColor:color}},{key:"isInvalidColor",value:function isInvalidColor(){return!this.hasColor()||!this.color.isValid()}},{key:"isAlphaEnabled",value:function isAlphaEnabled(){return this.colorpicker.options.useAlpha!==false}},{key:"hasColor",value:function hasColor(){return this.color instanceof _ColorItem2.default}},{key:"fallback",get:function get(){return this.colorpicker.options.fallbackColor?this.colorpicker.options.fallbackColor:this.hasColor()?this.color:null}},{key:"format",get:function get(){if(this.colorpicker.options.format){return this.colorpicker.options.format}if(this.hasColor()&&this.color.hasTransparency()&&this.color.format.match(/^hex/)){return this.isAlphaEnabled()?"rgba":"hex"}if(this.hasColor()){return this.color.format}return"rgb"}},{key:"color",get:function get(){return this.colorpicker.element.data("color")},set:function set(value){this.colorpicker.element.data("color",value);if(value instanceof _ColorItem2.default&&this.colorpicker.options.format==="auto"){this.colorpicker.options.format=this.color.format}}}]);return ColorHandler}();exports.default=ColorHandler;module.exports=exports.default},function(module,exports,__webpack_require__){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var _createClass=function(){function defineProperties(target,props){for(var i=0;i0){icn.css(styles)}else{this.addon.css(styles)}}}]);return AddonHandler}();exports.default=AddonHandler;module.exports=exports.default}])}); -//# sourceMappingURL=bootstrap-colorpicker.min.js.map \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE b/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE deleted file mode 100644 index 31e23cd1..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2015 The authors of Bootstrap Switch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE.txt b/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE.txt deleted file mode 100644 index 31e23cd1..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-switch.LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2015 The authors of Bootstrap Switch - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/hal-core/resources/web/js/lib/bootstrap-switch.js b/hal-core/resources/web/js/lib/bootstrap-switch.js deleted file mode 100644 index 511f08fa..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-switch.js +++ /dev/null @@ -1,784 +0,0 @@ -/** - * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches. - * - * @version v3.3.4 - * @homepage https://bttstrp.github.io/bootstrap-switch - * @author Mattia Larentis (http://larentis.eu) - * @license Apache-2.0 - */ - -(function (global, factory) { - if (typeof define === "function" && define.amd) { - define(['jquery'], factory); - } else if (typeof exports !== "undefined") { - factory(require('jquery')); - } else { - var mod = { - exports: {} - }; - factory(global.jquery); - global.bootstrapSwitch = mod.exports; - } -})(this, function (_jquery) { - 'use strict'; - - var _jquery2 = _interopRequireDefault(_jquery); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - var _extends = Object.assign || function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - - return target; - }; - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - - var $ = _jquery2.default || window.jQuery || window.$; - - var BootstrapSwitch = function () { - function BootstrapSwitch(element) { - var _this = this; - - var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - _classCallCheck(this, BootstrapSwitch); - - this.$element = $(element); - this.options = $.extend({}, $.fn.bootstrapSwitch.defaults, this._getElementOptions(), options); - this.prevOptions = {}; - this.$wrapper = $('
', { - class: function _class() { - var classes = []; - classes.push(_this.options.state ? 'on' : 'off'); - if (_this.options.size) { - classes.push(_this.options.size); - } - if (_this.options.disabled) { - classes.push('disabled'); - } - if (_this.options.readonly) { - classes.push('readonly'); - } - if (_this.options.indeterminate) { - classes.push('indeterminate'); - } - if (_this.options.inverse) { - classes.push('inverse'); - } - if (_this.$element.attr('id')) { - classes.push('id-' + _this.$element.attr('id')); - } - return classes.map(_this._getClass.bind(_this)).concat([_this.options.baseClass], _this._getClasses(_this.options.wrapperClass)).join(' '); - } - }); - this.$container = $('
', { class: this._getClass('container') }); - this.$on = $('', { - html: this.options.onText, - class: this._getClass('handle-on') + ' ' + this._getClass(this.options.onColor) - }); - this.$off = $('', { - html: this.options.offText, - class: this._getClass('handle-off') + ' ' + this._getClass(this.options.offColor) - }); - this.$label = $('', { - html: this.options.labelText, - class: this._getClass('label') - }); - - this.$element.on('init.bootstrapSwitch', this.options.onInit.bind(this, element)); - this.$element.on('switchChange.bootstrapSwitch', function () { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - if (_this.options.onSwitchChange.apply(element, args) === false) { - if (_this.$element.is(':radio')) { - $('[name="' + _this.$element.attr('name') + '"]').trigger('previousState.bootstrapSwitch', true); - } else { - _this.$element.trigger('previousState.bootstrapSwitch', true); - } - } - }); - - this.$container = this.$element.wrap(this.$container).parent(); - this.$wrapper = this.$container.wrap(this.$wrapper).parent(); - this.$element.before(this.options.inverse ? this.$off : this.$on).before(this.$label).before(this.options.inverse ? this.$on : this.$off); - - if (this.options.indeterminate) { - this.$element.prop('indeterminate', true); - } - - this._init(); - this._elementHandlers(); - this._handleHandlers(); - this._labelHandlers(); - this._formHandler(); - this._externalLabelHandler(); - this.$element.trigger('init.bootstrapSwitch', this.options.state); - } - - _createClass(BootstrapSwitch, [{ - key: 'setPrevOptions', - value: function setPrevOptions() { - this.prevOptions = _extends({}, this.options); - } - }, { - key: 'state', - value: function state(value, skip) { - if (typeof value === 'undefined') { - return this.options.state; - } - if (this.options.disabled || this.options.readonly || this.options.state && !this.options.radioAllOff && this.$element.is(':radio')) { - return this.$element; - } - if (this.$element.is(':radio')) { - $('[name="' + this.$element.attr('name') + '"]').trigger('setPreviousOptions.bootstrapSwitch'); - } else { - this.$element.trigger('setPreviousOptions.bootstrapSwitch'); - } - if (this.options.indeterminate) { - this.indeterminate(false); - } - this.$element.prop('checked', Boolean(value)).trigger('change.bootstrapSwitch', skip); - return this.$element; - } - }, { - key: 'toggleState', - value: function toggleState(skip) { - if (this.options.disabled || this.options.readonly) { - return this.$element; - } - if (this.options.indeterminate) { - this.indeterminate(false); - return this.state(true); - } else { - return this.$element.prop('checked', !this.options.state).trigger('change.bootstrapSwitch', skip); - } - } - }, { - key: 'size', - value: function size(value) { - if (typeof value === 'undefined') { - return this.options.size; - } - if (this.options.size != null) { - this.$wrapper.removeClass(this._getClass(this.options.size)); - } - if (value) { - this.$wrapper.addClass(this._getClass(value)); - } - this._width(); - this._containerPosition(); - this.options.size = value; - return this.$element; - } - }, { - key: 'animate', - value: function animate(value) { - if (typeof value === 'undefined') { - return this.options.animate; - } - if (this.options.animate === Boolean(value)) { - return this.$element; - } - return this.toggleAnimate(); - } - }, { - key: 'toggleAnimate', - value: function toggleAnimate() { - this.options.animate = !this.options.animate; - this.$wrapper.toggleClass(this._getClass('animate')); - return this.$element; - } - }, { - key: 'disabled', - value: function disabled(value) { - if (typeof value === 'undefined') { - return this.options.disabled; - } - if (this.options.disabled === Boolean(value)) { - return this.$element; - } - return this.toggleDisabled(); - } - }, { - key: 'toggleDisabled', - value: function toggleDisabled() { - this.options.disabled = !this.options.disabled; - this.$element.prop('disabled', this.options.disabled); - this.$wrapper.toggleClass(this._getClass('disabled')); - return this.$element; - } - }, { - key: 'readonly', - value: function readonly(value) { - if (typeof value === 'undefined') { - return this.options.readonly; - } - if (this.options.readonly === Boolean(value)) { - return this.$element; - } - return this.toggleReadonly(); - } - }, { - key: 'toggleReadonly', - value: function toggleReadonly() { - this.options.readonly = !this.options.readonly; - this.$element.prop('readonly', this.options.readonly); - this.$wrapper.toggleClass(this._getClass('readonly')); - return this.$element; - } - }, { - key: 'indeterminate', - value: function indeterminate(value) { - if (typeof value === 'undefined') { - return this.options.indeterminate; - } - if (this.options.indeterminate === Boolean(value)) { - return this.$element; - } - return this.toggleIndeterminate(); - } - }, { - key: 'toggleIndeterminate', - value: function toggleIndeterminate() { - this.options.indeterminate = !this.options.indeterminate; - this.$element.prop('indeterminate', this.options.indeterminate); - this.$wrapper.toggleClass(this._getClass('indeterminate')); - this._containerPosition(); - return this.$element; - } - }, { - key: 'inverse', - value: function inverse(value) { - if (typeof value === 'undefined') { - return this.options.inverse; - } - if (this.options.inverse === Boolean(value)) { - return this.$element; - } - return this.toggleInverse(); - } - }, { - key: 'toggleInverse', - value: function toggleInverse() { - this.$wrapper.toggleClass(this._getClass('inverse')); - var $on = this.$on.clone(true); - var $off = this.$off.clone(true); - this.$on.replaceWith($off); - this.$off.replaceWith($on); - this.$on = $off; - this.$off = $on; - this.options.inverse = !this.options.inverse; - return this.$element; - } - }, { - key: 'onColor', - value: function onColor(value) { - if (typeof value === 'undefined') { - return this.options.onColor; - } - if (this.options.onColor) { - this.$on.removeClass(this._getClass(this.options.onColor)); - } - this.$on.addClass(this._getClass(value)); - this.options.onColor = value; - return this.$element; - } - }, { - key: 'offColor', - value: function offColor(value) { - if (typeof value === 'undefined') { - return this.options.offColor; - } - if (this.options.offColor) { - this.$off.removeClass(this._getClass(this.options.offColor)); - } - this.$off.addClass(this._getClass(value)); - this.options.offColor = value; - return this.$element; - } - }, { - key: 'onText', - value: function onText(value) { - if (typeof value === 'undefined') { - return this.options.onText; - } - this.$on.html(value); - this._width(); - this._containerPosition(); - this.options.onText = value; - return this.$element; - } - }, { - key: 'offText', - value: function offText(value) { - if (typeof value === 'undefined') { - return this.options.offText; - } - this.$off.html(value); - this._width(); - this._containerPosition(); - this.options.offText = value; - return this.$element; - } - }, { - key: 'labelText', - value: function labelText(value) { - if (typeof value === 'undefined') { - return this.options.labelText; - } - this.$label.html(value); - this._width(); - this.options.labelText = value; - return this.$element; - } - }, { - key: 'handleWidth', - value: function handleWidth(value) { - if (typeof value === 'undefined') { - return this.options.handleWidth; - } - this.options.handleWidth = value; - this._width(); - this._containerPosition(); - return this.$element; - } - }, { - key: 'labelWidth', - value: function labelWidth(value) { - if (typeof value === 'undefined') { - return this.options.labelWidth; - } - this.options.labelWidth = value; - this._width(); - this._containerPosition(); - return this.$element; - } - }, { - key: 'baseClass', - value: function baseClass(value) { - return this.options.baseClass; - } - }, { - key: 'wrapperClass', - value: function wrapperClass(value) { - if (typeof value === 'undefined') { - return this.options.wrapperClass; - } - if (!value) { - value = $.fn.bootstrapSwitch.defaults.wrapperClass; - } - this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(' ')); - this.$wrapper.addClass(this._getClasses(value).join(' ')); - this.options.wrapperClass = value; - return this.$element; - } - }, { - key: 'radioAllOff', - value: function radioAllOff(value) { - if (typeof value === 'undefined') { - return this.options.radioAllOff; - } - var val = Boolean(value); - if (this.options.radioAllOff === val) { - return this.$element; - } - this.options.radioAllOff = val; - return this.$element; - } - }, { - key: 'onInit', - value: function onInit(value) { - if (typeof value === 'undefined') { - return this.options.onInit; - } - if (!value) { - value = $.fn.bootstrapSwitch.defaults.onInit; - } - this.options.onInit = value; - return this.$element; - } - }, { - key: 'onSwitchChange', - value: function onSwitchChange(value) { - if (typeof value === 'undefined') { - return this.options.onSwitchChange; - } - if (!value) { - value = $.fn.bootstrapSwitch.defaults.onSwitchChange; - } - this.options.onSwitchChange = value; - return this.$element; - } - }, { - key: 'destroy', - value: function destroy() { - var $form = this.$element.closest('form'); - if ($form.length) { - $form.off('reset.bootstrapSwitch').removeData('bootstrap-switch'); - } - this.$container.children().not(this.$element).remove(); - this.$element.unwrap().unwrap().off('.bootstrapSwitch').removeData('bootstrap-switch'); - return this.$element; - } - }, { - key: '_getElementOptions', - value: function _getElementOptions() { - return { - state: this.$element.is(':checked'), - size: this.$element.data('size'), - animate: this.$element.data('animate'), - disabled: this.$element.is(':disabled'), - readonly: this.$element.is('[readonly]'), - indeterminate: this.$element.data('indeterminate'), - inverse: this.$element.data('inverse'), - radioAllOff: this.$element.data('radio-all-off'), - onColor: this.$element.data('on-color'), - offColor: this.$element.data('off-color'), - onText: this.$element.data('on-text'), - offText: this.$element.data('off-text'), - labelText: this.$element.data('label-text'), - handleWidth: this.$element.data('handle-width'), - labelWidth: this.$element.data('label-width'), - baseClass: this.$element.data('base-class'), - wrapperClass: this.$element.data('wrapper-class') - }; - } - }, { - key: '_width', - value: function _width() { - var _this2 = this; - - var $handles = this.$on.add(this.$off).add(this.$label).css('width', ''); - var handleWidth = this.options.handleWidth === 'auto' ? Math.round(Math.max(this.$on.width(), this.$off.width())) : this.options.handleWidth; - $handles.width(handleWidth); - this.$label.width(function (index, width) { - if (_this2.options.labelWidth !== 'auto') { - return _this2.options.labelWidth; - } - if (width < handleWidth) { - return handleWidth; - } - return width; - }); - this._handleWidth = this.$on.outerWidth(); - this._labelWidth = this.$label.outerWidth(); - this.$container.width(this._handleWidth * 2 + this._labelWidth); - return this.$wrapper.width(this._handleWidth + this._labelWidth); - } - }, { - key: '_containerPosition', - value: function _containerPosition() { - var _this3 = this; - - var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.state; - var callback = arguments[1]; - - this.$container.css('margin-left', function () { - var values = [0, '-' + _this3._handleWidth + 'px']; - if (_this3.options.indeterminate) { - return '-' + _this3._handleWidth / 2 + 'px'; - } - if (state) { - if (_this3.options.inverse) { - return values[1]; - } else { - return values[0]; - } - } else { - if (_this3.options.inverse) { - return values[0]; - } else { - return values[1]; - } - } - }); - } - }, { - key: '_init', - value: function _init() { - var _this4 = this; - - var init = function init() { - _this4.setPrevOptions(); - _this4._width(); - _this4._containerPosition(); - setTimeout(function () { - if (_this4.options.animate) { - return _this4.$wrapper.addClass(_this4._getClass('animate')); - } - }, 50); - }; - if (this.$wrapper.is(':visible')) { - init(); - return; - } - var initInterval = window.setInterval(function () { - if (_this4.$wrapper.is(':visible')) { - init(); - return window.clearInterval(initInterval); - } - }, 50); - } - }, { - key: '_elementHandlers', - value: function _elementHandlers() { - var _this5 = this; - - return this.$element.on({ - 'setPreviousOptions.bootstrapSwitch': this.setPrevOptions.bind(this), - - 'previousState.bootstrapSwitch': function previousStateBootstrapSwitch() { - _this5.options = _this5.prevOptions; - if (_this5.options.indeterminate) { - _this5.$wrapper.addClass(_this5._getClass('indeterminate')); - } - _this5.$element.prop('checked', _this5.options.state).trigger('change.bootstrapSwitch', true); - }, - - 'change.bootstrapSwitch': function changeBootstrapSwitch(event, skip) { - event.preventDefault(); - event.stopImmediatePropagation(); - var state = _this5.$element.is(':checked'); - _this5._containerPosition(state); - if (state === _this5.options.state) { - return; - } - _this5.options.state = state; - _this5.$wrapper.toggleClass(_this5._getClass('off')).toggleClass(_this5._getClass('on')); - if (!skip) { - if (_this5.$element.is(':radio')) { - $('[name="' + _this5.$element.attr('name') + '"]').not(_this5.$element).prop('checked', false).trigger('change.bootstrapSwitch', true); - } - _this5.$element.trigger('switchChange.bootstrapSwitch', [state]); - } - }, - - 'focus.bootstrapSwitch': function focusBootstrapSwitch(event) { - event.preventDefault(); - _this5.$wrapper.addClass(_this5._getClass('focused')); - }, - - 'blur.bootstrapSwitch': function blurBootstrapSwitch(event) { - event.preventDefault(); - _this5.$wrapper.removeClass(_this5._getClass('focused')); - }, - - 'keydown.bootstrapSwitch': function keydownBootstrapSwitch(event) { - if (!event.which || _this5.options.disabled || _this5.options.readonly) { - return; - } - if (event.which === 37 || event.which === 39) { - event.preventDefault(); - event.stopImmediatePropagation(); - _this5.state(event.which === 39); - } - } - }); - } - }, { - key: '_handleHandlers', - value: function _handleHandlers() { - var _this6 = this; - - this.$on.on('click.bootstrapSwitch', function (event) { - event.preventDefault(); - event.stopPropagation(); - _this6.state(false); - return _this6.$element.trigger('focus.bootstrapSwitch'); - }); - return this.$off.on('click.bootstrapSwitch', function (event) { - event.preventDefault(); - event.stopPropagation(); - _this6.state(true); - return _this6.$element.trigger('focus.bootstrapSwitch'); - }); - } - }, { - key: '_labelHandlers', - value: function _labelHandlers() { - var _this7 = this; - - var handlers = { - click: function click(event) { - event.stopPropagation(); - }, - - - 'mousedown.bootstrapSwitch touchstart.bootstrapSwitch': function mousedownBootstrapSwitchTouchstartBootstrapSwitch(event) { - if (_this7._dragStart || _this7.options.disabled || _this7.options.readonly) { - return; - } - event.preventDefault(); - event.stopPropagation(); - _this7._dragStart = (event.pageX || event.originalEvent.touches[0].pageX) - parseInt(_this7.$container.css('margin-left'), 10); - if (_this7.options.animate) { - _this7.$wrapper.removeClass(_this7._getClass('animate')); - } - _this7.$element.trigger('focus.bootstrapSwitch'); - }, - - 'mousemove.bootstrapSwitch touchmove.bootstrapSwitch': function mousemoveBootstrapSwitchTouchmoveBootstrapSwitch(event) { - if (_this7._dragStart == null) { - return; - } - var difference = (event.pageX || event.originalEvent.touches[0].pageX) - _this7._dragStart; - event.preventDefault(); - if (difference < -_this7._handleWidth || difference > 0) { - return; - } - _this7._dragEnd = difference; - _this7.$container.css('margin-left', _this7._dragEnd + 'px'); - }, - - 'mouseup.bootstrapSwitch touchend.bootstrapSwitch': function mouseupBootstrapSwitchTouchendBootstrapSwitch(event) { - if (!_this7._dragStart) { - return; - } - event.preventDefault(); - if (_this7.options.animate) { - _this7.$wrapper.addClass(_this7._getClass('animate')); - } - if (_this7._dragEnd) { - var state = _this7._dragEnd > -(_this7._handleWidth / 2); - _this7._dragEnd = false; - _this7.state(_this7.options.inverse ? !state : state); - } else { - _this7.state(!_this7.options.state); - } - _this7._dragStart = false; - }, - - 'mouseleave.bootstrapSwitch': function mouseleaveBootstrapSwitch() { - _this7.$label.trigger('mouseup.bootstrapSwitch'); - } - }; - this.$label.on(handlers); - } - }, { - key: '_externalLabelHandler', - value: function _externalLabelHandler() { - var _this8 = this; - - var $externalLabel = this.$element.closest('label'); - $externalLabel.on('click', function (event) { - event.preventDefault(); - event.stopImmediatePropagation(); - if (event.target === $externalLabel[0]) { - _this8.toggleState(); - } - }); - } - }, { - key: '_formHandler', - value: function _formHandler() { - var $form = this.$element.closest('form'); - if ($form.data('bootstrap-switch')) { - return; - } - $form.on('reset.bootstrapSwitch', function () { - window.setTimeout(function () { - $form.find('input').filter(function () { - return $(this).data('bootstrap-switch'); - }).each(function () { - return $(this).bootstrapSwitch('state', this.checked); - }); - }, 1); - }).data('bootstrap-switch', true); - } - }, { - key: '_getClass', - value: function _getClass(name) { - return this.options.baseClass + '-' + name; - } - }, { - key: '_getClasses', - value: function _getClasses(classes) { - if (!$.isArray(classes)) { - return [this._getClass(classes)]; - } - return classes.map(this._getClass.bind(this)); - } - }]); - - return BootstrapSwitch; - }(); - - $.fn.bootstrapSwitch = function (option) { - for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { - args[_key2 - 1] = arguments[_key2]; - } - - function reducer(ret, next) { - var $this = $(next); - var existingData = $this.data('bootstrap-switch'); - var data = existingData || new BootstrapSwitch(next, option); - if (!existingData) { - $this.data('bootstrap-switch', data); - } - if (typeof option === 'string') { - return data[option].apply(data, args); - } - return ret; - } - return Array.prototype.reduce.call(this, reducer, this); - }; - $.fn.bootstrapSwitch.Constructor = BootstrapSwitch; - $.fn.bootstrapSwitch.defaults = { - state: true, - size: null, - animate: true, - disabled: false, - readonly: false, - indeterminate: false, - inverse: false, - radioAllOff: false, - onColor: 'primary', - offColor: 'default', - onText: 'ON', - offText: 'OFF', - labelText: ' ', - handleWidth: 'auto', - labelWidth: 'auto', - baseClass: 'bootstrap-switch', - wrapperClass: 'wrapper', - onInit: function onInit() {}, - onSwitchChange: function onSwitchChange() {} - }; -}); diff --git a/hal-core/resources/web/js/lib/bootstrap-switch.min.js b/hal-core/resources/web/js/lib/bootstrap-switch.min.js deleted file mode 100644 index 1381dc11..00000000 --- a/hal-core/resources/web/js/lib/bootstrap-switch.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-switch - Turn checkboxes and radio buttons into toggle switches. - * - * @version v3.3.4 - * @homepage https://bttstrp.github.io/bootstrap-switch - * @author Mattia Larentis (http://larentis.eu) - * @license Apache-2.0 - */ - -(function(a,b){if('function'==typeof define&&define.amd)define(['jquery'],b);else if('undefined'!=typeof exports)b(require('jquery'));else{b(a.jquery),a.bootstrapSwitch={exports:{}}.exports}})(this,function(a){'use strict';function c(j,k){if(!(j instanceof k))throw new TypeError('Cannot call a class as a function')}var d=function(j){return j&&j.__esModule?j:{default:j}}(a),e=Object.assign||function(j){for(var l,k=1;k',{class:function(){var o=[];return o.push(l.options.state?'on':'off'),l.options.size&&o.push(l.options.size),l.options.disabled&&o.push('disabled'),l.options.readonly&&o.push('readonly'),l.options.indeterminate&&o.push('indeterminate'),l.options.inverse&&o.push('inverse'),l.$element.attr('id')&&o.push('id-'+l.$element.attr('id')),o.map(l._getClass.bind(l)).concat([l.options.baseClass],l._getClasses(l.options.wrapperClass)).join(' ')}}),this.$container=g('
',{class:this._getClass('container')}),this.$on=g('',{html:this.options.onText,class:this._getClass('handle-on')+' '+this._getClass(this.options.onColor)}),this.$off=g('',{html:this.options.offText,class:this._getClass('handle-off')+' '+this._getClass(this.options.offColor)}),this.$label=g('',{html:this.options.labelText,class:this._getClass('label')}),this.$element.on('init.bootstrapSwitch',this.options.onInit.bind(this,k)),this.$element.on('switchChange.bootstrapSwitch',function(){for(var n=arguments.length,o=Array(n),p=0;p-(l._handleWidth/2);l._dragEnd=!1,l.state(l.options.inverse?!p:p)}else l.state(!l.options.state);l._dragStart=!1}},'mouseleave.bootstrapSwitch':function(){l.$label.trigger('mouseup.bootstrapSwitch')}})}},{key:'_externalLabelHandler',value:function(){var l=this,m=this.$element.closest('label');m.on('click',function(n){n.preventDefault(),n.stopImmediatePropagation(),n.target===m[0]&&l.toggleState()})}},{key:'_formHandler',value:function(){var l=this.$element.closest('form');l.data('bootstrap-switch')||l.on('reset.bootstrapSwitch',function(){window.setTimeout(function(){l.find('input').filter(function(){return g(this).data('bootstrap-switch')}).each(function(){return g(this).bootstrapSwitch('state',this.checked)})},1)}).data('bootstrap-switch',!0)}},{key:'_getClass',value:function(l){return this.options.baseClass+'-'+l}},{key:'_getClasses',value:function(l){return g.isArray(l)?l.map(this._getClass.bind(this)):[this._getClass(l)]}}]),j}();g.fn.bootstrapSwitch=function(j){for(var l=arguments.length,m=Array(13)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/c3.LICENSE.txt b/hal-core/resources/web/js/lib/c3.LICENSE.txt deleted file mode 100644 index 29ce9cb0..00000000 --- a/hal-core/resources/web/js/lib/c3.LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Masayuki Tanaka - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/hal-core/resources/web/js/lib/d3.LICENSE b/hal-core/resources/web/js/lib/d3.LICENSE deleted file mode 100644 index b0145150..00000000 --- a/hal-core/resources/web/js/lib/d3.LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2010-2021 Mike Bostock - -Permission to use, copy, modify, and/or distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright notice -and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. diff --git a/hal-core/resources/web/js/lib/d3.LICENSE.txt b/hal-core/resources/web/js/lib/d3.LICENSE.txt deleted file mode 100644 index b0145150..00000000 --- a/hal-core/resources/web/js/lib/d3.LICENSE.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2010-2021 Mike Bostock - -Permission to use, copy, modify, and/or distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright notice -and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. diff --git a/hal-core/resources/web/js/lib/force-graph.LICENSE.txt b/hal-core/resources/web/js/lib/force-graph.LICENSE.txt deleted file mode 100644 index a36ddd4f..00000000 --- a/hal-core/resources/web/js/lib/force-graph.LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Vasco Asturiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/hal-core/resources/web/js/lib/force-graph.js b/hal-core/resources/web/js/lib/force-graph.js deleted file mode 100644 index 13d6e200..00000000 --- a/hal-core/resources/web/js/lib/force-graph.js +++ /dev/null @@ -1,12201 +0,0 @@ -// Version 1.43.4 force-graph - https://github.com/vasturiano/force-graph -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ForceGraph = factory()); -})(this, (function () { 'use strict'; - - function styleInject(css, ref) { - if ( ref === void 0 ) ref = {}; - var insertAt = ref.insertAt; - - if (!css || typeof document === 'undefined') { return; } - - var head = document.head || document.getElementsByTagName('head')[0]; - var style = document.createElement('style'); - style.type = 'text/css'; - - if (insertAt === 'top') { - if (head.firstChild) { - head.insertBefore(style, head.firstChild); - } else { - head.appendChild(style); - } - } else { - head.appendChild(style); - } - - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.appendChild(document.createTextNode(css)); - } - } - - var css_248z = ".force-graph-container canvas {\n display: block;\n user-select: none;\n outline: none;\n -webkit-tap-highlight-color: transparent;\n}\n\n.force-graph-container .graph-tooltip {\n position: absolute;\n top: 0;\n font-family: sans-serif;\n font-size: 16px;\n padding: 4px;\n border-radius: 3px;\n color: #eee;\n background: rgba(0,0,0,0.65);\n visibility: hidden; /* by default */\n}\n\n.force-graph-container .clickable {\n cursor: pointer;\n}\n\n.force-graph-container .grabbable {\n cursor: move;\n cursor: grab;\n cursor: -moz-grab;\n cursor: -webkit-grab;\n}\n\n.force-graph-container .grabbable:active {\n cursor: grabbing;\n cursor: -moz-grabbing;\n cursor: -webkit-grabbing;\n}\n"; - styleInject(css_248z); - - function _iterableToArrayLimit$2(arr, i) { - var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; - if (null != _i) { - var _s, - _e, - _x, - _r, - _arr = [], - _n = !0, - _d = !1; - try { - if (_x = (_i = _i.call(arr)).next, 0 === i) { - if (Object(_i) !== _i) return; - _n = !1; - } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); - } catch (err) { - _d = !0, _e = err; - } finally { - try { - if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; - } finally { - if (_d) throw _e; - } - } - return _arr; - } - } - function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - return keys; - } - function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - return target; - } - function _typeof$1(obj) { - "@babel/helpers - typeof"; - - return _typeof$1 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof$1(obj); - } - function _defineProperty(obj, key, value) { - key = _toPropertyKey$3(key); - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - return obj; - } - function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - return _setPrototypeOf(o, p); - } - function _isNativeReflectConstruct() { - if (typeof Reflect === "undefined" || !Reflect.construct) return false; - if (Reflect.construct.sham) return false; - if (typeof Proxy === "function") return true; - try { - Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); - return true; - } catch (e) { - return false; - } - } - function _construct(Parent, args, Class) { - if (_isNativeReflectConstruct()) { - _construct = Reflect.construct.bind(); - } else { - _construct = function _construct(Parent, args, Class) { - var a = [null]; - a.push.apply(a, args); - var Constructor = Function.bind.apply(Parent, a); - var instance = new Constructor(); - if (Class) _setPrototypeOf(instance, Class.prototype); - return instance; - }; - } - return _construct.apply(null, arguments); - } - function _slicedToArray$2(arr, i) { - return _arrayWithHoles$2(arr) || _iterableToArrayLimit$2(arr, i) || _unsupportedIterableToArray$3(arr, i) || _nonIterableRest$2(); - } - function _toConsumableArray$2(arr) { - return _arrayWithoutHoles$2(arr) || _iterableToArray$2(arr) || _unsupportedIterableToArray$3(arr) || _nonIterableSpread$2(); - } - function _arrayWithoutHoles$2(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray$3(arr); - } - function _arrayWithHoles$2(arr) { - if (Array.isArray(arr)) return arr; - } - function _iterableToArray$2(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); - } - function _unsupportedIterableToArray$3(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); - } - function _arrayLikeToArray$3(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; - } - function _nonIterableSpread$2() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _nonIterableRest$2() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _toPrimitive$3(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey$3(arg) { - var key = _toPrimitive$3(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } - - var xhtml = "http://www.w3.org/1999/xhtml"; - - var namespaces = { - svg: "http://www.w3.org/2000/svg", - xhtml: xhtml, - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" - }; - - function namespace(name) { - var prefix = name += "", i = prefix.indexOf(":"); - if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); - return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name; // eslint-disable-line no-prototype-builtins - } - - function creatorInherit(name) { - return function() { - var document = this.ownerDocument, - uri = this.namespaceURI; - return uri === xhtml && document.documentElement.namespaceURI === xhtml - ? document.createElement(name) - : document.createElementNS(uri, name); - }; - } - - function creatorFixed(fullname) { - return function() { - return this.ownerDocument.createElementNS(fullname.space, fullname.local); - }; - } - - function creator(name) { - var fullname = namespace(name); - return (fullname.local - ? creatorFixed - : creatorInherit)(fullname); - } - - function none() {} - - function selector(selector) { - return selector == null ? none : function() { - return this.querySelector(selector); - }; - } - - function selection_select(select) { - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - } - } - } - - return new Selection$1(subgroups, this._parents); - } - - // Given something array like (or null), returns something that is strictly an - // array. This is used to ensure that array-like objects passed to d3.selectAll - // or selection.selectAll are converted into proper arrays when creating a - // selection; we don’t ever want to create a selection backed by a live - // HTMLCollection or NodeList. However, note that selection.selectAll will use a - // static NodeList as a group, since it safely derived from querySelectorAll. - function array(x) { - return x == null ? [] : Array.isArray(x) ? x : Array.from(x); - } - - function empty() { - return []; - } - - function selectorAll(selector) { - return selector == null ? empty : function() { - return this.querySelectorAll(selector); - }; - } - - function arrayAll(select) { - return function() { - return array(select.apply(this, arguments)); - }; - } - - function selection_selectAll(select) { - if (typeof select === "function") select = arrayAll(select); - else select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - subgroups.push(select.call(node, node.__data__, i, group)); - parents.push(node); - } - } - } - - return new Selection$1(subgroups, parents); - } - - function matcher(selector) { - return function() { - return this.matches(selector); - }; - } - - function childMatcher(selector) { - return function(node) { - return node.matches(selector); - }; - } - - var find$1 = Array.prototype.find; - - function childFind(match) { - return function() { - return find$1.call(this.children, match); - }; - } - - function childFirst() { - return this.firstElementChild; - } - - function selection_selectChild(match) { - return this.select(match == null ? childFirst - : childFind(typeof match === "function" ? match : childMatcher(match))); - } - - var filter = Array.prototype.filter; - - function children() { - return Array.from(this.children); - } - - function childrenFilter(match) { - return function() { - return filter.call(this.children, match); - }; - } - - function selection_selectChildren(match) { - return this.selectAll(match == null ? children - : childrenFilter(typeof match === "function" ? match : childMatcher(match))); - } - - function selection_filter(match) { - if (typeof match !== "function") match = matcher(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Selection$1(subgroups, this._parents); - } - - function sparse(update) { - return new Array(update.length); - } - - function selection_enter() { - return new Selection$1(this._enter || this._groups.map(sparse), this._parents); - } - - function EnterNode(parent, datum) { - this.ownerDocument = parent.ownerDocument; - this.namespaceURI = parent.namespaceURI; - this._next = null; - this._parent = parent; - this.__data__ = datum; - } - - EnterNode.prototype = { - constructor: EnterNode, - appendChild: function(child) { return this._parent.insertBefore(child, this._next); }, - insertBefore: function(child, next) { return this._parent.insertBefore(child, next); }, - querySelector: function(selector) { return this._parent.querySelector(selector); }, - querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); } - }; - - function constant$4(x) { - return function() { - return x; - }; - } - - function bindIndex(parent, group, enter, update, exit, data) { - var i = 0, - node, - groupLength = group.length, - dataLength = data.length; - - // Put any non-null nodes that fit into update. - // Put any null nodes into enter. - // Put any remaining data into enter. - for (; i < dataLength; ++i) { - if (node = group[i]) { - node.__data__ = data[i]; - update[i] = node; - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Put any non-null nodes that don’t fit into exit. - for (; i < groupLength; ++i) { - if (node = group[i]) { - exit[i] = node; - } - } - } - - function bindKey(parent, group, enter, update, exit, data, key) { - var i, - node, - nodeByKeyValue = new Map, - groupLength = group.length, - dataLength = data.length, - keyValues = new Array(groupLength), - keyValue; - - // Compute the key for each node. - // If multiple nodes have the same key, the duplicates are added to exit. - for (i = 0; i < groupLength; ++i) { - if (node = group[i]) { - keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + ""; - if (nodeByKeyValue.has(keyValue)) { - exit[i] = node; - } else { - nodeByKeyValue.set(keyValue, node); - } - } - } - - // Compute the key for each datum. - // If there a node associated with this key, join and add it to update. - // If there is not (or the key is a duplicate), add it to enter. - for (i = 0; i < dataLength; ++i) { - keyValue = key.call(parent, data[i], i, data) + ""; - if (node = nodeByKeyValue.get(keyValue)) { - update[i] = node; - node.__data__ = data[i]; - nodeByKeyValue.delete(keyValue); - } else { - enter[i] = new EnterNode(parent, data[i]); - } - } - - // Add any remaining nodes that were not bound to data to exit. - for (i = 0; i < groupLength; ++i) { - if ((node = group[i]) && (nodeByKeyValue.get(keyValues[i]) === node)) { - exit[i] = node; - } - } - } - - function datum(node) { - return node.__data__; - } - - function selection_data(value, key) { - if (!arguments.length) return Array.from(this, datum); - - var bind = key ? bindKey : bindIndex, - parents = this._parents, - groups = this._groups; - - if (typeof value !== "function") value = constant$4(value); - - for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) { - var parent = parents[j], - group = groups[j], - groupLength = group.length, - data = arraylike(value.call(parent, parent && parent.__data__, j, parents)), - dataLength = data.length, - enterGroup = enter[j] = new Array(dataLength), - updateGroup = update[j] = new Array(dataLength), - exitGroup = exit[j] = new Array(groupLength); - - bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); - - // Now connect the enter nodes to their following update node, such that - // appendChild can insert the materialized enter node before this node, - // rather than at the end of the parent node. - for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) { - if (previous = enterGroup[i0]) { - if (i0 >= i1) i1 = i0 + 1; - while (!(next = updateGroup[i1]) && ++i1 < dataLength); - previous._next = next || null; - } - } - } - - update = new Selection$1(update, parents); - update._enter = enter; - update._exit = exit; - return update; - } - - // Given some data, this returns an array-like view of it: an object that - // exposes a length property and allows numeric indexing. Note that unlike - // selectAll, this isn’t worried about “live†collections because the resulting - // array will only be used briefly while data is being bound. (It is possible to - // cause the data to change while iterating by using a key function, but please - // don’t; we’d rather avoid a gratuitous copy.) - function arraylike(data) { - return typeof data === "object" && "length" in data - ? data // Array, TypedArray, NodeList, array-like - : Array.from(data); // Map, Set, iterable, string, or anything else - } - - function selection_exit() { - return new Selection$1(this._exit || this._groups.map(sparse), this._parents); - } - - function selection_join(onenter, onupdate, onexit) { - var enter = this.enter(), update = this, exit = this.exit(); - if (typeof onenter === "function") { - enter = onenter(enter); - if (enter) enter = enter.selection(); - } else { - enter = enter.append(onenter + ""); - } - if (onupdate != null) { - update = onupdate(update); - if (update) update = update.selection(); - } - if (onexit == null) exit.remove(); else onexit(exit); - return enter && update ? enter.merge(update).order() : update; - } - - function selection_merge(context) { - var selection = context.selection ? context.selection() : context; - - for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Selection$1(merges, this._parents); - } - - function selection_order() { - - for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) { - for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) { - if (node = group[i]) { - if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - - return this; - } - - function selection_sort(compare) { - if (!compare) compare = ascending; - - function compareNode(a, b) { - return a && b ? compare(a.__data__, b.__data__) : !a - !b; - } - - for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group[i]) { - sortgroup[i] = node; - } - } - sortgroup.sort(compareNode); - } - - return new Selection$1(sortgroups, this._parents).order(); - } - - function ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - - function selection_call() { - var callback = arguments[0]; - arguments[0] = this; - callback.apply(null, arguments); - return this; - } - - function selection_nodes() { - return Array.from(this); - } - - function selection_node() { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length; i < n; ++i) { - var node = group[i]; - if (node) return node; - } - } - - return null; - } - - function selection_size() { - let size = 0; - for (const node of this) ++size; // eslint-disable-line no-unused-vars - return size; - } - - function selection_empty() { - return !this.node(); - } - - function selection_each(callback) { - - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { - if (node = group[i]) callback.call(node, node.__data__, i, group); - } - } - - return this; - } - - function attrRemove$1(name) { - return function() { - this.removeAttribute(name); - }; - } - - function attrRemoveNS$1(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; - } - - function attrConstant$1(name, value) { - return function() { - this.setAttribute(name, value); - }; - } - - function attrConstantNS$1(fullname, value) { - return function() { - this.setAttributeNS(fullname.space, fullname.local, value); - }; - } - - function attrFunction$1(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttribute(name); - else this.setAttribute(name, v); - }; - } - - function attrFunctionNS$1(fullname, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.removeAttributeNS(fullname.space, fullname.local); - else this.setAttributeNS(fullname.space, fullname.local, v); - }; - } - - function selection_attr(name, value) { - var fullname = namespace(name); - - if (arguments.length < 2) { - var node = this.node(); - return fullname.local - ? node.getAttributeNS(fullname.space, fullname.local) - : node.getAttribute(fullname); - } - - return this.each((value == null - ? (fullname.local ? attrRemoveNS$1 : attrRemove$1) : (typeof value === "function" - ? (fullname.local ? attrFunctionNS$1 : attrFunction$1) - : (fullname.local ? attrConstantNS$1 : attrConstant$1)))(fullname, value)); - } - - function defaultView(node) { - return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node - || (node.document && node) // node is a Window - || node.defaultView; // node is a Document - } - - function styleRemove$1(name) { - return function() { - this.style.removeProperty(name); - }; - } - - function styleConstant$1(name, value, priority) { - return function() { - this.style.setProperty(name, value, priority); - }; - } - - function styleFunction$1(name, value, priority) { - return function() { - var v = value.apply(this, arguments); - if (v == null) this.style.removeProperty(name); - else this.style.setProperty(name, v, priority); - }; - } - - function selection_style(name, value, priority) { - return arguments.length > 1 - ? this.each((value == null - ? styleRemove$1 : typeof value === "function" - ? styleFunction$1 - : styleConstant$1)(name, value, priority == null ? "" : priority)) - : styleValue(this.node(), name); - } - - function styleValue(node, name) { - return node.style.getPropertyValue(name) - || defaultView(node).getComputedStyle(node, null).getPropertyValue(name); - } - - function propertyRemove(name) { - return function() { - delete this[name]; - }; - } - - function propertyConstant(name, value) { - return function() { - this[name] = value; - }; - } - - function propertyFunction(name, value) { - return function() { - var v = value.apply(this, arguments); - if (v == null) delete this[name]; - else this[name] = v; - }; - } - - function selection_property(name, value) { - return arguments.length > 1 - ? this.each((value == null - ? propertyRemove : typeof value === "function" - ? propertyFunction - : propertyConstant)(name, value)) - : this.node()[name]; - } - - function classArray(string) { - return string.trim().split(/^|\s+/); - } - - function classList(node) { - return node.classList || new ClassList(node); - } - - function ClassList(node) { - this._node = node; - this._names = classArray(node.getAttribute("class") || ""); - } - - ClassList.prototype = { - add: function(name) { - var i = this._names.indexOf(name); - if (i < 0) { - this._names.push(name); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - remove: function(name) { - var i = this._names.indexOf(name); - if (i >= 0) { - this._names.splice(i, 1); - this._node.setAttribute("class", this._names.join(" ")); - } - }, - contains: function(name) { - return this._names.indexOf(name) >= 0; - } - }; - - function classedAdd(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.add(names[i]); - } - - function classedRemove(node, names) { - var list = classList(node), i = -1, n = names.length; - while (++i < n) list.remove(names[i]); - } - - function classedTrue(names) { - return function() { - classedAdd(this, names); - }; - } - - function classedFalse(names) { - return function() { - classedRemove(this, names); - }; - } - - function classedFunction(names, value) { - return function() { - (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names); - }; - } - - function selection_classed(name, value) { - var names = classArray(name + ""); - - if (arguments.length < 2) { - var list = classList(this.node()), i = -1, n = names.length; - while (++i < n) if (!list.contains(names[i])) return false; - return true; - } - - return this.each((typeof value === "function" - ? classedFunction : value - ? classedTrue - : classedFalse)(names, value)); - } - - function textRemove() { - this.textContent = ""; - } - - function textConstant$1(value) { - return function() { - this.textContent = value; - }; - } - - function textFunction$1(value) { - return function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - }; - } - - function selection_text(value) { - return arguments.length - ? this.each(value == null - ? textRemove : (typeof value === "function" - ? textFunction$1 - : textConstant$1)(value)) - : this.node().textContent; - } - - function htmlRemove() { - this.innerHTML = ""; - } - - function htmlConstant(value) { - return function() { - this.innerHTML = value; - }; - } - - function htmlFunction(value) { - return function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - }; - } - - function selection_html(value) { - return arguments.length - ? this.each(value == null - ? htmlRemove : (typeof value === "function" - ? htmlFunction - : htmlConstant)(value)) - : this.node().innerHTML; - } - - function raise() { - if (this.nextSibling) this.parentNode.appendChild(this); - } - - function selection_raise() { - return this.each(raise); - } - - function lower() { - if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild); - } - - function selection_lower() { - return this.each(lower); - } - - function selection_append(name) { - var create = typeof name === "function" ? name : creator(name); - return this.select(function() { - return this.appendChild(create.apply(this, arguments)); - }); - } - - function constantNull() { - return null; - } - - function selection_insert(name, before) { - var create = typeof name === "function" ? name : creator(name), - select = before == null ? constantNull : typeof before === "function" ? before : selector(before); - return this.select(function() { - return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null); - }); - } - - function remove() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - } - - function selection_remove() { - return this.each(remove); - } - - function selection_cloneShallow() { - var clone = this.cloneNode(false), parent = this.parentNode; - return parent ? parent.insertBefore(clone, this.nextSibling) : clone; - } - - function selection_cloneDeep() { - var clone = this.cloneNode(true), parent = this.parentNode; - return parent ? parent.insertBefore(clone, this.nextSibling) : clone; - } - - function selection_clone(deep) { - return this.select(deep ? selection_cloneDeep : selection_cloneShallow); - } - - function selection_datum(value) { - return arguments.length - ? this.property("__data__", value) - : this.node().__data__; - } - - function contextListener(listener) { - return function(event) { - listener.call(this, event, this.__data__); - }; - } - - function parseTypenames$1(typenames) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - return {type: t, name: name}; - }); - } - - function onRemove(typename) { - return function() { - var on = this.__on; - if (!on) return; - for (var j = 0, i = -1, m = on.length, o; j < m; ++j) { - if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.options); - } else { - on[++i] = o; - } - } - if (++i) on.length = i; - else delete this.__on; - }; - } - - function onAdd(typename, value, options) { - return function() { - var on = this.__on, o, listener = contextListener(value); - if (on) for (var j = 0, m = on.length; j < m; ++j) { - if ((o = on[j]).type === typename.type && o.name === typename.name) { - this.removeEventListener(o.type, o.listener, o.options); - this.addEventListener(o.type, o.listener = listener, o.options = options); - o.value = value; - return; - } - } - this.addEventListener(typename.type, listener, options); - o = {type: typename.type, name: typename.name, value: value, listener: listener, options: options}; - if (!on) this.__on = [o]; - else on.push(o); - }; - } - - function selection_on(typename, value, options) { - var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t; - - if (arguments.length < 2) { - var on = this.node().__on; - if (on) for (var j = 0, m = on.length, o; j < m; ++j) { - for (i = 0, o = on[j]; i < n; ++i) { - if ((t = typenames[i]).type === o.type && t.name === o.name) { - return o.value; - } - } - } - return; - } - - on = value ? onAdd : onRemove; - for (i = 0; i < n; ++i) this.each(on(typenames[i], value, options)); - return this; - } - - function dispatchEvent(node, type, params) { - var window = defaultView(node), - event = window.CustomEvent; - - if (typeof event === "function") { - event = new event(type, params); - } else { - event = window.document.createEvent("Event"); - if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; - else event.initEvent(type, false, false); - } - - node.dispatchEvent(event); - } - - function dispatchConstant(type, params) { - return function() { - return dispatchEvent(this, type, params); - }; - } - - function dispatchFunction(type, params) { - return function() { - return dispatchEvent(this, type, params.apply(this, arguments)); - }; - } - - function selection_dispatch(type, params) { - return this.each((typeof params === "function" - ? dispatchFunction - : dispatchConstant)(type, params)); - } - - function* selection_iterator() { - for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) { - if (node = group[i]) yield node; - } - } - } - - var root$2 = [null]; - - function Selection$1(groups, parents) { - this._groups = groups; - this._parents = parents; - } - - function selection() { - return new Selection$1([[document.documentElement]], root$2); - } - - function selection_selection() { - return this; - } - - Selection$1.prototype = selection.prototype = { - constructor: Selection$1, - select: selection_select, - selectAll: selection_selectAll, - selectChild: selection_selectChild, - selectChildren: selection_selectChildren, - filter: selection_filter, - data: selection_data, - enter: selection_enter, - exit: selection_exit, - join: selection_join, - merge: selection_merge, - selection: selection_selection, - order: selection_order, - sort: selection_sort, - call: selection_call, - nodes: selection_nodes, - node: selection_node, - size: selection_size, - empty: selection_empty, - each: selection_each, - attr: selection_attr, - style: selection_style, - property: selection_property, - classed: selection_classed, - text: selection_text, - html: selection_html, - raise: selection_raise, - lower: selection_lower, - append: selection_append, - insert: selection_insert, - remove: selection_remove, - clone: selection_clone, - datum: selection_datum, - on: selection_on, - dispatch: selection_dispatch, - [Symbol.iterator]: selection_iterator - }; - - function d3Select(selector) { - return typeof selector === "string" - ? new Selection$1([[document.querySelector(selector)]], [document.documentElement]) - : new Selection$1([[selector]], root$2); - } - - function sourceEvent(event) { - let sourceEvent; - while (sourceEvent = event.sourceEvent) event = sourceEvent; - return event; - } - - function pointer(event, node) { - event = sourceEvent(event); - if (node === undefined) node = event.currentTarget; - if (node) { - var svg = node.ownerSVGElement || node; - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - point.x = event.clientX, point.y = event.clientY; - point = point.matrixTransform(node.getScreenCTM().inverse()); - return [point.x, point.y]; - } - if (node.getBoundingClientRect) { - var rect = node.getBoundingClientRect(); - return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop]; - } - } - return [event.pageX, event.pageY]; - } - - var noop = {value: () => {}}; - - function dispatch() { - for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) { - if (!(t = arguments[i] + "") || (t in _) || /[\s.]/.test(t)) throw new Error("illegal type: " + t); - _[t] = []; - } - return new Dispatch(_); - } - - function Dispatch(_) { - this._ = _; - } - - function parseTypenames(typenames, types) { - return typenames.trim().split(/^|\s+/).map(function(t) { - var name = "", i = t.indexOf("."); - if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i); - if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t); - return {type: t, name: name}; - }); - } - - Dispatch.prototype = dispatch.prototype = { - constructor: Dispatch, - on: function(typename, callback) { - var _ = this._, - T = parseTypenames(typename + "", _), - t, - i = -1, - n = T.length; - - // If no callback was specified, return the callback of the given type and name. - if (arguments.length < 2) { - while (++i < n) if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) return t; - return; - } - - // If a type was specified, set the callback for the given type and name. - // Otherwise, if a null callback was specified, remove callbacks of the given name. - if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback); - while (++i < n) { - if (t = (typename = T[i]).type) _[t] = set$1(_[t], typename.name, callback); - else if (callback == null) for (t in _) _[t] = set$1(_[t], typename.name, null); - } - - return this; - }, - copy: function() { - var copy = {}, _ = this._; - for (var t in _) copy[t] = _[t].slice(); - return new Dispatch(copy); - }, - call: function(type, that) { - if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2]; - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - }, - apply: function(type, that, args) { - if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type); - for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args); - } - }; - - function get$1(type, name) { - for (var i = 0, n = type.length, c; i < n; ++i) { - if ((c = type[i]).name === name) { - return c.value; - } - } - } - - function set$1(type, name, callback) { - for (var i = 0, n = type.length; i < n; ++i) { - if (type[i].name === name) { - type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1)); - break; - } - } - if (callback != null) type.push({name: name, value: callback}); - return type; - } - - // These are typically used in conjunction with noevent to ensure that we can - // preventDefault on the event. - const nonpassive = {passive: false}; - const nonpassivecapture = {capture: true, passive: false}; - - function nopropagation$1(event) { - event.stopImmediatePropagation(); - } - - function noevent$1(event) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - - function dragDisable(view) { - var root = view.document.documentElement, - selection = d3Select(view).on("dragstart.drag", noevent$1, nonpassivecapture); - if ("onselectstart" in root) { - selection.on("selectstart.drag", noevent$1, nonpassivecapture); - } else { - root.__noselect = root.style.MozUserSelect; - root.style.MozUserSelect = "none"; - } - } - - function yesdrag(view, noclick) { - var root = view.document.documentElement, - selection = d3Select(view).on("dragstart.drag", null); - if (noclick) { - selection.on("click.drag", noevent$1, nonpassivecapture); - setTimeout(function() { selection.on("click.drag", null); }, 0); - } - if ("onselectstart" in root) { - selection.on("selectstart.drag", null); - } else { - root.style.MozUserSelect = root.__noselect; - delete root.__noselect; - } - } - - var constant$3 = x => () => x; - - function DragEvent(type, { - sourceEvent, - subject, - target, - identifier, - active, - x, y, dx, dy, - dispatch - }) { - Object.defineProperties(this, { - type: {value: type, enumerable: true, configurable: true}, - sourceEvent: {value: sourceEvent, enumerable: true, configurable: true}, - subject: {value: subject, enumerable: true, configurable: true}, - target: {value: target, enumerable: true, configurable: true}, - identifier: {value: identifier, enumerable: true, configurable: true}, - active: {value: active, enumerable: true, configurable: true}, - x: {value: x, enumerable: true, configurable: true}, - y: {value: y, enumerable: true, configurable: true}, - dx: {value: dx, enumerable: true, configurable: true}, - dy: {value: dy, enumerable: true, configurable: true}, - _: {value: dispatch} - }); - } - - DragEvent.prototype.on = function() { - var value = this._.on.apply(this._, arguments); - return value === this._ ? this : value; - }; - - // Ignore right-click, since that should open the context menu. - function defaultFilter$1(event) { - return !event.ctrlKey && !event.button; - } - - function defaultContainer() { - return this.parentNode; - } - - function defaultSubject(event, d) { - return d == null ? {x: event.x, y: event.y} : d; - } - - function defaultTouchable$1() { - return navigator.maxTouchPoints || ("ontouchstart" in this); - } - - function d3Drag() { - var filter = defaultFilter$1, - container = defaultContainer, - subject = defaultSubject, - touchable = defaultTouchable$1, - gestures = {}, - listeners = dispatch("start", "drag", "end"), - active = 0, - mousedownx, - mousedowny, - mousemoving, - touchending, - clickDistance2 = 0; - - function drag(selection) { - selection - .on("mousedown.drag", mousedowned) - .filter(touchable) - .on("touchstart.drag", touchstarted) - .on("touchmove.drag", touchmoved, nonpassive) - .on("touchend.drag touchcancel.drag", touchended) - .style("touch-action", "none") - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); - } - - function mousedowned(event, d) { - if (touchending || !filter.call(this, event, d)) return; - var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse"); - if (!gesture) return; - d3Select(event.view) - .on("mousemove.drag", mousemoved, nonpassivecapture) - .on("mouseup.drag", mouseupped, nonpassivecapture); - dragDisable(event.view); - nopropagation$1(event); - mousemoving = false; - mousedownx = event.clientX; - mousedowny = event.clientY; - gesture("start", event); - } - - function mousemoved(event) { - noevent$1(event); - if (!mousemoving) { - var dx = event.clientX - mousedownx, dy = event.clientY - mousedowny; - mousemoving = dx * dx + dy * dy > clickDistance2; - } - gestures.mouse("drag", event); - } - - function mouseupped(event) { - d3Select(event.view).on("mousemove.drag mouseup.drag", null); - yesdrag(event.view, mousemoving); - noevent$1(event); - gestures.mouse("end", event); - } - - function touchstarted(event, d) { - if (!filter.call(this, event, d)) return; - var touches = event.changedTouches, - c = container.call(this, event, d), - n = touches.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) { - nopropagation$1(event); - gesture("start", event, touches[i]); - } - } - } - - function touchmoved(event) { - var touches = event.changedTouches, - n = touches.length, i, gesture; - - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches[i].identifier]) { - noevent$1(event); - gesture("drag", event, touches[i]); - } - } - } - - function touchended(event) { - var touches = event.changedTouches, - n = touches.length, i, gesture; - - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! - for (i = 0; i < n; ++i) { - if (gesture = gestures[touches[i].identifier]) { - nopropagation$1(event); - gesture("end", event, touches[i]); - } - } - } - - function beforestart(that, container, event, d, identifier, touch) { - var dispatch = listeners.copy(), - p = pointer(touch || event, container), dx, dy, - s; - - if ((s = subject.call(that, new DragEvent("beforestart", { - sourceEvent: event, - target: drag, - identifier, - active, - x: p[0], - y: p[1], - dx: 0, - dy: 0, - dispatch - }), d)) == null) return; - - dx = s.x - p[0] || 0; - dy = s.y - p[1] || 0; - - return function gesture(type, event, touch) { - var p0 = p, n; - switch (type) { - case "start": gestures[identifier] = gesture, n = active++; break; - case "end": delete gestures[identifier], --active; // falls through - case "drag": p = pointer(touch || event, container), n = active; break; - } - dispatch.call( - type, - that, - new DragEvent(type, { - sourceEvent: event, - subject: s, - target: drag, - identifier, - active: n, - x: p[0] + dx, - y: p[1] + dy, - dx: p[0] - p0[0], - dy: p[1] - p0[1], - dispatch - }), - d - ); - }; - } - - drag.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), drag) : filter; - }; - - drag.container = function(_) { - return arguments.length ? (container = typeof _ === "function" ? _ : constant$3(_), drag) : container; - }; - - drag.subject = function(_) { - return arguments.length ? (subject = typeof _ === "function" ? _ : constant$3(_), drag) : subject; - }; - - drag.touchable = function(_) { - return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), drag) : touchable; - }; - - drag.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? drag : value; - }; - - drag.clickDistance = function(_) { - return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2); - }; - - return drag; - } - - function define(constructor, factory, prototype) { - constructor.prototype = factory.prototype = prototype; - prototype.constructor = constructor; - } - - function extend(parent, definition) { - var prototype = Object.create(parent.prototype); - for (var key in definition) prototype[key] = definition[key]; - return prototype; - } - - function Color() {} - - var darker = 0.7; - var brighter = 1 / darker; - - var reI = "\\s*([+-]?\\d+)\\s*", - reN = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*", - reP = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*", - reHex = /^#([0-9a-f]{3,8})$/, - reRgbInteger = new RegExp(`^rgb\\(${reI},${reI},${reI}\\)$`), - reRgbPercent = new RegExp(`^rgb\\(${reP},${reP},${reP}\\)$`), - reRgbaInteger = new RegExp(`^rgba\\(${reI},${reI},${reI},${reN}\\)$`), - reRgbaPercent = new RegExp(`^rgba\\(${reP},${reP},${reP},${reN}\\)$`), - reHslPercent = new RegExp(`^hsl\\(${reN},${reP},${reP}\\)$`), - reHslaPercent = new RegExp(`^hsla\\(${reN},${reP},${reP},${reN}\\)$`); - - var named = { - aliceblue: 0xf0f8ff, - antiquewhite: 0xfaebd7, - aqua: 0x00ffff, - aquamarine: 0x7fffd4, - azure: 0xf0ffff, - beige: 0xf5f5dc, - bisque: 0xffe4c4, - black: 0x000000, - blanchedalmond: 0xffebcd, - blue: 0x0000ff, - blueviolet: 0x8a2be2, - brown: 0xa52a2a, - burlywood: 0xdeb887, - cadetblue: 0x5f9ea0, - chartreuse: 0x7fff00, - chocolate: 0xd2691e, - coral: 0xff7f50, - cornflowerblue: 0x6495ed, - cornsilk: 0xfff8dc, - crimson: 0xdc143c, - cyan: 0x00ffff, - darkblue: 0x00008b, - darkcyan: 0x008b8b, - darkgoldenrod: 0xb8860b, - darkgray: 0xa9a9a9, - darkgreen: 0x006400, - darkgrey: 0xa9a9a9, - darkkhaki: 0xbdb76b, - darkmagenta: 0x8b008b, - darkolivegreen: 0x556b2f, - darkorange: 0xff8c00, - darkorchid: 0x9932cc, - darkred: 0x8b0000, - darksalmon: 0xe9967a, - darkseagreen: 0x8fbc8f, - darkslateblue: 0x483d8b, - darkslategray: 0x2f4f4f, - darkslategrey: 0x2f4f4f, - darkturquoise: 0x00ced1, - darkviolet: 0x9400d3, - deeppink: 0xff1493, - deepskyblue: 0x00bfff, - dimgray: 0x696969, - dimgrey: 0x696969, - dodgerblue: 0x1e90ff, - firebrick: 0xb22222, - floralwhite: 0xfffaf0, - forestgreen: 0x228b22, - fuchsia: 0xff00ff, - gainsboro: 0xdcdcdc, - ghostwhite: 0xf8f8ff, - gold: 0xffd700, - goldenrod: 0xdaa520, - gray: 0x808080, - green: 0x008000, - greenyellow: 0xadff2f, - grey: 0x808080, - honeydew: 0xf0fff0, - hotpink: 0xff69b4, - indianred: 0xcd5c5c, - indigo: 0x4b0082, - ivory: 0xfffff0, - khaki: 0xf0e68c, - lavender: 0xe6e6fa, - lavenderblush: 0xfff0f5, - lawngreen: 0x7cfc00, - lemonchiffon: 0xfffacd, - lightblue: 0xadd8e6, - lightcoral: 0xf08080, - lightcyan: 0xe0ffff, - lightgoldenrodyellow: 0xfafad2, - lightgray: 0xd3d3d3, - lightgreen: 0x90ee90, - lightgrey: 0xd3d3d3, - lightpink: 0xffb6c1, - lightsalmon: 0xffa07a, - lightseagreen: 0x20b2aa, - lightskyblue: 0x87cefa, - lightslategray: 0x778899, - lightslategrey: 0x778899, - lightsteelblue: 0xb0c4de, - lightyellow: 0xffffe0, - lime: 0x00ff00, - limegreen: 0x32cd32, - linen: 0xfaf0e6, - magenta: 0xff00ff, - maroon: 0x800000, - mediumaquamarine: 0x66cdaa, - mediumblue: 0x0000cd, - mediumorchid: 0xba55d3, - mediumpurple: 0x9370db, - mediumseagreen: 0x3cb371, - mediumslateblue: 0x7b68ee, - mediumspringgreen: 0x00fa9a, - mediumturquoise: 0x48d1cc, - mediumvioletred: 0xc71585, - midnightblue: 0x191970, - mintcream: 0xf5fffa, - mistyrose: 0xffe4e1, - moccasin: 0xffe4b5, - navajowhite: 0xffdead, - navy: 0x000080, - oldlace: 0xfdf5e6, - olive: 0x808000, - olivedrab: 0x6b8e23, - orange: 0xffa500, - orangered: 0xff4500, - orchid: 0xda70d6, - palegoldenrod: 0xeee8aa, - palegreen: 0x98fb98, - paleturquoise: 0xafeeee, - palevioletred: 0xdb7093, - papayawhip: 0xffefd5, - peachpuff: 0xffdab9, - peru: 0xcd853f, - pink: 0xffc0cb, - plum: 0xdda0dd, - powderblue: 0xb0e0e6, - purple: 0x800080, - rebeccapurple: 0x663399, - red: 0xff0000, - rosybrown: 0xbc8f8f, - royalblue: 0x4169e1, - saddlebrown: 0x8b4513, - salmon: 0xfa8072, - sandybrown: 0xf4a460, - seagreen: 0x2e8b57, - seashell: 0xfff5ee, - sienna: 0xa0522d, - silver: 0xc0c0c0, - skyblue: 0x87ceeb, - slateblue: 0x6a5acd, - slategray: 0x708090, - slategrey: 0x708090, - snow: 0xfffafa, - springgreen: 0x00ff7f, - steelblue: 0x4682b4, - tan: 0xd2b48c, - teal: 0x008080, - thistle: 0xd8bfd8, - tomato: 0xff6347, - turquoise: 0x40e0d0, - violet: 0xee82ee, - wheat: 0xf5deb3, - white: 0xffffff, - whitesmoke: 0xf5f5f5, - yellow: 0xffff00, - yellowgreen: 0x9acd32 - }; - - define(Color, color, { - copy(channels) { - return Object.assign(new this.constructor, this, channels); - }, - displayable() { - return this.rgb().displayable(); - }, - hex: color_formatHex, // Deprecated! Use color.formatHex. - formatHex: color_formatHex, - formatHex8: color_formatHex8, - formatHsl: color_formatHsl, - formatRgb: color_formatRgb, - toString: color_formatRgb - }); - - function color_formatHex() { - return this.rgb().formatHex(); - } - - function color_formatHex8() { - return this.rgb().formatHex8(); - } - - function color_formatHsl() { - return hslConvert(this).formatHsl(); - } - - function color_formatRgb() { - return this.rgb().formatRgb(); - } - - function color(format) { - var m, l; - format = (format + "").trim().toLowerCase(); - return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000 - : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00 - : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000 - : l === 4 ? rgba((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000 - : null) // invalid hex - : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0) - : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%) - : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1) - : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1) - : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%) - : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1) - : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins - : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) - : null; - } - - function rgbn(n) { - return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1); - } - - function rgba(r, g, b, a) { - if (a <= 0) r = g = b = NaN; - return new Rgb(r, g, b, a); - } - - function rgbConvert(o) { - if (!(o instanceof Color)) o = color(o); - if (!o) return new Rgb; - o = o.rgb(); - return new Rgb(o.r, o.g, o.b, o.opacity); - } - - function rgb(r, g, b, opacity) { - return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity); - } - - function Rgb(r, g, b, opacity) { - this.r = +r; - this.g = +g; - this.b = +b; - this.opacity = +opacity; - } - - define(Rgb, rgb, extend(Color, { - brighter(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - darker(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity); - }, - rgb() { - return this; - }, - clamp() { - return new Rgb(clampi(this.r), clampi(this.g), clampi(this.b), clampa(this.opacity)); - }, - displayable() { - return (-0.5 <= this.r && this.r < 255.5) - && (-0.5 <= this.g && this.g < 255.5) - && (-0.5 <= this.b && this.b < 255.5) - && (0 <= this.opacity && this.opacity <= 1); - }, - hex: rgb_formatHex, // Deprecated! Use color.formatHex. - formatHex: rgb_formatHex, - formatHex8: rgb_formatHex8, - formatRgb: rgb_formatRgb, - toString: rgb_formatRgb - })); - - function rgb_formatHex() { - return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}`; - } - - function rgb_formatHex8() { - return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}${hex((isNaN(this.opacity) ? 1 : this.opacity) * 255)}`; - } - - function rgb_formatRgb() { - const a = clampa(this.opacity); - return `${a === 1 ? "rgb(" : "rgba("}${clampi(this.r)}, ${clampi(this.g)}, ${clampi(this.b)}${a === 1 ? ")" : `, ${a})`}`; - } - - function clampa(opacity) { - return isNaN(opacity) ? 1 : Math.max(0, Math.min(1, opacity)); - } - - function clampi(value) { - return Math.max(0, Math.min(255, Math.round(value) || 0)); - } - - function hex(value) { - value = clampi(value); - return (value < 16 ? "0" : "") + value.toString(16); - } - - function hsla(h, s, l, a) { - if (a <= 0) h = s = l = NaN; - else if (l <= 0 || l >= 1) h = s = NaN; - else if (s <= 0) h = NaN; - return new Hsl(h, s, l, a); - } - - function hslConvert(o) { - if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity); - if (!(o instanceof Color)) o = color(o); - if (!o) return new Hsl; - if (o instanceof Hsl) return o; - o = o.rgb(); - var r = o.r / 255, - g = o.g / 255, - b = o.b / 255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - h = NaN, - s = max - min, - l = (max + min) / 2; - if (s) { - if (r === max) h = (g - b) / s + (g < b) * 6; - else if (g === max) h = (b - r) / s + 2; - else h = (r - g) / s + 4; - s /= l < 0.5 ? max + min : 2 - max - min; - h *= 60; - } else { - s = l > 0 && l < 1 ? 0 : h; - } - return new Hsl(h, s, l, o.opacity); - } - - function hsl(h, s, l, opacity) { - return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity); - } - - function Hsl(h, s, l, opacity) { - this.h = +h; - this.s = +s; - this.l = +l; - this.opacity = +opacity; - } - - define(Hsl, hsl, extend(Color, { - brighter(k) { - k = k == null ? brighter : Math.pow(brighter, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - darker(k) { - k = k == null ? darker : Math.pow(darker, k); - return new Hsl(this.h, this.s, this.l * k, this.opacity); - }, - rgb() { - var h = this.h % 360 + (this.h < 0) * 360, - s = isNaN(h) || isNaN(this.s) ? 0 : this.s, - l = this.l, - m2 = l + (l < 0.5 ? l : 1 - l) * s, - m1 = 2 * l - m2; - return new Rgb( - hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2), - hsl2rgb(h, m1, m2), - hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2), - this.opacity - ); - }, - clamp() { - return new Hsl(clamph(this.h), clampt(this.s), clampt(this.l), clampa(this.opacity)); - }, - displayable() { - return (0 <= this.s && this.s <= 1 || isNaN(this.s)) - && (0 <= this.l && this.l <= 1) - && (0 <= this.opacity && this.opacity <= 1); - }, - formatHsl() { - const a = clampa(this.opacity); - return `${a === 1 ? "hsl(" : "hsla("}${clamph(this.h)}, ${clampt(this.s) * 100}%, ${clampt(this.l) * 100}%${a === 1 ? ")" : `, ${a})`}`; - } - })); - - function clamph(value) { - value = (value || 0) % 360; - return value < 0 ? value + 360 : value; - } - - function clampt(value) { - return Math.max(0, Math.min(1, value || 0)); - } - - /* From FvD 13.37, CSS Color Module Level 3 */ - function hsl2rgb(h, m1, m2) { - return (h < 60 ? m1 + (m2 - m1) * h / 60 - : h < 180 ? m2 - : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 - : m1) * 255; - } - - var constant$2 = x => () => x; - - function linear(a, d) { - return function(t) { - return a + t * d; - }; - } - - function exponential(a, b, y) { - return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) { - return Math.pow(a + t * b, y); - }; - } - - function gamma(y) { - return (y = +y) === 1 ? nogamma : function(a, b) { - return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a); - }; - } - - function nogamma(a, b) { - var d = b - a; - return d ? linear(a, d) : constant$2(isNaN(a) ? b : a); - } - - var interpolateRgb = (function rgbGamma(y) { - var color = gamma(y); - - function rgb$1(start, end) { - var r = color((start = rgb(start)).r, (end = rgb(end)).r), - g = color(start.g, end.g), - b = color(start.b, end.b), - opacity = nogamma(start.opacity, end.opacity); - return function(t) { - start.r = r(t); - start.g = g(t); - start.b = b(t); - start.opacity = opacity(t); - return start + ""; - }; - } - - rgb$1.gamma = rgbGamma; - - return rgb$1; - })(1); - - function interpolateNumber(a, b) { - return a = +a, b = +b, function(t) { - return a * (1 - t) + b * t; - }; - } - - var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, - reB = new RegExp(reA.source, "g"); - - function zero(b) { - return function() { - return b; - }; - } - - function one(b) { - return function(t) { - return b(t) + ""; - }; - } - - function interpolateString(a, b) { - var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b - am, // current match in a - bm, // current match in b - bs, // string preceding current number in b, if any - i = -1, // index in s - s = [], // string constants and placeholders - q = []; // number interpolators - - // Coerce inputs to strings. - a = a + "", b = b + ""; - - // Interpolate pairs of numbers in a & b. - while ((am = reA.exec(a)) - && (bm = reB.exec(b))) { - if ((bs = bm.index) > bi) { // a string precedes the next number in b - bs = b.slice(bi, bs); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match - if (s[i]) s[i] += bm; // coalesce with previous string - else s[++i] = bm; - } else { // interpolate non-matching numbers - s[++i] = null; - q.push({i: i, x: interpolateNumber(am, bm)}); - } - bi = reB.lastIndex; - } - - // Add remains of b. - if (bi < b.length) { - bs = b.slice(bi); - if (s[i]) s[i] += bs; // coalesce with previous string - else s[++i] = bs; - } - - // Special optimization for only a single match. - // Otherwise, interpolate each of the numbers and rejoin the string. - return s.length < 2 ? (q[0] - ? one(q[0].x) - : zero(b)) - : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); - } - - var degrees = 180 / Math.PI; - - var identity$1 = { - translateX: 0, - translateY: 0, - rotate: 0, - skewX: 0, - scaleX: 1, - scaleY: 1 - }; - - function decompose(a, b, c, d, e, f) { - var scaleX, scaleY, skewX; - if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX; - if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX; - if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY; - if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; - return { - translateX: e, - translateY: f, - rotate: Math.atan2(b, a) * degrees, - skewX: Math.atan(skewX) * degrees, - scaleX: scaleX, - scaleY: scaleY - }; - } - - var svgNode; - - /* eslint-disable no-undef */ - function parseCss(value) { - const m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + ""); - return m.isIdentity ? identity$1 : decompose(m.a, m.b, m.c, m.d, m.e, m.f); - } - - function parseSvg(value) { - if (value == null) return identity$1; - if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); - svgNode.setAttribute("transform", value); - if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1; - value = value.matrix; - return decompose(value.a, value.b, value.c, value.d, value.e, value.f); - } - - function interpolateTransform(parse, pxComma, pxParen, degParen) { - - function pop(s) { - return s.length ? s.pop() + " " : ""; - } - - function translate(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push("translate(", null, pxComma, null, pxParen); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb || yb) { - s.push("translate(" + xb + pxComma + yb + pxParen); - } - } - - function rotate(a, b, s, q) { - if (a !== b) { - if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path - q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "rotate(" + b + degParen); - } - } - - function skewX(a, b, s, q) { - if (a !== b) { - q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: interpolateNumber(a, b)}); - } else if (b) { - s.push(pop(s) + "skewX(" + b + degParen); - } - } - - function scale(xa, ya, xb, yb, s, q) { - if (xa !== xb || ya !== yb) { - var i = s.push(pop(s) + "scale(", null, ",", null, ")"); - q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)}); - } else if (xb !== 1 || yb !== 1) { - s.push(pop(s) + "scale(" + xb + "," + yb + ")"); - } - } - - return function(a, b) { - var s = [], // string constants and placeholders - q = []; // number interpolators - a = parse(a), b = parse(b); - translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q); - rotate(a.rotate, b.rotate, s, q); - skewX(a.skewX, b.skewX, s, q); - scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q); - a = b = null; // gc - return function(t) { - var i = -1, n = q.length, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - }; - } - - var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)"); - var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")"); - - var epsilon2 = 1e-12; - - function cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; - } - - function sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; - } - - function tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); - } - - var interpolateZoom = (function zoomRho(rho, rho2, rho4) { - - // p0 = [ux0, uy0, w0] - // p1 = [ux1, uy1, w1] - function zoom(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], - ux1 = p1[0], uy1 = p1[1], w1 = p1[2], - dx = ux1 - ux0, - dy = uy1 - uy0, - d2 = dx * dx + dy * dy, - i, - S; - - // Special case for u0 ≅ u1. - if (d2 < epsilon2) { - S = Math.log(w1 / w0) / rho; - i = function(t) { - return [ - ux0 + t * dx, - uy0 + t * dy, - w0 * Math.exp(rho * t * S) - ]; - }; - } - - // General case. - else { - var d1 = Math.sqrt(d2), - b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), - b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), - r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), - r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); - S = (r1 - r0) / rho; - i = function(t) { - var s = t * S, - coshr0 = cosh(r0), - u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0)); - return [ - ux0 + u * dx, - uy0 + u * dy, - w0 * coshr0 / cosh(rho * s + r0) - ]; - }; - } - - i.duration = S * 1000 * rho / Math.SQRT2; - - return i; - } - - zoom.rho = function(_) { - var _1 = Math.max(1e-3, +_), _2 = _1 * _1, _4 = _2 * _2; - return zoomRho(_1, _2, _4); - }; - - return zoom; - })(Math.SQRT2, 2, 4); - - var frame = 0, // is an animation frame pending? - timeout$1 = 0, // is a timeout pending? - interval = 0, // are any timers active? - pokeDelay = 1000, // how frequently we check for clock skew - taskHead, - taskTail, - clockLast = 0, - clockNow = 0, - clockSkew = 0, - clock = typeof performance === "object" && performance.now ? performance : Date, - setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; - - function now$3() { - return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); - } - - function clearNow() { - clockNow = 0; - } - - function Timer() { - this._call = - this._time = - this._next = null; - } - - Timer.prototype = timer.prototype = { - constructor: Timer, - restart: function(callback, delay, time) { - if (typeof callback !== "function") throw new TypeError("callback is not a function"); - time = (time == null ? now$3() : +time) + (delay == null ? 0 : +delay); - if (!this._next && taskTail !== this) { - if (taskTail) taskTail._next = this; - else taskHead = this; - taskTail = this; - } - this._call = callback; - this._time = time; - sleep(); - }, - stop: function() { - if (this._call) { - this._call = null; - this._time = Infinity; - sleep(); - } - } - }; - - function timer(callback, delay, time) { - var t = new Timer; - t.restart(callback, delay, time); - return t; - } - - function timerFlush() { - now$3(); // Get the current time, if not already set. - ++frame; // Pretend we’ve set an alarm, if we haven’t already. - var t = taskHead, e; - while (t) { - if ((e = clockNow - t._time) >= 0) t._call.call(undefined, e); - t = t._next; - } - --frame; - } - - function wake() { - clockNow = (clockLast = clock.now()) + clockSkew; - frame = timeout$1 = 0; - try { - timerFlush(); - } finally { - frame = 0; - nap(); - clockNow = 0; - } - } - - function poke() { - var now = clock.now(), delay = now - clockLast; - if (delay > pokeDelay) clockSkew -= delay, clockLast = now; - } - - function nap() { - var t0, t1 = taskHead, t2, time = Infinity; - while (t1) { - if (t1._call) { - if (time > t1._time) time = t1._time; - t0 = t1, t1 = t1._next; - } else { - t2 = t1._next, t1._next = null; - t1 = t0 ? t0._next = t2 : taskHead = t2; - } - } - taskTail = t0; - sleep(time); - } - - function sleep(time) { - if (frame) return; // Soonest alarm already set, or will be. - if (timeout$1) timeout$1 = clearTimeout(timeout$1); - var delay = time - clockNow; // Strictly less than if we recomputed clockNow. - if (delay > 24) { - if (time < Infinity) timeout$1 = setTimeout(wake, time - clock.now() - clockSkew); - if (interval) interval = clearInterval(interval); - } else { - if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); - frame = 1, setFrame(wake); - } - } - - function timeout(callback, delay, time) { - var t = new Timer; - delay = delay == null ? 0 : +delay; - t.restart(elapsed => { - t.stop(); - callback(elapsed + delay); - }, delay, time); - return t; - } - - var emptyOn = dispatch("start", "end", "cancel", "interrupt"); - var emptyTween = []; - - var CREATED = 0; - var SCHEDULED = 1; - var STARTING = 2; - var STARTED = 3; - var RUNNING = 4; - var ENDING = 5; - var ENDED = 6; - - function schedule(node, name, id, index, group, timing) { - var schedules = node.__transition; - if (!schedules) node.__transition = {}; - else if (id in schedules) return; - create(node, id, { - name: name, - index: index, // For context during callback. - group: group, // For context during callback. - on: emptyOn, - tween: emptyTween, - time: timing.time, - delay: timing.delay, - duration: timing.duration, - ease: timing.ease, - timer: null, - state: CREATED - }); - } - - function init(node, id) { - var schedule = get(node, id); - if (schedule.state > CREATED) throw new Error("too late; already scheduled"); - return schedule; - } - - function set(node, id) { - var schedule = get(node, id); - if (schedule.state > STARTED) throw new Error("too late; already running"); - return schedule; - } - - function get(node, id) { - var schedule = node.__transition; - if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found"); - return schedule; - } - - function create(node, id, self) { - var schedules = node.__transition, - tween; - - // Initialize the self timer when the transition is created. - // Note the actual delay is not known until the first callback! - schedules[id] = self; - self.timer = timer(schedule, 0, self.time); - - function schedule(elapsed) { - self.state = SCHEDULED; - self.timer.restart(start, self.delay, self.time); - - // If the elapsed delay is less than our first sleep, start immediately. - if (self.delay <= elapsed) start(elapsed - self.delay); - } - - function start(elapsed) { - var i, j, n, o; - - // If the state is not SCHEDULED, then we previously errored on start. - if (self.state !== SCHEDULED) return stop(); - - for (i in schedules) { - o = schedules[i]; - if (o.name !== self.name) continue; - - // While this element already has a starting transition during this frame, - // defer starting an interrupting transition until that transition has a - // chance to tick (and possibly end); see d3/d3-transition#54! - if (o.state === STARTED) return timeout(start); - - // Interrupt the active transition, if any. - if (o.state === RUNNING) { - o.state = ENDED; - o.timer.stop(); - o.on.call("interrupt", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - - // Cancel any pre-empted transitions. - else if (+i < id) { - o.state = ENDED; - o.timer.stop(); - o.on.call("cancel", node, node.__data__, o.index, o.group); - delete schedules[i]; - } - } - - // Defer the first tick to end of the current frame; see d3/d3#1576. - // Note the transition may be canceled after start and before the first tick! - // Note this must be scheduled before the start event; see d3/d3-transition#16! - // Assuming this is successful, subsequent callbacks go straight to tick. - timeout(function() { - if (self.state === STARTED) { - self.state = RUNNING; - self.timer.restart(tick, self.delay, self.time); - tick(elapsed); - } - }); - - // Dispatch the start event. - // Note this must be done before the tween are initialized. - self.state = STARTING; - self.on.call("start", node, node.__data__, self.index, self.group); - if (self.state !== STARTING) return; // interrupted - self.state = STARTED; - - // Initialize the tween, deleting null tween. - tween = new Array(n = self.tween.length); - for (i = 0, j = -1; i < n; ++i) { - if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) { - tween[++j] = o; - } - } - tween.length = j + 1; - } - - function tick(elapsed) { - var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), - i = -1, - n = tween.length; - - while (++i < n) { - tween[i].call(node, t); - } - - // Dispatch the end event. - if (self.state === ENDING) { - self.on.call("end", node, node.__data__, self.index, self.group); - stop(); - } - } - - function stop() { - self.state = ENDED; - self.timer.stop(); - delete schedules[id]; - for (var i in schedules) return; // eslint-disable-line no-unused-vars - delete node.__transition; - } - } - - function interrupt(node, name) { - var schedules = node.__transition, - schedule, - active, - empty = true, - i; - - if (!schedules) return; - - name = name == null ? null : name + ""; - - for (i in schedules) { - if ((schedule = schedules[i]).name !== name) { empty = false; continue; } - active = schedule.state > STARTING && schedule.state < ENDING; - schedule.state = ENDED; - schedule.timer.stop(); - schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group); - delete schedules[i]; - } - - if (empty) delete node.__transition; - } - - function selection_interrupt(name) { - return this.each(function() { - interrupt(this, name); - }); - } - - function tweenRemove(id, name) { - var tween0, tween1; - return function() { - var schedule = set(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = tween0 = tween; - for (var i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1 = tween1.slice(); - tween1.splice(i, 1); - break; - } - } - } - - schedule.tween = tween1; - }; - } - - function tweenFunction(id, name, value) { - var tween0, tween1; - if (typeof value !== "function") throw new Error; - return function() { - var schedule = set(this, id), - tween = schedule.tween; - - // If this node shared tween with the previous node, - // just assign the updated shared tween and we’re done! - // Otherwise, copy-on-write. - if (tween !== tween0) { - tween1 = (tween0 = tween).slice(); - for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) { - if (tween1[i].name === name) { - tween1[i] = t; - break; - } - } - if (i === n) tween1.push(t); - } - - schedule.tween = tween1; - }; - } - - function transition_tween(name, value) { - var id = this._id; - - name += ""; - - if (arguments.length < 2) { - var tween = get(this.node(), id).tween; - for (var i = 0, n = tween.length, t; i < n; ++i) { - if ((t = tween[i]).name === name) { - return t.value; - } - } - return null; - } - - return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value)); - } - - function tweenValue(transition, name, value) { - var id = transition._id; - - transition.each(function() { - var schedule = set(this, id); - (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments); - }); - - return function(node) { - return get(node, id).value[name]; - }; - } - - function interpolate(a, b) { - var c; - return (typeof b === "number" ? interpolateNumber - : b instanceof color ? interpolateRgb - : (c = color(b)) ? (b = c, interpolateRgb) - : interpolateString)(a, b); - } - - function attrRemove(name) { - return function() { - this.removeAttribute(name); - }; - } - - function attrRemoveNS(fullname) { - return function() { - this.removeAttributeNS(fullname.space, fullname.local); - }; - } - - function attrConstant(name, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = this.getAttribute(name); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function attrConstantNS(fullname, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = this.getAttributeNS(fullname.space, fullname.local); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function attrFunction(name, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0, value1 = value(this), string1; - if (value1 == null) return void this.removeAttribute(name); - string0 = this.getAttribute(name); - string1 = value1 + ""; - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function attrFunctionNS(fullname, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0, value1 = value(this), string1; - if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local); - string0 = this.getAttributeNS(fullname.space, fullname.local); - string1 = value1 + ""; - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function transition_attr(name, value) { - var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate; - return this.attrTween(name, typeof value === "function" - ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value)) - : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) - : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value)); - } - - function attrInterpolate(name, i) { - return function(t) { - this.setAttribute(name, i.call(this, t)); - }; - } - - function attrInterpolateNS(fullname, i) { - return function(t) { - this.setAttributeNS(fullname.space, fullname.local, i.call(this, t)); - }; - } - - function attrTweenNS(fullname, value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i); - return t0; - } - tween._value = value; - return tween; - } - - function attrTween(name, value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i); - return t0; - } - tween._value = value; - return tween; - } - - function transition_attrTween(name, value) { - var key = "attr." + name; - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - var fullname = namespace(name); - return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value)); - } - - function delayFunction(id, value) { - return function() { - init(this, id).delay = +value.apply(this, arguments); - }; - } - - function delayConstant(id, value) { - return value = +value, function() { - init(this, id).delay = value; - }; - } - - function transition_delay(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? delayFunction - : delayConstant)(id, value)) - : get(this.node(), id).delay; - } - - function durationFunction(id, value) { - return function() { - set(this, id).duration = +value.apply(this, arguments); - }; - } - - function durationConstant(id, value) { - return value = +value, function() { - set(this, id).duration = value; - }; - } - - function transition_duration(value) { - var id = this._id; - - return arguments.length - ? this.each((typeof value === "function" - ? durationFunction - : durationConstant)(id, value)) - : get(this.node(), id).duration; - } - - function easeConstant(id, value) { - if (typeof value !== "function") throw new Error; - return function() { - set(this, id).ease = value; - }; - } - - function transition_ease(value) { - var id = this._id; - - return arguments.length - ? this.each(easeConstant(id, value)) - : get(this.node(), id).ease; - } - - function easeVarying(id, value) { - return function() { - var v = value.apply(this, arguments); - if (typeof v !== "function") throw new Error; - set(this, id).ease = v; - }; - } - - function transition_easeVarying(value) { - if (typeof value !== "function") throw new Error; - return this.each(easeVarying(this._id, value)); - } - - function transition_filter(match) { - if (typeof match !== "function") match = matcher(match); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) { - if ((node = group[i]) && match.call(node, node.__data__, i, group)) { - subgroup.push(node); - } - } - } - - return new Transition(subgroups, this._parents, this._name, this._id); - } - - function transition_merge(transition) { - if (transition._id !== this._id) throw new Error; - - for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) { - for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) { - if (node = group0[i] || group1[i]) { - merge[i] = node; - } - } - } - - for (; j < m0; ++j) { - merges[j] = groups0[j]; - } - - return new Transition(merges, this._parents, this._name, this._id); - } - - function start(name) { - return (name + "").trim().split(/^|\s+/).every(function(t) { - var i = t.indexOf("."); - if (i >= 0) t = t.slice(0, i); - return !t || t === "start"; - }); - } - - function onFunction(id, name, listener) { - var on0, on1, sit = start(name) ? init : set; - return function() { - var schedule = sit(this, id), - on = schedule.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener); - - schedule.on = on1; - }; - } - - function transition_on(name, listener) { - var id = this._id; - - return arguments.length < 2 - ? get(this.node(), id).on.on(name) - : this.each(onFunction(id, name, listener)); - } - - function removeFunction(id) { - return function() { - var parent = this.parentNode; - for (var i in this.__transition) if (+i !== id) return; - if (parent) parent.removeChild(this); - }; - } - - function transition_remove() { - return this.on("end.remove", removeFunction(this._id)); - } - - function transition_select(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selector(select); - - for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) { - if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - subgroup[i] = subnode; - schedule(subgroup[i], name, id, i, subgroup, get(node, id)); - } - } - } - - return new Transition(subgroups, this._parents, name, id); - } - - function transition_selectAll(select) { - var name = this._name, - id = this._id; - - if (typeof select !== "function") select = selectorAll(select); - - for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - for (var children = select.call(node, node.__data__, i, group), child, inherit = get(node, id), k = 0, l = children.length; k < l; ++k) { - if (child = children[k]) { - schedule(child, name, id, k, children, inherit); - } - } - subgroups.push(children); - parents.push(node); - } - } - } - - return new Transition(subgroups, parents, name, id); - } - - var Selection = selection.prototype.constructor; - - function transition_selection() { - return new Selection(this._groups, this._parents); - } - - function styleNull(name, interpolate) { - var string00, - string10, - interpolate0; - return function() { - var string0 = styleValue(this, name), - string1 = (this.style.removeProperty(name), styleValue(this, name)); - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, string10 = string1); - }; - } - - function styleRemove(name) { - return function() { - this.style.removeProperty(name); - }; - } - - function styleConstant(name, interpolate, value1) { - var string00, - string1 = value1 + "", - interpolate0; - return function() { - var string0 = styleValue(this, name); - return string0 === string1 ? null - : string0 === string00 ? interpolate0 - : interpolate0 = interpolate(string00 = string0, value1); - }; - } - - function styleFunction(name, interpolate, value) { - var string00, - string10, - interpolate0; - return function() { - var string0 = styleValue(this, name), - value1 = value(this), - string1 = value1 + ""; - if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name)); - return string0 === string1 ? null - : string0 === string00 && string1 === string10 ? interpolate0 - : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1)); - }; - } - - function styleMaybeRemove(id, name) { - var on0, on1, listener0, key = "style." + name, event = "end." + key, remove; - return function() { - var schedule = set(this, id), - on = schedule.on, - listener = schedule.value[key] == null ? remove || (remove = styleRemove(name)) : undefined; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener); - - schedule.on = on1; - }; - } - - function transition_style(name, value, priority) { - var i = (name += "") === "transform" ? interpolateTransformCss : interpolate; - return value == null ? this - .styleTween(name, styleNull(name, i)) - .on("end.style." + name, styleRemove(name)) - : typeof value === "function" ? this - .styleTween(name, styleFunction(name, i, tweenValue(this, "style." + name, value))) - .each(styleMaybeRemove(this._id, name)) - : this - .styleTween(name, styleConstant(name, i, value), priority) - .on("end.style." + name, null); - } - - function styleInterpolate(name, i, priority) { - return function(t) { - this.style.setProperty(name, i.call(this, t), priority); - }; - } - - function styleTween(name, value, priority) { - var t, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority); - return t; - } - tween._value = value; - return tween; - } - - function transition_styleTween(name, value, priority) { - var key = "style." + (name += ""); - if (arguments.length < 2) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, styleTween(name, value, priority == null ? "" : priority)); - } - - function textConstant(value) { - return function() { - this.textContent = value; - }; - } - - function textFunction(value) { - return function() { - var value1 = value(this); - this.textContent = value1 == null ? "" : value1; - }; - } - - function transition_text(value) { - return this.tween("text", typeof value === "function" - ? textFunction(tweenValue(this, "text", value)) - : textConstant(value == null ? "" : value + "")); - } - - function textInterpolate(i) { - return function(t) { - this.textContent = i.call(this, t); - }; - } - - function textTween(value) { - var t0, i0; - function tween() { - var i = value.apply(this, arguments); - if (i !== i0) t0 = (i0 = i) && textInterpolate(i); - return t0; - } - tween._value = value; - return tween; - } - - function transition_textTween(value) { - var key = "text"; - if (arguments.length < 1) return (key = this.tween(key)) && key._value; - if (value == null) return this.tween(key, null); - if (typeof value !== "function") throw new Error; - return this.tween(key, textTween(value)); - } - - function transition_transition() { - var name = this._name, - id0 = this._id, - id1 = newId(); - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - var inherit = get(node, id0); - schedule(node, name, id1, i, group, { - time: inherit.time + inherit.delay + inherit.duration, - delay: 0, - duration: inherit.duration, - ease: inherit.ease - }); - } - } - } - - return new Transition(groups, this._parents, name, id1); - } - - function transition_end() { - var on0, on1, that = this, id = that._id, size = that.size(); - return new Promise(function(resolve, reject) { - var cancel = {value: reject}, - end = {value: function() { if (--size === 0) resolve(); }}; - - that.each(function() { - var schedule = set(this, id), - on = schedule.on; - - // If this node shared a dispatch with the previous node, - // just assign the updated shared dispatch and we’re done! - // Otherwise, copy-on-write. - if (on !== on0) { - on1 = (on0 = on).copy(); - on1._.cancel.push(cancel); - on1._.interrupt.push(cancel); - on1._.end.push(end); - } - - schedule.on = on1; - }); - - // The selection was empty, resolve end immediately - if (size === 0) resolve(); - }); - } - - var id = 0; - - function Transition(groups, parents, name, id) { - this._groups = groups; - this._parents = parents; - this._name = name; - this._id = id; - } - - function newId() { - return ++id; - } - - var selection_prototype = selection.prototype; - - Transition.prototype = { - constructor: Transition, - select: transition_select, - selectAll: transition_selectAll, - selectChild: selection_prototype.selectChild, - selectChildren: selection_prototype.selectChildren, - filter: transition_filter, - merge: transition_merge, - selection: transition_selection, - transition: transition_transition, - call: selection_prototype.call, - nodes: selection_prototype.nodes, - node: selection_prototype.node, - size: selection_prototype.size, - empty: selection_prototype.empty, - each: selection_prototype.each, - on: transition_on, - attr: transition_attr, - attrTween: transition_attrTween, - style: transition_style, - styleTween: transition_styleTween, - text: transition_text, - textTween: transition_textTween, - remove: transition_remove, - tween: transition_tween, - delay: transition_delay, - duration: transition_duration, - ease: transition_ease, - easeVarying: transition_easeVarying, - end: transition_end, - [Symbol.iterator]: selection_prototype[Symbol.iterator] - }; - - function cubicInOut(t) { - return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2; - } - - var defaultTiming = { - time: null, // Set on use. - delay: 0, - duration: 250, - ease: cubicInOut - }; - - function inherit(node, id) { - var timing; - while (!(timing = node.__transition) || !(timing = timing[id])) { - if (!(node = node.parentNode)) { - throw new Error(`transition ${id} not found`); - } - } - return timing; - } - - function selection_transition(name) { - var id, - timing; - - if (name instanceof Transition) { - id = name._id, name = name._name; - } else { - id = newId(), (timing = defaultTiming).time = now$3(), name = name == null ? null : name + ""; - } - - for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) { - for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) { - if (node = group[i]) { - schedule(node, name, id, i, group, timing || inherit(node, id)); - } - } - } - - return new Transition(groups, this._parents, name, id); - } - - selection.prototype.interrupt = selection_interrupt; - selection.prototype.transition = selection_transition; - - var constant$1 = x => () => x; - - function ZoomEvent(type, { - sourceEvent, - target, - transform, - dispatch - }) { - Object.defineProperties(this, { - type: {value: type, enumerable: true, configurable: true}, - sourceEvent: {value: sourceEvent, enumerable: true, configurable: true}, - target: {value: target, enumerable: true, configurable: true}, - transform: {value: transform, enumerable: true, configurable: true}, - _: {value: dispatch} - }); - } - - function Transform(k, x, y) { - this.k = k; - this.x = x; - this.y = y; - } - - Transform.prototype = { - constructor: Transform, - scale: function(k) { - return k === 1 ? this : new Transform(this.k * k, this.x, this.y); - }, - translate: function(x, y) { - return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y); - }, - apply: function(point) { - return [point[0] * this.k + this.x, point[1] * this.k + this.y]; - }, - applyX: function(x) { - return x * this.k + this.x; - }, - applyY: function(y) { - return y * this.k + this.y; - }, - invert: function(location) { - return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k]; - }, - invertX: function(x) { - return (x - this.x) / this.k; - }, - invertY: function(y) { - return (y - this.y) / this.k; - }, - rescaleX: function(x) { - return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x)); - }, - rescaleY: function(y) { - return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y)); - }, - toString: function() { - return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; - } - }; - - var identity = new Transform(1, 0, 0); - - transform.prototype = Transform.prototype; - - function transform(node) { - while (!node.__zoom) if (!(node = node.parentNode)) return identity; - return node.__zoom; - } - - function nopropagation(event) { - event.stopImmediatePropagation(); - } - - function noevent(event) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - - // Ignore right-click, since that should open the context menu. - // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event - function defaultFilter(event) { - return (!event.ctrlKey || event.type === 'wheel') && !event.button; - } - - function defaultExtent() { - var e = this; - if (e instanceof SVGElement) { - e = e.ownerSVGElement || e; - if (e.hasAttribute("viewBox")) { - e = e.viewBox.baseVal; - return [[e.x, e.y], [e.x + e.width, e.y + e.height]]; - } - return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]; - } - return [[0, 0], [e.clientWidth, e.clientHeight]]; - } - - function defaultTransform() { - return this.__zoom || identity; - } - - function defaultWheelDelta(event) { - return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1); - } - - function defaultTouchable() { - return navigator.maxTouchPoints || ("ontouchstart" in this); - } - - function defaultConstrain(transform, extent, translateExtent) { - var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], - dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], - dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], - dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1]; - return transform.translate( - dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), - dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1) - ); - } - - function d3Zoom() { - var filter = defaultFilter, - extent = defaultExtent, - constrain = defaultConstrain, - wheelDelta = defaultWheelDelta, - touchable = defaultTouchable, - scaleExtent = [0, Infinity], - translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], - duration = 250, - interpolate = interpolateZoom, - listeners = dispatch("start", "zoom", "end"), - touchstarting, - touchfirst, - touchending, - touchDelay = 500, - wheelDelay = 150, - clickDistance2 = 0, - tapDistance = 10; - - function zoom(selection) { - selection - .property("__zoom", defaultTransform) - .on("wheel.zoom", wheeled, {passive: false}) - .on("mousedown.zoom", mousedowned) - .on("dblclick.zoom", dblclicked) - .filter(touchable) - .on("touchstart.zoom", touchstarted) - .on("touchmove.zoom", touchmoved) - .on("touchend.zoom touchcancel.zoom", touchended) - .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); - } - - zoom.transform = function(collection, transform, point, event) { - var selection = collection.selection ? collection.selection() : collection; - selection.property("__zoom", defaultTransform); - if (collection !== selection) { - schedule(collection, transform, point, event); - } else { - selection.interrupt().each(function() { - gesture(this, arguments) - .event(event) - .start() - .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform) - .end(); - }); - } - }; - - zoom.scaleBy = function(selection, k, p, event) { - zoom.scaleTo(selection, function() { - var k0 = this.__zoom.k, - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return k0 * k1; - }, p, event); - }; - - zoom.scaleTo = function(selection, k, p, event) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t0 = this.__zoom, - p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, - p1 = t0.invert(p0), - k1 = typeof k === "function" ? k.apply(this, arguments) : k; - return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent); - }, p, event); - }; - - zoom.translateBy = function(selection, x, y, event) { - zoom.transform(selection, function() { - return constrain(this.__zoom.translate( - typeof x === "function" ? x.apply(this, arguments) : x, - typeof y === "function" ? y.apply(this, arguments) : y - ), extent.apply(this, arguments), translateExtent); - }, null, event); - }; - - zoom.translateTo = function(selection, x, y, p, event) { - zoom.transform(selection, function() { - var e = extent.apply(this, arguments), - t = this.__zoom, - p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p; - return constrain(identity.translate(p0[0], p0[1]).scale(t.k).translate( - typeof x === "function" ? -x.apply(this, arguments) : -x, - typeof y === "function" ? -y.apply(this, arguments) : -y - ), e, translateExtent); - }, p, event); - }; - - function scale(transform, k) { - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k)); - return k === transform.k ? transform : new Transform(k, transform.x, transform.y); - } - - function translate(transform, p0, p1) { - var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k; - return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y); - } - - function centroid(extent) { - return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2]; - } - - function schedule(transition, transform, point, event) { - transition - .on("start.zoom", function() { gesture(this, arguments).event(event).start(); }) - .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).event(event).end(); }) - .tween("zoom", function() { - var that = this, - args = arguments, - g = gesture(that, args).event(event), - e = extent.apply(that, args), - p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, - w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), - a = that.__zoom, - b = typeof transform === "function" ? transform.apply(that, args) : transform, - i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k)); - return function(t) { - if (t === 1) t = b; // Avoid rounding error on end. - else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); } - g.zoom(null, t); - }; - }); - } - - function gesture(that, args, clean) { - return (!clean && that.__zooming) || new Gesture(that, args); - } - - function Gesture(that, args) { - this.that = that; - this.args = args; - this.active = 0; - this.sourceEvent = null; - this.extent = extent.apply(that, args); - this.taps = 0; - } - - Gesture.prototype = { - event: function(event) { - if (event) this.sourceEvent = event; - return this; - }, - start: function() { - if (++this.active === 1) { - this.that.__zooming = this; - this.emit("start"); - } - return this; - }, - zoom: function(key, transform) { - if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]); - if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]); - if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]); - this.that.__zoom = transform; - this.emit("zoom"); - return this; - }, - end: function() { - if (--this.active === 0) { - delete this.that.__zooming; - this.emit("end"); - } - return this; - }, - emit: function(type) { - var d = d3Select(this.that).datum(); - listeners.call( - type, - this.that, - new ZoomEvent(type, { - sourceEvent: this.sourceEvent, - target: zoom, - type, - transform: this.that.__zoom, - dispatch: listeners - }), - d - ); - } - }; - - function wheeled(event, ...args) { - if (!filter.apply(this, arguments)) return; - var g = gesture(this, args).event(event), - t = this.__zoom, - k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))), - p = pointer(event); - - // If the mouse is in the same location as before, reuse it. - // If there were recent wheel events, reset the wheel idle timeout. - if (g.wheel) { - if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) { - g.mouse[1] = t.invert(g.mouse[0] = p); - } - clearTimeout(g.wheel); - } - - // If this wheel event won’t trigger a transform change, ignore it. - else if (t.k === k) return; - - // Otherwise, capture the mouse point and location at the start. - else { - g.mouse = [p, t.invert(p)]; - interrupt(this); - g.start(); - } - - noevent(event); - g.wheel = setTimeout(wheelidled, wheelDelay); - g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent)); - - function wheelidled() { - g.wheel = null; - g.end(); - } - } - - function mousedowned(event, ...args) { - if (touchending || !filter.apply(this, arguments)) return; - var currentTarget = event.currentTarget, - g = gesture(this, args, true).event(event), - v = d3Select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), - p = pointer(event, currentTarget), - x0 = event.clientX, - y0 = event.clientY; - - dragDisable(event.view); - nopropagation(event); - g.mouse = [p, this.__zoom.invert(p)]; - interrupt(this); - g.start(); - - function mousemoved(event) { - noevent(event); - if (!g.moved) { - var dx = event.clientX - x0, dy = event.clientY - y0; - g.moved = dx * dx + dy * dy > clickDistance2; - } - g.event(event) - .zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent)); - } - - function mouseupped(event) { - v.on("mousemove.zoom mouseup.zoom", null); - yesdrag(event.view, g.moved); - noevent(event); - g.event(event).end(); - } - } - - function dblclicked(event, ...args) { - if (!filter.apply(this, arguments)) return; - var t0 = this.__zoom, - p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this), - p1 = t0.invert(p0), - k1 = t0.k * (event.shiftKey ? 0.5 : 2), - t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent); - - noevent(event); - if (duration > 0) d3Select(this).transition().duration(duration).call(schedule, t1, p0, event); - else d3Select(this).call(zoom.transform, t1, p0, event); - } - - function touchstarted(event, ...args) { - if (!filter.apply(this, arguments)) return; - var touches = event.touches, - n = touches.length, - g = gesture(this, args, event.changedTouches.length === n).event(event), - started, i, t, p; - - nopropagation(event); - for (i = 0; i < n; ++i) { - t = touches[i], p = pointer(t, this); - p = [p, this.__zoom.invert(p), t.identifier]; - if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting; - else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0; - } - - if (touchstarting) touchstarting = clearTimeout(touchstarting); - - if (started) { - if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); - interrupt(this); - g.start(); - } - } - - function touchmoved(event, ...args) { - if (!this.__zooming) return; - var g = gesture(this, args).event(event), - touches = event.changedTouches, - n = touches.length, i, t, p, l; - - noevent(event); - for (i = 0; i < n; ++i) { - t = touches[i], p = pointer(t, this); - if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p; - else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p; - } - t = g.that.__zoom; - if (g.touch1) { - var p0 = g.touch0[0], l0 = g.touch0[1], - p1 = g.touch1[0], l1 = g.touch1[1], - dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, - dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl; - t = scale(t, Math.sqrt(dp / dl)); - p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2]; - l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2]; - } - else if (g.touch0) p = g.touch0[0], l = g.touch0[1]; - else return; - - g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent)); - } - - function touchended(event, ...args) { - if (!this.__zooming) return; - var g = gesture(this, args).event(event), - touches = event.changedTouches, - n = touches.length, i, t; - - nopropagation(event); - if (touchending) clearTimeout(touchending); - touchending = setTimeout(function() { touchending = null; }, touchDelay); - for (i = 0; i < n; ++i) { - t = touches[i]; - if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0; - else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1; - } - if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1; - if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]); - else { - g.end(); - // If this was a dbltap, reroute to the (optional) dblclick.zoom handler. - if (g.taps === 2) { - t = pointer(t, this); - if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) { - var p = d3Select(this).on("dblclick.zoom"); - if (p) p.apply(this, arguments); - } - } - } - } - - zoom.wheelDelta = function(_) { - return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$1(+_), zoom) : wheelDelta; - }; - - zoom.filter = function(_) { - return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), zoom) : filter; - }; - - zoom.touchable = function(_) { - return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), zoom) : touchable; - }; - - zoom.extent = function(_) { - return arguments.length ? (extent = typeof _ === "function" ? _ : constant$1([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent; - }; - - zoom.scaleExtent = function(_) { - return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]]; - }; - - zoom.translateExtent = function(_) { - return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]]; - }; - - zoom.constrain = function(_) { - return arguments.length ? (constrain = _, zoom) : constrain; - }; - - zoom.duration = function(_) { - return arguments.length ? (duration = +_, zoom) : duration; - }; - - zoom.interpolate = function(_) { - return arguments.length ? (interpolate = _, zoom) : interpolate; - }; - - zoom.on = function() { - var value = listeners.on.apply(listeners, arguments); - return value === listeners ? zoom : value; - }; - - zoom.clickDistance = function(_) { - return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2); - }; - - zoom.tapDistance = function(_) { - return arguments.length ? (tapDistance = +_, zoom) : tapDistance; - }; - - return zoom; - } - - class InternMap extends Map { - constructor(entries, key = keyof) { - super(); - Object.defineProperties(this, {_intern: {value: new Map()}, _key: {value: key}}); - if (entries != null) for (const [key, value] of entries) this.set(key, value); - } - get(key) { - return super.get(intern_get(this, key)); - } - has(key) { - return super.has(intern_get(this, key)); - } - set(key, value) { - return super.set(intern_set(this, key), value); - } - delete(key) { - return super.delete(intern_delete(this, key)); - } - } - - function intern_get({_intern, _key}, value) { - const key = _key(value); - return _intern.has(key) ? _intern.get(key) : value; - } - - function intern_set({_intern, _key}, value) { - const key = _key(value); - if (_intern.has(key)) return _intern.get(key); - _intern.set(key, value); - return value; - } - - function intern_delete({_intern, _key}, value) { - const key = _key(value); - if (_intern.has(key)) { - value = _intern.get(key); - _intern.delete(key); - } - return value; - } - - function keyof(value) { - return value !== null && typeof value === "object" ? value.valueOf() : value; - } - - function max$1(values, valueof) { - let max; - if (valueof === undefined) { - for (const value of values) { - if (value != null - && (max < value || (max === undefined && value >= value))) { - max = value; - } - } - } else { - let index = -1; - for (let value of values) { - if ((value = valueof(value, ++index, values)) != null - && (max < value || (max === undefined && value >= value))) { - max = value; - } - } - } - return max; - } - - function min$1(values, valueof) { - let min; - if (valueof === undefined) { - for (const value of values) { - if (value != null - && (min > value || (min === undefined && value >= value))) { - min = value; - } - } - } else { - let index = -1; - for (let value of values) { - if ((value = valueof(value, ++index, values)) != null - && (min > value || (min === undefined && value >= value))) { - min = value; - } - } - } - return min; - } - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - - var freeGlobal$1 = freeGlobal; - - /** Detect free variable `self`. */ - var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - - /** Used as a reference to the global object. */ - var root = freeGlobal$1 || freeSelf || Function('return this')(); - - var root$1 = root; - - /** Built-in value references. */ - var Symbol$1 = root$1.Symbol; - - var Symbol$2 = Symbol$1; - - /** Used for built-in method references. */ - var objectProto$1 = Object.prototype; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto$1.hasOwnProperty; - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString$1 = objectProto$1.toString; - - /** Built-in value references. */ - var symToStringTag$1 = Symbol$2 ? Symbol$2.toStringTag : undefined; - - /** - * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the raw `toStringTag`. - */ - function getRawTag(value) { - var isOwn = hasOwnProperty.call(value, symToStringTag$1), - tag = value[symToStringTag$1]; - - try { - value[symToStringTag$1] = undefined; - var unmasked = true; - } catch (e) {} - - var result = nativeObjectToString$1.call(value); - if (unmasked) { - if (isOwn) { - value[symToStringTag$1] = tag; - } else { - delete value[symToStringTag$1]; - } - } - return result; - } - - /** Used for built-in method references. */ - var objectProto = Object.prototype; - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var nativeObjectToString = objectProto.toString; - - /** - * Converts `value` to a string using `Object.prototype.toString`. - * - * @private - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - */ - function objectToString(value) { - return nativeObjectToString.call(value); - } - - /** `Object#toString` result references. */ - var nullTag = '[object Null]', - undefinedTag = '[object Undefined]'; - - /** Built-in value references. */ - var symToStringTag = Symbol$2 ? Symbol$2.toStringTag : undefined; - - /** - * The base implementation of `getTag` without fallbacks for buggy environments. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - function baseGetTag(value) { - if (value == null) { - return value === undefined ? undefinedTag : nullTag; - } - return (symToStringTag && symToStringTag in Object(value)) - ? getRawTag(value) - : objectToString(value); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return value != null && typeof value == 'object'; - } - - /** `Object#toString` result references. */ - var symbolTag = '[object Symbol]'; - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && baseGetTag(value) == symbolTag); - } - - /** Used to match a single whitespace character. */ - var reWhitespace = /\s/; - - /** - * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace - * character of `string`. - * - * @private - * @param {string} string The string to inspect. - * @returns {number} Returns the index of the last non-whitespace character. - */ - function trimmedEndIndex(string) { - var index = string.length; - - while (index-- && reWhitespace.test(string.charAt(index))) {} - return index; - } - - /** Used to match leading whitespace. */ - var reTrimStart = /^\s+/; - - /** - * The base implementation of `_.trim`. - * - * @private - * @param {string} string The string to trim. - * @returns {string} Returns the trimmed string. - */ - function baseTrim(string) { - return string - ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') - : string; - } - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject(value) { - var type = typeof value; - return value != null && (type == 'object' || type == 'function'); - } - - /** Used as references for various `Number` constants. */ - var NAN = 0 / 0; - - /** Used to detect bad signed hexadecimal string values. */ - var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; - - /** Used to detect binary string values. */ - var reIsBinary = /^0b[01]+$/i; - - /** Used to detect octal string values. */ - var reIsOctal = /^0o[0-7]+$/i; - - /** Built-in method references without a dependency on `root`. */ - var freeParseInt = parseInt; - - /** - * Converts `value` to a number. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @example - * - * _.toNumber(3.2); - * // => 3.2 - * - * _.toNumber(Number.MIN_VALUE); - * // => 5e-324 - * - * _.toNumber(Infinity); - * // => Infinity - * - * _.toNumber('3.2'); - * // => 3.2 - */ - function toNumber(value) { - if (typeof value == 'number') { - return value; - } - if (isSymbol(value)) { - return NAN; - } - if (isObject(value)) { - var other = typeof value.valueOf == 'function' ? value.valueOf() : value; - value = isObject(other) ? (other + '') : other; - } - if (typeof value != 'string') { - return value === 0 ? value : +value; - } - value = baseTrim(value); - var isBinary = reIsBinary.test(value); - return (isBinary || reIsOctal.test(value)) - ? freeParseInt(value.slice(2), isBinary ? 2 : 8) - : (reIsBadHex.test(value) ? NAN : +value); - } - - /** - * Gets the timestamp of the number of milliseconds that have elapsed since - * the Unix epoch (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Date - * @returns {number} Returns the timestamp. - * @example - * - * _.defer(function(stamp) { - * console.log(_.now() - stamp); - * }, _.now()); - * // => Logs the number of milliseconds it took for the deferred invocation. - */ - var now$1 = function() { - return root$1.Date.now(); - }; - - var now$2 = now$1; - - /** Error message constants. */ - var FUNC_ERROR_TEXT$1 = 'Expected a function'; - - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeMax = Math.max, - nativeMin = Math.min; - - /** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked. The debounced function comes with a `cancel` method to cancel - * delayed `func` invocations and a `flush` method to immediately invoke them. - * Provide `options` to indicate whether `func` should be invoked on the - * leading and/or trailing edge of the `wait` timeout. The `func` is invoked - * with the last arguments provided to the debounced function. Subsequent - * calls to the debounced function return the result of the last `func` - * invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.debounce` and `_.throttle`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] The number of milliseconds to delay. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })); - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); - * var source = new EventSource('/stream'); - * jQuery(source).on('message', debounced); - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel); - */ - function debounce(func, wait, options) { - var lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime, - lastInvokeTime = 0, - leading = false, - maxing = false, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT$1); - } - wait = toNumber(wait) || 0; - if (isObject(options)) { - leading = !!options.leading; - maxing = 'maxWait' in options; - maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - - function invokeFunc(time) { - var args = lastArgs, - thisArg = lastThis; - - lastArgs = lastThis = undefined; - lastInvokeTime = time; - result = func.apply(thisArg, args); - return result; - } - - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time; - // Start the timer for the trailing edge. - timerId = setTimeout(timerExpired, wait); - // Invoke the leading edge. - return leading ? invokeFunc(time) : result; - } - - function remainingWait(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime, - timeWaiting = wait - timeSinceLastCall; - - return maxing - ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) - : timeWaiting; - } - - function shouldInvoke(time) { - var timeSinceLastCall = time - lastCallTime, - timeSinceLastInvoke = time - lastInvokeTime; - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return (lastCallTime === undefined || (timeSinceLastCall >= wait) || - (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); - } - - function timerExpired() { - var time = now$2(); - if (shouldInvoke(time)) { - return trailingEdge(time); - } - // Restart the timer. - timerId = setTimeout(timerExpired, remainingWait(time)); - } - - function trailingEdge(time) { - timerId = undefined; - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time); - } - lastArgs = lastThis = undefined; - return result; - } - - function cancel() { - if (timerId !== undefined) { - clearTimeout(timerId); - } - lastInvokeTime = 0; - lastArgs = lastCallTime = lastThis = timerId = undefined; - } - - function flush() { - return timerId === undefined ? result : trailingEdge(now$2()); - } - - function debounced() { - var time = now$2(), - isInvoking = shouldInvoke(time); - - lastArgs = arguments; - lastThis = this; - lastCallTime = time; - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime); - } - if (maxing) { - // Handle invocations in a tight loop. - clearTimeout(timerId); - timerId = setTimeout(timerExpired, wait); - return invokeFunc(lastCallTime); - } - } - if (timerId === undefined) { - timerId = setTimeout(timerExpired, wait); - } - return result; - } - debounced.cancel = cancel; - debounced.flush = flush; - return debounced; - } - - /** Error message constants. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds. The throttled function comes with a `cancel` - * method to cancel delayed `func` invocations and a `flush` method to - * immediately invoke them. Provide `options` to indicate whether `func` - * should be invoked on the leading and/or trailing edge of the `wait` - * timeout. The `func` is invoked with the last arguments provided to the - * throttled function. Subsequent calls to the throttled function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the throttled function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until to the next tick, similar to `setTimeout` with a timeout of `0`. - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `_.throttle` and `_.debounce`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] The number of milliseconds to throttle invocations to. - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=true] - * Specify invoking on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // Avoid excessively updating the position while scrolling. - * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); - * - * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. - * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); - * jQuery(element).on('click', throttled); - * - * // Cancel the trailing throttled invocation. - * jQuery(window).on('popstate', throttled.cancel); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (typeof func != 'function') { - throw new TypeError(FUNC_ERROR_TEXT); - } - if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading; - trailing = 'trailing' in options ? !!options.trailing : trailing; - } - return debounce(func, wait, { - 'leading': leading, - 'maxWait': wait, - 'trailing': trailing - }); - } - - /** - * The Ease class provides a collection of easing functions for use with tween.js. - */ - var Easing = Object.freeze({ - Linear: Object.freeze({ - None: function (amount) { - return amount; - }, - In: function (amount) { - return this.None(amount); - }, - Out: function (amount) { - return this.None(amount); - }, - InOut: function (amount) { - return this.None(amount); - }, - }), - Quadratic: Object.freeze({ - In: function (amount) { - return amount * amount; - }, - Out: function (amount) { - return amount * (2 - amount); - }, - InOut: function (amount) { - if ((amount *= 2) < 1) { - return 0.5 * amount * amount; - } - return -0.5 * (--amount * (amount - 2) - 1); - }, - }), - Cubic: Object.freeze({ - In: function (amount) { - return amount * amount * amount; - }, - Out: function (amount) { - return --amount * amount * amount + 1; - }, - InOut: function (amount) { - if ((amount *= 2) < 1) { - return 0.5 * amount * amount * amount; - } - return 0.5 * ((amount -= 2) * amount * amount + 2); - }, - }), - Quartic: Object.freeze({ - In: function (amount) { - return amount * amount * amount * amount; - }, - Out: function (amount) { - return 1 - --amount * amount * amount * amount; - }, - InOut: function (amount) { - if ((amount *= 2) < 1) { - return 0.5 * amount * amount * amount * amount; - } - return -0.5 * ((amount -= 2) * amount * amount * amount - 2); - }, - }), - Quintic: Object.freeze({ - In: function (amount) { - return amount * amount * amount * amount * amount; - }, - Out: function (amount) { - return --amount * amount * amount * amount * amount + 1; - }, - InOut: function (amount) { - if ((amount *= 2) < 1) { - return 0.5 * amount * amount * amount * amount * amount; - } - return 0.5 * ((amount -= 2) * amount * amount * amount * amount + 2); - }, - }), - Sinusoidal: Object.freeze({ - In: function (amount) { - return 1 - Math.sin(((1.0 - amount) * Math.PI) / 2); - }, - Out: function (amount) { - return Math.sin((amount * Math.PI) / 2); - }, - InOut: function (amount) { - return 0.5 * (1 - Math.sin(Math.PI * (0.5 - amount))); - }, - }), - Exponential: Object.freeze({ - In: function (amount) { - return amount === 0 ? 0 : Math.pow(1024, amount - 1); - }, - Out: function (amount) { - return amount === 1 ? 1 : 1 - Math.pow(2, -10 * amount); - }, - InOut: function (amount) { - if (amount === 0) { - return 0; - } - if (amount === 1) { - return 1; - } - if ((amount *= 2) < 1) { - return 0.5 * Math.pow(1024, amount - 1); - } - return 0.5 * (-Math.pow(2, -10 * (amount - 1)) + 2); - }, - }), - Circular: Object.freeze({ - In: function (amount) { - return 1 - Math.sqrt(1 - amount * amount); - }, - Out: function (amount) { - return Math.sqrt(1 - --amount * amount); - }, - InOut: function (amount) { - if ((amount *= 2) < 1) { - return -0.5 * (Math.sqrt(1 - amount * amount) - 1); - } - return 0.5 * (Math.sqrt(1 - (amount -= 2) * amount) + 1); - }, - }), - Elastic: Object.freeze({ - In: function (amount) { - if (amount === 0) { - return 0; - } - if (amount === 1) { - return 1; - } - return -Math.pow(2, 10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI); - }, - Out: function (amount) { - if (amount === 0) { - return 0; - } - if (amount === 1) { - return 1; - } - return Math.pow(2, -10 * amount) * Math.sin((amount - 0.1) * 5 * Math.PI) + 1; - }, - InOut: function (amount) { - if (amount === 0) { - return 0; - } - if (amount === 1) { - return 1; - } - amount *= 2; - if (amount < 1) { - return -0.5 * Math.pow(2, 10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI); - } - return 0.5 * Math.pow(2, -10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI) + 1; - }, - }), - Back: Object.freeze({ - In: function (amount) { - var s = 1.70158; - return amount === 1 ? 1 : amount * amount * ((s + 1) * amount - s); - }, - Out: function (amount) { - var s = 1.70158; - return amount === 0 ? 0 : --amount * amount * ((s + 1) * amount + s) + 1; - }, - InOut: function (amount) { - var s = 1.70158 * 1.525; - if ((amount *= 2) < 1) { - return 0.5 * (amount * amount * ((s + 1) * amount - s)); - } - return 0.5 * ((amount -= 2) * amount * ((s + 1) * amount + s) + 2); - }, - }), - Bounce: Object.freeze({ - In: function (amount) { - return 1 - Easing.Bounce.Out(1 - amount); - }, - Out: function (amount) { - if (amount < 1 / 2.75) { - return 7.5625 * amount * amount; - } - else if (amount < 2 / 2.75) { - return 7.5625 * (amount -= 1.5 / 2.75) * amount + 0.75; - } - else if (amount < 2.5 / 2.75) { - return 7.5625 * (amount -= 2.25 / 2.75) * amount + 0.9375; - } - else { - return 7.5625 * (amount -= 2.625 / 2.75) * amount + 0.984375; - } - }, - InOut: function (amount) { - if (amount < 0.5) { - return Easing.Bounce.In(amount * 2) * 0.5; - } - return Easing.Bounce.Out(amount * 2 - 1) * 0.5 + 0.5; - }, - }), - generatePow: function (power) { - if (power === void 0) { power = 4; } - power = power < Number.EPSILON ? Number.EPSILON : power; - power = power > 10000 ? 10000 : power; - return { - In: function (amount) { - return Math.pow(amount, power); - }, - Out: function (amount) { - return 1 - Math.pow((1 - amount), power); - }, - InOut: function (amount) { - if (amount < 0.5) { - return Math.pow((amount * 2), power) / 2; - } - return (1 - Math.pow((2 - amount * 2), power)) / 2 + 0.5; - }, - }; - }, - }); - - var now = function () { return performance.now(); }; - - /** - * Controlling groups of tweens - * - * Using the TWEEN singleton to manage your tweens can cause issues in large apps with many components. - * In these cases, you may want to create your own smaller groups of tween - */ - var Group = /** @class */ (function () { - function Group() { - this._tweens = {}; - this._tweensAddedDuringUpdate = {}; - } - Group.prototype.getAll = function () { - var _this = this; - return Object.keys(this._tweens).map(function (tweenId) { - return _this._tweens[tweenId]; - }); - }; - Group.prototype.removeAll = function () { - this._tweens = {}; - }; - Group.prototype.add = function (tween) { - this._tweens[tween.getId()] = tween; - this._tweensAddedDuringUpdate[tween.getId()] = tween; - }; - Group.prototype.remove = function (tween) { - delete this._tweens[tween.getId()]; - delete this._tweensAddedDuringUpdate[tween.getId()]; - }; - Group.prototype.update = function (time, preserve) { - if (time === void 0) { time = now(); } - if (preserve === void 0) { preserve = false; } - var tweenIds = Object.keys(this._tweens); - if (tweenIds.length === 0) { - return false; - } - // Tweens are updated in "batches". If you add a new tween during an - // update, then the new tween will be updated in the next batch. - // If you remove a tween during an update, it may or may not be updated. - // However, if the removed tween was added during the current batch, - // then it will not be updated. - while (tweenIds.length > 0) { - this._tweensAddedDuringUpdate = {}; - for (var i = 0; i < tweenIds.length; i++) { - var tween = this._tweens[tweenIds[i]]; - var autoStart = !preserve; - if (tween && tween.update(time, autoStart) === false && !preserve) { - delete this._tweens[tweenIds[i]]; - } - } - tweenIds = Object.keys(this._tweensAddedDuringUpdate); - } - return true; - }; - return Group; - }()); - - /** - * - */ - var Interpolation = { - Linear: function (v, k) { - var m = v.length - 1; - var f = m * k; - var i = Math.floor(f); - var fn = Interpolation.Utils.Linear; - if (k < 0) { - return fn(v[0], v[1], f); - } - if (k > 1) { - return fn(v[m], v[m - 1], m - f); - } - return fn(v[i], v[i + 1 > m ? m : i + 1], f - i); - }, - Bezier: function (v, k) { - var b = 0; - var n = v.length - 1; - var pw = Math.pow; - var bn = Interpolation.Utils.Bernstein; - for (var i = 0; i <= n; i++) { - b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i); - } - return b; - }, - CatmullRom: function (v, k) { - var m = v.length - 1; - var f = m * k; - var i = Math.floor(f); - var fn = Interpolation.Utils.CatmullRom; - if (v[0] === v[m]) { - if (k < 0) { - i = Math.floor((f = m * (1 + k))); - } - return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i); - } - else { - if (k < 0) { - return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]); - } - if (k > 1) { - return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]); - } - return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i); - } - }, - Utils: { - Linear: function (p0, p1, t) { - return (p1 - p0) * t + p0; - }, - Bernstein: function (n, i) { - var fc = Interpolation.Utils.Factorial; - return fc(n) / fc(i) / fc(n - i); - }, - Factorial: (function () { - var a = [1]; - return function (n) { - var s = 1; - if (a[n]) { - return a[n]; - } - for (var i = n; i > 1; i--) { - s *= i; - } - a[n] = s; - return s; - }; - })(), - CatmullRom: function (p0, p1, p2, p3, t) { - var v0 = (p2 - p0) * 0.5; - var v1 = (p3 - p1) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; - }, - }, - }; - - /** - * Utils - */ - var Sequence = /** @class */ (function () { - function Sequence() { - } - Sequence.nextId = function () { - return Sequence._nextId++; - }; - Sequence._nextId = 0; - return Sequence; - }()); - - var mainGroup = new Group(); - - /** - * Tween.js - Licensed under the MIT license - * https://github.com/tweenjs/tween.js - * ---------------------------------------------- - * - * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors. - * Thank you all, you're awesome! - */ - var Tween = /** @class */ (function () { - function Tween(_object, _group) { - if (_group === void 0) { _group = mainGroup; } - this._object = _object; - this._group = _group; - this._isPaused = false; - this._pauseStart = 0; - this._valuesStart = {}; - this._valuesEnd = {}; - this._valuesStartRepeat = {}; - this._duration = 1000; - this._isDynamic = false; - this._initialRepeat = 0; - this._repeat = 0; - this._yoyo = false; - this._isPlaying = false; - this._reversed = false; - this._delayTime = 0; - this._startTime = 0; - this._easingFunction = Easing.Linear.None; - this._interpolationFunction = Interpolation.Linear; - // eslint-disable-next-line - this._chainedTweens = []; - this._onStartCallbackFired = false; - this._onEveryStartCallbackFired = false; - this._id = Sequence.nextId(); - this._isChainStopped = false; - this._propertiesAreSetUp = false; - this._goToEnd = false; - } - Tween.prototype.getId = function () { - return this._id; - }; - Tween.prototype.isPlaying = function () { - return this._isPlaying; - }; - Tween.prototype.isPaused = function () { - return this._isPaused; - }; - Tween.prototype.to = function (target, duration) { - if (duration === void 0) { duration = 1000; } - if (this._isPlaying) - throw new Error('Can not call Tween.to() while Tween is already started or paused. Stop the Tween first.'); - this._valuesEnd = target; - this._propertiesAreSetUp = false; - this._duration = duration; - return this; - }; - Tween.prototype.duration = function (duration) { - if (duration === void 0) { duration = 1000; } - this._duration = duration; - return this; - }; - Tween.prototype.dynamic = function (dynamic) { - if (dynamic === void 0) { dynamic = false; } - this._isDynamic = dynamic; - return this; - }; - Tween.prototype.start = function (time, overrideStartingValues) { - if (time === void 0) { time = now(); } - if (overrideStartingValues === void 0) { overrideStartingValues = false; } - if (this._isPlaying) { - return this; - } - // eslint-disable-next-line - this._group && this._group.add(this); - this._repeat = this._initialRepeat; - if (this._reversed) { - // If we were reversed (f.e. using the yoyo feature) then we need to - // flip the tween direction back to forward. - this._reversed = false; - for (var property in this._valuesStartRepeat) { - this._swapEndStartRepeatValues(property); - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - } - this._isPlaying = true; - this._isPaused = false; - this._onStartCallbackFired = false; - this._onEveryStartCallbackFired = false; - this._isChainStopped = false; - this._startTime = time; - this._startTime += this._delayTime; - if (!this._propertiesAreSetUp || overrideStartingValues) { - this._propertiesAreSetUp = true; - // If dynamic is not enabled, clone the end values instead of using the passed-in end values. - if (!this._isDynamic) { - var tmp = {}; - for (var prop in this._valuesEnd) - tmp[prop] = this._valuesEnd[prop]; - this._valuesEnd = tmp; - } - this._setupProperties(this._object, this._valuesStart, this._valuesEnd, this._valuesStartRepeat, overrideStartingValues); - } - return this; - }; - Tween.prototype.startFromCurrentValues = function (time) { - return this.start(time, true); - }; - Tween.prototype._setupProperties = function (_object, _valuesStart, _valuesEnd, _valuesStartRepeat, overrideStartingValues) { - for (var property in _valuesEnd) { - var startValue = _object[property]; - var startValueIsArray = Array.isArray(startValue); - var propType = startValueIsArray ? 'array' : typeof startValue; - var isInterpolationList = !startValueIsArray && Array.isArray(_valuesEnd[property]); - // If `to()` specifies a property that doesn't exist in the source object, - // we should not set that property in the object - if (propType === 'undefined' || propType === 'function') { - continue; - } - // Check if an Array was provided as property value - if (isInterpolationList) { - var endValues = _valuesEnd[property]; - if (endValues.length === 0) { - continue; - } - // Handle an array of relative values. - // Creates a local copy of the Array with the start value at the front - var temp = [startValue]; - for (var i = 0, l = endValues.length; i < l; i += 1) { - var value = this._handleRelativeValue(startValue, endValues[i]); - if (isNaN(value)) { - isInterpolationList = false; - console.warn('Found invalid interpolation list. Skipping.'); - break; - } - temp.push(value); - } - if (isInterpolationList) { - // if (_valuesStart[property] === undefined) { // handle end values only the first time. NOT NEEDED? setupProperties is now guarded by _propertiesAreSetUp. - _valuesEnd[property] = temp; - // } - } - } - // handle the deepness of the values - if ((propType === 'object' || startValueIsArray) && startValue && !isInterpolationList) { - _valuesStart[property] = startValueIsArray ? [] : {}; - var nestedObject = startValue; - for (var prop in nestedObject) { - _valuesStart[property][prop] = nestedObject[prop]; - } - // TODO? repeat nested values? And yoyo? And array values? - _valuesStartRepeat[property] = startValueIsArray ? [] : {}; - var endValues = _valuesEnd[property]; - // If dynamic is not enabled, clone the end values instead of using the passed-in end values. - if (!this._isDynamic) { - var tmp = {}; - for (var prop in endValues) - tmp[prop] = endValues[prop]; - _valuesEnd[property] = endValues = tmp; - } - this._setupProperties(nestedObject, _valuesStart[property], endValues, _valuesStartRepeat[property], overrideStartingValues); - } - else { - // Save the starting value, but only once unless override is requested. - if (typeof _valuesStart[property] === 'undefined' || overrideStartingValues) { - _valuesStart[property] = startValue; - } - if (!startValueIsArray) { - // eslint-disable-next-line - // @ts-ignore FIXME? - _valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings - } - if (isInterpolationList) { - // eslint-disable-next-line - // @ts-ignore FIXME? - _valuesStartRepeat[property] = _valuesEnd[property].slice().reverse(); - } - else { - _valuesStartRepeat[property] = _valuesStart[property] || 0; - } - } - } - }; - Tween.prototype.stop = function () { - if (!this._isChainStopped) { - this._isChainStopped = true; - this.stopChainedTweens(); - } - if (!this._isPlaying) { - return this; - } - // eslint-disable-next-line - this._group && this._group.remove(this); - this._isPlaying = false; - this._isPaused = false; - if (this._onStopCallback) { - this._onStopCallback(this._object); - } - return this; - }; - Tween.prototype.end = function () { - this._goToEnd = true; - this.update(Infinity); - return this; - }; - Tween.prototype.pause = function (time) { - if (time === void 0) { time = now(); } - if (this._isPaused || !this._isPlaying) { - return this; - } - this._isPaused = true; - this._pauseStart = time; - // eslint-disable-next-line - this._group && this._group.remove(this); - return this; - }; - Tween.prototype.resume = function (time) { - if (time === void 0) { time = now(); } - if (!this._isPaused || !this._isPlaying) { - return this; - } - this._isPaused = false; - this._startTime += time - this._pauseStart; - this._pauseStart = 0; - // eslint-disable-next-line - this._group && this._group.add(this); - return this; - }; - Tween.prototype.stopChainedTweens = function () { - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - this._chainedTweens[i].stop(); - } - return this; - }; - Tween.prototype.group = function (group) { - if (group === void 0) { group = mainGroup; } - this._group = group; - return this; - }; - Tween.prototype.delay = function (amount) { - if (amount === void 0) { amount = 0; } - this._delayTime = amount; - return this; - }; - Tween.prototype.repeat = function (times) { - if (times === void 0) { times = 0; } - this._initialRepeat = times; - this._repeat = times; - return this; - }; - Tween.prototype.repeatDelay = function (amount) { - this._repeatDelayTime = amount; - return this; - }; - Tween.prototype.yoyo = function (yoyo) { - if (yoyo === void 0) { yoyo = false; } - this._yoyo = yoyo; - return this; - }; - Tween.prototype.easing = function (easingFunction) { - if (easingFunction === void 0) { easingFunction = Easing.Linear.None; } - this._easingFunction = easingFunction; - return this; - }; - Tween.prototype.interpolation = function (interpolationFunction) { - if (interpolationFunction === void 0) { interpolationFunction = Interpolation.Linear; } - this._interpolationFunction = interpolationFunction; - return this; - }; - // eslint-disable-next-line - Tween.prototype.chain = function () { - var tweens = []; - for (var _i = 0; _i < arguments.length; _i++) { - tweens[_i] = arguments[_i]; - } - this._chainedTweens = tweens; - return this; - }; - Tween.prototype.onStart = function (callback) { - this._onStartCallback = callback; - return this; - }; - Tween.prototype.onEveryStart = function (callback) { - this._onEveryStartCallback = callback; - return this; - }; - Tween.prototype.onUpdate = function (callback) { - this._onUpdateCallback = callback; - return this; - }; - Tween.prototype.onRepeat = function (callback) { - this._onRepeatCallback = callback; - return this; - }; - Tween.prototype.onComplete = function (callback) { - this._onCompleteCallback = callback; - return this; - }; - Tween.prototype.onStop = function (callback) { - this._onStopCallback = callback; - return this; - }; - /** - * @returns true if the tween is still playing after the update, false - * otherwise (calling update on a paused tween still returns true because - * it is still playing, just paused). - */ - Tween.prototype.update = function (time, autoStart) { - if (time === void 0) { time = now(); } - if (autoStart === void 0) { autoStart = true; } - if (this._isPaused) - return true; - var property; - var elapsed; - var endTime = this._startTime + this._duration; - if (!this._goToEnd && !this._isPlaying) { - if (time > endTime) - return false; - if (autoStart) - this.start(time, true); - } - this._goToEnd = false; - if (time < this._startTime) { - return true; - } - if (this._onStartCallbackFired === false) { - if (this._onStartCallback) { - this._onStartCallback(this._object); - } - this._onStartCallbackFired = true; - } - if (this._onEveryStartCallbackFired === false) { - if (this._onEveryStartCallback) { - this._onEveryStartCallback(this._object); - } - this._onEveryStartCallbackFired = true; - } - elapsed = (time - this._startTime) / this._duration; - elapsed = this._duration === 0 || elapsed > 1 ? 1 : elapsed; - var value = this._easingFunction(elapsed); - // properties transformations - this._updateProperties(this._object, this._valuesStart, this._valuesEnd, value); - if (this._onUpdateCallback) { - this._onUpdateCallback(this._object, elapsed); - } - if (elapsed === 1) { - if (this._repeat > 0) { - if (isFinite(this._repeat)) { - this._repeat--; - } - // Reassign starting values, restart by making startTime = now - for (property in this._valuesStartRepeat) { - if (!this._yoyo && typeof this._valuesEnd[property] === 'string') { - this._valuesStartRepeat[property] = - // eslint-disable-next-line - // @ts-ignore FIXME? - this._valuesStartRepeat[property] + parseFloat(this._valuesEnd[property]); - } - if (this._yoyo) { - this._swapEndStartRepeatValues(property); - } - this._valuesStart[property] = this._valuesStartRepeat[property]; - } - if (this._yoyo) { - this._reversed = !this._reversed; - } - if (this._repeatDelayTime !== undefined) { - this._startTime = time + this._repeatDelayTime; - } - else { - this._startTime = time + this._delayTime; - } - if (this._onRepeatCallback) { - this._onRepeatCallback(this._object); - } - this._onEveryStartCallbackFired = false; - return true; - } - else { - if (this._onCompleteCallback) { - this._onCompleteCallback(this._object); - } - for (var i = 0, numChainedTweens = this._chainedTweens.length; i < numChainedTweens; i++) { - // Make the chained tweens start exactly at the time they should, - // even if the `update()` method was called way past the duration of the tween - this._chainedTweens[i].start(this._startTime + this._duration, false); - } - this._isPlaying = false; - return false; - } - } - return true; - }; - Tween.prototype._updateProperties = function (_object, _valuesStart, _valuesEnd, value) { - for (var property in _valuesEnd) { - // Don't update properties that do not exist in the source object - if (_valuesStart[property] === undefined) { - continue; - } - var start = _valuesStart[property] || 0; - var end = _valuesEnd[property]; - var startIsArray = Array.isArray(_object[property]); - var endIsArray = Array.isArray(end); - var isInterpolationList = !startIsArray && endIsArray; - if (isInterpolationList) { - _object[property] = this._interpolationFunction(end, value); - } - else if (typeof end === 'object' && end) { - // eslint-disable-next-line - // @ts-ignore FIXME? - this._updateProperties(_object[property], start, end, value); - } - else { - // Parses relative end values with start as base (e.g.: +10, -3) - end = this._handleRelativeValue(start, end); - // Protect against non numeric properties. - if (typeof end === 'number') { - // eslint-disable-next-line - // @ts-ignore FIXME? - _object[property] = start + (end - start) * value; - } - } - } - }; - Tween.prototype._handleRelativeValue = function (start, end) { - if (typeof end !== 'string') { - return end; - } - if (end.charAt(0) === '+' || end.charAt(0) === '-') { - return start + parseFloat(end); - } - return parseFloat(end); - }; - Tween.prototype._swapEndStartRepeatValues = function (property) { - var tmp = this._valuesStartRepeat[property]; - var endValue = this._valuesEnd[property]; - if (typeof endValue === 'string') { - this._valuesStartRepeat[property] = this._valuesStartRepeat[property] + parseFloat(endValue); - } - else { - this._valuesStartRepeat[property] = this._valuesEnd[property]; - } - this._valuesEnd[property] = tmp; - }; - return Tween; - }()); - /** - * Controlling groups of tweens - * - * Using the TWEEN singleton to manage your tweens can cause issues in large apps with many components. - * In these cases, you may want to create your own smaller groups of tweens. - */ - var TWEEN = mainGroup; - // This is the best way to export things in a way that's compatible with both ES - // Modules and CommonJS, without build hacks, and so as not to break the - // existing API. - // https://github.com/rollup/rollup/issues/1961#issuecomment-423037881 - TWEEN.getAll.bind(TWEEN); - TWEEN.removeAll.bind(TWEEN); - TWEEN.add.bind(TWEEN); - TWEEN.remove.bind(TWEEN); - var update = TWEEN.update.bind(TWEEN); - - function _iterableToArrayLimit$1(arr, i) { - var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; - if (null != _i) { - var _s, - _e, - _x, - _r, - _arr = [], - _n = !0, - _d = !1; - try { - if (_x = (_i = _i.call(arr)).next, 0 === i) { - if (Object(_i) !== _i) return; - _n = !1; - } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); - } catch (err) { - _d = !0, _e = err; - } finally { - try { - if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; - } finally { - if (_d) throw _e; - } - } - return _arr; - } - } - function _classCallCheck$1(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - function _defineProperties$1(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, _toPropertyKey$2(descriptor.key), descriptor); - } - } - function _createClass$1(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties$1(Constructor.prototype, protoProps); - if (staticProps) _defineProperties$1(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; - } - function _slicedToArray$1(arr, i) { - return _arrayWithHoles$1(arr) || _iterableToArrayLimit$1(arr, i) || _unsupportedIterableToArray$2(arr, i) || _nonIterableRest$1(); - } - function _arrayWithHoles$1(arr) { - if (Array.isArray(arr)) return arr; - } - function _unsupportedIterableToArray$2(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); - } - function _arrayLikeToArray$2(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; - } - function _nonIterableRest$1() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _toPrimitive$2(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey$2(arg) { - var key = _toPrimitive$2(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } - - var Prop = /*#__PURE__*/_createClass$1(function Prop(name, _ref) { - var _ref$default = _ref["default"], - defaultVal = _ref$default === void 0 ? null : _ref$default, - _ref$triggerUpdate = _ref.triggerUpdate, - triggerUpdate = _ref$triggerUpdate === void 0 ? true : _ref$triggerUpdate, - _ref$onChange = _ref.onChange, - onChange = _ref$onChange === void 0 ? function (newVal, state) {} : _ref$onChange; - _classCallCheck$1(this, Prop); - this.name = name; - this.defaultVal = defaultVal; - this.triggerUpdate = triggerUpdate; - this.onChange = onChange; - }); - function index$3 (_ref2) { - var _ref2$stateInit = _ref2.stateInit, - stateInit = _ref2$stateInit === void 0 ? function () { - return {}; - } : _ref2$stateInit, - _ref2$props = _ref2.props, - rawProps = _ref2$props === void 0 ? {} : _ref2$props, - _ref2$methods = _ref2.methods, - methods = _ref2$methods === void 0 ? {} : _ref2$methods, - _ref2$aliases = _ref2.aliases, - aliases = _ref2$aliases === void 0 ? {} : _ref2$aliases, - _ref2$init = _ref2.init, - initFn = _ref2$init === void 0 ? function () {} : _ref2$init, - _ref2$update = _ref2.update, - updateFn = _ref2$update === void 0 ? function () {} : _ref2$update; - // Parse props into Prop instances - var props = Object.keys(rawProps).map(function (propName) { - return new Prop(propName, rawProps[propName]); - }); - return function () { - var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - // Holds component state - var state = Object.assign({}, stateInit instanceof Function ? stateInit(options) : stateInit, - // Support plain objects for backwards compatibility - { - initialised: false - }); - - // keeps track of which props triggered an update - var changedProps = {}; - - // Component constructor - function comp(nodeElement) { - initStatic(nodeElement, options); - digest(); - return comp; - } - var initStatic = function initStatic(nodeElement, options) { - initFn.call(comp, nodeElement, state, options); - state.initialised = true; - }; - var digest = debounce(function () { - if (!state.initialised) { - return; - } - updateFn.call(comp, state, changedProps); - changedProps = {}; - }, 1); - - // Getter/setter methods - props.forEach(function (prop) { - comp[prop.name] = getSetProp(prop); - function getSetProp(_ref3) { - var prop = _ref3.name, - _ref3$triggerUpdate = _ref3.triggerUpdate, - redigest = _ref3$triggerUpdate === void 0 ? false : _ref3$triggerUpdate, - _ref3$onChange = _ref3.onChange, - onChange = _ref3$onChange === void 0 ? function (newVal, state) {} : _ref3$onChange, - _ref3$defaultVal = _ref3.defaultVal, - defaultVal = _ref3$defaultVal === void 0 ? null : _ref3$defaultVal; - return function (_) { - var curVal = state[prop]; - if (!arguments.length) { - return curVal; - } // Getter mode - - var val = _ === undefined ? defaultVal : _; // pick default if value passed is undefined - state[prop] = val; - onChange.call(comp, val, state, curVal); - - // track changed props - !changedProps.hasOwnProperty(prop) && (changedProps[prop] = curVal); - if (redigest) { - digest(); - } - return comp; - }; - } - }); - - // Other methods - Object.keys(methods).forEach(function (methodName) { - comp[methodName] = function () { - var _methods$methodName; - for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - return (_methods$methodName = methods[methodName]).call.apply(_methods$methodName, [comp, state].concat(args)); - }; - }); - - // Link aliases - Object.entries(aliases).forEach(function (_ref4) { - var _ref5 = _slicedToArray$1(_ref4, 2), - alias = _ref5[0], - target = _ref5[1]; - return comp[alias] = comp[target]; - }); - - // Reset all component props to their default value - comp.resetProps = function () { - props.forEach(function (prop) { - comp[prop.name](prop.defaultVal); - }); - return comp; - }; - - // - - comp.resetProps(); // Apply all prop defaults - state._rerender = digest; // Expose digest method - - return comp; - }; - } - - var index$2 = (function (p) { - return typeof p === 'function' ? p // fn - : typeof p === 'string' ? function (obj) { - return obj[p]; - } // property name - : function (obj) { - return p; - }; - }); // constant - - // This file is autogenerated. It's used to publish ESM to npm. - function _typeof(obj) { - "@babel/helpers - typeof"; - - return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }, _typeof(obj); - } - - // https://github.com/bgrins/TinyColor - // Brian Grinstead, MIT License - - var trimLeft = /^\s+/; - var trimRight = /\s+$/; - function tinycolor(color, opts) { - color = color ? color : ""; - opts = opts || {}; - - // If input is already a tinycolor, return itself - if (color instanceof tinycolor) { - return color; - } - // If we are called as a function, call using new instead - if (!(this instanceof tinycolor)) { - return new tinycolor(color, opts); - } - var rgb = inputToRGB(color); - this._originalInput = color, this._r = rgb.r, this._g = rgb.g, this._b = rgb.b, this._a = rgb.a, this._roundA = Math.round(100 * this._a) / 100, this._format = opts.format || rgb.format; - this._gradientType = opts.gradientType; - - // Don't let the range of [0,255] come back in [0,1]. - // Potentially lose a little bit of precision here, but will fix issues where - // .5 gets interpreted as half of the total, instead of half of 1 - // If it was supposed to be 128, this was already taken care of by `inputToRgb` - if (this._r < 1) this._r = Math.round(this._r); - if (this._g < 1) this._g = Math.round(this._g); - if (this._b < 1) this._b = Math.round(this._b); - this._ok = rgb.ok; - } - tinycolor.prototype = { - isDark: function isDark() { - return this.getBrightness() < 128; - }, - isLight: function isLight() { - return !this.isDark(); - }, - isValid: function isValid() { - return this._ok; - }, - getOriginalInput: function getOriginalInput() { - return this._originalInput; - }, - getFormat: function getFormat() { - return this._format; - }, - getAlpha: function getAlpha() { - return this._a; - }, - getBrightness: function getBrightness() { - //http://www.w3.org/TR/AERT#color-contrast - var rgb = this.toRgb(); - return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; - }, - getLuminance: function getLuminance() { - //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef - var rgb = this.toRgb(); - var RsRGB, GsRGB, BsRGB, R, G, B; - RsRGB = rgb.r / 255; - GsRGB = rgb.g / 255; - BsRGB = rgb.b / 255; - if (RsRGB <= 0.03928) R = RsRGB / 12.92;else R = Math.pow((RsRGB + 0.055) / 1.055, 2.4); - if (GsRGB <= 0.03928) G = GsRGB / 12.92;else G = Math.pow((GsRGB + 0.055) / 1.055, 2.4); - if (BsRGB <= 0.03928) B = BsRGB / 12.92;else B = Math.pow((BsRGB + 0.055) / 1.055, 2.4); - return 0.2126 * R + 0.7152 * G + 0.0722 * B; - }, - setAlpha: function setAlpha(value) { - this._a = boundAlpha(value); - this._roundA = Math.round(100 * this._a) / 100; - return this; - }, - toHsv: function toHsv() { - var hsv = rgbToHsv(this._r, this._g, this._b); - return { - h: hsv.h * 360, - s: hsv.s, - v: hsv.v, - a: this._a - }; - }, - toHsvString: function toHsvString() { - var hsv = rgbToHsv(this._r, this._g, this._b); - var h = Math.round(hsv.h * 360), - s = Math.round(hsv.s * 100), - v = Math.round(hsv.v * 100); - return this._a == 1 ? "hsv(" + h + ", " + s + "%, " + v + "%)" : "hsva(" + h + ", " + s + "%, " + v + "%, " + this._roundA + ")"; - }, - toHsl: function toHsl() { - var hsl = rgbToHsl(this._r, this._g, this._b); - return { - h: hsl.h * 360, - s: hsl.s, - l: hsl.l, - a: this._a - }; - }, - toHslString: function toHslString() { - var hsl = rgbToHsl(this._r, this._g, this._b); - var h = Math.round(hsl.h * 360), - s = Math.round(hsl.s * 100), - l = Math.round(hsl.l * 100); - return this._a == 1 ? "hsl(" + h + ", " + s + "%, " + l + "%)" : "hsla(" + h + ", " + s + "%, " + l + "%, " + this._roundA + ")"; - }, - toHex: function toHex(allow3Char) { - return rgbToHex(this._r, this._g, this._b, allow3Char); - }, - toHexString: function toHexString(allow3Char) { - return "#" + this.toHex(allow3Char); - }, - toHex8: function toHex8(allow4Char) { - return rgbaToHex(this._r, this._g, this._b, this._a, allow4Char); - }, - toHex8String: function toHex8String(allow4Char) { - return "#" + this.toHex8(allow4Char); - }, - toRgb: function toRgb() { - return { - r: Math.round(this._r), - g: Math.round(this._g), - b: Math.round(this._b), - a: this._a - }; - }, - toRgbString: function toRgbString() { - return this._a == 1 ? "rgb(" + Math.round(this._r) + ", " + Math.round(this._g) + ", " + Math.round(this._b) + ")" : "rgba(" + Math.round(this._r) + ", " + Math.round(this._g) + ", " + Math.round(this._b) + ", " + this._roundA + ")"; - }, - toPercentageRgb: function toPercentageRgb() { - return { - r: Math.round(bound01(this._r, 255) * 100) + "%", - g: Math.round(bound01(this._g, 255) * 100) + "%", - b: Math.round(bound01(this._b, 255) * 100) + "%", - a: this._a - }; - }, - toPercentageRgbString: function toPercentageRgbString() { - return this._a == 1 ? "rgb(" + Math.round(bound01(this._r, 255) * 100) + "%, " + Math.round(bound01(this._g, 255) * 100) + "%, " + Math.round(bound01(this._b, 255) * 100) + "%)" : "rgba(" + Math.round(bound01(this._r, 255) * 100) + "%, " + Math.round(bound01(this._g, 255) * 100) + "%, " + Math.round(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; - }, - toName: function toName() { - if (this._a === 0) { - return "transparent"; - } - if (this._a < 1) { - return false; - } - return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; - }, - toFilter: function toFilter(secondColor) { - var hex8String = "#" + rgbaToArgbHex(this._r, this._g, this._b, this._a); - var secondHex8String = hex8String; - var gradientType = this._gradientType ? "GradientType = 1, " : ""; - if (secondColor) { - var s = tinycolor(secondColor); - secondHex8String = "#" + rgbaToArgbHex(s._r, s._g, s._b, s._a); - } - return "progid:DXImageTransform.Microsoft.gradient(" + gradientType + "startColorstr=" + hex8String + ",endColorstr=" + secondHex8String + ")"; - }, - toString: function toString(format) { - var formatSet = !!format; - format = format || this._format; - var formattedString = false; - var hasAlpha = this._a < 1 && this._a >= 0; - var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "hex4" || format === "hex8" || format === "name"); - if (needsAlphaFormat) { - // Special case for "transparent", all other non-alpha formats - // will return rgba when there is transparency. - if (format === "name" && this._a === 0) { - return this.toName(); - } - return this.toRgbString(); - } - if (format === "rgb") { - formattedString = this.toRgbString(); - } - if (format === "prgb") { - formattedString = this.toPercentageRgbString(); - } - if (format === "hex" || format === "hex6") { - formattedString = this.toHexString(); - } - if (format === "hex3") { - formattedString = this.toHexString(true); - } - if (format === "hex4") { - formattedString = this.toHex8String(true); - } - if (format === "hex8") { - formattedString = this.toHex8String(); - } - if (format === "name") { - formattedString = this.toName(); - } - if (format === "hsl") { - formattedString = this.toHslString(); - } - if (format === "hsv") { - formattedString = this.toHsvString(); - } - return formattedString || this.toHexString(); - }, - clone: function clone() { - return tinycolor(this.toString()); - }, - _applyModification: function _applyModification(fn, args) { - var color = fn.apply(null, [this].concat([].slice.call(args))); - this._r = color._r; - this._g = color._g; - this._b = color._b; - this.setAlpha(color._a); - return this; - }, - lighten: function lighten() { - return this._applyModification(_lighten, arguments); - }, - brighten: function brighten() { - return this._applyModification(_brighten, arguments); - }, - darken: function darken() { - return this._applyModification(_darken, arguments); - }, - desaturate: function desaturate() { - return this._applyModification(_desaturate, arguments); - }, - saturate: function saturate() { - return this._applyModification(_saturate, arguments); - }, - greyscale: function greyscale() { - return this._applyModification(_greyscale, arguments); - }, - spin: function spin() { - return this._applyModification(_spin, arguments); - }, - _applyCombination: function _applyCombination(fn, args) { - return fn.apply(null, [this].concat([].slice.call(args))); - }, - analogous: function analogous() { - return this._applyCombination(_analogous, arguments); - }, - complement: function complement() { - return this._applyCombination(_complement, arguments); - }, - monochromatic: function monochromatic() { - return this._applyCombination(_monochromatic, arguments); - }, - splitcomplement: function splitcomplement() { - return this._applyCombination(_splitcomplement, arguments); - }, - // Disabled until https://github.com/bgrins/TinyColor/issues/254 - // polyad: function (number) { - // return this._applyCombination(polyad, [number]); - // }, - triad: function triad() { - return this._applyCombination(polyad, [3]); - }, - tetrad: function tetrad() { - return this._applyCombination(polyad, [4]); - } - }; - - // If input is an object, force 1 into "1.0" to handle ratios properly - // String input requires "1.0" as input, so 1 will be treated as 1 - tinycolor.fromRatio = function (color, opts) { - if (_typeof(color) == "object") { - var newColor = {}; - for (var i in color) { - if (color.hasOwnProperty(i)) { - if (i === "a") { - newColor[i] = color[i]; - } else { - newColor[i] = convertToPercentage(color[i]); - } - } - } - color = newColor; - } - return tinycolor(color, opts); - }; - - // Given a string or object, convert that input to RGB - // Possible string inputs: - // - // "red" - // "#f00" or "f00" - // "#ff0000" or "ff0000" - // "#ff000000" or "ff000000" - // "rgb 255 0 0" or "rgb (255, 0, 0)" - // "rgb 1.0 0 0" or "rgb (1, 0, 0)" - // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" - // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" - // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" - // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" - // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" - // - function inputToRGB(color) { - var rgb = { - r: 0, - g: 0, - b: 0 - }; - var a = 1; - var s = null; - var v = null; - var l = null; - var ok = false; - var format = false; - if (typeof color == "string") { - color = stringInputToObject(color); - } - if (_typeof(color) == "object") { - if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) { - rgb = rgbToRgb(color.r, color.g, color.b); - ok = true; - format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; - } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) { - s = convertToPercentage(color.s); - v = convertToPercentage(color.v); - rgb = hsvToRgb(color.h, s, v); - ok = true; - format = "hsv"; - } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) { - s = convertToPercentage(color.s); - l = convertToPercentage(color.l); - rgb = hslToRgb(color.h, s, l); - ok = true; - format = "hsl"; - } - if (color.hasOwnProperty("a")) { - a = color.a; - } - } - a = boundAlpha(a); - return { - ok: ok, - format: color.format || format, - r: Math.min(255, Math.max(rgb.r, 0)), - g: Math.min(255, Math.max(rgb.g, 0)), - b: Math.min(255, Math.max(rgb.b, 0)), - a: a - }; - } - - // Conversion Functions - // -------------------- - - // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: - // - - // `rgbToRgb` - // Handle bounds / percentage checking to conform to CSS color spec - // - // *Assumes:* r, g, b in [0, 255] or [0, 1] - // *Returns:* { r, g, b } in [0, 255] - function rgbToRgb(r, g, b) { - return { - r: bound01(r, 255) * 255, - g: bound01(g, 255) * 255, - b: bound01(b, 255) * 255 - }; - } - - // `rgbToHsl` - // Converts an RGB color value to HSL. - // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] - // *Returns:* { h, s, l } in [0,1] - function rgbToHsl(r, g, b) { - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); - var max = Math.max(r, g, b), - min = Math.min(r, g, b); - var h, - s, - l = (max + min) / 2; - if (max == min) { - h = s = 0; // achromatic - } else { - var d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch (max) { - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break; - case g: - h = (b - r) / d + 2; - break; - case b: - h = (r - g) / d + 4; - break; - } - h /= 6; - } - return { - h: h, - s: s, - l: l - }; - } - - // `hslToRgb` - // Converts an HSL color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hslToRgb(h, s, l) { - var r, g, b; - h = bound01(h, 360); - s = bound01(s, 100); - l = bound01(l, 100); - function hue2rgb(p, q, t) { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6) return p + (q - p) * 6 * t; - if (t < 1 / 2) return q; - if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; - return p; - } - if (s === 0) { - r = g = b = l; // achromatic - } else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - } - - // `rgbToHsv` - // Converts an RGB color value to HSV - // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] - // *Returns:* { h, s, v } in [0,1] - function rgbToHsv(r, g, b) { - r = bound01(r, 255); - g = bound01(g, 255); - b = bound01(b, 255); - var max = Math.max(r, g, b), - min = Math.min(r, g, b); - var h, - s, - v = max; - var d = max - min; - s = max === 0 ? 0 : d / max; - if (max == min) { - h = 0; // achromatic - } else { - switch (max) { - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break; - case g: - h = (b - r) / d + 2; - break; - case b: - h = (r - g) / d + 4; - break; - } - h /= 6; - } - return { - h: h, - s: s, - v: v - }; - } - - // `hsvToRgb` - // Converts an HSV color value to RGB. - // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] - // *Returns:* { r, g, b } in the set [0, 255] - function hsvToRgb(h, s, v) { - h = bound01(h, 360) * 6; - s = bound01(s, 100); - v = bound01(v, 100); - var i = Math.floor(h), - f = h - i, - p = v * (1 - s), - q = v * (1 - f * s), - t = v * (1 - (1 - f) * s), - mod = i % 6, - r = [v, q, p, p, t, v][mod], - g = [t, v, v, q, p, p][mod], - b = [p, p, t, v, v, q][mod]; - return { - r: r * 255, - g: g * 255, - b: b * 255 - }; - } - - // `rgbToHex` - // Converts an RGB color to hex - // Assumes r, g, and b are contained in the set [0, 255] - // Returns a 3 or 6 character hex - function rgbToHex(r, g, b, allow3Char) { - var hex = [pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16))]; - - // Return a 3 character hex if possible - if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { - return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); - } - return hex.join(""); - } - - // `rgbaToHex` - // Converts an RGBA color plus alpha transparency to hex - // Assumes r, g, b are contained in the set [0, 255] and - // a in [0, 1]. Returns a 4 or 8 character rgba hex - function rgbaToHex(r, g, b, a, allow4Char) { - var hex = [pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16)), pad2(convertDecimalToHex(a))]; - - // Return a 4 character hex if possible - if (allow4Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1) && hex[3].charAt(0) == hex[3].charAt(1)) { - return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0); - } - return hex.join(""); - } - - // `rgbaToArgbHex` - // Converts an RGBA color to an ARGB Hex8 string - // Rarely used, but required for "toFilter()" - function rgbaToArgbHex(r, g, b, a) { - var hex = [pad2(convertDecimalToHex(a)), pad2(Math.round(r).toString(16)), pad2(Math.round(g).toString(16)), pad2(Math.round(b).toString(16))]; - return hex.join(""); - } - - // `equals` - // Can be called with any tinycolor input - tinycolor.equals = function (color1, color2) { - if (!color1 || !color2) return false; - return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); - }; - tinycolor.random = function () { - return tinycolor.fromRatio({ - r: Math.random(), - g: Math.random(), - b: Math.random() - }); - }; - - // Modification Functions - // ---------------------- - // Thanks to less.js for some of the basics here - // - - function _desaturate(color, amount) { - amount = amount === 0 ? 0 : amount || 10; - var hsl = tinycolor(color).toHsl(); - hsl.s -= amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - function _saturate(color, amount) { - amount = amount === 0 ? 0 : amount || 10; - var hsl = tinycolor(color).toHsl(); - hsl.s += amount / 100; - hsl.s = clamp01(hsl.s); - return tinycolor(hsl); - } - function _greyscale(color) { - return tinycolor(color).desaturate(100); - } - function _lighten(color, amount) { - amount = amount === 0 ? 0 : amount || 10; - var hsl = tinycolor(color).toHsl(); - hsl.l += amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - function _brighten(color, amount) { - amount = amount === 0 ? 0 : amount || 10; - var rgb = tinycolor(color).toRgb(); - rgb.r = Math.max(0, Math.min(255, rgb.r - Math.round(255 * -(amount / 100)))); - rgb.g = Math.max(0, Math.min(255, rgb.g - Math.round(255 * -(amount / 100)))); - rgb.b = Math.max(0, Math.min(255, rgb.b - Math.round(255 * -(amount / 100)))); - return tinycolor(rgb); - } - function _darken(color, amount) { - amount = amount === 0 ? 0 : amount || 10; - var hsl = tinycolor(color).toHsl(); - hsl.l -= amount / 100; - hsl.l = clamp01(hsl.l); - return tinycolor(hsl); - } - - // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. - // Values outside of this range will be wrapped into this range. - function _spin(color, amount) { - var hsl = tinycolor(color).toHsl(); - var hue = (hsl.h + amount) % 360; - hsl.h = hue < 0 ? 360 + hue : hue; - return tinycolor(hsl); - } - - // Combination Functions - // --------------------- - // Thanks to jQuery xColor for some of the ideas behind these - // - - function _complement(color) { - var hsl = tinycolor(color).toHsl(); - hsl.h = (hsl.h + 180) % 360; - return tinycolor(hsl); - } - function polyad(color, number) { - if (isNaN(number) || number <= 0) { - throw new Error("Argument to polyad must be a positive number"); - } - var hsl = tinycolor(color).toHsl(); - var result = [tinycolor(color)]; - var step = 360 / number; - for (var i = 1; i < number; i++) { - result.push(tinycolor({ - h: (hsl.h + i * step) % 360, - s: hsl.s, - l: hsl.l - })); - } - return result; - } - function _splitcomplement(color) { - var hsl = tinycolor(color).toHsl(); - var h = hsl.h; - return [tinycolor(color), tinycolor({ - h: (h + 72) % 360, - s: hsl.s, - l: hsl.l - }), tinycolor({ - h: (h + 216) % 360, - s: hsl.s, - l: hsl.l - })]; - } - function _analogous(color, results, slices) { - results = results || 6; - slices = slices || 30; - var hsl = tinycolor(color).toHsl(); - var part = 360 / slices; - var ret = [tinycolor(color)]; - for (hsl.h = (hsl.h - (part * results >> 1) + 720) % 360; --results;) { - hsl.h = (hsl.h + part) % 360; - ret.push(tinycolor(hsl)); - } - return ret; - } - function _monochromatic(color, results) { - results = results || 6; - var hsv = tinycolor(color).toHsv(); - var h = hsv.h, - s = hsv.s, - v = hsv.v; - var ret = []; - var modification = 1 / results; - while (results--) { - ret.push(tinycolor({ - h: h, - s: s, - v: v - })); - v = (v + modification) % 1; - } - return ret; - } - - // Utility Functions - // --------------------- - - tinycolor.mix = function (color1, color2, amount) { - amount = amount === 0 ? 0 : amount || 50; - var rgb1 = tinycolor(color1).toRgb(); - var rgb2 = tinycolor(color2).toRgb(); - var p = amount / 100; - var rgba = { - r: (rgb2.r - rgb1.r) * p + rgb1.r, - g: (rgb2.g - rgb1.g) * p + rgb1.g, - b: (rgb2.b - rgb1.b) * p + rgb1.b, - a: (rgb2.a - rgb1.a) * p + rgb1.a - }; - return tinycolor(rgba); - }; - - // Readability Functions - // --------------------- - // false - // tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false - tinycolor.isReadable = function (color1, color2, wcag2) { - var readability = tinycolor.readability(color1, color2); - var wcag2Parms, out; - out = false; - wcag2Parms = validateWCAG2Parms(wcag2); - switch (wcag2Parms.level + wcag2Parms.size) { - case "AAsmall": - case "AAAlarge": - out = readability >= 4.5; - break; - case "AAlarge": - out = readability >= 3; - break; - case "AAAsmall": - out = readability >= 7; - break; - } - return out; - }; - - // `mostReadable` - // Given a base color and a list of possible foreground or background - // colors for that base, returns the most readable color. - // Optionally returns Black or White if the most readable color is unreadable. - // *Example* - // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" - // tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" - // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" - // tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" - tinycolor.mostReadable = function (baseColor, colorList, args) { - var bestColor = null; - var bestScore = 0; - var readability; - var includeFallbackColors, level, size; - args = args || {}; - includeFallbackColors = args.includeFallbackColors; - level = args.level; - size = args.size; - for (var i = 0; i < colorList.length; i++) { - readability = tinycolor.readability(baseColor, colorList[i]); - if (readability > bestScore) { - bestScore = readability; - bestColor = tinycolor(colorList[i]); - } - } - if (tinycolor.isReadable(baseColor, bestColor, { - level: level, - size: size - }) || !includeFallbackColors) { - return bestColor; - } else { - args.includeFallbackColors = false; - return tinycolor.mostReadable(baseColor, ["#fff", "#000"], args); - } - }; - - // Big List of Colors - // ------------------ - // - var names = tinycolor.names = { - aliceblue: "f0f8ff", - antiquewhite: "faebd7", - aqua: "0ff", - aquamarine: "7fffd4", - azure: "f0ffff", - beige: "f5f5dc", - bisque: "ffe4c4", - black: "000", - blanchedalmond: "ffebcd", - blue: "00f", - blueviolet: "8a2be2", - brown: "a52a2a", - burlywood: "deb887", - burntsienna: "ea7e5d", - cadetblue: "5f9ea0", - chartreuse: "7fff00", - chocolate: "d2691e", - coral: "ff7f50", - cornflowerblue: "6495ed", - cornsilk: "fff8dc", - crimson: "dc143c", - cyan: "0ff", - darkblue: "00008b", - darkcyan: "008b8b", - darkgoldenrod: "b8860b", - darkgray: "a9a9a9", - darkgreen: "006400", - darkgrey: "a9a9a9", - darkkhaki: "bdb76b", - darkmagenta: "8b008b", - darkolivegreen: "556b2f", - darkorange: "ff8c00", - darkorchid: "9932cc", - darkred: "8b0000", - darksalmon: "e9967a", - darkseagreen: "8fbc8f", - darkslateblue: "483d8b", - darkslategray: "2f4f4f", - darkslategrey: "2f4f4f", - darkturquoise: "00ced1", - darkviolet: "9400d3", - deeppink: "ff1493", - deepskyblue: "00bfff", - dimgray: "696969", - dimgrey: "696969", - dodgerblue: "1e90ff", - firebrick: "b22222", - floralwhite: "fffaf0", - forestgreen: "228b22", - fuchsia: "f0f", - gainsboro: "dcdcdc", - ghostwhite: "f8f8ff", - gold: "ffd700", - goldenrod: "daa520", - gray: "808080", - green: "008000", - greenyellow: "adff2f", - grey: "808080", - honeydew: "f0fff0", - hotpink: "ff69b4", - indianred: "cd5c5c", - indigo: "4b0082", - ivory: "fffff0", - khaki: "f0e68c", - lavender: "e6e6fa", - lavenderblush: "fff0f5", - lawngreen: "7cfc00", - lemonchiffon: "fffacd", - lightblue: "add8e6", - lightcoral: "f08080", - lightcyan: "e0ffff", - lightgoldenrodyellow: "fafad2", - lightgray: "d3d3d3", - lightgreen: "90ee90", - lightgrey: "d3d3d3", - lightpink: "ffb6c1", - lightsalmon: "ffa07a", - lightseagreen: "20b2aa", - lightskyblue: "87cefa", - lightslategray: "789", - lightslategrey: "789", - lightsteelblue: "b0c4de", - lightyellow: "ffffe0", - lime: "0f0", - limegreen: "32cd32", - linen: "faf0e6", - magenta: "f0f", - maroon: "800000", - mediumaquamarine: "66cdaa", - mediumblue: "0000cd", - mediumorchid: "ba55d3", - mediumpurple: "9370db", - mediumseagreen: "3cb371", - mediumslateblue: "7b68ee", - mediumspringgreen: "00fa9a", - mediumturquoise: "48d1cc", - mediumvioletred: "c71585", - midnightblue: "191970", - mintcream: "f5fffa", - mistyrose: "ffe4e1", - moccasin: "ffe4b5", - navajowhite: "ffdead", - navy: "000080", - oldlace: "fdf5e6", - olive: "808000", - olivedrab: "6b8e23", - orange: "ffa500", - orangered: "ff4500", - orchid: "da70d6", - palegoldenrod: "eee8aa", - palegreen: "98fb98", - paleturquoise: "afeeee", - palevioletred: "db7093", - papayawhip: "ffefd5", - peachpuff: "ffdab9", - peru: "cd853f", - pink: "ffc0cb", - plum: "dda0dd", - powderblue: "b0e0e6", - purple: "800080", - rebeccapurple: "663399", - red: "f00", - rosybrown: "bc8f8f", - royalblue: "4169e1", - saddlebrown: "8b4513", - salmon: "fa8072", - sandybrown: "f4a460", - seagreen: "2e8b57", - seashell: "fff5ee", - sienna: "a0522d", - silver: "c0c0c0", - skyblue: "87ceeb", - slateblue: "6a5acd", - slategray: "708090", - slategrey: "708090", - snow: "fffafa", - springgreen: "00ff7f", - steelblue: "4682b4", - tan: "d2b48c", - teal: "008080", - thistle: "d8bfd8", - tomato: "ff6347", - turquoise: "40e0d0", - violet: "ee82ee", - wheat: "f5deb3", - white: "fff", - whitesmoke: "f5f5f5", - yellow: "ff0", - yellowgreen: "9acd32" - }; - - // Make it easy to access colors via `hexNames[hex]` - var hexNames = tinycolor.hexNames = flip(names); - - // Utilities - // --------- - - // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` - function flip(o) { - var flipped = {}; - for (var i in o) { - if (o.hasOwnProperty(i)) { - flipped[o[i]] = i; - } - } - return flipped; - } - - // Return a valid alpha value [0,1] with all invalid values being set to 1 - function boundAlpha(a) { - a = parseFloat(a); - if (isNaN(a) || a < 0 || a > 1) { - a = 1; - } - return a; - } - - // Take input from [0, n] and return it as [0, 1] - function bound01(n, max) { - if (isOnePointZero(n)) n = "100%"; - var processPercent = isPercentage(n); - n = Math.min(max, Math.max(0, parseFloat(n))); - - // Automatically convert percentage into number - if (processPercent) { - n = parseInt(n * max, 10) / 100; - } - - // Handle floating point rounding errors - if (Math.abs(n - max) < 0.000001) { - return 1; - } - - // Convert into [0, 1] range if it isn't already - return n % max / parseFloat(max); - } - - // Force a number between 0 and 1 - function clamp01(val) { - return Math.min(1, Math.max(0, val)); - } - - // Parse a base-16 hex value into a base-10 integer - function parseIntFromHex(val) { - return parseInt(val, 16); - } - - // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 - // - function isOnePointZero(n) { - return typeof n == "string" && n.indexOf(".") != -1 && parseFloat(n) === 1; - } - - // Check to see if string passed in is a percentage - function isPercentage(n) { - return typeof n === "string" && n.indexOf("%") != -1; - } - - // Force a hex value to have 2 characters - function pad2(c) { - return c.length == 1 ? "0" + c : "" + c; - } - - // Replace a decimal with it's percentage value - function convertToPercentage(n) { - if (n <= 1) { - n = n * 100 + "%"; - } - return n; - } - - // Converts a decimal to a hex value - function convertDecimalToHex(d) { - return Math.round(parseFloat(d) * 255).toString(16); - } - // Converts a hex value to a decimal - function convertHexToDecimal(h) { - return parseIntFromHex(h) / 255; - } - var matchers = function () { - // - var CSS_INTEGER = "[-\\+]?\\d+%?"; - - // - var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; - - // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. - var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; - - // Actual matching. - // Parentheses and commas are optional, but not required. - // Whitespace can take the place of commas or opening paren - var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; - return { - CSS_UNIT: new RegExp(CSS_UNIT), - rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), - rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), - hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), - hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), - hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), - hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), - hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, - hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, - hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, - hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ - }; - }(); - - // `isValidCSSUnit` - // Take in a single string / number and check to see if it looks like a CSS unit - // (see `matchers` above for definition). - function isValidCSSUnit(color) { - return !!matchers.CSS_UNIT.exec(color); - } - - // `stringInputToObject` - // Permissive string parsing. Take in a number of formats, and output an object - // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` - function stringInputToObject(color) { - color = color.replace(trimLeft, "").replace(trimRight, "").toLowerCase(); - var named = false; - if (names[color]) { - color = names[color]; - named = true; - } else if (color == "transparent") { - return { - r: 0, - g: 0, - b: 0, - a: 0, - format: "name" - }; - } - - // Try to match string input using regular expressions. - // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] - // Just return an object and let the conversion functions handle that. - // This way the result will be the same whether the tinycolor is initialized with string or object. - var match; - if (match = matchers.rgb.exec(color)) { - return { - r: match[1], - g: match[2], - b: match[3] - }; - } - if (match = matchers.rgba.exec(color)) { - return { - r: match[1], - g: match[2], - b: match[3], - a: match[4] - }; - } - if (match = matchers.hsl.exec(color)) { - return { - h: match[1], - s: match[2], - l: match[3] - }; - } - if (match = matchers.hsla.exec(color)) { - return { - h: match[1], - s: match[2], - l: match[3], - a: match[4] - }; - } - if (match = matchers.hsv.exec(color)) { - return { - h: match[1], - s: match[2], - v: match[3] - }; - } - if (match = matchers.hsva.exec(color)) { - return { - h: match[1], - s: match[2], - v: match[3], - a: match[4] - }; - } - if (match = matchers.hex8.exec(color)) { - return { - r: parseIntFromHex(match[1]), - g: parseIntFromHex(match[2]), - b: parseIntFromHex(match[3]), - a: convertHexToDecimal(match[4]), - format: named ? "name" : "hex8" - }; - } - if (match = matchers.hex6.exec(color)) { - return { - r: parseIntFromHex(match[1]), - g: parseIntFromHex(match[2]), - b: parseIntFromHex(match[3]), - format: named ? "name" : "hex" - }; - } - if (match = matchers.hex4.exec(color)) { - return { - r: parseIntFromHex(match[1] + "" + match[1]), - g: parseIntFromHex(match[2] + "" + match[2]), - b: parseIntFromHex(match[3] + "" + match[3]), - a: convertHexToDecimal(match[4] + "" + match[4]), - format: named ? "name" : "hex8" - }; - } - if (match = matchers.hex3.exec(color)) { - return { - r: parseIntFromHex(match[1] + "" + match[1]), - g: parseIntFromHex(match[2] + "" + match[2]), - b: parseIntFromHex(match[3] + "" + match[3]), - format: named ? "name" : "hex" - }; - } - return false; - } - function validateWCAG2Parms(parms) { - // return valid WCAG2 parms for isReadable. - // If input parms are invalid, return {"level":"AA", "size":"small"} - var level, size; - parms = parms || { - level: "AA", - size: "small" - }; - level = (parms.level || "AA").toUpperCase(); - size = (parms.size || "small").toLowerCase(); - if (level !== "AA" && level !== "AAA") { - level = "AA"; - } - if (size !== "small" && size !== "large") { - size = "small"; - } - return { - level: level, - size: size - }; - } - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, _toPropertyKey$1(descriptor.key), descriptor); - } - } - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; - } - function _toConsumableArray$1(arr) { - return _arrayWithoutHoles$1(arr) || _iterableToArray$1(arr) || _unsupportedIterableToArray$1(arr) || _nonIterableSpread$1(); - } - function _arrayWithoutHoles$1(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray$1(arr); - } - function _iterableToArray$1(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); - } - function _unsupportedIterableToArray$1(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); - } - function _arrayLikeToArray$1(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; - } - function _nonIterableSpread$1() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _toPrimitive$1(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey$1(arg) { - var key = _toPrimitive$1(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } - - var ENTROPY = 123; // Raise numbers to prevent collisions in lower indexes - - var int2HexColor = function int2HexColor(num) { - return "#".concat(Math.min(num, Math.pow(2, 24)).toString(16).padStart(6, '0')); - }; - var rgb2Int = function rgb2Int(r, g, b) { - return (r << 16) + (g << 8) + b; - }; - var colorStr2Int = function colorStr2Int(str) { - var _tinyColor$toRgb = tinycolor(str).toRgb(), - r = _tinyColor$toRgb.r, - g = _tinyColor$toRgb.g, - b = _tinyColor$toRgb.b; - return rgb2Int(r, g, b); - }; - var checksum = function checksum(n, csBits) { - return n * ENTROPY % Math.pow(2, csBits); - }; - var _default = /*#__PURE__*/function () { - function _default() { - var csBits = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 6; - _classCallCheck(this, _default); - this.csBits = csBits; // How many bits to reserve for checksum. Will eat away into the usable size of the registry. - this.registry = ['__reserved for background__']; // indexed objects for rgb lookup; - } - _createClass(_default, [{ - key: "register", - value: function register(obj) { - if (this.registry.length >= Math.pow(2, 24 - this.csBits)) { - // color has 24 bits (-checksum) - return null; // Registry is full - } - - var idx = this.registry.length; - var cs = checksum(idx, this.csBits); - var color = int2HexColor(idx + (cs << 24 - this.csBits)); - this.registry.push(obj); - return color; - } - }, { - key: "lookup", - value: function lookup(color) { - var n = typeof color === 'string' ? colorStr2Int(color) : rgb2Int.apply(void 0, _toConsumableArray$1(color)); - if (!n) return null; // 0 index is reserved for background - - var idx = n & Math.pow(2, 24 - this.csBits) - 1; // registry index - var cs = n >> 24 - this.csBits & Math.pow(2, this.csBits) - 1; // extract bits reserved for checksum - - if (checksum(idx, this.csBits) !== cs || idx >= this.registry.length) return null; // failed checksum or registry out of bounds - - return this.registry[idx]; - } - }]); - return _default; - }(); - - function d3ForceCenter(x, y, z) { - var nodes, strength = 1; - - if (x == null) x = 0; - if (y == null) y = 0; - if (z == null) z = 0; - - function force() { - var i, - n = nodes.length, - node, - sx = 0, - sy = 0, - sz = 0; - - for (i = 0; i < n; ++i) { - node = nodes[i], sx += node.x || 0, sy += node.y || 0, sz += node.z || 0; - } - - for (sx = (sx / n - x) * strength, sy = (sy / n - y) * strength, sz = (sz / n - z) * strength, i = 0; i < n; ++i) { - node = nodes[i]; - if (sx) { node.x -= sx; } - if (sy) { node.y -= sy; } - if (sz) { node.z -= sz; } - } - } - - force.initialize = function(_) { - nodes = _; - }; - - force.x = function(_) { - return arguments.length ? (x = +_, force) : x; - }; - - force.y = function(_) { - return arguments.length ? (y = +_, force) : y; - }; - - force.z = function(_) { - return arguments.length ? (z = +_, force) : z; - }; - - force.strength = function(_) { - return arguments.length ? (strength = +_, force) : strength; - }; - - return force; - } - - function tree_add$2(d) { - const x = +this._x.call(null, d); - return add$2(this.cover(x), x, d); - } - - function add$2(tree, x, d) { - if (isNaN(x)) return tree; // ignore invalid points - - var parent, - node = tree._root, - leaf = {data: d}, - x0 = tree._x0, - x1 = tree._x1, - xm, - xp, - right, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return tree._root = leaf, tree; - - // Find the existing leaf for the new point, or add it. - while (node.length) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (parent = node, !(node = node[i = +right])) return parent[i] = leaf, tree; - } - - // Is the new point is exactly coincident with the existing point? - xp = +tree._x.call(null, node.data); - if (x === xp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; - - // Otherwise, split the leaf node until the old and new point are separated. - do { - parent = parent ? parent[i] = new Array(2) : tree._root = new Array(2); - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - } while ((i = +right) === (j = +(xp >= xm))); - return parent[j] = node, parent[i] = leaf, tree; - } - - function addAll$2(data) { - if (!Array.isArray(data)) data = Array.from(data); - const n = data.length; - const xz = new Float64Array(n); - let x0 = Infinity, - x1 = -Infinity; - - // Compute the points and their extent. - for (let i = 0, x; i < n; ++i) { - if (isNaN(x = +this._x.call(null, data[i]))) continue; - xz[i] = x; - if (x < x0) x0 = x; - if (x > x1) x1 = x; - } - - // If there were no (valid) points, abort. - if (x0 > x1) return this; - - // Expand the tree to cover the new points. - this.cover(x0).cover(x1); - - // Add the new points. - for (let i = 0; i < n; ++i) { - add$2(this, xz[i], data[i]); - } - - return this; - } - - function tree_cover$2(x) { - if (isNaN(x = +x)) return this; // ignore invalid points - - var x0 = this._x0, - x1 = this._x1; - - // If the binarytree has no extent, initialize them. - // Integer extent are necessary so that if we later double the extent, - // the existing half boundaries don’t change due to floating point error! - if (isNaN(x0)) { - x1 = (x0 = Math.floor(x)) + 1; - } - - // Otherwise, double repeatedly to cover. - else { - var z = x1 - x0 || 1, - node = this._root, - parent, - i; - - while (x0 > x || x >= x1) { - i = +(x < x0); - parent = new Array(2), parent[i] = node, node = parent, z *= 2; - switch (i) { - case 0: x1 = x0 + z; break; - case 1: x0 = x1 - z; break; - } - } - - if (this._root && this._root.length) this._root = node; - } - - this._x0 = x0; - this._x1 = x1; - return this; - } - - function tree_data$2() { - var data = []; - this.visit(function(node) { - if (!node.length) do data.push(node.data); while (node = node.next) - }); - return data; - } - - function tree_extent$2(_) { - return arguments.length - ? this.cover(+_[0][0]).cover(+_[1][0]) - : isNaN(this._x0) ? undefined : [[this._x0], [this._x1]]; - } - - function Half(node, x0, x1) { - this.node = node; - this.x0 = x0; - this.x1 = x1; - } - - function tree_find$2(x, radius) { - var data, - x0 = this._x0, - x1, - x2, - x3 = this._x1, - halves = [], - node = this._root, - q, - i; - - if (node) halves.push(new Half(node, x0, x3)); - if (radius == null) radius = Infinity; - else { - x0 = x - radius; - x3 = x + radius; - } - - while (q = halves.pop()) { - - // Stop searching if this half can’t contain a closer node. - if (!(node = q.node) - || (x1 = q.x0) > x3 - || (x2 = q.x1) < x0) continue; - - // Bisect the current half. - if (node.length) { - var xm = (x1 + x2) / 2; - - halves.push( - new Half(node[1], xm, x2), - new Half(node[0], x1, xm) - ); - - // Visit the closest half first. - if (i = +(x >= xm)) { - q = halves[halves.length - 1]; - halves[halves.length - 1] = halves[halves.length - 1 - i]; - halves[halves.length - 1 - i] = q; - } - } - - // Visit this point. (Visiting coincident points isn’t necessary!) - else { - var d = Math.abs(x - +this._x.call(null, node.data)); - if (d < radius) { - radius = d; - x0 = x - d; - x3 = x + d; - data = node.data; - } - } - } - - return data; - } - - function tree_remove$2(d) { - if (isNaN(x = +this._x.call(null, d))) return this; // ignore invalid points - - var parent, - node = this._root, - retainer, - previous, - next, - x0 = this._x0, - x1 = this._x1, - x, - xm, - right, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return this; - - // Find the leaf node for the point. - // While descending, also retain the deepest parent with a non-removed sibling. - if (node.length) while (true) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (!(parent = node, node = node[i = +right])) return this; - if (!node.length) break; - if (parent[(i + 1) & 1]) retainer = parent, j = i; - } - - // Find the point to remove. - while (node.data !== d) if (!(previous = node, node = node.next)) return this; - if (next = node.next) delete node.next; - - // If there are multiple coincident points, remove just the point. - if (previous) return (next ? previous.next = next : delete previous.next), this; - - // If this is the root point, remove it. - if (!parent) return this._root = next, this; - - // Remove this leaf. - next ? parent[i] = next : delete parent[i]; - - // If the parent now contains exactly one leaf, collapse superfluous parents. - if ((node = parent[0] || parent[1]) - && node === (parent[1] || parent[0]) - && !node.length) { - if (retainer) retainer[j] = node; - else this._root = node; - } - - return this; - } - - function removeAll$2(data) { - for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); - return this; - } - - function tree_root$2() { - return this._root; - } - - function tree_size$2() { - var size = 0; - this.visit(function(node) { - if (!node.length) do ++size; while (node = node.next) - }); - return size; - } - - function tree_visit$2(callback) { - var halves = [], q, node = this._root, child, x0, x1; - if (node) halves.push(new Half(node, this._x0, this._x1)); - while (q = halves.pop()) { - if (!callback(node = q.node, x0 = q.x0, x1 = q.x1) && node.length) { - var xm = (x0 + x1) / 2; - if (child = node[1]) halves.push(new Half(child, xm, x1)); - if (child = node[0]) halves.push(new Half(child, x0, xm)); - } - } - return this; - } - - function tree_visitAfter$2(callback) { - var halves = [], next = [], q; - if (this._root) halves.push(new Half(this._root, this._x0, this._x1)); - while (q = halves.pop()) { - var node = q.node; - if (node.length) { - var child, x0 = q.x0, x1 = q.x1, xm = (x0 + x1) / 2; - if (child = node[0]) halves.push(new Half(child, x0, xm)); - if (child = node[1]) halves.push(new Half(child, xm, x1)); - } - next.push(q); - } - while (q = next.pop()) { - callback(q.node, q.x0, q.x1); - } - return this; - } - - function defaultX$2(d) { - return d[0]; - } - - function tree_x$2(_) { - return arguments.length ? (this._x = _, this) : this._x; - } - - function binarytree(nodes, x) { - var tree = new Binarytree(x == null ? defaultX$2 : x, NaN, NaN); - return nodes == null ? tree : tree.addAll(nodes); - } - - function Binarytree(x, x0, x1) { - this._x = x; - this._x0 = x0; - this._x1 = x1; - this._root = undefined; - } - - function leaf_copy$2(leaf) { - var copy = {data: leaf.data}, next = copy; - while (leaf = leaf.next) next = next.next = {data: leaf.data}; - return copy; - } - - var treeProto$2 = binarytree.prototype = Binarytree.prototype; - - treeProto$2.copy = function() { - var copy = new Binarytree(this._x, this._x0, this._x1), - node = this._root, - nodes, - child; - - if (!node) return copy; - - if (!node.length) return copy._root = leaf_copy$2(node), copy; - - nodes = [{source: node, target: copy._root = new Array(2)}]; - while (node = nodes.pop()) { - for (var i = 0; i < 2; ++i) { - if (child = node.source[i]) { - if (child.length) nodes.push({source: child, target: node.target[i] = new Array(2)}); - else node.target[i] = leaf_copy$2(child); - } - } - } - - return copy; - }; - - treeProto$2.add = tree_add$2; - treeProto$2.addAll = addAll$2; - treeProto$2.cover = tree_cover$2; - treeProto$2.data = tree_data$2; - treeProto$2.extent = tree_extent$2; - treeProto$2.find = tree_find$2; - treeProto$2.remove = tree_remove$2; - treeProto$2.removeAll = removeAll$2; - treeProto$2.root = tree_root$2; - treeProto$2.size = tree_size$2; - treeProto$2.visit = tree_visit$2; - treeProto$2.visitAfter = tree_visitAfter$2; - treeProto$2.x = tree_x$2; - - function tree_add$1(d) { - const x = +this._x.call(null, d), - y = +this._y.call(null, d); - return add$1(this.cover(x, y), x, y, d); - } - - function add$1(tree, x, y, d) { - if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points - - var parent, - node = tree._root, - leaf = {data: d}, - x0 = tree._x0, - y0 = tree._y0, - x1 = tree._x1, - y1 = tree._y1, - xm, - ym, - xp, - yp, - right, - bottom, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return tree._root = leaf, tree; - - // Find the existing leaf for the new point, or add it. - while (node.length) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; - } - - // Is the new point is exactly coincident with the existing point? - xp = +tree._x.call(null, node.data); - yp = +tree._y.call(null, node.data); - if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; - - // Otherwise, split the leaf node until the old and new point are separated. - do { - parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); - return parent[j] = node, parent[i] = leaf, tree; - } - - function addAll$1(data) { - var d, i, n = data.length, - x, - y, - xz = new Array(n), - yz = new Array(n), - x0 = Infinity, - y0 = Infinity, - x1 = -Infinity, - y1 = -Infinity; - - // Compute the points and their extent. - for (i = 0; i < n; ++i) { - if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; - xz[i] = x; - yz[i] = y; - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - } - - // If there were no (valid) points, abort. - if (x0 > x1 || y0 > y1) return this; - - // Expand the tree to cover the new points. - this.cover(x0, y0).cover(x1, y1); - - // Add the new points. - for (i = 0; i < n; ++i) { - add$1(this, xz[i], yz[i], data[i]); - } - - return this; - } - - function tree_cover$1(x, y) { - if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points - - var x0 = this._x0, - y0 = this._y0, - x1 = this._x1, - y1 = this._y1; - - // If the quadtree has no extent, initialize them. - // Integer extent are necessary so that if we later double the extent, - // the existing quadrant boundaries don’t change due to floating point error! - if (isNaN(x0)) { - x1 = (x0 = Math.floor(x)) + 1; - y1 = (y0 = Math.floor(y)) + 1; - } - - // Otherwise, double repeatedly to cover. - else { - var z = x1 - x0 || 1, - node = this._root, - parent, - i; - - while (x0 > x || x >= x1 || y0 > y || y >= y1) { - i = (y < y0) << 1 | (x < x0); - parent = new Array(4), parent[i] = node, node = parent, z *= 2; - switch (i) { - case 0: x1 = x0 + z, y1 = y0 + z; break; - case 1: x0 = x1 - z, y1 = y0 + z; break; - case 2: x1 = x0 + z, y0 = y1 - z; break; - case 3: x0 = x1 - z, y0 = y1 - z; break; - } - } - - if (this._root && this._root.length) this._root = node; - } - - this._x0 = x0; - this._y0 = y0; - this._x1 = x1; - this._y1 = y1; - return this; - } - - function tree_data$1() { - var data = []; - this.visit(function(node) { - if (!node.length) do data.push(node.data); while (node = node.next) - }); - return data; - } - - function tree_extent$1(_) { - return arguments.length - ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) - : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; - } - - function Quad(node, x0, y0, x1, y1) { - this.node = node; - this.x0 = x0; - this.y0 = y0; - this.x1 = x1; - this.y1 = y1; - } - - function tree_find$1(x, y, radius) { - var data, - x0 = this._x0, - y0 = this._y0, - x1, - y1, - x2, - y2, - x3 = this._x1, - y3 = this._y1, - quads = [], - node = this._root, - q, - i; - - if (node) quads.push(new Quad(node, x0, y0, x3, y3)); - if (radius == null) radius = Infinity; - else { - x0 = x - radius, y0 = y - radius; - x3 = x + radius, y3 = y + radius; - radius *= radius; - } - - while (q = quads.pop()) { - - // Stop searching if this quadrant can’t contain a closer node. - if (!(node = q.node) - || (x1 = q.x0) > x3 - || (y1 = q.y0) > y3 - || (x2 = q.x1) < x0 - || (y2 = q.y1) < y0) continue; - - // Bisect the current quadrant. - if (node.length) { - var xm = (x1 + x2) / 2, - ym = (y1 + y2) / 2; - - quads.push( - new Quad(node[3], xm, ym, x2, y2), - new Quad(node[2], x1, ym, xm, y2), - new Quad(node[1], xm, y1, x2, ym), - new Quad(node[0], x1, y1, xm, ym) - ); - - // Visit the closest quadrant first. - if (i = (y >= ym) << 1 | (x >= xm)) { - q = quads[quads.length - 1]; - quads[quads.length - 1] = quads[quads.length - 1 - i]; - quads[quads.length - 1 - i] = q; - } - } - - // Visit this point. (Visiting coincident points isn’t necessary!) - else { - var dx = x - +this._x.call(null, node.data), - dy = y - +this._y.call(null, node.data), - d2 = dx * dx + dy * dy; - if (d2 < radius) { - var d = Math.sqrt(radius = d2); - x0 = x - d, y0 = y - d; - x3 = x + d, y3 = y + d; - data = node.data; - } - } - } - - return data; - } - - function tree_remove$1(d) { - if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points - - var parent, - node = this._root, - retainer, - previous, - next, - x0 = this._x0, - y0 = this._y0, - x1 = this._x1, - y1 = this._y1, - x, - y, - xm, - ym, - right, - bottom, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return this; - - // Find the leaf node for the point. - // While descending, also retain the deepest parent with a non-removed sibling. - if (node.length) while (true) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (!(parent = node, node = node[i = bottom << 1 | right])) return this; - if (!node.length) break; - if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; - } - - // Find the point to remove. - while (node.data !== d) if (!(previous = node, node = node.next)) return this; - if (next = node.next) delete node.next; - - // If there are multiple coincident points, remove just the point. - if (previous) return (next ? previous.next = next : delete previous.next), this; - - // If this is the root point, remove it. - if (!parent) return this._root = next, this; - - // Remove this leaf. - next ? parent[i] = next : delete parent[i]; - - // If the parent now contains exactly one leaf, collapse superfluous parents. - if ((node = parent[0] || parent[1] || parent[2] || parent[3]) - && node === (parent[3] || parent[2] || parent[1] || parent[0]) - && !node.length) { - if (retainer) retainer[j] = node; - else this._root = node; - } - - return this; - } - - function removeAll$1(data) { - for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); - return this; - } - - function tree_root$1() { - return this._root; - } - - function tree_size$1() { - var size = 0; - this.visit(function(node) { - if (!node.length) do ++size; while (node = node.next) - }); - return size; - } - - function tree_visit$1(callback) { - var quads = [], q, node = this._root, child, x0, y0, x1, y1; - if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); - while (q = quads.pop()) { - if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { - var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; - if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); - if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); - if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); - if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); - } - } - return this; - } - - function tree_visitAfter$1(callback) { - var quads = [], next = [], q; - if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); - while (q = quads.pop()) { - var node = q.node; - if (node.length) { - var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; - if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); - if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); - if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); - if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); - } - next.push(q); - } - while (q = next.pop()) { - callback(q.node, q.x0, q.y0, q.x1, q.y1); - } - return this; - } - - function defaultX$1(d) { - return d[0]; - } - - function tree_x$1(_) { - return arguments.length ? (this._x = _, this) : this._x; - } - - function defaultY$1(d) { - return d[1]; - } - - function tree_y$1(_) { - return arguments.length ? (this._y = _, this) : this._y; - } - - function quadtree(nodes, x, y) { - var tree = new Quadtree(x == null ? defaultX$1 : x, y == null ? defaultY$1 : y, NaN, NaN, NaN, NaN); - return nodes == null ? tree : tree.addAll(nodes); - } - - function Quadtree(x, y, x0, y0, x1, y1) { - this._x = x; - this._y = y; - this._x0 = x0; - this._y0 = y0; - this._x1 = x1; - this._y1 = y1; - this._root = undefined; - } - - function leaf_copy$1(leaf) { - var copy = {data: leaf.data}, next = copy; - while (leaf = leaf.next) next = next.next = {data: leaf.data}; - return copy; - } - - var treeProto$1 = quadtree.prototype = Quadtree.prototype; - - treeProto$1.copy = function() { - var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), - node = this._root, - nodes, - child; - - if (!node) return copy; - - if (!node.length) return copy._root = leaf_copy$1(node), copy; - - nodes = [{source: node, target: copy._root = new Array(4)}]; - while (node = nodes.pop()) { - for (var i = 0; i < 4; ++i) { - if (child = node.source[i]) { - if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); - else node.target[i] = leaf_copy$1(child); - } - } - } - - return copy; - }; - - treeProto$1.add = tree_add$1; - treeProto$1.addAll = addAll$1; - treeProto$1.cover = tree_cover$1; - treeProto$1.data = tree_data$1; - treeProto$1.extent = tree_extent$1; - treeProto$1.find = tree_find$1; - treeProto$1.remove = tree_remove$1; - treeProto$1.removeAll = removeAll$1; - treeProto$1.root = tree_root$1; - treeProto$1.size = tree_size$1; - treeProto$1.visit = tree_visit$1; - treeProto$1.visitAfter = tree_visitAfter$1; - treeProto$1.x = tree_x$1; - treeProto$1.y = tree_y$1; - - function tree_add(d) { - const x = +this._x.call(null, d), - y = +this._y.call(null, d), - z = +this._z.call(null, d); - return add(this.cover(x, y, z), x, y, z, d); - } - - function add(tree, x, y, z, d) { - if (isNaN(x) || isNaN(y) || isNaN(z)) return tree; // ignore invalid points - - var parent, - node = tree._root, - leaf = {data: d}, - x0 = tree._x0, - y0 = tree._y0, - z0 = tree._z0, - x1 = tree._x1, - y1 = tree._y1, - z1 = tree._z1, - xm, - ym, - zm, - xp, - yp, - zp, - right, - bottom, - deep, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return tree._root = leaf, tree; - - // Find the existing leaf for the new point, or add it. - while (node.length) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm; - if (parent = node, !(node = node[i = deep << 2 | bottom << 1 | right])) return parent[i] = leaf, tree; - } - - // Is the new point is exactly coincident with the existing point? - xp = +tree._x.call(null, node.data); - yp = +tree._y.call(null, node.data); - zp = +tree._z.call(null, node.data); - if (x === xp && y === yp && z === zp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; - - // Otherwise, split the leaf node until the old and new point are separated. - do { - parent = parent ? parent[i] = new Array(8) : tree._root = new Array(8); - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm; - } while ((i = deep << 2 | bottom << 1 | right) === (j = (zp >= zm) << 2 | (yp >= ym) << 1 | (xp >= xm))); - return parent[j] = node, parent[i] = leaf, tree; - } - - function addAll(data) { - if (!Array.isArray(data)) data = Array.from(data); - const n = data.length; - const xz = new Float64Array(n); - const yz = new Float64Array(n); - const zz = new Float64Array(n); - let x0 = Infinity, - y0 = Infinity, - z0 = Infinity, - x1 = -Infinity, - y1 = -Infinity, - z1 = -Infinity; - - // Compute the points and their extent. - for (let i = 0, d, x, y, z; i < n; ++i) { - if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d)) || isNaN(z = +this._z.call(null, d))) continue; - xz[i] = x; - yz[i] = y; - zz[i] = z; - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - if (z < z0) z0 = z; - if (z > z1) z1 = z; - } - - // If there were no (valid) points, abort. - if (x0 > x1 || y0 > y1 || z0 > z1) return this; - - // Expand the tree to cover the new points. - this.cover(x0, y0, z0).cover(x1, y1, z1); - - // Add the new points. - for (let i = 0; i < n; ++i) { - add(this, xz[i], yz[i], zz[i], data[i]); - } - - return this; - } - - function tree_cover(x, y, z) { - if (isNaN(x = +x) || isNaN(y = +y) || isNaN(z = +z)) return this; // ignore invalid points - - var x0 = this._x0, - y0 = this._y0, - z0 = this._z0, - x1 = this._x1, - y1 = this._y1, - z1 = this._z1; - - // If the octree has no extent, initialize them. - // Integer extent are necessary so that if we later double the extent, - // the existing octant boundaries don’t change due to floating point error! - if (isNaN(x0)) { - x1 = (x0 = Math.floor(x)) + 1; - y1 = (y0 = Math.floor(y)) + 1; - z1 = (z0 = Math.floor(z)) + 1; - } - - // Otherwise, double repeatedly to cover. - else { - var t = x1 - x0 || 1, - node = this._root, - parent, - i; - - while (x0 > x || x >= x1 || y0 > y || y >= y1 || z0 > z || z >= z1) { - i = (z < z0) << 2 | (y < y0) << 1 | (x < x0); - parent = new Array(8), parent[i] = node, node = parent, t *= 2; - switch (i) { - case 0: x1 = x0 + t, y1 = y0 + t, z1 = z0 + t; break; - case 1: x0 = x1 - t, y1 = y0 + t, z1 = z0 + t; break; - case 2: x1 = x0 + t, y0 = y1 - t, z1 = z0 + t; break; - case 3: x0 = x1 - t, y0 = y1 - t, z1 = z0 + t; break; - case 4: x1 = x0 + t, y1 = y0 + t, z0 = z1 - t; break; - case 5: x0 = x1 - t, y1 = y0 + t, z0 = z1 - t; break; - case 6: x1 = x0 + t, y0 = y1 - t, z0 = z1 - t; break; - case 7: x0 = x1 - t, y0 = y1 - t, z0 = z1 - t; break; - } - } - - if (this._root && this._root.length) this._root = node; - } - - this._x0 = x0; - this._y0 = y0; - this._z0 = z0; - this._x1 = x1; - this._y1 = y1; - this._z1 = z1; - return this; - } - - function tree_data() { - var data = []; - this.visit(function(node) { - if (!node.length) do data.push(node.data); while (node = node.next) - }); - return data; - } - - function tree_extent(_) { - return arguments.length - ? this.cover(+_[0][0], +_[0][1], +_[0][2]).cover(+_[1][0], +_[1][1], +_[1][2]) - : isNaN(this._x0) ? undefined : [[this._x0, this._y0, this._z0], [this._x1, this._y1, this._z1]]; - } - - function Octant(node, x0, y0, z0, x1, y1, z1) { - this.node = node; - this.x0 = x0; - this.y0 = y0; - this.z0 = z0; - this.x1 = x1; - this.y1 = y1; - this.z1 = z1; - } - - function tree_find(x, y, z, radius) { - var data, - x0 = this._x0, - y0 = this._y0, - z0 = this._z0, - x1, - y1, - z1, - x2, - y2, - z2, - x3 = this._x1, - y3 = this._y1, - z3 = this._z1, - octs = [], - node = this._root, - q, - i; - - if (node) octs.push(new Octant(node, x0, y0, z0, x3, y3, z3)); - if (radius == null) radius = Infinity; - else { - x0 = x - radius, y0 = y - radius, z0 = z - radius; - x3 = x + radius, y3 = y + radius, z3 = z + radius; - radius *= radius; - } - - while (q = octs.pop()) { - - // Stop searching if this octant can’t contain a closer node. - if (!(node = q.node) - || (x1 = q.x0) > x3 - || (y1 = q.y0) > y3 - || (z1 = q.z0) > z3 - || (x2 = q.x1) < x0 - || (y2 = q.y1) < y0 - || (z2 = q.z1) < z0) continue; - - // Bisect the current octant. - if (node.length) { - var xm = (x1 + x2) / 2, - ym = (y1 + y2) / 2, - zm = (z1 + z2) / 2; - - octs.push( - new Octant(node[7], xm, ym, zm, x2, y2, z2), - new Octant(node[6], x1, ym, zm, xm, y2, z2), - new Octant(node[5], xm, y1, zm, x2, ym, z2), - new Octant(node[4], x1, y1, zm, xm, ym, z2), - new Octant(node[3], xm, ym, z1, x2, y2, zm), - new Octant(node[2], x1, ym, z1, xm, y2, zm), - new Octant(node[1], xm, y1, z1, x2, ym, zm), - new Octant(node[0], x1, y1, z1, xm, ym, zm) - ); - - // Visit the closest octant first. - if (i = (z >= zm) << 2 | (y >= ym) << 1 | (x >= xm)) { - q = octs[octs.length - 1]; - octs[octs.length - 1] = octs[octs.length - 1 - i]; - octs[octs.length - 1 - i] = q; - } - } - - // Visit this point. (Visiting coincident points isn’t necessary!) - else { - var dx = x - +this._x.call(null, node.data), - dy = y - +this._y.call(null, node.data), - dz = z - +this._z.call(null, node.data), - d2 = dx * dx + dy * dy + dz * dz; - if (d2 < radius) { - var d = Math.sqrt(radius = d2); - x0 = x - d, y0 = y - d, z0 = z - d; - x3 = x + d, y3 = y + d, z3 = z + d; - data = node.data; - } - } - } - - return data; - } - - function tree_remove(d) { - if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d)) || isNaN(z = +this._z.call(null, d))) return this; // ignore invalid points - - var parent, - node = this._root, - retainer, - previous, - next, - x0 = this._x0, - y0 = this._y0, - z0 = this._z0, - x1 = this._x1, - y1 = this._y1, - z1 = this._z1, - x, - y, - z, - xm, - ym, - zm, - right, - bottom, - deep, - i, - j; - - // If the tree is empty, initialize the root as a leaf. - if (!node) return this; - - // Find the leaf node for the point. - // While descending, also retain the deepest parent with a non-removed sibling. - if (node.length) while (true) { - if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; - if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; - if (deep = z >= (zm = (z0 + z1) / 2)) z0 = zm; else z1 = zm; - if (!(parent = node, node = node[i = deep << 2 | bottom << 1 | right])) return this; - if (!node.length) break; - if (parent[(i + 1) & 7] || parent[(i + 2) & 7] || parent[(i + 3) & 7] || parent[(i + 4) & 7] || parent[(i + 5) & 7] || parent[(i + 6) & 7] || parent[(i + 7) & 7]) retainer = parent, j = i; - } - - // Find the point to remove. - while (node.data !== d) if (!(previous = node, node = node.next)) return this; - if (next = node.next) delete node.next; - - // If there are multiple coincident points, remove just the point. - if (previous) return (next ? previous.next = next : delete previous.next), this; - - // If this is the root point, remove it. - if (!parent) return this._root = next, this; - - // Remove this leaf. - next ? parent[i] = next : delete parent[i]; - - // If the parent now contains exactly one leaf, collapse superfluous parents. - if ((node = parent[0] || parent[1] || parent[2] || parent[3] || parent[4] || parent[5] || parent[6] || parent[7]) - && node === (parent[7] || parent[6] || parent[5] || parent[4] || parent[3] || parent[2] || parent[1] || parent[0]) - && !node.length) { - if (retainer) retainer[j] = node; - else this._root = node; - } - - return this; - } - - function removeAll(data) { - for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); - return this; - } - - function tree_root() { - return this._root; - } - - function tree_size() { - var size = 0; - this.visit(function(node) { - if (!node.length) do ++size; while (node = node.next) - }); - return size; - } - - function tree_visit(callback) { - var octs = [], q, node = this._root, child, x0, y0, z0, x1, y1, z1; - if (node) octs.push(new Octant(node, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1)); - while (q = octs.pop()) { - if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, z0 = q.z0, x1 = q.x1, y1 = q.y1, z1 = q.z1) && node.length) { - var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2, zm = (z0 + z1) / 2; - if (child = node[7]) octs.push(new Octant(child, xm, ym, zm, x1, y1, z1)); - if (child = node[6]) octs.push(new Octant(child, x0, ym, zm, xm, y1, z1)); - if (child = node[5]) octs.push(new Octant(child, xm, y0, zm, x1, ym, z1)); - if (child = node[4]) octs.push(new Octant(child, x0, y0, zm, xm, ym, z1)); - if (child = node[3]) octs.push(new Octant(child, xm, ym, z0, x1, y1, zm)); - if (child = node[2]) octs.push(new Octant(child, x0, ym, z0, xm, y1, zm)); - if (child = node[1]) octs.push(new Octant(child, xm, y0, z0, x1, ym, zm)); - if (child = node[0]) octs.push(new Octant(child, x0, y0, z0, xm, ym, zm)); - } - } - return this; - } - - function tree_visitAfter(callback) { - var octs = [], next = [], q; - if (this._root) octs.push(new Octant(this._root, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1)); - while (q = octs.pop()) { - var node = q.node; - if (node.length) { - var child, x0 = q.x0, y0 = q.y0, z0 = q.z0, x1 = q.x1, y1 = q.y1, z1 = q.z1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2, zm = (z0 + z1) / 2; - if (child = node[0]) octs.push(new Octant(child, x0, y0, z0, xm, ym, zm)); - if (child = node[1]) octs.push(new Octant(child, xm, y0, z0, x1, ym, zm)); - if (child = node[2]) octs.push(new Octant(child, x0, ym, z0, xm, y1, zm)); - if (child = node[3]) octs.push(new Octant(child, xm, ym, z0, x1, y1, zm)); - if (child = node[4]) octs.push(new Octant(child, x0, y0, zm, xm, ym, z1)); - if (child = node[5]) octs.push(new Octant(child, xm, y0, zm, x1, ym, z1)); - if (child = node[6]) octs.push(new Octant(child, x0, ym, zm, xm, y1, z1)); - if (child = node[7]) octs.push(new Octant(child, xm, ym, zm, x1, y1, z1)); - } - next.push(q); - } - while (q = next.pop()) { - callback(q.node, q.x0, q.y0, q.z0, q.x1, q.y1, q.z1); - } - return this; - } - - function defaultX(d) { - return d[0]; - } - - function tree_x(_) { - return arguments.length ? (this._x = _, this) : this._x; - } - - function defaultY(d) { - return d[1]; - } - - function tree_y(_) { - return arguments.length ? (this._y = _, this) : this._y; - } - - function defaultZ(d) { - return d[2]; - } - - function tree_z(_) { - return arguments.length ? (this._z = _, this) : this._z; - } - - function octree(nodes, x, y, z) { - var tree = new Octree(x == null ? defaultX : x, y == null ? defaultY : y, z == null ? defaultZ : z, NaN, NaN, NaN, NaN, NaN, NaN); - return nodes == null ? tree : tree.addAll(nodes); - } - - function Octree(x, y, z, x0, y0, z0, x1, y1, z1) { - this._x = x; - this._y = y; - this._z = z; - this._x0 = x0; - this._y0 = y0; - this._z0 = z0; - this._x1 = x1; - this._y1 = y1; - this._z1 = z1; - this._root = undefined; - } - - function leaf_copy(leaf) { - var copy = {data: leaf.data}, next = copy; - while (leaf = leaf.next) next = next.next = {data: leaf.data}; - return copy; - } - - var treeProto = octree.prototype = Octree.prototype; - - treeProto.copy = function() { - var copy = new Octree(this._x, this._y, this._z, this._x0, this._y0, this._z0, this._x1, this._y1, this._z1), - node = this._root, - nodes, - child; - - if (!node) return copy; - - if (!node.length) return copy._root = leaf_copy(node), copy; - - nodes = [{source: node, target: copy._root = new Array(8)}]; - while (node = nodes.pop()) { - for (var i = 0; i < 8; ++i) { - if (child = node.source[i]) { - if (child.length) nodes.push({source: child, target: node.target[i] = new Array(8)}); - else node.target[i] = leaf_copy(child); - } - } - } - - return copy; - }; - - treeProto.add = tree_add; - treeProto.addAll = addAll; - treeProto.cover = tree_cover; - treeProto.data = tree_data; - treeProto.extent = tree_extent; - treeProto.find = tree_find; - treeProto.remove = tree_remove; - treeProto.removeAll = removeAll; - treeProto.root = tree_root; - treeProto.size = tree_size; - treeProto.visit = tree_visit; - treeProto.visitAfter = tree_visitAfter; - treeProto.x = tree_x; - treeProto.y = tree_y; - treeProto.z = tree_z; - - function constant(x) { - return function() { - return x; - }; - } - - function jiggle(random) { - return (random() - 0.5) * 1e-6; - } - - function index$1(d) { - return d.index; - } - - function find(nodeById, nodeId) { - var node = nodeById.get(nodeId); - if (!node) throw new Error("node not found: " + nodeId); - return node; - } - - function d3ForceLink(links) { - var id = index$1, - strength = defaultStrength, - strengths, - distance = constant(30), - distances, - nodes, - nDim, - count, - bias, - random, - iterations = 1; - - if (links == null) links = []; - - function defaultStrength(link) { - return 1 / Math.min(count[link.source.index], count[link.target.index]); - } - - function force(alpha) { - for (var k = 0, n = links.length; k < iterations; ++k) { - for (var i = 0, link, source, target, x = 0, y = 0, z = 0, l, b; i < n; ++i) { - link = links[i], source = link.source, target = link.target; - x = target.x + target.vx - source.x - source.vx || jiggle(random); - if (nDim > 1) { y = target.y + target.vy - source.y - source.vy || jiggle(random); } - if (nDim > 2) { z = target.z + target.vz - source.z - source.vz || jiggle(random); } - l = Math.sqrt(x * x + y * y + z * z); - l = (l - distances[i]) / l * alpha * strengths[i]; - x *= l, y *= l, z *= l; - - target.vx -= x * (b = bias[i]); - if (nDim > 1) { target.vy -= y * b; } - if (nDim > 2) { target.vz -= z * b; } - - source.vx += x * (b = 1 - b); - if (nDim > 1) { source.vy += y * b; } - if (nDim > 2) { source.vz += z * b; } - } - } - } - - function initialize() { - if (!nodes) return; - - var i, - n = nodes.length, - m = links.length, - nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])), - link; - - for (i = 0, count = new Array(n); i < m; ++i) { - link = links[i], link.index = i; - if (typeof link.source !== "object") link.source = find(nodeById, link.source); - if (typeof link.target !== "object") link.target = find(nodeById, link.target); - count[link.source.index] = (count[link.source.index] || 0) + 1; - count[link.target.index] = (count[link.target.index] || 0) + 1; - } - - for (i = 0, bias = new Array(m); i < m; ++i) { - link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]); - } - - strengths = new Array(m), initializeStrength(); - distances = new Array(m), initializeDistance(); - } - - function initializeStrength() { - if (!nodes) return; - - for (var i = 0, n = links.length; i < n; ++i) { - strengths[i] = +strength(links[i], i, links); - } - } - - function initializeDistance() { - if (!nodes) return; - - for (var i = 0, n = links.length; i < n; ++i) { - distances[i] = +distance(links[i], i, links); - } - } - - force.initialize = function(_nodes, ...args) { - nodes = _nodes; - random = args.find(arg => typeof arg === 'function') || Math.random; - nDim = args.find(arg => [1, 2, 3].includes(arg)) || 2; - initialize(); - }; - - force.links = function(_) { - return arguments.length ? (links = _, initialize(), force) : links; - }; - - force.id = function(_) { - return arguments.length ? (id = _, force) : id; - }; - - force.iterations = function(_) { - return arguments.length ? (iterations = +_, force) : iterations; - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initializeStrength(), force) : strength; - }; - - force.distance = function(_) { - return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance; - }; - - return force; - } - - // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use - const a = 1664525; - const c = 1013904223; - const m = 4294967296; // 2^32 - - function lcg() { - let s = 1; - return () => (s = (a * s + c) % m) / m; - } - - var MAX_DIMENSIONS = 3; - - function x(d) { - return d.x; - } - - function y(d) { - return d.y; - } - - function z(d) { - return d.z; - } - - var initialRadius = 10, - initialAngleRoll = Math.PI * (3 - Math.sqrt(5)), // Golden ratio angle - initialAngleYaw = Math.PI * 20 / (9 + Math.sqrt(221)); // Markov irrational number - - function d3ForceSimulation(nodes, numDimensions) { - numDimensions = numDimensions || 2; - - var nDim = Math.min(MAX_DIMENSIONS, Math.max(1, Math.round(numDimensions))), - simulation, - alpha = 1, - alphaMin = 0.001, - alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), - alphaTarget = 0, - velocityDecay = 0.6, - forces = new Map(), - stepper = timer(step), - event = dispatch("tick", "end"), - random = lcg(); - - if (nodes == null) nodes = []; - - function step() { - tick(); - event.call("tick", simulation); - if (alpha < alphaMin) { - stepper.stop(); - event.call("end", simulation); - } - } - - function tick(iterations) { - var i, n = nodes.length, node; - - if (iterations === undefined) iterations = 1; - - for (var k = 0; k < iterations; ++k) { - alpha += (alphaTarget - alpha) * alphaDecay; - - forces.forEach(function (force) { - force(alpha); - }); - - for (i = 0; i < n; ++i) { - node = nodes[i]; - if (node.fx == null) node.x += node.vx *= velocityDecay; - else node.x = node.fx, node.vx = 0; - if (nDim > 1) { - if (node.fy == null) node.y += node.vy *= velocityDecay; - else node.y = node.fy, node.vy = 0; - } - if (nDim > 2) { - if (node.fz == null) node.z += node.vz *= velocityDecay; - else node.z = node.fz, node.vz = 0; - } - } - } - - return simulation; - } - - function initializeNodes() { - for (var i = 0, n = nodes.length, node; i < n; ++i) { - node = nodes[i], node.index = i; - if (node.fx != null) node.x = node.fx; - if (node.fy != null) node.y = node.fy; - if (node.fz != null) node.z = node.fz; - if (isNaN(node.x) || (nDim > 1 && isNaN(node.y)) || (nDim > 2 && isNaN(node.z))) { - var radius = initialRadius * (nDim > 2 ? Math.cbrt(0.5 + i) : (nDim > 1 ? Math.sqrt(0.5 + i) : i)), - rollAngle = i * initialAngleRoll, - yawAngle = i * initialAngleYaw; - - if (nDim === 1) { - node.x = radius; - } else if (nDim === 2) { - node.x = radius * Math.cos(rollAngle); - node.y = radius * Math.sin(rollAngle); - } else { // 3 dimensions: use spherical distribution along 2 irrational number angles - node.x = radius * Math.sin(rollAngle) * Math.cos(yawAngle); - node.y = radius * Math.cos(rollAngle); - node.z = radius * Math.sin(rollAngle) * Math.sin(yawAngle); - } - } - if (isNaN(node.vx) || (nDim > 1 && isNaN(node.vy)) || (nDim > 2 && isNaN(node.vz))) { - node.vx = 0; - if (nDim > 1) { node.vy = 0; } - if (nDim > 2) { node.vz = 0; } - } - } - } - - function initializeForce(force) { - if (force.initialize) force.initialize(nodes, random, nDim); - return force; - } - - initializeNodes(); - - return simulation = { - tick: tick, - - restart: function() { - return stepper.restart(step), simulation; - }, - - stop: function() { - return stepper.stop(), simulation; - }, - - numDimensions: function(_) { - return arguments.length - ? (nDim = Math.min(MAX_DIMENSIONS, Math.max(1, Math.round(_))), forces.forEach(initializeForce), simulation) - : nDim; - }, - - nodes: function(_) { - return arguments.length ? (nodes = _, initializeNodes(), forces.forEach(initializeForce), simulation) : nodes; - }, - - alpha: function(_) { - return arguments.length ? (alpha = +_, simulation) : alpha; - }, - - alphaMin: function(_) { - return arguments.length ? (alphaMin = +_, simulation) : alphaMin; - }, - - alphaDecay: function(_) { - return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay; - }, - - alphaTarget: function(_) { - return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget; - }, - - velocityDecay: function(_) { - return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay; - }, - - randomSource: function(_) { - return arguments.length ? (random = _, forces.forEach(initializeForce), simulation) : random; - }, - - force: function(name, _) { - return arguments.length > 1 ? ((_ == null ? forces.delete(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name); - }, - - find: function() { - var args = Array.prototype.slice.call(arguments); - var x = args.shift() || 0, - y = (nDim > 1 ? args.shift() : null) || 0, - z = (nDim > 2 ? args.shift() : null) || 0, - radius = args.shift() || Infinity; - - var i = 0, - n = nodes.length, - dx, - dy, - dz, - d2, - node, - closest; - - radius *= radius; - - for (i = 0; i < n; ++i) { - node = nodes[i]; - dx = x - node.x; - dy = y - (node.y || 0); - dz = z - (node.z ||0); - d2 = dx * dx + dy * dy + dz * dz; - if (d2 < radius) closest = node, radius = d2; - } - - return closest; - }, - - on: function(name, _) { - return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name); - } - }; - } - - function d3ForceManyBody() { - var nodes, - nDim, - node, - random, - alpha, - strength = constant(-30), - strengths, - distanceMin2 = 1, - distanceMax2 = Infinity, - theta2 = 0.81; - - function force(_) { - var i, - n = nodes.length, - tree = - (nDim === 1 ? binarytree(nodes, x) - :(nDim === 2 ? quadtree(nodes, x, y) - :(nDim === 3 ? octree(nodes, x, y, z) - :null - ))).visitAfter(accumulate); - - for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply); - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length, node; - strengths = new Array(n); - for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes); - } - - function accumulate(treeNode) { - var strength = 0, q, c, weight = 0, x, y, z, i; - var numChildren = treeNode.length; - - // For internal nodes, accumulate forces from children. - if (numChildren) { - for (x = y = z = i = 0; i < numChildren; ++i) { - if ((q = treeNode[i]) && (c = Math.abs(q.value))) { - strength += q.value, weight += c, x += c * (q.x || 0), y += c * (q.y || 0), z += c * (q.z || 0); - } - } - strength *= Math.sqrt(4 / numChildren); // scale accumulated strength according to number of dimensions - - treeNode.x = x / weight; - if (nDim > 1) { treeNode.y = y / weight; } - if (nDim > 2) { treeNode.z = z / weight; } - } - - // For leaf nodes, accumulate forces from coincident nodes. - else { - q = treeNode; - q.x = q.data.x; - if (nDim > 1) { q.y = q.data.y; } - if (nDim > 2) { q.z = q.data.z; } - do strength += strengths[q.data.index]; - while (q = q.next); - } - - treeNode.value = strength; - } - - function apply(treeNode, x1, arg1, arg2, arg3) { - if (!treeNode.value) return true; - var x2 = [arg1, arg2, arg3][nDim-1]; - - var x = treeNode.x - node.x, - y = (nDim > 1 ? treeNode.y - node.y : 0), - z = (nDim > 2 ? treeNode.z - node.z : 0), - w = x2 - x1, - l = x * x + y * y + z * z; - - // Apply the Barnes-Hut approximation if possible. - // Limit forces for very close nodes; randomize direction if coincident. - if (w * w / theta2 < l) { - if (l < distanceMax2) { - if (x === 0) x = jiggle(random), l += x * x; - if (nDim > 1 && y === 0) y = jiggle(random), l += y * y; - if (nDim > 2 && z === 0) z = jiggle(random), l += z * z; - if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); - node.vx += x * treeNode.value * alpha / l; - if (nDim > 1) { node.vy += y * treeNode.value * alpha / l; } - if (nDim > 2) { node.vz += z * treeNode.value * alpha / l; } - } - return true; - } - - // Otherwise, process points directly. - else if (treeNode.length || l >= distanceMax2) return; - - // Limit forces for very close nodes; randomize direction if coincident. - if (treeNode.data !== node || treeNode.next) { - if (x === 0) x = jiggle(random), l += x * x; - if (nDim > 1 && y === 0) y = jiggle(random), l += y * y; - if (nDim > 2 && z === 0) z = jiggle(random), l += z * z; - if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l); - } - - do if (treeNode.data !== node) { - w = strengths[treeNode.data.index] * alpha / l; - node.vx += x * w; - if (nDim > 1) { node.vy += y * w; } - if (nDim > 2) { node.vz += z * w; } - } while (treeNode = treeNode.next); - } - - force.initialize = function(_nodes, ...args) { - nodes = _nodes; - random = args.find(arg => typeof arg === 'function') || Math.random; - nDim = args.find(arg => [1, 2, 3].includes(arg)) || 2; - initialize(); - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; - }; - - force.distanceMin = function(_) { - return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2); - }; - - force.distanceMax = function(_) { - return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2); - }; - - force.theta = function(_) { - return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2); - }; - - return force; - } - - function d3ForceRadial(radius, x, y, z) { - var nodes, - nDim, - strength = constant(0.1), - strengths, - radiuses; - - if (typeof radius !== "function") radius = constant(+radius); - if (x == null) x = 0; - if (y == null) y = 0; - if (z == null) z = 0; - - function force(alpha) { - for (var i = 0, n = nodes.length; i < n; ++i) { - var node = nodes[i], - dx = node.x - x || 1e-6, - dy = (node.y || 0) - y || 1e-6, - dz = (node.z || 0) - z || 1e-6, - r = Math.sqrt(dx * dx + dy * dy + dz * dz), - k = (radiuses[i] - r) * strengths[i] * alpha / r; - node.vx += dx * k; - if (nDim>1) { node.vy += dy * k; } - if (nDim>2) { node.vz += dz * k; } - } - } - - function initialize() { - if (!nodes) return; - var i, n = nodes.length; - strengths = new Array(n); - radiuses = new Array(n); - for (i = 0; i < n; ++i) { - radiuses[i] = +radius(nodes[i], i, nodes); - strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes); - } - } - - force.initialize = function(initNodes, ...args) { - nodes = initNodes; - nDim = args.find(arg => [1, 2, 3].includes(arg)) || 2; - initialize(); - }; - - force.strength = function(_) { - return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength; - }; - - force.radius = function(_) { - return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius; - }; - - force.x = function(_) { - return arguments.length ? (x = +_, force) : x; - }; - - force.y = function(_) { - return arguments.length ? (y = +_, force) : y; - }; - - force.z = function(_) { - return arguments.length ? (z = +_, force) : z; - }; - - return force; - } - - // math-inlining. - const { abs: abs$1, cos: cos$1, sin: sin$1, acos: acos$1, atan2, sqrt: sqrt$1, pow } = Math; - - // cube root function yielding real roots - function crt(v) { - return v < 0 ? -pow(-v, 1 / 3) : pow(v, 1 / 3); - } - - // trig constants - const pi$1 = Math.PI, - tau = 2 * pi$1, - quart = pi$1 / 2, - // float precision significant decimal - epsilon = 0.000001, - // extremas used in bbox calculation and similar algorithms - nMax = Number.MAX_SAFE_INTEGER || 9007199254740991, - nMin = Number.MIN_SAFE_INTEGER || -9007199254740991, - // a zero coordinate, which is surprisingly useful - ZERO = { x: 0, y: 0, z: 0 }; - - // Bezier utility functions - const utils = { - // Legendre-Gauss abscissae with n=24 (x_i values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x)) - Tvalues: [ - -0.0640568928626056260850430826247450385909, - 0.0640568928626056260850430826247450385909, - -0.1911188674736163091586398207570696318404, - 0.1911188674736163091586398207570696318404, - -0.3150426796961633743867932913198102407864, - 0.3150426796961633743867932913198102407864, - -0.4337935076260451384870842319133497124524, - 0.4337935076260451384870842319133497124524, - -0.5454214713888395356583756172183723700107, - 0.5454214713888395356583756172183723700107, - -0.6480936519369755692524957869107476266696, - 0.6480936519369755692524957869107476266696, - -0.7401241915785543642438281030999784255232, - 0.7401241915785543642438281030999784255232, - -0.8200019859739029219539498726697452080761, - 0.8200019859739029219539498726697452080761, - -0.8864155270044010342131543419821967550873, - 0.8864155270044010342131543419821967550873, - -0.9382745520027327585236490017087214496548, - 0.9382745520027327585236490017087214496548, - -0.9747285559713094981983919930081690617411, - 0.9747285559713094981983919930081690617411, - -0.9951872199970213601799974097007368118745, - 0.9951872199970213601799974097007368118745, - ], - - // Legendre-Gauss weights with n=24 (w_i values, defined by a function linked to in the Bezier primer article) - Cvalues: [ - 0.1279381953467521569740561652246953718517, - 0.1279381953467521569740561652246953718517, - 0.1258374563468282961213753825111836887264, - 0.1258374563468282961213753825111836887264, - 0.121670472927803391204463153476262425607, - 0.121670472927803391204463153476262425607, - 0.1155056680537256013533444839067835598622, - 0.1155056680537256013533444839067835598622, - 0.1074442701159656347825773424466062227946, - 0.1074442701159656347825773424466062227946, - 0.0976186521041138882698806644642471544279, - 0.0976186521041138882698806644642471544279, - 0.086190161531953275917185202983742667185, - 0.086190161531953275917185202983742667185, - 0.0733464814110803057340336152531165181193, - 0.0733464814110803057340336152531165181193, - 0.0592985849154367807463677585001085845412, - 0.0592985849154367807463677585001085845412, - 0.0442774388174198061686027482113382288593, - 0.0442774388174198061686027482113382288593, - 0.0285313886289336631813078159518782864491, - 0.0285313886289336631813078159518782864491, - 0.0123412297999871995468056670700372915759, - 0.0123412297999871995468056670700372915759, - ], - - arcfn: function (t, derivativeFn) { - const d = derivativeFn(t); - let l = d.x * d.x + d.y * d.y; - if (typeof d.z !== "undefined") { - l += d.z * d.z; - } - return sqrt$1(l); - }, - - compute: function (t, points, _3d) { - // shortcuts - if (t === 0) { - points[0].t = 0; - return points[0]; - } - - const order = points.length - 1; - - if (t === 1) { - points[order].t = 1; - return points[order]; - } - - const mt = 1 - t; - let p = points; - - // constant? - if (order === 0) { - points[0].t = t; - return points[0]; - } - - // linear? - if (order === 1) { - const ret = { - x: mt * p[0].x + t * p[1].x, - y: mt * p[0].y + t * p[1].y, - t: t, - }; - if (_3d) { - ret.z = mt * p[0].z + t * p[1].z; - } - return ret; - } - - // quadratic/cubic curve? - if (order < 4) { - let mt2 = mt * mt, - t2 = t * t, - a, - b, - c, - d = 0; - if (order === 2) { - p = [p[0], p[1], p[2], ZERO]; - a = mt2; - b = mt * t * 2; - c = t2; - } else if (order === 3) { - a = mt2 * mt; - b = mt2 * t * 3; - c = mt * t2 * 3; - d = t * t2; - } - const ret = { - x: a * p[0].x + b * p[1].x + c * p[2].x + d * p[3].x, - y: a * p[0].y + b * p[1].y + c * p[2].y + d * p[3].y, - t: t, - }; - if (_3d) { - ret.z = a * p[0].z + b * p[1].z + c * p[2].z + d * p[3].z; - } - return ret; - } - - // higher order curves: use de Casteljau's computation - const dCpts = JSON.parse(JSON.stringify(points)); - while (dCpts.length > 1) { - for (let i = 0; i < dCpts.length - 1; i++) { - dCpts[i] = { - x: dCpts[i].x + (dCpts[i + 1].x - dCpts[i].x) * t, - y: dCpts[i].y + (dCpts[i + 1].y - dCpts[i].y) * t, - }; - if (typeof dCpts[i].z !== "undefined") { - dCpts[i].z = dCpts[i].z + (dCpts[i + 1].z - dCpts[i].z) * t; - } - } - dCpts.splice(dCpts.length - 1, 1); - } - dCpts[0].t = t; - return dCpts[0]; - }, - - computeWithRatios: function (t, points, ratios, _3d) { - const mt = 1 - t, - r = ratios, - p = points; - - let f1 = r[0], - f2 = r[1], - f3 = r[2], - f4 = r[3], - d; - - // spec for linear - f1 *= mt; - f2 *= t; - - if (p.length === 2) { - d = f1 + f2; - return { - x: (f1 * p[0].x + f2 * p[1].x) / d, - y: (f1 * p[0].y + f2 * p[1].y) / d, - z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z) / d, - t: t, - }; - } - - // upgrade to quadratic - f1 *= mt; - f2 *= 2 * mt; - f3 *= t * t; - - if (p.length === 3) { - d = f1 + f2 + f3; - return { - x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x) / d, - y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y) / d, - z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z) / d, - t: t, - }; - } - - // upgrade to cubic - f1 *= mt; - f2 *= 1.5 * mt; - f3 *= 3 * mt; - f4 *= t * t * t; - - if (p.length === 4) { - d = f1 + f2 + f3 + f4; - return { - x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x + f4 * p[3].x) / d, - y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y + f4 * p[3].y) / d, - z: !_3d - ? false - : (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z + f4 * p[3].z) / d, - t: t, - }; - } - }, - - derive: function (points, _3d) { - const dpoints = []; - for (let p = points, d = p.length, c = d - 1; d > 1; d--, c--) { - const list = []; - for (let j = 0, dpt; j < c; j++) { - dpt = { - x: c * (p[j + 1].x - p[j].x), - y: c * (p[j + 1].y - p[j].y), - }; - if (_3d) { - dpt.z = c * (p[j + 1].z - p[j].z); - } - list.push(dpt); - } - dpoints.push(list); - p = list; - } - return dpoints; - }, - - between: function (v, m, M) { - return ( - (m <= v && v <= M) || - utils.approximately(v, m) || - utils.approximately(v, M) - ); - }, - - approximately: function (a, b, precision) { - return abs$1(a - b) <= (precision || epsilon); - }, - - length: function (derivativeFn) { - const z = 0.5, - len = utils.Tvalues.length; - - let sum = 0; - - for (let i = 0, t; i < len; i++) { - t = z * utils.Tvalues[i] + z; - sum += utils.Cvalues[i] * utils.arcfn(t, derivativeFn); - } - return z * sum; - }, - - map: function (v, ds, de, ts, te) { - const d1 = de - ds, - d2 = te - ts, - v2 = v - ds, - r = v2 / d1; - return ts + d2 * r; - }, - - lerp: function (r, v1, v2) { - const ret = { - x: v1.x + r * (v2.x - v1.x), - y: v1.y + r * (v2.y - v1.y), - }; - if (v1.z !== undefined && v2.z !== undefined) { - ret.z = v1.z + r * (v2.z - v1.z); - } - return ret; - }, - - pointToString: function (p) { - let s = p.x + "/" + p.y; - if (typeof p.z !== "undefined") { - s += "/" + p.z; - } - return s; - }, - - pointsToString: function (points) { - return "[" + points.map(utils.pointToString).join(", ") + "]"; - }, - - copy: function (obj) { - return JSON.parse(JSON.stringify(obj)); - }, - - angle: function (o, v1, v2) { - const dx1 = v1.x - o.x, - dy1 = v1.y - o.y, - dx2 = v2.x - o.x, - dy2 = v2.y - o.y, - cross = dx1 * dy2 - dy1 * dx2, - dot = dx1 * dx2 + dy1 * dy2; - return atan2(cross, dot); - }, - - // round as string, to avoid rounding errors - round: function (v, d) { - const s = "" + v; - const pos = s.indexOf("."); - return parseFloat(s.substring(0, pos + 1 + d)); - }, - - dist: function (p1, p2) { - const dx = p1.x - p2.x, - dy = p1.y - p2.y; - return sqrt$1(dx * dx + dy * dy); - }, - - closest: function (LUT, point) { - let mdist = pow(2, 63), - mpos, - d; - LUT.forEach(function (p, idx) { - d = utils.dist(point, p); - if (d < mdist) { - mdist = d; - mpos = idx; - } - }); - return { mdist: mdist, mpos: mpos }; - }, - - abcratio: function (t, n) { - // see ratio(t) note on http://pomax.github.io/bezierinfo/#abc - if (n !== 2 && n !== 3) { - return false; - } - if (typeof t === "undefined") { - t = 0.5; - } else if (t === 0 || t === 1) { - return t; - } - const bottom = pow(t, n) + pow(1 - t, n), - top = bottom - 1; - return abs$1(top / bottom); - }, - - projectionratio: function (t, n) { - // see u(t) note on http://pomax.github.io/bezierinfo/#abc - if (n !== 2 && n !== 3) { - return false; - } - if (typeof t === "undefined") { - t = 0.5; - } else if (t === 0 || t === 1) { - return t; - } - const top = pow(1 - t, n), - bottom = pow(t, n) + top; - return top / bottom; - }, - - lli8: function (x1, y1, x2, y2, x3, y3, x4, y4) { - const nx = - (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4), - ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4), - d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - if (d == 0) { - return false; - } - return { x: nx / d, y: ny / d }; - }, - - lli4: function (p1, p2, p3, p4) { - const x1 = p1.x, - y1 = p1.y, - x2 = p2.x, - y2 = p2.y, - x3 = p3.x, - y3 = p3.y, - x4 = p4.x, - y4 = p4.y; - return utils.lli8(x1, y1, x2, y2, x3, y3, x4, y4); - }, - - lli: function (v1, v2) { - return utils.lli4(v1, v1.c, v2, v2.c); - }, - - makeline: function (p1, p2) { - return new Bezier( - p1.x, - p1.y, - (p1.x + p2.x) / 2, - (p1.y + p2.y) / 2, - p2.x, - p2.y - ); - }, - - findbbox: function (sections) { - let mx = nMax, - my = nMax, - MX = nMin, - MY = nMin; - sections.forEach(function (s) { - const bbox = s.bbox(); - if (mx > bbox.x.min) mx = bbox.x.min; - if (my > bbox.y.min) my = bbox.y.min; - if (MX < bbox.x.max) MX = bbox.x.max; - if (MY < bbox.y.max) MY = bbox.y.max; - }); - return { - x: { min: mx, mid: (mx + MX) / 2, max: MX, size: MX - mx }, - y: { min: my, mid: (my + MY) / 2, max: MY, size: MY - my }, - }; - }, - - shapeintersections: function ( - s1, - bbox1, - s2, - bbox2, - curveIntersectionThreshold - ) { - if (!utils.bboxoverlap(bbox1, bbox2)) return []; - const intersections = []; - const a1 = [s1.startcap, s1.forward, s1.back, s1.endcap]; - const a2 = [s2.startcap, s2.forward, s2.back, s2.endcap]; - a1.forEach(function (l1) { - if (l1.virtual) return; - a2.forEach(function (l2) { - if (l2.virtual) return; - const iss = l1.intersects(l2, curveIntersectionThreshold); - if (iss.length > 0) { - iss.c1 = l1; - iss.c2 = l2; - iss.s1 = s1; - iss.s2 = s2; - intersections.push(iss); - } - }); - }); - return intersections; - }, - - makeshape: function (forward, back, curveIntersectionThreshold) { - const bpl = back.points.length; - const fpl = forward.points.length; - const start = utils.makeline(back.points[bpl - 1], forward.points[0]); - const end = utils.makeline(forward.points[fpl - 1], back.points[0]); - const shape = { - startcap: start, - forward: forward, - back: back, - endcap: end, - bbox: utils.findbbox([start, forward, back, end]), - }; - shape.intersections = function (s2) { - return utils.shapeintersections( - shape, - shape.bbox, - s2, - s2.bbox, - curveIntersectionThreshold - ); - }; - return shape; - }, - - getminmax: function (curve, d, list) { - if (!list) return { min: 0, max: 0 }; - let min = nMax, - max = nMin, - t, - c; - if (list.indexOf(0) === -1) { - list = [0].concat(list); - } - if (list.indexOf(1) === -1) { - list.push(1); - } - for (let i = 0, len = list.length; i < len; i++) { - t = list[i]; - c = curve.get(t); - if (c[d] < min) { - min = c[d]; - } - if (c[d] > max) { - max = c[d]; - } - } - return { min: min, mid: (min + max) / 2, max: max, size: max - min }; - }, - - align: function (points, line) { - const tx = line.p1.x, - ty = line.p1.y, - a = -atan2(line.p2.y - ty, line.p2.x - tx), - d = function (v) { - return { - x: (v.x - tx) * cos$1(a) - (v.y - ty) * sin$1(a), - y: (v.x - tx) * sin$1(a) + (v.y - ty) * cos$1(a), - }; - }; - return points.map(d); - }, - - roots: function (points, line) { - line = line || { p1: { x: 0, y: 0 }, p2: { x: 1, y: 0 } }; - - const order = points.length - 1; - const aligned = utils.align(points, line); - const reduce = function (t) { - return 0 <= t && t <= 1; - }; - - if (order === 2) { - const a = aligned[0].y, - b = aligned[1].y, - c = aligned[2].y, - d = a - 2 * b + c; - if (d !== 0) { - const m1 = -sqrt$1(b * b - a * c), - m2 = -a + b, - v1 = -(m1 + m2) / d, - v2 = -(-m1 + m2) / d; - return [v1, v2].filter(reduce); - } else if (b !== c && d === 0) { - return [(2 * b - c) / (2 * b - 2 * c)].filter(reduce); - } - return []; - } - - // see http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm - const pa = aligned[0].y, - pb = aligned[1].y, - pc = aligned[2].y, - pd = aligned[3].y; - - let d = -pa + 3 * pb - 3 * pc + pd, - a = 3 * pa - 6 * pb + 3 * pc, - b = -3 * pa + 3 * pb, - c = pa; - - if (utils.approximately(d, 0)) { - // this is not a cubic curve. - if (utils.approximately(a, 0)) { - // in fact, this is not a quadratic curve either. - if (utils.approximately(b, 0)) { - // in fact in fact, there are no solutions. - return []; - } - // linear solution: - return [-c / b].filter(reduce); - } - // quadratic solution: - const q = sqrt$1(b * b - 4 * a * c), - a2 = 2 * a; - return [(q - b) / a2, (-b - q) / a2].filter(reduce); - } - - // at this point, we know we need a cubic solution: - - a /= d; - b /= d; - c /= d; - - const p = (3 * b - a * a) / 3, - p3 = p / 3, - q = (2 * a * a * a - 9 * a * b + 27 * c) / 27, - q2 = q / 2, - discriminant = q2 * q2 + p3 * p3 * p3; - - let u1, v1, x1, x2, x3; - if (discriminant < 0) { - const mp3 = -p / 3, - mp33 = mp3 * mp3 * mp3, - r = sqrt$1(mp33), - t = -q / (2 * r), - cosphi = t < -1 ? -1 : t > 1 ? 1 : t, - phi = acos$1(cosphi), - crtr = crt(r), - t1 = 2 * crtr; - x1 = t1 * cos$1(phi / 3) - a / 3; - x2 = t1 * cos$1((phi + tau) / 3) - a / 3; - x3 = t1 * cos$1((phi + 2 * tau) / 3) - a / 3; - return [x1, x2, x3].filter(reduce); - } else if (discriminant === 0) { - u1 = q2 < 0 ? crt(-q2) : -crt(q2); - x1 = 2 * u1 - a / 3; - x2 = -u1 - a / 3; - return [x1, x2].filter(reduce); - } else { - const sd = sqrt$1(discriminant); - u1 = crt(-q2 + sd); - v1 = crt(q2 + sd); - return [u1 - v1 - a / 3].filter(reduce); - } - }, - - droots: function (p) { - // quadratic roots are easy - if (p.length === 3) { - const a = p[0], - b = p[1], - c = p[2], - d = a - 2 * b + c; - if (d !== 0) { - const m1 = -sqrt$1(b * b - a * c), - m2 = -a + b, - v1 = -(m1 + m2) / d, - v2 = -(-m1 + m2) / d; - return [v1, v2]; - } else if (b !== c && d === 0) { - return [(2 * b - c) / (2 * (b - c))]; - } - return []; - } - - // linear roots are even easier - if (p.length === 2) { - const a = p[0], - b = p[1]; - if (a !== b) { - return [a / (a - b)]; - } - return []; - } - - return []; - }, - - curvature: function (t, d1, d2, _3d, kOnly) { - let num, - dnm, - adk, - dk, - k = 0, - r = 0; - - // - // We're using the following formula for curvature: - // - // x'y" - y'x" - // k(t) = ------------------ - // (x'² + y'²)^(3/2) - // - // from https://en.wikipedia.org/wiki/Radius_of_curvature#Definition - // - // With it corresponding 3D counterpart: - // - // sqrt( (y'z" - y"z')² + (z'x" - z"x')² + (x'y" - x"y')²) - // k(t) = ------------------------------------------------------- - // (x'² + y'² + z'²)^(3/2) - // - - const d = utils.compute(t, d1); - const dd = utils.compute(t, d2); - const qdsum = d.x * d.x + d.y * d.y; - - if (_3d) { - num = sqrt$1( - pow(d.y * dd.z - dd.y * d.z, 2) + - pow(d.z * dd.x - dd.z * d.x, 2) + - pow(d.x * dd.y - dd.x * d.y, 2) - ); - dnm = pow(qdsum + d.z * d.z, 3 / 2); - } else { - num = d.x * dd.y - d.y * dd.x; - dnm = pow(qdsum, 3 / 2); - } - - if (num === 0 || dnm === 0) { - return { k: 0, r: 0 }; - } - - k = num / dnm; - r = dnm / num; - - // We're also computing the derivative of kappa, because - // there is value in knowing the rate of change for the - // curvature along the curve. And we're just going to - // ballpark it based on an epsilon. - if (!kOnly) { - // compute k'(t) based on the interval before, and after it, - // to at least try to not introduce forward/backward pass bias. - const pk = utils.curvature(t - 0.001, d1, d2, _3d, true).k; - const nk = utils.curvature(t + 0.001, d1, d2, _3d, true).k; - dk = (nk - k + (k - pk)) / 2; - adk = (abs$1(nk - k) + abs$1(k - pk)) / 2; - } - - return { k: k, r: r, dk: dk, adk: adk }; - }, - - inflections: function (points) { - if (points.length < 4) return []; - - // FIXME: TODO: add in inflection abstraction for quartic+ curves? - - const p = utils.align(points, { p1: points[0], p2: points.slice(-1)[0] }), - a = p[2].x * p[1].y, - b = p[3].x * p[1].y, - c = p[1].x * p[2].y, - d = p[3].x * p[2].y, - v1 = 18 * (-3 * a + 2 * b + 3 * c - d), - v2 = 18 * (3 * a - b - 3 * c), - v3 = 18 * (c - a); - - if (utils.approximately(v1, 0)) { - if (!utils.approximately(v2, 0)) { - let t = -v3 / v2; - if (0 <= t && t <= 1) return [t]; - } - return []; - } - - const d2 = 2 * v1; - - if (utils.approximately(d2, 0)) return []; - - const trm = v2 * v2 - 4 * v1 * v3; - - if (trm < 0) return []; - - const sq = Math.sqrt(trm); - - return [(sq - v2) / d2, -(v2 + sq) / d2].filter(function (r) { - return 0 <= r && r <= 1; - }); - }, - - bboxoverlap: function (b1, b2) { - const dims = ["x", "y"], - len = dims.length; - - for (let i = 0, dim, l, t, d; i < len; i++) { - dim = dims[i]; - l = b1[dim].mid; - t = b2[dim].mid; - d = (b1[dim].size + b2[dim].size) / 2; - if (abs$1(l - t) >= d) return false; - } - return true; - }, - - expandbox: function (bbox, _bbox) { - if (_bbox.x.min < bbox.x.min) { - bbox.x.min = _bbox.x.min; - } - if (_bbox.y.min < bbox.y.min) { - bbox.y.min = _bbox.y.min; - } - if (_bbox.z && _bbox.z.min < bbox.z.min) { - bbox.z.min = _bbox.z.min; - } - if (_bbox.x.max > bbox.x.max) { - bbox.x.max = _bbox.x.max; - } - if (_bbox.y.max > bbox.y.max) { - bbox.y.max = _bbox.y.max; - } - if (_bbox.z && _bbox.z.max > bbox.z.max) { - bbox.z.max = _bbox.z.max; - } - bbox.x.mid = (bbox.x.min + bbox.x.max) / 2; - bbox.y.mid = (bbox.y.min + bbox.y.max) / 2; - if (bbox.z) { - bbox.z.mid = (bbox.z.min + bbox.z.max) / 2; - } - bbox.x.size = bbox.x.max - bbox.x.min; - bbox.y.size = bbox.y.max - bbox.y.min; - if (bbox.z) { - bbox.z.size = bbox.z.max - bbox.z.min; - } - }, - - pairiteration: function (c1, c2, curveIntersectionThreshold) { - const c1b = c1.bbox(), - c2b = c2.bbox(), - r = 100000, - threshold = curveIntersectionThreshold || 0.5; - - if ( - c1b.x.size + c1b.y.size < threshold && - c2b.x.size + c2b.y.size < threshold - ) { - return [ - (((r * (c1._t1 + c1._t2)) / 2) | 0) / r + - "/" + - (((r * (c2._t1 + c2._t2)) / 2) | 0) / r, - ]; - } - - let cc1 = c1.split(0.5), - cc2 = c2.split(0.5), - pairs = [ - { left: cc1.left, right: cc2.left }, - { left: cc1.left, right: cc2.right }, - { left: cc1.right, right: cc2.right }, - { left: cc1.right, right: cc2.left }, - ]; - - pairs = pairs.filter(function (pair) { - return utils.bboxoverlap(pair.left.bbox(), pair.right.bbox()); - }); - - let results = []; - - if (pairs.length === 0) return results; - - pairs.forEach(function (pair) { - results = results.concat( - utils.pairiteration(pair.left, pair.right, threshold) - ); - }); - - results = results.filter(function (v, i) { - return results.indexOf(v) === i; - }); - - return results; - }, - - getccenter: function (p1, p2, p3) { - const dx1 = p2.x - p1.x, - dy1 = p2.y - p1.y, - dx2 = p3.x - p2.x, - dy2 = p3.y - p2.y, - dx1p = dx1 * cos$1(quart) - dy1 * sin$1(quart), - dy1p = dx1 * sin$1(quart) + dy1 * cos$1(quart), - dx2p = dx2 * cos$1(quart) - dy2 * sin$1(quart), - dy2p = dx2 * sin$1(quart) + dy2 * cos$1(quart), - // chord midpoints - mx1 = (p1.x + p2.x) / 2, - my1 = (p1.y + p2.y) / 2, - mx2 = (p2.x + p3.x) / 2, - my2 = (p2.y + p3.y) / 2, - // midpoint offsets - mx1n = mx1 + dx1p, - my1n = my1 + dy1p, - mx2n = mx2 + dx2p, - my2n = my2 + dy2p, - // intersection of these lines: - arc = utils.lli8(mx1, my1, mx1n, my1n, mx2, my2, mx2n, my2n), - r = utils.dist(arc, p1); - - // arc start/end values, over mid point: - let s = atan2(p1.y - arc.y, p1.x - arc.x), - m = atan2(p2.y - arc.y, p2.x - arc.x), - e = atan2(p3.y - arc.y, p3.x - arc.x), - _; - - // determine arc direction (cw/ccw correction) - if (s < e) { - // if s m || m > e) { - s += tau; - } - if (s > e) { - _ = e; - e = s; - s = _; - } - } else { - // if e 4) { - if (arguments.length !== 1) { - throw new Error( - "Only new Bezier(point[]) is accepted for 4th and higher order curves" - ); - } - higher = true; - } - } else { - if (len !== 6 && len !== 8 && len !== 9 && len !== 12) { - if (arguments.length !== 1) { - throw new Error( - "Only new Bezier(point[]) is accepted for 4th and higher order curves" - ); - } - } - } - - const _3d = (this._3d = - (!higher && (len === 9 || len === 12)) || - (coords && coords[0] && typeof coords[0].z !== "undefined")); - - const points = (this.points = []); - for (let idx = 0, step = _3d ? 3 : 2; idx < len; idx += step) { - var point = { - x: args[idx], - y: args[idx + 1], - }; - if (_3d) { - point.z = args[idx + 2]; - } - points.push(point); - } - const order = (this.order = points.length - 1); - - const dims = (this.dims = ["x", "y"]); - if (_3d) dims.push("z"); - this.dimlen = dims.length; - - // is this curve, practically speaking, a straight line? - const aligned = utils.align(points, { p1: points[0], p2: points[order] }); - const baselength = utils.dist(points[0], points[order]); - this._linear = aligned.reduce((t, p) => t + abs(p.y), 0) < baselength / 50; - - this._lut = []; - this._t1 = 0; - this._t2 = 1; - this.update(); - } - - static quadraticFromPoints(p1, p2, p3, t) { - if (typeof t === "undefined") { - t = 0.5; - } - // shortcuts, although they're really dumb - if (t === 0) { - return new Bezier(p2, p2, p3); - } - if (t === 1) { - return new Bezier(p1, p2, p2); - } - // real fitting. - const abc = Bezier.getABC(2, p1, p2, p3, t); - return new Bezier(p1, abc.A, p3); - } - - static cubicFromPoints(S, B, E, t, d1) { - if (typeof t === "undefined") { - t = 0.5; - } - const abc = Bezier.getABC(3, S, B, E, t); - if (typeof d1 === "undefined") { - d1 = utils.dist(B, abc.C); - } - const d2 = (d1 * (1 - t)) / t; - - const selen = utils.dist(S, E), - lx = (E.x - S.x) / selen, - ly = (E.y - S.y) / selen, - bx1 = d1 * lx, - by1 = d1 * ly, - bx2 = d2 * lx, - by2 = d2 * ly; - // derivation of new hull coordinates - const e1 = { x: B.x - bx1, y: B.y - by1 }, - e2 = { x: B.x + bx2, y: B.y + by2 }, - A = abc.A, - v1 = { x: A.x + (e1.x - A.x) / (1 - t), y: A.y + (e1.y - A.y) / (1 - t) }, - v2 = { x: A.x + (e2.x - A.x) / t, y: A.y + (e2.y - A.y) / t }, - nc1 = { x: S.x + (v1.x - S.x) / t, y: S.y + (v1.y - S.y) / t }, - nc2 = { - x: E.x + (v2.x - E.x) / (1 - t), - y: E.y + (v2.y - E.y) / (1 - t), - }; - // ...done - return new Bezier(S, nc1, nc2, E); - } - - static getUtils() { - return utils; - } - - getUtils() { - return Bezier.getUtils(); - } - - static get PolyBezier() { - return PolyBezier; - } - - valueOf() { - return this.toString(); - } - - toString() { - return utils.pointsToString(this.points); - } - - toSVG() { - if (this._3d) return false; - const p = this.points, - x = p[0].x, - y = p[0].y, - s = ["M", x, y, this.order === 2 ? "Q" : "C"]; - for (let i = 1, last = p.length; i < last; i++) { - s.push(p[i].x); - s.push(p[i].y); - } - return s.join(" "); - } - - setRatios(ratios) { - if (ratios.length !== this.points.length) { - throw new Error("incorrect number of ratio values"); - } - this.ratios = ratios; - this._lut = []; // invalidate any precomputed LUT - } - - verify() { - const print = this.coordDigest(); - if (print !== this._print) { - this._print = print; - this.update(); - } - } - - coordDigest() { - return this.points - .map(function (c, pos) { - return "" + pos + c.x + c.y + (c.z ? c.z : 0); - }) - .join(""); - } - - update() { - // invalidate any precomputed LUT - this._lut = []; - this.dpoints = utils.derive(this.points, this._3d); - this.computedirection(); - } - - computedirection() { - const points = this.points; - const angle = utils.angle(points[0], points[this.order], points[1]); - this.clockwise = angle > 0; - } - - length() { - return utils.length(this.derivative.bind(this)); - } - - static getABC(order = 2, S, B, E, t = 0.5) { - const u = utils.projectionratio(t, order), - um = 1 - u, - C = { - x: u * S.x + um * E.x, - y: u * S.y + um * E.y, - }, - s = utils.abcratio(t, order), - A = { - x: B.x + (B.x - C.x) / s, - y: B.y + (B.y - C.y) / s, - }; - return { A, B, C, S, E }; - } - - getABC(t, B) { - B = B || this.get(t); - let S = this.points[0]; - let E = this.points[this.order]; - return Bezier.getABC(this.order, S, B, E, t); - } - - getLUT(steps) { - this.verify(); - steps = steps || 100; - if (this._lut.length === steps + 1) { - return this._lut; - } - this._lut = []; - // n steps means n+1 points - steps++; - this._lut = []; - for (let i = 0, p, t; i < steps; i++) { - t = i / (steps - 1); - p = this.compute(t); - p.t = t; - this._lut.push(p); - } - return this._lut; - } - - on(point, error) { - error = error || 5; - const lut = this.getLUT(), - hits = []; - for (let i = 0, c, t = 0; i < lut.length; i++) { - c = lut[i]; - if (utils.dist(c, point) < error) { - hits.push(c); - t += i / lut.length; - } - } - if (!hits.length) return false; - return (t /= hits.length); - } - - project(point) { - // step 1: coarse check - const LUT = this.getLUT(), - l = LUT.length - 1, - closest = utils.closest(LUT, point), - mpos = closest.mpos, - t1 = (mpos - 1) / l, - t2 = (mpos + 1) / l, - step = 0.1 / l; - - // step 2: fine check - let mdist = closest.mdist, - t = t1, - ft = t, - p; - mdist += 1; - for (let d; t < t2 + step; t += step) { - p = this.compute(t); - d = utils.dist(point, p); - if (d < mdist) { - mdist = d; - ft = t; - } - } - ft = ft < 0 ? 0 : ft > 1 ? 1 : ft; - p = this.compute(ft); - p.t = ft; - p.d = mdist; - return p; - } - - get(t) { - return this.compute(t); - } - - point(idx) { - return this.points[idx]; - } - - compute(t) { - if (this.ratios) { - return utils.computeWithRatios(t, this.points, this.ratios, this._3d); - } - return utils.compute(t, this.points, this._3d, this.ratios); - } - - raise() { - const p = this.points, - np = [p[0]], - k = p.length; - for (let i = 1, pi, pim; i < k; i++) { - pi = p[i]; - pim = p[i - 1]; - np[i] = { - x: ((k - i) / k) * pi.x + (i / k) * pim.x, - y: ((k - i) / k) * pi.y + (i / k) * pim.y, - }; - } - np[k] = p[k - 1]; - return new Bezier(np); - } - - derivative(t) { - return utils.compute(t, this.dpoints[0], this._3d); - } - - dderivative(t) { - return utils.compute(t, this.dpoints[1], this._3d); - } - - align() { - let p = this.points; - return new Bezier(utils.align(p, { p1: p[0], p2: p[p.length - 1] })); - } - - curvature(t) { - return utils.curvature(t, this.dpoints[0], this.dpoints[1], this._3d); - } - - inflections() { - return utils.inflections(this.points); - } - - normal(t) { - return this._3d ? this.__normal3(t) : this.__normal2(t); - } - - __normal2(t) { - const d = this.derivative(t); - const q = sqrt(d.x * d.x + d.y * d.y); - return { t, x: -d.y / q, y: d.x / q }; - } - - __normal3(t) { - // see http://stackoverflow.com/questions/25453159 - const r1 = this.derivative(t), - r2 = this.derivative(t + 0.01), - q1 = sqrt(r1.x * r1.x + r1.y * r1.y + r1.z * r1.z), - q2 = sqrt(r2.x * r2.x + r2.y * r2.y + r2.z * r2.z); - r1.x /= q1; - r1.y /= q1; - r1.z /= q1; - r2.x /= q2; - r2.y /= q2; - r2.z /= q2; - // cross product - const c = { - x: r2.y * r1.z - r2.z * r1.y, - y: r2.z * r1.x - r2.x * r1.z, - z: r2.x * r1.y - r2.y * r1.x, - }; - const m = sqrt(c.x * c.x + c.y * c.y + c.z * c.z); - c.x /= m; - c.y /= m; - c.z /= m; - // rotation matrix - const R = [ - c.x * c.x, - c.x * c.y - c.z, - c.x * c.z + c.y, - c.x * c.y + c.z, - c.y * c.y, - c.y * c.z - c.x, - c.x * c.z - c.y, - c.y * c.z + c.x, - c.z * c.z, - ]; - // normal vector: - const n = { - t, - x: R[0] * r1.x + R[1] * r1.y + R[2] * r1.z, - y: R[3] * r1.x + R[4] * r1.y + R[5] * r1.z, - z: R[6] * r1.x + R[7] * r1.y + R[8] * r1.z, - }; - return n; - } - - hull(t) { - let p = this.points, - _p = [], - q = [], - idx = 0; - q[idx++] = p[0]; - q[idx++] = p[1]; - q[idx++] = p[2]; - if (this.order === 3) { - q[idx++] = p[3]; - } - // we lerp between all points at each iteration, until we have 1 point left. - while (p.length > 1) { - _p = []; - for (let i = 0, pt, l = p.length - 1; i < l; i++) { - pt = utils.lerp(t, p[i], p[i + 1]); - q[idx++] = pt; - _p.push(pt); - } - p = _p; - } - return q; - } - - split(t1, t2) { - // shortcuts - if (t1 === 0 && !!t2) { - return this.split(t2).left; - } - if (t2 === 1) { - return this.split(t1).right; - } - - // no shortcut: use "de Casteljau" iteration. - const q = this.hull(t1); - const result = { - left: - this.order === 2 - ? new Bezier([q[0], q[3], q[5]]) - : new Bezier([q[0], q[4], q[7], q[9]]), - right: - this.order === 2 - ? new Bezier([q[5], q[4], q[2]]) - : new Bezier([q[9], q[8], q[6], q[3]]), - span: q, - }; - - // make sure we bind _t1/_t2 information! - result.left._t1 = utils.map(0, 0, 1, this._t1, this._t2); - result.left._t2 = utils.map(t1, 0, 1, this._t1, this._t2); - result.right._t1 = utils.map(t1, 0, 1, this._t1, this._t2); - result.right._t2 = utils.map(1, 0, 1, this._t1, this._t2); - - // if we have no t2, we're done - if (!t2) { - return result; - } - - // if we have a t2, split again: - t2 = utils.map(t2, t1, 1, 0, 1); - return result.right.split(t2).left; - } - - extrema() { - const result = {}; - let roots = []; - - this.dims.forEach( - function (dim) { - let mfn = function (v) { - return v[dim]; - }; - let p = this.dpoints[0].map(mfn); - result[dim] = utils.droots(p); - if (this.order === 3) { - p = this.dpoints[1].map(mfn); - result[dim] = result[dim].concat(utils.droots(p)); - } - result[dim] = result[dim].filter(function (t) { - return t >= 0 && t <= 1; - }); - roots = roots.concat(result[dim].sort(utils.numberSort)); - }.bind(this) - ); - - result.values = roots.sort(utils.numberSort).filter(function (v, idx) { - return roots.indexOf(v) === idx; - }); - - return result; - } - - bbox() { - const extrema = this.extrema(), - result = {}; - this.dims.forEach( - function (d) { - result[d] = utils.getminmax(this, d, extrema[d]); - }.bind(this) - ); - return result; - } - - overlaps(curve) { - const lbbox = this.bbox(), - tbbox = curve.bbox(); - return utils.bboxoverlap(lbbox, tbbox); - } - - offset(t, d) { - if (typeof d !== "undefined") { - const c = this.get(t), - n = this.normal(t); - const ret = { - c: c, - n: n, - x: c.x + n.x * d, - y: c.y + n.y * d, - }; - if (this._3d) { - ret.z = c.z + n.z * d; - } - return ret; - } - if (this._linear) { - const nv = this.normal(0), - coords = this.points.map(function (p) { - const ret = { - x: p.x + t * nv.x, - y: p.y + t * nv.y, - }; - if (p.z && nv.z) { - ret.z = p.z + t * nv.z; - } - return ret; - }); - return [new Bezier(coords)]; - } - return this.reduce().map(function (s) { - if (s._linear) { - return s.offset(t)[0]; - } - return s.scale(t); - }); - } - - simple() { - if (this.order === 3) { - const a1 = utils.angle(this.points[0], this.points[3], this.points[1]); - const a2 = utils.angle(this.points[0], this.points[3], this.points[2]); - if ((a1 > 0 && a2 < 0) || (a1 < 0 && a2 > 0)) return false; - } - const n1 = this.normal(0); - const n2 = this.normal(1); - let s = n1.x * n2.x + n1.y * n2.y; - if (this._3d) { - s += n1.z * n2.z; - } - return abs(acos(s)) < pi / 3; - } - - reduce() { - // TODO: examine these var types in more detail... - let i, - t1 = 0, - t2 = 0, - step = 0.01, - segment, - pass1 = [], - pass2 = []; - // first pass: split on extrema - let extrema = this.extrema().values; - if (extrema.indexOf(0) === -1) { - extrema = [0].concat(extrema); - } - if (extrema.indexOf(1) === -1) { - extrema.push(1); - } - - for (t1 = extrema[0], i = 1; i < extrema.length; i++) { - t2 = extrema[i]; - segment = this.split(t1, t2); - segment._t1 = t1; - segment._t2 = t2; - pass1.push(segment); - t1 = t2; - } - - // second pass: further reduce these segments to simple segments - pass1.forEach(function (p1) { - t1 = 0; - t2 = 0; - while (t2 <= 1) { - for (t2 = t1 + step; t2 <= 1 + step; t2 += step) { - segment = p1.split(t1, t2); - if (!segment.simple()) { - t2 -= step; - if (abs(t1 - t2) < step) { - // we can never form a reduction - return []; - } - segment = p1.split(t1, t2); - segment._t1 = utils.map(t1, 0, 1, p1._t1, p1._t2); - segment._t2 = utils.map(t2, 0, 1, p1._t1, p1._t2); - pass2.push(segment); - t1 = t2; - break; - } - } - } - if (t1 < 1) { - segment = p1.split(t1, 1); - segment._t1 = utils.map(t1, 0, 1, p1._t1, p1._t2); - segment._t2 = p1._t2; - pass2.push(segment); - } - }); - return pass2; - } - - translate(v, d1, d2) { - d2 = typeof d2 === "number" ? d2 : d1; - - // TODO: make this take curves with control points outside - // of the start-end interval into account - - const o = this.order; - let d = this.points.map((_, i) => (1 - i / o) * d1 + (i / o) * d2); - return new Bezier( - this.points.map((p, i) => ({ - x: p.x + v.x * d[i], - y: p.y + v.y * d[i], - })) - ); - } - - scale(d) { - const order = this.order; - let distanceFn = false; - if (typeof d === "function") { - distanceFn = d; - } - if (distanceFn && order === 2) { - return this.raise().scale(distanceFn); - } - - // TODO: add special handling for non-linear degenerate curves. - - const clockwise = this.clockwise; - const points = this.points; - - if (this._linear) { - return this.translate( - this.normal(0), - distanceFn ? distanceFn(0) : d, - distanceFn ? distanceFn(1) : d - ); - } - - const r1 = distanceFn ? distanceFn(0) : d; - const r2 = distanceFn ? distanceFn(1) : d; - const v = [this.offset(0, 10), this.offset(1, 10)]; - const np = []; - const o = utils.lli4(v[0], v[0].c, v[1], v[1].c); - - if (!o) { - throw new Error("cannot scale this curve. Try reducing it first."); - } - - // move all points by distance 'd' wrt the origin 'o', - // and move end points by fixed distance along normal. - [0, 1].forEach(function (t) { - const p = (np[t * order] = utils.copy(points[t * order])); - p.x += (t ? r2 : r1) * v[t].n.x; - p.y += (t ? r2 : r1) * v[t].n.y; - }); - - if (!distanceFn) { - // move control points to lie on the intersection of the offset - // derivative vector, and the origin-through-control vector - [0, 1].forEach((t) => { - if (order === 2 && !!t) return; - const p = np[t * order]; - const d = this.derivative(t); - const p2 = { x: p.x + d.x, y: p.y + d.y }; - np[t + 1] = utils.lli4(p, p2, o, points[t + 1]); - }); - return new Bezier(np); - } - - // move control points by "however much necessary to - // ensure the correct tangent to endpoint". - [0, 1].forEach(function (t) { - if (order === 2 && !!t) return; - var p = points[t + 1]; - var ov = { - x: p.x - o.x, - y: p.y - o.y, - }; - var rc = distanceFn ? distanceFn((t + 1) / order) : d; - if (distanceFn && !clockwise) rc = -rc; - var m = sqrt(ov.x * ov.x + ov.y * ov.y); - ov.x /= m; - ov.y /= m; - np[t + 1] = { - x: p.x + rc * ov.x, - y: p.y + rc * ov.y, - }; - }); - return new Bezier(np); - } - - outline(d1, d2, d3, d4) { - d2 = d2 === undefined ? d1 : d2; - - if (this._linear) { - // TODO: find the actual extrema, because they might - // be before the start, or past the end. - - const n = this.normal(0); - const start = this.points[0]; - const end = this.points[this.points.length - 1]; - let s, mid, e; - - if (d3 === undefined) { - d3 = d1; - d4 = d2; - } - - s = { x: start.x + n.x * d1, y: start.y + n.y * d1 }; - e = { x: end.x + n.x * d3, y: end.y + n.y * d3 }; - mid = { x: (s.x + e.x) / 2, y: (s.y + e.y) / 2 }; - const fline = [s, mid, e]; - - s = { x: start.x - n.x * d2, y: start.y - n.y * d2 }; - e = { x: end.x - n.x * d4, y: end.y - n.y * d4 }; - mid = { x: (s.x + e.x) / 2, y: (s.y + e.y) / 2 }; - const bline = [e, mid, s]; - - const ls = utils.makeline(bline[2], fline[0]); - const le = utils.makeline(fline[2], bline[0]); - const segments = [ls, new Bezier(fline), le, new Bezier(bline)]; - return new PolyBezier(segments); - } - - const reduced = this.reduce(), - len = reduced.length, - fcurves = []; - - let bcurves = [], - p, - alen = 0, - tlen = this.length(); - - const graduated = typeof d3 !== "undefined" && typeof d4 !== "undefined"; - - function linearDistanceFunction(s, e, tlen, alen, slen) { - return function (v) { - const f1 = alen / tlen, - f2 = (alen + slen) / tlen, - d = e - s; - return utils.map(v, 0, 1, s + f1 * d, s + f2 * d); - }; - } - - // form curve oulines - reduced.forEach(function (segment) { - const slen = segment.length(); - if (graduated) { - fcurves.push( - segment.scale(linearDistanceFunction(d1, d3, tlen, alen, slen)) - ); - bcurves.push( - segment.scale(linearDistanceFunction(-d2, -d4, tlen, alen, slen)) - ); - } else { - fcurves.push(segment.scale(d1)); - bcurves.push(segment.scale(-d2)); - } - alen += slen; - }); - - // reverse the "return" outline - bcurves = bcurves - .map(function (s) { - p = s.points; - if (p[3]) { - s.points = [p[3], p[2], p[1], p[0]]; - } else { - s.points = [p[2], p[1], p[0]]; - } - return s; - }) - .reverse(); - - // form the endcaps as lines - const fs = fcurves[0].points[0], - fe = fcurves[len - 1].points[fcurves[len - 1].points.length - 1], - bs = bcurves[len - 1].points[bcurves[len - 1].points.length - 1], - be = bcurves[0].points[0], - ls = utils.makeline(bs, fs), - le = utils.makeline(fe, be), - segments = [ls].concat(fcurves).concat([le]).concat(bcurves); - - return new PolyBezier(segments); - } - - outlineshapes(d1, d2, curveIntersectionThreshold) { - d2 = d2 || d1; - const outline = this.outline(d1, d2).curves; - const shapes = []; - for (let i = 1, len = outline.length; i < len / 2; i++) { - const shape = utils.makeshape( - outline[i], - outline[len - i], - curveIntersectionThreshold - ); - shape.startcap.virtual = i > 1; - shape.endcap.virtual = i < len / 2 - 1; - shapes.push(shape); - } - return shapes; - } - - intersects(curve, curveIntersectionThreshold) { - if (!curve) return this.selfintersects(curveIntersectionThreshold); - if (curve.p1 && curve.p2) { - return this.lineIntersects(curve); - } - if (curve instanceof Bezier) { - curve = curve.reduce(); - } - return this.curveintersects( - this.reduce(), - curve, - curveIntersectionThreshold - ); - } - - lineIntersects(line) { - const mx = min(line.p1.x, line.p2.x), - my = min(line.p1.y, line.p2.y), - MX = max(line.p1.x, line.p2.x), - MY = max(line.p1.y, line.p2.y); - return utils.roots(this.points, line).filter((t) => { - var p = this.get(t); - return utils.between(p.x, mx, MX) && utils.between(p.y, my, MY); - }); - } - - selfintersects(curveIntersectionThreshold) { - // "simple" curves cannot intersect with their direct - // neighbour, so for each segment X we check whether - // it intersects [0:x-2][x+2:last]. - - const reduced = this.reduce(), - len = reduced.length - 2, - results = []; - - for (let i = 0, result, left, right; i < len; i++) { - left = reduced.slice(i, i + 1); - right = reduced.slice(i + 2); - result = this.curveintersects(left, right, curveIntersectionThreshold); - results.push(...result); - } - return results; - } - - curveintersects(c1, c2, curveIntersectionThreshold) { - const pairs = []; - // step 1: pair off any overlapping segments - c1.forEach(function (l) { - c2.forEach(function (r) { - if (l.overlaps(r)) { - pairs.push({ left: l, right: r }); - } - }); - }); - // step 2: for each pairing, run through the convergence algorithm. - let intersections = []; - pairs.forEach(function (pair) { - const result = utils.pairiteration( - pair.left, - pair.right, - curveIntersectionThreshold - ); - if (result.length > 0) { - intersections = intersections.concat(result); - } - }); - return intersections; - } - - arcs(errorThreshold) { - errorThreshold = errorThreshold || 0.5; - return this._iterate(errorThreshold, []); - } - - _error(pc, np1, s, e) { - const q = (e - s) / 4, - c1 = this.get(s + q), - c2 = this.get(e - q), - ref = utils.dist(pc, np1), - d1 = utils.dist(pc, c1), - d2 = utils.dist(pc, c2); - return abs(d1 - ref) + abs(d2 - ref); - } - - _iterate(errorThreshold, circles) { - let t_s = 0, - t_e = 1, - safety; - // we do a binary search to find the "good `t` closest to no-longer-good" - do { - safety = 0; - - // step 1: start with the maximum possible arc - t_e = 1; - - // points: - let np1 = this.get(t_s), - np2, - np3, - arc, - prev_arc; - - // booleans: - let curr_good = false, - prev_good = false, - done; - - // numbers: - let t_m = t_e, - prev_e = 1; - - // step 2: find the best possible arc - do { - prev_good = curr_good; - prev_arc = arc; - t_m = (t_s + t_e) / 2; - - np2 = this.get(t_m); - np3 = this.get(t_e); - - arc = utils.getccenter(np1, np2, np3); - - //also save the t values - arc.interval = { - start: t_s, - end: t_e, - }; - - let error = this._error(arc, np1, t_s, t_e); - curr_good = error <= errorThreshold; - - done = prev_good && !curr_good; - if (!done) prev_e = t_e; - - // this arc is fine: we can move 'e' up to see if we can find a wider arc - if (curr_good) { - // if e is already at max, then we're done for this arc. - if (t_e >= 1) { - // make sure we cap at t=1 - arc.interval.end = prev_e = 1; - prev_arc = arc; - // if we capped the arc segment to t=1 we also need to make sure that - // the arc's end angle is correct with respect to the bezier end point. - if (t_e > 1) { - let d = { - x: arc.x + arc.r * cos(arc.e), - y: arc.y + arc.r * sin(arc.e), - }; - arc.e += utils.angle({ x: arc.x, y: arc.y }, d, this.get(1)); - } - break; - } - // if not, move it up by half the iteration distance - t_e = t_e + (t_e - t_s) / 2; - } else { - // this is a bad arc: we need to move 'e' down to find a good arc - t_e = t_m; - } - } while (!done && safety++ < 100); - - if (safety >= 100) { - break; - } - - // console.log("L835: [F] arc found", t_s, prev_e, prev_arc.x, prev_arc.y, prev_arc.s, prev_arc.e); - - prev_arc = prev_arc ? prev_arc : arc; - circles.push(prev_arc); - t_s = prev_e; - } while (t_e < 1); - return circles; - } - } - - function _iterableToArrayLimit(arr, i) { - var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; - if (null != _i) { - var _s, - _e, - _x, - _r, - _arr = [], - _n = !0, - _d = !1; - try { - if (_x = (_i = _i.call(arr)).next, 0 === i) { - if (Object(_i) !== _i) return; - _n = !1; - } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); - } catch (err) { - _d = !0, _e = err; - } finally { - try { - if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; - } finally { - if (_d) throw _e; - } - } - return _arr; - } - } - function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - return target; - } - function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = _objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - return target; - } - function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); - } - function _toConsumableArray(arr) { - return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); - } - function _arrayWithoutHoles(arr) { - if (Array.isArray(arr)) return _arrayLikeToArray(arr); - } - function _arrayWithHoles(arr) { - if (Array.isArray(arr)) return arr; - } - function _iterableToArray(iter) { - if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); - } - function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); - } - function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; - } - function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); - } - function _toPrimitive(input, hint) { - if (typeof input !== "object" || input === null) return input; - var prim = input[Symbol.toPrimitive]; - if (prim !== undefined) { - var res = prim.call(input, hint || "default"); - if (typeof res !== "object") return res; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return (hint === "string" ? String : Number)(input); - } - function _toPropertyKey(arg) { - var key = _toPrimitive(arg, "string"); - return typeof key === "symbol" ? key : String(key); - } - - var index = (function () { - var list = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; - var keyAccessors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var multiItem = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - var flattenKeys = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; - var keys = (keyAccessors instanceof Array ? keyAccessors.length ? keyAccessors : [undefined] : [keyAccessors]).map(function (key) { - return { - keyAccessor: key, - isProp: !(key instanceof Function) - }; - }); - var indexedResult = list.reduce(function (res, item) { - var iterObj = res; - var itemVal = item; - keys.forEach(function (_ref, idx) { - var keyAccessor = _ref.keyAccessor, - isProp = _ref.isProp; - var key; - if (isProp) { - var _itemVal = itemVal, - propVal = _itemVal[keyAccessor], - rest = _objectWithoutProperties(_itemVal, [keyAccessor].map(_toPropertyKey)); - key = propVal; - itemVal = rest; - } else { - key = keyAccessor(itemVal, idx); - } - if (idx + 1 < keys.length) { - if (!iterObj.hasOwnProperty(key)) { - iterObj[key] = {}; - } - iterObj = iterObj[key]; - } else { - // Leaf key - if (multiItem) { - if (!iterObj.hasOwnProperty(key)) { - iterObj[key] = []; - } - iterObj[key].push(itemVal); - } else { - iterObj[key] = itemVal; - } - } - }); - return res; - }, {}); - if (multiItem instanceof Function) { - // Reduce leaf multiple values - (function reduce(node) { - var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; - if (level === keys.length) { - Object.keys(node).forEach(function (k) { - return node[k] = multiItem(node[k]); - }); - } else { - Object.values(node).forEach(function (child) { - return reduce(child, level + 1); - }); - } - })(indexedResult); // IIFE - } - - var result = indexedResult; - if (flattenKeys) { - // flatten into array - result = []; - (function flatten(node) { - var accKeys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - if (accKeys.length === keys.length) { - result.push({ - keys: accKeys, - vals: node - }); - } else { - Object.entries(node).forEach(function (_ref2) { - var _ref3 = _slicedToArray(_ref2, 2), - key = _ref3[0], - val = _ref3[1]; - return flatten(val, [].concat(_toConsumableArray(accKeys), [key])); - }); - } - })(indexedResult); //IIFE - - if (keyAccessors instanceof Array && keyAccessors.length === 0 && result.length === 1) { - // clear keys if there's no key accessors (single result) - result[0].keys = []; - } - } - return result; - }); - - function initRange(domain, range) { - switch (arguments.length) { - case 0: break; - case 1: this.range(domain); break; - default: this.range(range).domain(domain); break; - } - return this; - } - - const implicit = Symbol("implicit"); - - function ordinal() { - var index = new InternMap(), - domain = [], - range = [], - unknown = implicit; - - function scale(d) { - let i = index.get(d); - if (i === undefined) { - if (unknown !== implicit) return unknown; - index.set(d, i = domain.push(d) - 1); - } - return range[i % range.length]; - } - - scale.domain = function(_) { - if (!arguments.length) return domain.slice(); - domain = [], index = new InternMap(); - for (const value of _) { - if (index.has(value)) continue; - index.set(value, domain.push(value) - 1); - } - return scale; - }; - - scale.range = function(_) { - return arguments.length ? (range = Array.from(_), scale) : range.slice(); - }; - - scale.unknown = function(_) { - return arguments.length ? (unknown = _, scale) : unknown; - }; - - scale.copy = function() { - return ordinal(domain, range).unknown(unknown); - }; - - initRange.apply(scale, arguments); - - return scale; - } - - function colors(specifier) { - var n = specifier.length / 6 | 0, colors = new Array(n), i = 0; - while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6); - return colors; - } - - var schemePaired = colors("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"); - - var autoColorScale = ordinal(schemePaired); - - // Autoset attribute colorField by colorByAccessor property - // If an object has already a color, don't set it - // Objects can be nodes or links - function autoColorObjects(objects, colorByAccessor, colorField) { - if (!colorByAccessor || typeof colorField !== 'string') return; - objects.filter(function (obj) { - return !obj[colorField]; - }).forEach(function (obj) { - obj[colorField] = autoColorScale(colorByAccessor(obj)); - }); - } - - function getDagDepths (_ref, idAccessor) { - var nodes = _ref.nodes, - links = _ref.links; - var _ref2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, - _ref2$nodeFilter = _ref2.nodeFilter, - nodeFilter = _ref2$nodeFilter === void 0 ? function () { - return true; - } : _ref2$nodeFilter, - _ref2$onLoopError = _ref2.onLoopError, - onLoopError = _ref2$onLoopError === void 0 ? function (loopIds) { - throw "Invalid DAG structure! Found cycle in node path: ".concat(loopIds.join(' -> '), "."); - } : _ref2$onLoopError; - // linked graph - var graph = {}; - nodes.forEach(function (node) { - return graph[idAccessor(node)] = { - data: node, - out: [], - depth: -1, - skip: !nodeFilter(node) - }; - }); - links.forEach(function (_ref3) { - var source = _ref3.source, - target = _ref3.target; - var sourceId = getNodeId(source); - var targetId = getNodeId(target); - if (!graph.hasOwnProperty(sourceId)) throw "Missing source node with id: ".concat(sourceId); - if (!graph.hasOwnProperty(targetId)) throw "Missing target node with id: ".concat(targetId); - var sourceNode = graph[sourceId]; - var targetNode = graph[targetId]; - sourceNode.out.push(targetNode); - function getNodeId(node) { - return _typeof$1(node) === 'object' ? idAccessor(node) : node; - } - }); - var foundLoops = []; - traverse(Object.values(graph)); - var nodeDepths = Object.assign.apply(Object, [{}].concat(_toConsumableArray$2(Object.entries(graph).filter(function (_ref4) { - var _ref5 = _slicedToArray$2(_ref4, 2), - node = _ref5[1]; - return !node.skip; - }).map(function (_ref6) { - var _ref7 = _slicedToArray$2(_ref6, 2), - id = _ref7[0], - node = _ref7[1]; - return _defineProperty({}, id, node.depth); - })))); - return nodeDepths; - function traverse(nodes) { - var nodeStack = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var currentDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - var _loop = function _loop() { - var node = nodes[i]; - if (nodeStack.indexOf(node) !== -1) { - var loop = [].concat(_toConsumableArray$2(nodeStack.slice(nodeStack.indexOf(node))), [node]).map(function (d) { - return idAccessor(d.data); - }); - if (!foundLoops.some(function (foundLoop) { - return foundLoop.length === loop.length && foundLoop.every(function (id, idx) { - return id === loop[idx]; - }); - })) { - foundLoops.push(loop); - onLoopError(loop); - } - return 1; // continue - } - if (currentDepth > node.depth) { - // Don't unnecessarily revisit chunks of the graph - node.depth = currentDepth; - traverse(node.out, [].concat(_toConsumableArray$2(nodeStack), [node]), currentDepth + (node.skip ? 0 : 1)); - } - }; - for (var i = 0, l = nodes.length; i < l; i++) { - if (_loop()) continue; - } - } - } - - // - - var DAG_LEVEL_NODE_RATIO = 2; - - // whenever styling props are changed that require a canvas redraw - var notifyRedraw = function notifyRedraw(_, state) { - return state.onNeedsRedraw && state.onNeedsRedraw(); - }; - var updDataPhotons = function updDataPhotons(_, state) { - if (!state.isShadow) { - // Add photon particles - var linkParticlesAccessor = index$2(state.linkDirectionalParticles); - state.graphData.links.forEach(function (link) { - var numPhotons = Math.round(Math.abs(linkParticlesAccessor(link))); - if (numPhotons) { - link.__photons = _toConsumableArray$2(Array(numPhotons)).map(function () { - return {}; - }); - } else { - delete link.__photons; - } - }); - } - }; - var CanvasForceGraph = index$3({ - props: { - graphData: { - "default": { - nodes: [], - links: [] - }, - onChange: function onChange(_, state) { - state.engineRunning = false; // Pause simulation - updDataPhotons(_, state); - } - }, - dagMode: { - onChange: function onChange(dagMode, state) { - // td, bu, lr, rl, radialin, radialout - !dagMode && (state.graphData.nodes || []).forEach(function (n) { - return n.fx = n.fy = undefined; - }); // unfix nodes when disabling dag mode - } - }, - - dagLevelDistance: {}, - dagNodeFilter: { - "default": function _default(node) { - return true; - } - }, - onDagError: { - triggerUpdate: false - }, - nodeRelSize: { - "default": 4, - triggerUpdate: false, - onChange: notifyRedraw - }, - // area per val unit - nodeId: { - "default": 'id' - }, - nodeVal: { - "default": 'val', - triggerUpdate: false, - onChange: notifyRedraw - }, - nodeColor: { - "default": 'color', - triggerUpdate: false, - onChange: notifyRedraw - }, - nodeAutoColorBy: {}, - nodeCanvasObject: { - triggerUpdate: false, - onChange: notifyRedraw - }, - nodeCanvasObjectMode: { - "default": function _default() { - return 'replace'; - }, - triggerUpdate: false, - onChange: notifyRedraw - }, - nodeVisibility: { - "default": true, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkSource: { - "default": 'source' - }, - linkTarget: { - "default": 'target' - }, - linkVisibility: { - "default": true, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkColor: { - "default": 'color', - triggerUpdate: false, - onChange: notifyRedraw - }, - linkAutoColorBy: {}, - linkLineDash: { - triggerUpdate: false, - onChange: notifyRedraw - }, - linkWidth: { - "default": 1, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkCurvature: { - "default": 0, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkCanvasObject: { - triggerUpdate: false, - onChange: notifyRedraw - }, - linkCanvasObjectMode: { - "default": function _default() { - return 'replace'; - }, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkDirectionalArrowLength: { - "default": 0, - triggerUpdate: false, - onChange: notifyRedraw - }, - linkDirectionalArrowColor: { - triggerUpdate: false, - onChange: notifyRedraw - }, - linkDirectionalArrowRelPos: { - "default": 0.5, - triggerUpdate: false, - onChange: notifyRedraw - }, - // value between 0<>1 indicating the relative pos along the (exposed) line - linkDirectionalParticles: { - "default": 0, - triggerUpdate: false, - onChange: updDataPhotons - }, - // animate photons travelling in the link direction - linkDirectionalParticleSpeed: { - "default": 0.01, - triggerUpdate: false - }, - // in link length ratio per frame - linkDirectionalParticleWidth: { - "default": 4, - triggerUpdate: false - }, - linkDirectionalParticleColor: { - triggerUpdate: false - }, - globalScale: { - "default": 1, - triggerUpdate: false - }, - d3AlphaMin: { - "default": 0, - triggerUpdate: false - }, - d3AlphaDecay: { - "default": 0.0228, - triggerUpdate: false, - onChange: function onChange(alphaDecay, state) { - state.forceLayout.alphaDecay(alphaDecay); - } - }, - d3AlphaTarget: { - "default": 0, - triggerUpdate: false, - onChange: function onChange(alphaTarget, state) { - state.forceLayout.alphaTarget(alphaTarget); - } - }, - d3VelocityDecay: { - "default": 0.4, - triggerUpdate: false, - onChange: function onChange(velocityDecay, state) { - state.forceLayout.velocityDecay(velocityDecay); - } - }, - warmupTicks: { - "default": 0, - triggerUpdate: false - }, - // how many times to tick the force engine at init before starting to render - cooldownTicks: { - "default": Infinity, - triggerUpdate: false - }, - cooldownTime: { - "default": 15000, - triggerUpdate: false - }, - // ms - onUpdate: { - "default": function _default() {}, - triggerUpdate: false - }, - onFinishUpdate: { - "default": function _default() {}, - triggerUpdate: false - }, - onEngineTick: { - "default": function _default() {}, - triggerUpdate: false - }, - onEngineStop: { - "default": function _default() {}, - triggerUpdate: false - }, - onNeedsRedraw: { - triggerUpdate: false - }, - isShadow: { - "default": false, - triggerUpdate: false - } - }, - methods: { - // Expose d3 forces for external manipulation - d3Force: function d3Force(state, forceName, forceFn) { - if (forceFn === undefined) { - return state.forceLayout.force(forceName); // Force getter - } - - state.forceLayout.force(forceName, forceFn); // Force setter - return this; - }, - d3ReheatSimulation: function d3ReheatSimulation(state) { - state.forceLayout.alpha(1); - this.resetCountdown(); - return this; - }, - // reset cooldown state - resetCountdown: function resetCountdown(state) { - state.cntTicks = 0; - state.startTickTime = new Date(); - state.engineRunning = true; - return this; - }, - isEngineRunning: function isEngineRunning(state) { - return !!state.engineRunning; - }, - tickFrame: function tickFrame(state) { - !state.isShadow && layoutTick(); - paintLinks(); - !state.isShadow && paintArrows(); - !state.isShadow && paintPhotons(); - paintNodes(); - return this; - - // - - function layoutTick() { - if (state.engineRunning) { - if (++state.cntTicks > state.cooldownTicks || new Date() - state.startTickTime > state.cooldownTime || state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin) { - state.engineRunning = false; // Stop ticking graph - state.onEngineStop(); - } else { - state.forceLayout.tick(); // Tick it - state.onEngineTick(); - } - } - } - function paintNodes() { - var getVisibility = index$2(state.nodeVisibility); - var getVal = index$2(state.nodeVal); - var getColor = index$2(state.nodeColor); - var getNodeCanvasObjectMode = index$2(state.nodeCanvasObjectMode); - var ctx = state.ctx; - - // Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing) - var padAmount = state.isShadow / state.globalScale; - var visibleNodes = state.graphData.nodes.filter(getVisibility); - ctx.save(); - visibleNodes.forEach(function (node) { - var nodeCanvasObjectMode = getNodeCanvasObjectMode(node); - if (state.nodeCanvasObject && (nodeCanvasObjectMode === 'before' || nodeCanvasObjectMode === 'replace')) { - // Custom node before/replace paint - state.nodeCanvasObject(node, ctx, state.globalScale); - if (nodeCanvasObjectMode === 'replace') { - ctx.restore(); - return; - } - } - - // Draw wider nodes by 1px on shadow canvas for more precise hovering (due to boundary anti-aliasing) - var r = Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize + padAmount; - ctx.beginPath(); - ctx.arc(node.x, node.y, r, 0, 2 * Math.PI, false); - ctx.fillStyle = getColor(node) || 'rgba(31, 120, 180, 0.92)'; - ctx.fill(); - if (state.nodeCanvasObject && nodeCanvasObjectMode === 'after') { - // Custom node after paint - state.nodeCanvasObject(node, state.ctx, state.globalScale); - } - }); - ctx.restore(); - } - function paintLinks() { - var getVisibility = index$2(state.linkVisibility); - var getColor = index$2(state.linkColor); - var getWidth = index$2(state.linkWidth); - var getLineDash = index$2(state.linkLineDash); - var getCurvature = index$2(state.linkCurvature); - var getLinkCanvasObjectMode = index$2(state.linkCanvasObjectMode); - var ctx = state.ctx; - - // Draw wider lines by 2px on shadow canvas for more precise hovering (due to boundary anti-aliasing) - var padAmount = state.isShadow * 2; - var visibleLinks = state.graphData.links.filter(getVisibility); - visibleLinks.forEach(calcLinkControlPoints); // calculate curvature control points for all visible links - - var beforeCustomLinks = [], - afterCustomLinks = [], - defaultPaintLinks = visibleLinks; - if (state.linkCanvasObject) { - var replaceCustomLinks = [], - otherCustomLinks = []; - visibleLinks.forEach(function (d) { - return ({ - before: beforeCustomLinks, - after: afterCustomLinks, - replace: replaceCustomLinks - }[getLinkCanvasObjectMode(d)] || otherCustomLinks).push(d); - }); - defaultPaintLinks = [].concat(_toConsumableArray$2(beforeCustomLinks), afterCustomLinks, otherCustomLinks); - beforeCustomLinks = beforeCustomLinks.concat(replaceCustomLinks); - } - - // Custom link before paints - ctx.save(); - beforeCustomLinks.forEach(function (link) { - return state.linkCanvasObject(link, ctx, state.globalScale); - }); - ctx.restore(); - - // Bundle strokes per unique color/width/dash for performance optimization - var linksPerColor = index(defaultPaintLinks, [getColor, getWidth, getLineDash]); - ctx.save(); - Object.entries(linksPerColor).forEach(function (_ref) { - var _ref2 = _slicedToArray$2(_ref, 2), - color = _ref2[0], - linksPerWidth = _ref2[1]; - var lineColor = !color || color === 'undefined' ? 'rgba(0,0,0,0.15)' : color; - Object.entries(linksPerWidth).forEach(function (_ref3) { - var _ref4 = _slicedToArray$2(_ref3, 2), - width = _ref4[0], - linesPerLineDash = _ref4[1]; - var lineWidth = (width || 1) / state.globalScale + padAmount; - Object.entries(linesPerLineDash).forEach(function (_ref5) { - var _ref6 = _slicedToArray$2(_ref5, 2); - _ref6[0]; - var links = _ref6[1]; - var lineDashSegments = getLineDash(links[0]); - ctx.beginPath(); - links.forEach(function (link) { - var start = link.source; - var end = link.target; - if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link - - ctx.moveTo(start.x, start.y); - var controlPoints = link.__controlPoints; - if (!controlPoints) { - // Straight line - ctx.lineTo(end.x, end.y); - } else { - // Use quadratic curves for regular lines and bezier for loops - ctx[controlPoints.length === 2 ? 'quadraticCurveTo' : 'bezierCurveTo'].apply(ctx, _toConsumableArray$2(controlPoints).concat([end.x, end.y])); - } - }); - ctx.strokeStyle = lineColor; - ctx.lineWidth = lineWidth; - ctx.setLineDash(lineDashSegments || []); - ctx.stroke(); - }); - }); - }); - ctx.restore(); - - // Custom link after paints - ctx.save(); - afterCustomLinks.forEach(function (link) { - return state.linkCanvasObject(link, ctx, state.globalScale); - }); - ctx.restore(); - - // - - function calcLinkControlPoints(link) { - var curvature = getCurvature(link); - if (!curvature) { - // straight line - link.__controlPoints = null; - return; - } - var start = link.source; - var end = link.target; - if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link - - var l = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)); // line length - - if (l > 0) { - var a = Math.atan2(end.y - start.y, end.x - start.x); // line angle - var d = l * curvature; // control point distance - - var cp = { - // control point - x: (start.x + end.x) / 2 + d * Math.cos(a - Math.PI / 2), - y: (start.y + end.y) / 2 + d * Math.sin(a - Math.PI / 2) - }; - link.__controlPoints = [cp.x, cp.y]; - } else { - // Same point, draw a loop - var _d = curvature * 70; - link.__controlPoints = [end.x, end.y - _d, end.x + _d, end.y]; - } - } - } - function paintArrows() { - var ARROW_WH_RATIO = 1.6; - var ARROW_VLEN_RATIO = 0.2; - var getLength = index$2(state.linkDirectionalArrowLength); - var getRelPos = index$2(state.linkDirectionalArrowRelPos); - var getVisibility = index$2(state.linkVisibility); - var getColor = index$2(state.linkDirectionalArrowColor || state.linkColor); - var getNodeVal = index$2(state.nodeVal); - var ctx = state.ctx; - ctx.save(); - state.graphData.links.filter(getVisibility).forEach(function (link) { - var arrowLength = getLength(link); - if (!arrowLength || arrowLength < 0) return; - var start = link.source; - var end = link.target; - if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link - - var startR = Math.sqrt(Math.max(0, getNodeVal(start) || 1)) * state.nodeRelSize; - var endR = Math.sqrt(Math.max(0, getNodeVal(end) || 1)) * state.nodeRelSize; - var arrowRelPos = Math.min(1, Math.max(0, getRelPos(link))); - var arrowColor = getColor(link) || 'rgba(0,0,0,0.28)'; - var arrowHalfWidth = arrowLength / ARROW_WH_RATIO / 2; - - // Construct bezier for curved lines - var bzLine = link.__controlPoints && _construct(Bezier, [start.x, start.y].concat(_toConsumableArray$2(link.__controlPoints), [end.x, end.y])); - var getCoordsAlongLine = bzLine ? function (t) { - return bzLine.get(t); - } // get position along bezier line - : function (t) { - return { - // straight line: interpolate linearly - x: start.x + (end.x - start.x) * t || 0, - y: start.y + (end.y - start.y) * t || 0 - }; - }; - var lineLen = bzLine ? bzLine.length() : Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)); - var posAlongLine = startR + arrowLength + (lineLen - startR - endR - arrowLength) * arrowRelPos; - var arrowHead = getCoordsAlongLine(posAlongLine / lineLen); - var arrowTail = getCoordsAlongLine((posAlongLine - arrowLength) / lineLen); - var arrowTailVertex = getCoordsAlongLine((posAlongLine - arrowLength * (1 - ARROW_VLEN_RATIO)) / lineLen); - var arrowTailAngle = Math.atan2(arrowHead.y - arrowTail.y, arrowHead.x - arrowTail.x) - Math.PI / 2; - ctx.beginPath(); - ctx.moveTo(arrowHead.x, arrowHead.y); - ctx.lineTo(arrowTail.x + arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y + arrowHalfWidth * Math.sin(arrowTailAngle)); - ctx.lineTo(arrowTailVertex.x, arrowTailVertex.y); - ctx.lineTo(arrowTail.x - arrowHalfWidth * Math.cos(arrowTailAngle), arrowTail.y - arrowHalfWidth * Math.sin(arrowTailAngle)); - ctx.fillStyle = arrowColor; - ctx.fill(); - }); - ctx.restore(); - } - function paintPhotons() { - var getNumPhotons = index$2(state.linkDirectionalParticles); - var getSpeed = index$2(state.linkDirectionalParticleSpeed); - var getDiameter = index$2(state.linkDirectionalParticleWidth); - var getVisibility = index$2(state.linkVisibility); - var getColor = index$2(state.linkDirectionalParticleColor || state.linkColor); - var ctx = state.ctx; - ctx.save(); - state.graphData.links.filter(getVisibility).forEach(function (link) { - var numCyclePhotons = getNumPhotons(link); - if (!link.hasOwnProperty('__photons') || !link.__photons.length) return; - var start = link.source; - var end = link.target; - if (!start || !end || !start.hasOwnProperty('x') || !end.hasOwnProperty('x')) return; // skip invalid link - - var particleSpeed = getSpeed(link); - var photons = link.__photons || []; - var photonR = Math.max(0, getDiameter(link) / 2) / Math.sqrt(state.globalScale); - var photonColor = getColor(link) || 'rgba(0,0,0,0.28)'; - ctx.fillStyle = photonColor; - - // Construct bezier for curved lines - var bzLine = link.__controlPoints ? _construct(Bezier, [start.x, start.y].concat(_toConsumableArray$2(link.__controlPoints), [end.x, end.y])) : null; - var cyclePhotonIdx = 0; - var needsCleanup = false; // whether some photons need to be removed from list - photons.forEach(function (photon) { - var singleHop = !!photon.__singleHop; - if (!photon.hasOwnProperty('__progressRatio')) { - photon.__progressRatio = singleHop ? 0 : cyclePhotonIdx / numCyclePhotons; - } - !singleHop && cyclePhotonIdx++; // increase regular photon index - - photon.__progressRatio += particleSpeed; - if (photon.__progressRatio >= 1) { - if (!singleHop) { - photon.__progressRatio = photon.__progressRatio % 1; - } else { - needsCleanup = true; - return; - } - } - var photonPosRatio = photon.__progressRatio; - var coords = bzLine ? bzLine.get(photonPosRatio) // get position along bezier line - : { - // straight line: interpolate linearly - x: start.x + (end.x - start.x) * photonPosRatio || 0, - y: start.y + (end.y - start.y) * photonPosRatio || 0 - }; - ctx.beginPath(); - ctx.arc(coords.x, coords.y, photonR, 0, 2 * Math.PI, false); - ctx.fill(); - }); - if (needsCleanup) { - // remove expired single hop photons - link.__photons = link.__photons.filter(function (photon) { - return !photon.__singleHop || photon.__progressRatio <= 1; - }); - } - }); - ctx.restore(); - } - }, - emitParticle: function emitParticle(state, link) { - if (link) { - !link.__photons && (link.__photons = []); - link.__photons.push({ - __singleHop: true - }); // add a single hop particle - } - - return this; - } - }, - stateInit: function stateInit() { - return { - forceLayout: d3ForceSimulation().force('link', d3ForceLink()).force('charge', d3ForceManyBody()).force('center', d3ForceCenter()).force('dagRadial', null).stop(), - engineRunning: false - }; - }, - init: function init(canvasCtx, state) { - // Main canvas object to manipulate - state.ctx = canvasCtx; - }, - update: function update(state) { - state.engineRunning = false; // Pause simulation - state.onUpdate(); - if (state.nodeAutoColorBy !== null) { - // Auto add color to uncolored nodes - autoColorObjects(state.graphData.nodes, index$2(state.nodeAutoColorBy), state.nodeColor); - } - if (state.linkAutoColorBy !== null) { - // Auto add color to uncolored links - autoColorObjects(state.graphData.links, index$2(state.linkAutoColorBy), state.linkColor); - } - - // parse links - state.graphData.links.forEach(function (link) { - link.source = link[state.linkSource]; - link.target = link[state.linkTarget]; - }); - - // Feed data to force-directed layout - state.forceLayout.stop().alpha(1) // re-heat the simulation - .nodes(state.graphData.nodes); - - // add links (if link force is still active) - var linkForce = state.forceLayout.force('link'); - if (linkForce) { - linkForce.id(function (d) { - return d[state.nodeId]; - }).links(state.graphData.links); - } - - // setup dag force constraints - var nodeDepths = state.dagMode && getDagDepths(state.graphData, function (node) { - return node[state.nodeId]; - }, { - nodeFilter: state.dagNodeFilter, - onLoopError: state.onDagError || undefined - }); - var maxDepth = Math.max.apply(Math, _toConsumableArray$2(Object.values(nodeDepths || []))); - var dagLevelDistance = state.dagLevelDistance || state.graphData.nodes.length / (maxDepth || 1) * DAG_LEVEL_NODE_RATIO * (['radialin', 'radialout'].indexOf(state.dagMode) !== -1 ? 0.7 : 1); - - // Fix nodes to x,y for dag mode - if (state.dagMode) { - var getFFn = function getFFn(fix, invert) { - return function (node) { - return !fix ? undefined : (nodeDepths[node[state.nodeId]] - maxDepth / 2) * dagLevelDistance * (invert ? -1 : 1); - }; - }; - var fxFn = getFFn(['lr', 'rl'].indexOf(state.dagMode) !== -1, state.dagMode === 'rl'); - var fyFn = getFFn(['td', 'bu'].indexOf(state.dagMode) !== -1, state.dagMode === 'bu'); - state.graphData.nodes.filter(state.dagNodeFilter).forEach(function (node) { - node.fx = fxFn(node); - node.fy = fyFn(node); - }); - } - - // Use radial force for radial dags - state.forceLayout.force('dagRadial', ['radialin', 'radialout'].indexOf(state.dagMode) !== -1 ? d3ForceRadial(function (node) { - var nodeDepth = nodeDepths[node[state.nodeId]] || -1; - return (state.dagMode === 'radialin' ? maxDepth - nodeDepth : nodeDepth) * dagLevelDistance; - }).strength(function (node) { - return state.dagNodeFilter(node) ? 1 : 0; - }) : null); - for (var i = 0; i < state.warmupTicks && !(state.d3AlphaMin > 0 && state.forceLayout.alpha() < state.d3AlphaMin); i++) { - state.forceLayout.tick(); - } // Initial ticks before starting to render - - this.resetCountdown(); - state.onFinishUpdate(); - } - }); - - function linkKapsule (kapsulePropNames, kapsuleType) { - var propNames = kapsulePropNames instanceof Array ? kapsulePropNames : [kapsulePropNames]; - var dummyK = new kapsuleType(); // To extract defaults - dummyK._destructor && dummyK._destructor(); - return { - linkProp: function linkProp(prop) { - // link property config - return { - "default": dummyK[prop](), - onChange: function onChange(v, state) { - propNames.forEach(function (propName) { - return state[propName][prop](v); - }); - }, - triggerUpdate: false - }; - }, - linkMethod: function linkMethod(method) { - // link method pass-through - return function (state) { - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - var returnVals = []; - propNames.forEach(function (propName) { - var kapsuleInstance = state[propName]; - var returnVal = kapsuleInstance[method].apply(kapsuleInstance, args); - if (returnVal !== kapsuleInstance) { - returnVals.push(returnVal); - } - }); - return returnVals.length ? returnVals[0] : this; // chain based on the parent object, not the inner kapsule - }; - } - }; - } - - var HOVER_CANVAS_THROTTLE_DELAY = 800; // ms to throttle shadow canvas updates for perf improvement - var ZOOM2NODES_FACTOR = 4; - - // Expose config from forceGraph - var bindFG = linkKapsule('forceGraph', CanvasForceGraph); - var bindBoth = linkKapsule(['forceGraph', 'shadowGraph'], CanvasForceGraph); - var linkedProps = Object.assign.apply(Object, _toConsumableArray$2(['nodeColor', 'nodeAutoColorBy', 'nodeCanvasObject', 'nodeCanvasObjectMode', 'linkColor', 'linkAutoColorBy', 'linkLineDash', 'linkWidth', 'linkCanvasObject', 'linkCanvasObjectMode', 'linkDirectionalArrowLength', 'linkDirectionalArrowColor', 'linkDirectionalArrowRelPos', 'linkDirectionalParticles', 'linkDirectionalParticleSpeed', 'linkDirectionalParticleWidth', 'linkDirectionalParticleColor', 'dagMode', 'dagLevelDistance', 'dagNodeFilter', 'onDagError', 'd3AlphaMin', 'd3AlphaDecay', 'd3VelocityDecay', 'warmupTicks', 'cooldownTicks', 'cooldownTime', 'onEngineTick', 'onEngineStop'].map(function (p) { - return _defineProperty({}, p, bindFG.linkProp(p)); - })).concat(_toConsumableArray$2(['nodeRelSize', 'nodeId', 'nodeVal', 'nodeVisibility', 'linkSource', 'linkTarget', 'linkVisibility', 'linkCurvature'].map(function (p) { - return _defineProperty({}, p, bindBoth.linkProp(p)); - })))); - var linkedMethods = Object.assign.apply(Object, _toConsumableArray$2(['d3Force', 'd3ReheatSimulation', 'emitParticle'].map(function (p) { - return _defineProperty({}, p, bindFG.linkMethod(p)); - }))); - function adjustCanvasSize(state) { - if (state.canvas) { - var curWidth = state.canvas.width; - var curHeight = state.canvas.height; - if (curWidth === 300 && curHeight === 150) { - // Default canvas dimensions - curWidth = curHeight = 0; - } - var pxScale = window.devicePixelRatio; // 2 on retina displays - curWidth /= pxScale; - curHeight /= pxScale; - - // Resize canvases - [state.canvas, state.shadowCanvas].forEach(function (canvas) { - // Element size - canvas.style.width = "".concat(state.width, "px"); - canvas.style.height = "".concat(state.height, "px"); - - // Memory size (scaled to avoid blurriness) - canvas.width = state.width * pxScale; - canvas.height = state.height * pxScale; - - // Normalize coordinate system to use css pixels (on init only) - if (!curWidth && !curHeight) { - canvas.getContext('2d').scale(pxScale, pxScale); - } - }); - - // Relative center panning based on 0,0 - var k = transform(state.canvas).k; - state.zoom.translateBy(state.zoom.__baseElem, (state.width - curWidth) / 2 / k, (state.height - curHeight) / 2 / k); - state.needsRedraw = true; - } - } - function resetTransform(ctx) { - var pxRatio = window.devicePixelRatio; - ctx.setTransform(pxRatio, 0, 0, pxRatio, 0, 0); - } - function clearCanvas(ctx, width, height) { - ctx.save(); - resetTransform(ctx); // reset transform - ctx.clearRect(0, 0, width, height); - ctx.restore(); //restore transforms - } - - // - - var forceGraph = index$3({ - props: _objectSpread2({ - width: { - "default": window.innerWidth, - onChange: function onChange(_, state) { - return adjustCanvasSize(state); - }, - triggerUpdate: false - }, - height: { - "default": window.innerHeight, - onChange: function onChange(_, state) { - return adjustCanvasSize(state); - }, - triggerUpdate: false - }, - graphData: { - "default": { - nodes: [], - links: [] - }, - onChange: function onChange(d, state) { - [{ - type: 'Node', - objs: d.nodes - }, { - type: 'Link', - objs: d.links - }].forEach(hexIndex); - state.forceGraph.graphData(d); - state.shadowGraph.graphData(d); - function hexIndex(_ref4) { - var type = _ref4.type, - objs = _ref4.objs; - objs.filter(function (d) { - if (!d.hasOwnProperty('__indexColor')) return true; - var cur = state.colorTracker.lookup(d.__indexColor); - return !cur || !cur.hasOwnProperty('d') || cur.d !== d; - }).forEach(function (d) { - // store object lookup color - d.__indexColor = state.colorTracker.register({ - type: type, - d: d - }); - }); - } - }, - triggerUpdate: false - }, - backgroundColor: { - onChange: function onChange(color, state) { - state.canvas && color && (state.canvas.style.background = color); - }, - triggerUpdate: false - }, - nodeLabel: { - "default": 'name', - triggerUpdate: false - }, - nodePointerAreaPaint: { - onChange: function onChange(paintFn, state) { - state.shadowGraph.nodeCanvasObject(!paintFn ? null : function (node, ctx, globalScale) { - return paintFn(node, node.__indexColor, ctx, globalScale); - }); - state.flushShadowCanvas && state.flushShadowCanvas(); - }, - triggerUpdate: false - }, - linkPointerAreaPaint: { - onChange: function onChange(paintFn, state) { - state.shadowGraph.linkCanvasObject(!paintFn ? null : function (link, ctx, globalScale) { - return paintFn(link, link.__indexColor, ctx, globalScale); - }); - state.flushShadowCanvas && state.flushShadowCanvas(); - }, - triggerUpdate: false - }, - linkLabel: { - "default": 'name', - triggerUpdate: false - }, - linkHoverPrecision: { - "default": 4, - triggerUpdate: false - }, - minZoom: { - "default": 0.01, - onChange: function onChange(minZoom, state) { - state.zoom.scaleExtent([minZoom, state.zoom.scaleExtent()[1]]); - }, - triggerUpdate: false - }, - maxZoom: { - "default": 1000, - onChange: function onChange(maxZoom, state) { - state.zoom.scaleExtent([state.zoom.scaleExtent()[0], maxZoom]); - }, - triggerUpdate: false - }, - enableNodeDrag: { - "default": true, - triggerUpdate: false - }, - enableZoomInteraction: { - "default": true, - triggerUpdate: false - }, - enablePanInteraction: { - "default": true, - triggerUpdate: false - }, - enableZoomPanInteraction: { - "default": true, - triggerUpdate: false - }, - // to be deprecated - enablePointerInteraction: { - "default": true, - onChange: function onChange(_, state) { - state.hoverObj = null; - }, - triggerUpdate: false - }, - autoPauseRedraw: { - "default": true, - triggerUpdate: false - }, - onNodeDrag: { - "default": function _default() {}, - triggerUpdate: false - }, - onNodeDragEnd: { - "default": function _default() {}, - triggerUpdate: false - }, - onNodeClick: { - triggerUpdate: false - }, - onNodeRightClick: { - triggerUpdate: false - }, - onNodeHover: { - triggerUpdate: false - }, - onLinkClick: { - triggerUpdate: false - }, - onLinkRightClick: { - triggerUpdate: false - }, - onLinkHover: { - triggerUpdate: false - }, - onBackgroundClick: { - triggerUpdate: false - }, - onBackgroundRightClick: { - triggerUpdate: false - }, - onZoom: { - triggerUpdate: false - }, - onZoomEnd: { - triggerUpdate: false - }, - onRenderFramePre: { - triggerUpdate: false - }, - onRenderFramePost: { - triggerUpdate: false - } - }, linkedProps), - aliases: { - // Prop names supported for backwards compatibility - stopAnimation: 'pauseAnimation' - }, - methods: _objectSpread2({ - graph2ScreenCoords: function graph2ScreenCoords(state, x, y) { - var t = transform(state.canvas); - return { - x: x * t.k + t.x, - y: y * t.k + t.y - }; - }, - screen2GraphCoords: function screen2GraphCoords(state, x, y) { - var t = transform(state.canvas); - return { - x: (x - t.x) / t.k, - y: (y - t.y) / t.k - }; - }, - centerAt: function centerAt(state, x, y, transitionDuration) { - if (!state.canvas) return null; // no canvas yet - - // setter - if (x !== undefined || y !== undefined) { - var finalPos = Object.assign({}, x !== undefined ? { - x: x - } : {}, y !== undefined ? { - y: y - } : {}); - if (!transitionDuration) { - // no animation - setCenter(finalPos); - } else { - new Tween(getCenter()).to(finalPos, transitionDuration).easing(Easing.Quadratic.Out).onUpdate(setCenter).start(); - } - return this; - } - - // getter - return getCenter(); - - // - - function getCenter() { - var t = transform(state.canvas); - return { - x: (state.width / 2 - t.x) / t.k, - y: (state.height / 2 - t.y) / t.k - }; - } - function setCenter(_ref5) { - var x = _ref5.x, - y = _ref5.y; - state.zoom.translateTo(state.zoom.__baseElem, x === undefined ? getCenter().x : x, y === undefined ? getCenter().y : y); - state.needsRedraw = true; - } - }, - zoom: function zoom(state, k, transitionDuration) { - if (!state.canvas) return null; // no canvas yet - - // setter - if (k !== undefined) { - if (!transitionDuration) { - // no animation - setZoom(k); - } else { - new Tween({ - k: getZoom() - }).to({ - k: k - }, transitionDuration).easing(Easing.Quadratic.Out).onUpdate(function (_ref6) { - var k = _ref6.k; - return setZoom(k); - }).start(); - } - return this; - } - - // getter - return getZoom(); - - // - - function getZoom() { - return transform(state.canvas).k; - } - function setZoom(k) { - state.zoom.scaleTo(state.zoom.__baseElem, k); - state.needsRedraw = true; - } - }, - zoomToFit: function zoomToFit(state) { - var transitionDuration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var padding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10; - for (var _len = arguments.length, bboxArgs = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { - bboxArgs[_key - 3] = arguments[_key]; - } - var bbox = this.getGraphBbox.apply(this, bboxArgs); - if (bbox) { - var center = { - x: (bbox.x[0] + bbox.x[1]) / 2, - y: (bbox.y[0] + bbox.y[1]) / 2 - }; - var zoomK = Math.max(1e-12, Math.min(1e12, (state.width - padding * 2) / (bbox.x[1] - bbox.x[0]), (state.height - padding * 2) / (bbox.y[1] - bbox.y[0]))); - this.centerAt(center.x, center.y, transitionDuration); - this.zoom(zoomK, transitionDuration); - } - return this; - }, - getGraphBbox: function getGraphBbox(state) { - var nodeFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () { - return true; - }; - var getVal = index$2(state.nodeVal); - var getR = function getR(node) { - return Math.sqrt(Math.max(0, getVal(node) || 1)) * state.nodeRelSize; - }; - var nodesPos = state.graphData.nodes.filter(nodeFilter).map(function (node) { - return { - x: node.x, - y: node.y, - r: getR(node) - }; - }); - return !nodesPos.length ? null : { - x: [min$1(nodesPos, function (node) { - return node.x - node.r; - }), max$1(nodesPos, function (node) { - return node.x + node.r; - })], - y: [min$1(nodesPos, function (node) { - return node.y - node.r; - }), max$1(nodesPos, function (node) { - return node.y + node.r; - })] - }; - }, - pauseAnimation: function pauseAnimation(state) { - if (state.animationFrameRequestId) { - cancelAnimationFrame(state.animationFrameRequestId); - state.animationFrameRequestId = null; - } - return this; - }, - resumeAnimation: function resumeAnimation(state) { - if (!state.animationFrameRequestId) { - this._animationCycle(); - } - return this; - }, - _destructor: function _destructor() { - this.pauseAnimation(); - this.graphData({ - nodes: [], - links: [] - }); - } - }, linkedMethods), - stateInit: function stateInit() { - return { - lastSetZoom: 1, - zoom: d3Zoom(), - forceGraph: new CanvasForceGraph(), - shadowGraph: new CanvasForceGraph().cooldownTicks(0).nodeColor('__indexColor').linkColor('__indexColor').isShadow(true), - colorTracker: new _default() // indexed objects for rgb lookup - }; - }, - - init: function init(domNode, state) { - var _this = this; - // Wipe DOM - domNode.innerHTML = ''; - - // Container anchor for canvas and tooltip - var container = document.createElement('div'); - container.classList.add('force-graph-container'); - container.style.position = 'relative'; - domNode.appendChild(container); - state.canvas = document.createElement('canvas'); - if (state.backgroundColor) state.canvas.style.background = state.backgroundColor; - container.appendChild(state.canvas); - state.shadowCanvas = document.createElement('canvas'); - - // Show shadow canvas - //state.shadowCanvas.style.position = 'absolute'; - //state.shadowCanvas.style.top = '0'; - //state.shadowCanvas.style.left = '0'; - //container.appendChild(state.shadowCanvas); - - var ctx = state.canvas.getContext('2d'); - var shadowCtx = state.shadowCanvas.getContext('2d', { - willReadFrequently: true - }); - var pointerPos = { - x: -1e12, - y: -1e12 - }; - var getObjUnderPointer = function getObjUnderPointer() { - var obj = null; - var pxScale = window.devicePixelRatio; - var px = pointerPos.x > 0 && pointerPos.y > 0 ? shadowCtx.getImageData(pointerPos.x * pxScale, pointerPos.y * pxScale, 1, 1) : null; - // Lookup object per pixel color - px && (obj = state.colorTracker.lookup(px.data)); - return obj; - }; - - // Setup node drag interaction - d3Select(state.canvas).call(d3Drag().subject(function () { - if (!state.enableNodeDrag) { - return null; - } - var obj = getObjUnderPointer(); - return obj && obj.type === 'Node' ? obj.d : null; // Only drag nodes - }).on('start', function (ev) { - var obj = ev.subject; - obj.__initialDragPos = { - x: obj.x, - y: obj.y, - fx: obj.fx, - fy: obj.fy - }; - - // keep engine running at low intensity throughout drag - if (!ev.active) { - obj.fx = obj.x; - obj.fy = obj.y; // Fix points - } - - // drag cursor - state.canvas.classList.add('grabbable'); - }).on('drag', function (ev) { - var obj = ev.subject; - var initPos = obj.__initialDragPos; - var dragPos = ev; - var k = transform(state.canvas).k; - var translate = { - x: initPos.x + (dragPos.x - initPos.x) / k - obj.x, - y: initPos.y + (dragPos.y - initPos.y) / k - obj.y - }; - - // Move fx/fy (and x/y) of nodes based on the scaled drag distance since the drag start - ['x', 'y'].forEach(function (c) { - return obj["f".concat(c)] = obj[c] = initPos[c] + (dragPos[c] - initPos[c]) / k; - }); - - // prevent freeze while dragging - state.forceGraph.d3AlphaTarget(0.3) // keep engine running at low intensity throughout drag - .resetCountdown(); // prevent freeze while dragging - - state.isPointerDragging = true; - obj.__dragged = true; - state.onNodeDrag(obj, translate); - }).on('end', function (ev) { - var obj = ev.subject; - var initPos = obj.__initialDragPos; - var translate = { - x: obj.x - initPos.x, - y: obj.y - initPos.y - }; - if (initPos.fx === undefined) { - obj.fx = undefined; - } - if (initPos.fy === undefined) { - obj.fy = undefined; - } - delete obj.__initialDragPos; - if (state.forceGraph.d3AlphaTarget()) { - state.forceGraph.d3AlphaTarget(0) // release engine low intensity - .resetCountdown(); // let the engine readjust after releasing fixed nodes - } - - // drag cursor - state.canvas.classList.remove('grabbable'); - state.isPointerDragging = false; - if (obj.__dragged) { - delete obj.__dragged; - state.onNodeDragEnd(obj, translate); - } - })); - - // Setup zoom / pan interaction - state.zoom(state.zoom.__baseElem = d3Select(state.canvas)); // Attach controlling elem for easy access - - state.zoom.__baseElem.on('dblclick.zoom', null); // Disable double-click to zoom - - state.zoom.filter(function (ev) { - return ( - // disable zoom interaction - !ev.button && state.enableZoomPanInteraction && (state.enableZoomInteraction || ev.type !== 'wheel') && (state.enablePanInteraction || ev.type === 'wheel') - ); - }).on('zoom', function (ev) { - var t = ev.transform; - [ctx, shadowCtx].forEach(function (c) { - resetTransform(c); - c.translate(t.x, t.y); - c.scale(t.k, t.k); - }); - state.onZoom && state.onZoom(_objectSpread2(_objectSpread2({}, t), _this.centerAt())); // report x,y coordinates relative to canvas center - state.needsRedraw = true; - }).on('end', function (ev) { - return state.onZoomEnd && state.onZoomEnd(_objectSpread2(_objectSpread2({}, ev.transform), _this.centerAt())); - }); - adjustCanvasSize(state); - state.forceGraph.onNeedsRedraw(function () { - return state.needsRedraw = true; - }).onFinishUpdate(function () { - // re-zoom, if still in default position (not user modified) - if (transform(state.canvas).k === state.lastSetZoom && state.graphData.nodes.length) { - state.zoom.scaleTo(state.zoom.__baseElem, state.lastSetZoom = ZOOM2NODES_FACTOR / Math.cbrt(state.graphData.nodes.length)); - state.needsRedraw = true; - } - }); - - // Setup tooltip - var toolTipElem = document.createElement('div'); - toolTipElem.classList.add('graph-tooltip'); - container.appendChild(toolTipElem); - - // Capture pointer coords on move or touchstart - ['pointermove', 'pointerdown'].forEach(function (evType) { - return container.addEventListener(evType, function (ev) { - if (evType === 'pointerdown') { - state.isPointerPressed = true; // track click state - state.pointerDownEvent = ev; - } - - // detect pointer drag on canvas pan - !state.isPointerDragging && ev.type === 'pointermove' && state.onBackgroundClick // only bother detecting drags this way if background clicks are enabled (so they don't trigger accidentally on canvas panning) - && (ev.pressure > 0 || state.isPointerPressed) // ev.pressure always 0 on Safari, so we use the isPointerPressed tracker - && (ev.pointerType !== 'touch' || ev.movementX === undefined || [ev.movementX, ev.movementY].some(function (m) { - return Math.abs(m) > 1; - })) // relax drag trigger sensitivity on touch events - && (state.isPointerDragging = true); - - // update the pointer pos - var offset = getOffset(container); - pointerPos.x = ev.pageX - offset.left; - pointerPos.y = ev.pageY - offset.top; - - // Move tooltip - toolTipElem.style.top = "".concat(pointerPos.y, "px"); - toolTipElem.style.left = "".concat(pointerPos.x, "px"); - - // adjust horizontal position to not exceed canvas boundaries - toolTipElem.style.transform = "translate(-".concat(pointerPos.x / state.width * 100, "%, ").concat( - // flip to above if near bottom - state.height - pointerPos.y < 100 ? 'calc(-100% - 8px)' : '21px', ")"); - - // - - function getOffset(el) { - var rect = el.getBoundingClientRect(), - scrollLeft = window.pageXOffset || document.documentElement.scrollLeft, - scrollTop = window.pageYOffset || document.documentElement.scrollTop; - return { - top: rect.top + scrollTop, - left: rect.left + scrollLeft - }; - } - }, { - passive: true - }); - }); - - // Handle click/touch events on nodes/links - container.addEventListener('pointerup', function (ev) { - state.isPointerPressed = false; - if (state.isPointerDragging) { - state.isPointerDragging = false; - return; // don't trigger click events after pointer drag (pan / node drag functionality) - } - - var cbEvents = [ev, state.pointerDownEvent]; - requestAnimationFrame(function () { - // trigger click events asynchronously, to allow hoverObj to be set (on frame) - if (ev.button === 0) { - // mouse left-click or touch - if (state.hoverObj) { - var fn = state["on".concat(state.hoverObj.type, "Click")]; - fn && fn.apply(void 0, [state.hoverObj.d].concat(cbEvents)); - } else { - state.onBackgroundClick && state.onBackgroundClick.apply(state, cbEvents); - } - } - if (ev.button === 2) { - // mouse right-click - if (state.hoverObj) { - var _fn = state["on".concat(state.hoverObj.type, "RightClick")]; - _fn && _fn.apply(void 0, [state.hoverObj.d].concat(cbEvents)); - } else { - state.onBackgroundRightClick && state.onBackgroundRightClick.apply(state, cbEvents); - } - } - }); - }, { - passive: true - }); - container.addEventListener('contextmenu', function (ev) { - if (!state.onBackgroundRightClick && !state.onNodeRightClick && !state.onLinkRightClick) return true; // default contextmenu behavior - ev.preventDefault(); - return false; - }); - state.forceGraph(ctx); - state.shadowGraph(shadowCtx); - - // - - var refreshShadowCanvas = throttle(function () { - // wipe canvas - clearCanvas(shadowCtx, state.width, state.height); - - // Adjust link hover area - state.shadowGraph.linkWidth(function (l) { - return index$2(state.linkWidth)(l) + state.linkHoverPrecision; - }); - - // redraw - var t = transform(state.canvas); - state.shadowGraph.globalScale(t.k).tickFrame(); - }, HOVER_CANVAS_THROTTLE_DELAY); - state.flushShadowCanvas = refreshShadowCanvas.flush; // hook to immediately invoke shadow canvas paint - - // Kick-off renderer - (this._animationCycle = function animate() { - // IIFE - var doRedraw = !state.autoPauseRedraw || !!state.needsRedraw || state.forceGraph.isEngineRunning() || state.graphData.links.some(function (d) { - return d.__photons && d.__photons.length; - }); - state.needsRedraw = false; - if (state.enablePointerInteraction) { - // Update tooltip and trigger onHover events - var obj = !state.isPointerDragging ? getObjUnderPointer() : null; // don't hover during drag - if (obj !== state.hoverObj) { - var prevObj = state.hoverObj; - var prevObjType = prevObj ? prevObj.type : null; - var objType = obj ? obj.type : null; - if (prevObjType && prevObjType !== objType) { - // Hover out - var fn = state["on".concat(prevObjType, "Hover")]; - fn && fn(null, prevObj.d); - } - if (objType) { - // Hover in - var _fn2 = state["on".concat(objType, "Hover")]; - _fn2 && _fn2(obj.d, prevObjType === objType ? prevObj.d : null); - } - var tooltipContent = obj ? index$2(state["".concat(obj.type.toLowerCase(), "Label")])(obj.d) || '' : ''; - toolTipElem.style.visibility = tooltipContent ? 'visible' : 'hidden'; - toolTipElem.innerHTML = tooltipContent; - - // set pointer if hovered object is clickable - state.canvas.classList[obj && state["on".concat(objType, "Click")] || !obj && state.onBackgroundClick ? 'add' : 'remove']('clickable'); - state.hoverObj = obj; - } - doRedraw && refreshShadowCanvas(); - } - if (doRedraw) { - // Wipe canvas - clearCanvas(ctx, state.width, state.height); - - // Frame cycle - var globalScale = transform(state.canvas).k; - state.onRenderFramePre && state.onRenderFramePre(ctx, globalScale); - state.forceGraph.globalScale(globalScale).tickFrame(); - state.onRenderFramePost && state.onRenderFramePost(ctx, globalScale); - } - update(); // update canvas animation tweens - - state.animationFrameRequestId = requestAnimationFrame(animate); - })(); - }, - update: function updateFn(state) {} - }); - - return forceGraph; - -})); -//# sourceMappingURL=force-graph.js.map diff --git a/hal-core/resources/web/js/lib/force-graph.min.js b/hal-core/resources/web/js/lib/force-graph.min.js deleted file mode 100644 index 42b88f34..00000000 --- a/hal-core/resources/web/js/lib/force-graph.min.js +++ /dev/null @@ -1,5 +0,0 @@ -// Version 1.43.4 force-graph - https://github.com/vasturiano/force-graph -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(t="undefined"!=typeof globalThis?globalThis:t||self).ForceGraph=n()}(this,(function(){"use strict";function n(t,n){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(t,n).enumerable}))),e.push.apply(e,r)}return e}function e(t){for(var e=1;et.length)&&(n=t.length);for(var e=0,r=new Array(n);e=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),f.hasOwnProperty(n)?{space:f[n],local:t}:t}function p(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===h&&n.documentElement.namespaceURI===h?n.createElement(t):n.createElementNS(e,t)}}function g(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function y(t){var n=d(t);return(n.local?g:p)(n)}function v(){}function _(t){return null==t?v:function(){return this.querySelector(t)}}function m(){return[]}function x(t){return null==t?m:function(){return this.querySelectorAll(t)}}function b(t){return function(){return function(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}(t.apply(this,arguments))}}function w(t){return function(){return this.matches(t)}}function k(t){return function(n){return n.matches(t)}}var M=Array.prototype.find;function z(){return this.firstElementChild}var A=Array.prototype.filter;function S(){return Array.from(this.children)}function C(t){return new Array(t.length)}function E(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function O(t,n,e,r,i,o){for(var a,u=0,s=n.length,l=o.length;un?1:t>=n?0:NaN}function R(t){return function(){this.removeAttribute(t)}}function D(t){return function(){this.removeAttributeNS(t.space,t.local)}}function I(t,n){return function(){this.setAttribute(t,n)}}function U(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function F(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function L(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function q(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function B(t){return function(){this.style.removeProperty(t)}}function $(t,n,e){return function(){this.style.setProperty(t,n,e)}}function H(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function V(t,n){return t.style.getPropertyValue(n)||q(t).getComputedStyle(t,null).getPropertyValue(n)}function X(t){return function(){delete this[t]}}function G(t,n){return function(){this[t]=n}}function Y(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function W(t){return t.trim().split(/^|\s+/)}function Z(t){return t.classList||new Q(t)}function Q(t){this._node=t,this._names=W(t.getAttribute("class")||"")}function K(t,n){for(var e=Z(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var xt=[null];function bt(t,n){this._groups=t,this._parents=n}function wt(){return new bt([[document.documentElement]],xt)}function kt(t){return"string"==typeof t?new bt([[document.querySelector(t)]],[document.documentElement]):new bt([[t]],xt)}function Mt(t,n){if(t=function(t){let n;for(;n=t.sourceEvent;)t=n;return t}(t),void 0===n&&(n=t.currentTarget),n){var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();return r.x=t.clientX,r.y=t.clientY,[(r=r.matrixTransform(n.getScreenCTM().inverse())).x,r.y]}if(n.getBoundingClientRect){var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}}return[t.pageX,t.pageY]}bt.prototype=wt.prototype={constructor:bt,select:function(t){"function"!=typeof t&&(t=_(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(_=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=T);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?B:"function"==typeof n?H:$)(t,n,null==e?"":e)):V(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?X:"function"==typeof n?Y:G)(t,n)):this.node()[t]},classed:function(t,n){var e=W(t+"");if(arguments.length<2){for(var r=Z(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}(t+""),a=o.length;if(!(arguments.length<2)){for(u=n?yt:gt,r=0;r{}};function At(){for(var t,n=0,e=arguments.length,r={};n=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))),a=-1,u=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++a0)for(var e,r,i=new Array(e),o=0;o()=>t;function It(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:s,dy:l,dispatch:c}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:s,enumerable:!0,configurable:!0},dy:{value:l,enumerable:!0,configurable:!0},_:{value:c}})}function Ut(t){return!t.ctrlKey&&!t.button}function Ft(){return this.parentNode}function Lt(t,n){return null==n?{x:t.x,y:t.y}:n}function qt(){return navigator.maxTouchPoints||"ontouchstart"in this}function Bt(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function $t(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function Ht(){}It.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Vt=.7,Xt=1/Vt,Gt="\\s*([+-]?\\d+)\\s*",Yt="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",Wt="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Zt=/^#([0-9a-f]{3,8})$/,Qt=new RegExp(`^rgb\\(${Gt},${Gt},${Gt}\\)$`),Kt=new RegExp(`^rgb\\(${Wt},${Wt},${Wt}\\)$`),Jt=new RegExp(`^rgba\\(${Gt},${Gt},${Gt},${Yt}\\)$`),tn=new RegExp(`^rgba\\(${Wt},${Wt},${Wt},${Yt}\\)$`),nn=new RegExp(`^hsl\\(${Yt},${Wt},${Wt}\\)$`),en=new RegExp(`^hsla\\(${Yt},${Wt},${Wt},${Yt}\\)$`),rn={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function on(){return this.rgb().formatHex()}function an(){return this.rgb().formatRgb()}function un(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=Zt.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?sn(n):3===e?new hn(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?ln(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?ln(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=Qt.exec(t))?new hn(n[1],n[2],n[3],1):(n=Kt.exec(t))?new hn(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=Jt.exec(t))?ln(n[1],n[2],n[3],n[4]):(n=tn.exec(t))?ln(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=nn.exec(t))?vn(n[1],n[2]/100,n[3]/100,1):(n=en.exec(t))?vn(n[1],n[2]/100,n[3]/100,n[4]):rn.hasOwnProperty(t)?sn(rn[t]):"transparent"===t?new hn(NaN,NaN,NaN,0):null}function sn(t){return new hn(t>>16&255,t>>8&255,255&t,1)}function ln(t,n,e,r){return r<=0&&(t=n=e=NaN),new hn(t,n,e,r)}function cn(t,n,e,r){return 1===arguments.length?((i=t)instanceof Ht||(i=un(i)),i?new hn((i=i.rgb()).r,i.g,i.b,i.opacity):new hn):new hn(t,n,e,null==r?1:r);var i}function hn(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function fn(){return`#${yn(this.r)}${yn(this.g)}${yn(this.b)}`}function dn(){const t=pn(this.opacity);return`${1===t?"rgb(":"rgba("}${gn(this.r)}, ${gn(this.g)}, ${gn(this.b)}${1===t?")":`, ${t})`}`}function pn(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function gn(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function yn(t){return((t=gn(t))<16?"0":"")+t.toString(16)}function vn(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new mn(t,n,e,r)}function _n(t){if(t instanceof mn)return new mn(t.h,t.s,t.l,t.opacity);if(t instanceof Ht||(t=un(t)),!t)return new mn;if(t instanceof mn)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,s=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&s<1?0:a,new mn(a,u,s,t.opacity)}function mn(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function xn(t){return(t=(t||0)%360)<0?t+360:t}function bn(t){return Math.max(0,Math.min(1,t||0))}function wn(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Bt(Ht,un,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:on,formatHex:on,formatHex8:function(){return this.rgb().formatHex8()},formatHsl:function(){return _n(this).formatHsl()},formatRgb:an,toString:an}),Bt(hn,cn,$t(Ht,{brighter(t){return t=null==t?Xt:Math.pow(Xt,t),new hn(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=null==t?Vt:Math.pow(Vt,t),new hn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new hn(gn(this.r),gn(this.g),gn(this.b),pn(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:fn,formatHex:fn,formatHex8:function(){return`#${yn(this.r)}${yn(this.g)}${yn(this.b)}${yn(255*(isNaN(this.opacity)?1:this.opacity))}`},formatRgb:dn,toString:dn})),Bt(mn,(function(t,n,e,r){return 1===arguments.length?_n(t):new mn(t,n,e,null==r?1:r)}),$t(Ht,{brighter(t){return t=null==t?Xt:Math.pow(Xt,t),new mn(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=null==t?Vt:Math.pow(Vt,t),new mn(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new hn(wn(t>=240?t-240:t+120,i,r),wn(t,i,r),wn(t<120?t+240:t-120,i,r),this.opacity)},clamp(){return new mn(xn(this.h),bn(this.s),bn(this.l),pn(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=pn(this.opacity);return`${1===t?"hsl(":"hsla("}${xn(this.h)}, ${100*bn(this.s)}%, ${100*bn(this.l)}%${1===t?")":`, ${t})`}`}}));var kn=t=>()=>t;function Mn(t){return 1==(t=+t)?zn:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):kn(isNaN(n)?e:n)}}function zn(t,n){var e=n-t;return e?function(t,n){return function(e){return t+e*n}}(t,e):kn(isNaN(t)?n:t)}var An=function t(n){var e=Mn(n);function r(t,n){var r=e((t=cn(t)).r,(n=cn(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=zn(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function Sn(t,n){return t=+t,n=+n,function(e){return t*(1-e)+n*e}}var Cn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,En=new RegExp(Cn.source,"g");function On(t,n){var e,r,i,o=Cn.lastIndex=En.lastIndex=0,a=-1,u=[],s=[];for(t+="",n+="";(e=Cn.exec(t))&&(r=En.exec(n));)(i=r.index)>o&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,s.push({i:a,x:Sn(e,r)})),o=En.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Sn(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,s),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Sn(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,s),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:Sn(t,e)},{i:u-2,x:Sn(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,s),o=a=null,function(t){for(var n,e=-1,r=s.length;++e=0&&n._call.call(void 0,t),n=n._next;--Bn}()}finally{Bn=0,function(){var t,n,e=Fn,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Fn=n);Ln=t,re(r)}(),Gn=0}}function ee(){var t=Wn.now(),n=t-Xn;n>Vn&&(Yn-=n,Xn=t)}function re(t){Bn||($n&&($n=clearTimeout($n)),t-Gn>24?(t<1/0&&($n=setTimeout(ne,t-Wn.now()-Yn)),Hn&&(Hn=clearInterval(Hn))):(Hn||(Xn=Wn.now(),Hn=setInterval(ee,Vn)),Bn=1,Zn(ne)))}function ie(t,n,e){var r=new Jn;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}Jn.prototype=te.prototype={constructor:Jn,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?Qn():+e)+(null==n?0:+n),this._next||Ln===this||(Ln?Ln._next=this:Fn=this,Ln=this),this._call=t,this._time=e,re()},stop:function(){this._call&&(this._call=null,this._time=1/0,re())}};var oe=At("start","end","cancel","interrupt"),ae=[],ue=0,se=1,le=2,ce=3,he=4,fe=5,de=6;function pe(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=se,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var l,c,h,f;if(e.state!==se)return s();for(l in i)if((f=i[l]).name===e.name){if(f.state===ce)return ie(a);f.state===he?(f.state=de,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[l]):+lue)throw new Error("too late; already scheduled");return e}function ye(t,n){var e=ve(t,n);if(e.state>ce)throw new Error("too late; already running");return e}function ve(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function _e(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>le&&e.state=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?ge:ye;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}(e,t,n))},attr:function(t,n){var e=d(t),r="transform"===e?In:we;return this.attrTween(t,"function"==typeof n?(e.local?Ce:Se)(e,r,be(this,"attr."+t,n)):null==n?(e.local?Me:ke)(e):(e.local?Ae:ze)(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=d(t);return this.tween(e,(r.local?Ee:Oe)(r,n))},style:function(t,n,e){var r="transform"==(t+="")?Dn:we;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=V(this,t),a=(this.style.removeProperty(t),V(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on("end.style."+t,De(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=V(this,t),u=e(this),s=u+"";return null==u&&(this.style.removeProperty(t),s=u=V(this,t)),a===s?null:a===r&&s===i?o:(i=s,o=n(r=a,u))}}(t,r,be(this,"style."+t,n))).each(function(t,n){var e,r,i,o,a="style."+n,u="end."+a;return function(){var s=ye(this,t),l=s.on,c=null==s.value[a]?o||(o=De(n)):void 0;l===e&&i===c||(r=(e=l).copy()).on(u,i=c),s.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var a=V(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(be(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var n="text";if(arguments.length<1)return(n=this.tween(n))&&n._value;if(null==t)return this.tween(n,null);if("function"!=typeof t)throw new Error;return this.tween(n,function(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&function(t){return function(n){this.textContent=t.call(this,n)}}(r)),n}return r._value=t,r}(t))},remove:function(){return this.on("end.remove",function(t){return function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}}(this._id))},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=ve(this.node(),e).tween,o=0,a=i.length;o()=>t;function He(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function Ve(t,n,e){this.k=t,this.x=n,this.y=e}Ve.prototype={constructor:Ve,scale:function(t){return 1===t?this:new Ve(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new Ve(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Xe=new Ve(1,0,0);function Ge(t){for(;!t.__zoom;)if(!(t=t.parentNode))return Xe;return t.__zoom}function Ye(t){t.stopImmediatePropagation()}function We(t){t.preventDefault(),t.stopImmediatePropagation()}function Ze(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function Qe(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function Ke(){return this.__zoom||Xe}function Je(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function tr(){return navigator.maxTouchPoints||"ontouchstart"in this}function nr(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}function er(){var t,n,e,r=Ze,i=Qe,o=nr,a=Je,u=tr,s=[0,1/0],l=[[-1/0,-1/0],[1/0,1/0]],c=250,h=qn,f=At("start","zoom","end"),d=500,p=150,g=0,y=10;function v(t){t.property("__zoom",Ke).on("wheel.zoom",M,{passive:!1}).on("mousedown.zoom",z).on("dblclick.zoom",A).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",C).on("touchend.zoom touchcancel.zoom",E).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function _(t,n){return(n=Math.max(s[0],Math.min(s[1],n)))===t.k?t:new Ve(n,t.x,t.y)}function m(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Ve(t.k,r,i)}function x(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function b(t,n,e,r){t.on("start.zoom",(function(){w(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){w(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=w(t,o).event(r),u=i.apply(t,o),s=null==e?x(u):"function"==typeof e?e.apply(t,o):e,l=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),c=t.__zoom,f="function"==typeof n?n.apply(t,o):n,d=h(c.invert(s).concat(l/c.k),f.invert(s).concat(l/f.k));return function(t){if(1===t)t=f;else{var n=d(t),e=l/n[2];t=new Ve(e,s[0]-n[0]*e,s[1]-n[1]*e)}a.zoom(null,t)}}))}function w(t,n,e){return!e&&t.__zooming||new k(t,n)}function k(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function M(t,...n){if(r.apply(this,arguments)){var e=w(this,n).event(t),i=this.__zoom,u=Math.max(s[0],Math.min(s[1],i.k*Math.pow(2,a.apply(this,arguments)))),c=Mt(t);if(e.wheel)e.mouse[0][0]===c[0]&&e.mouse[0][1]===c[1]||(e.mouse[1]=i.invert(e.mouse[0]=c)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[c,i.invert(c)],_e(this),e.start()}We(t),e.wheel=setTimeout((function(){e.wheel=null,e.end()}),p),e.zoom("mouse",o(m(_(i,u),e.mouse[0],e.mouse[1]),e.extent,l))}}function z(t,...n){if(!e&&r.apply(this,arguments)){var i=t.currentTarget,a=w(this,n,!0).event(t),u=kt(t.view).on("mousemove.zoom",(function(t){if(We(t),!a.moved){var n=t.clientX-c,e=t.clientY-h;a.moved=n*n+e*e>g}a.event(t).zoom("mouse",o(m(a.that.__zoom,a.mouse[0]=Mt(t,i),a.mouse[1]),a.extent,l))}),!0).on("mouseup.zoom",(function(t){u.on("mousemove.zoom mouseup.zoom",null),Rt(t.view,a.moved),We(t),a.event(t).end()}),!0),s=Mt(t,i),c=t.clientX,h=t.clientY;Tt(t.view),Ye(t),a.mouse=[s,this.__zoom.invert(s)],_e(this),a.start()}}function A(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=Mt(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),s=e.k*(t.shiftKey?.5:2),h=o(m(_(e,s),a,u),i.apply(this,n),l);We(t),c>0?kt(this).transition().duration(c).call(b,h,a,t):kt(this).call(v.transform,h,a,t)}}function S(e,...i){if(r.apply(this,arguments)){var o,a,u,s,l=e.touches,c=l.length,h=w(this,i,e.changedTouches.length===c).event(e);for(Ye(e),a=0;a=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function ur(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}var sr="object"==typeof global&&global&&global.Object===Object&&global,lr="object"==typeof self&&self&&self.Object===Object&&self,cr=sr||lr||Function("return this")(),hr=cr.Symbol,fr=Object.prototype,dr=fr.hasOwnProperty,pr=fr.toString,gr=hr?hr.toStringTag:void 0;var yr=Object.prototype.toString;var vr="[object Null]",_r="[object Undefined]",mr=hr?hr.toStringTag:void 0;function xr(t){return null==t?void 0===t?_r:vr:mr&&mr in Object(t)?function(t){var n=dr.call(t,gr),e=t[gr];try{t[gr]=void 0;var r=!0}catch(t){}var i=pr.call(t);return r&&(n?t[gr]=e:delete t[gr]),i}(t):function(t){return yr.call(t)}(t)}var br="[object Symbol]";var wr=/\s/;var kr=/^\s+/;function Mr(t){return t?t.slice(0,function(t){for(var n=t.length;n--&&wr.test(t.charAt(n)););return n}(t)+1).replace(kr,""):t}function zr(t){var n=typeof t;return null!=t&&("object"==n||"function"==n)}var Ar=NaN,Sr=/^[-+]0x[0-9a-f]+$/i,Cr=/^0b[01]+$/i,Er=/^0o[0-7]+$/i,Or=parseInt;function Nr(t){if("number"==typeof t)return t;if(function(t){return"symbol"==typeof t||function(t){return null!=t&&"object"==typeof t}(t)&&xr(t)==br}(t))return Ar;if(zr(t)){var n="function"==typeof t.valueOf?t.valueOf():t;t=zr(n)?n+"":n}if("string"!=typeof t)return 0===t?t:+t;t=Mr(t);var e=Cr.test(t);return e||Er.test(t)?Or(t.slice(2),e?2:8):Sr.test(t)?Ar:+t}var Pr=function(){return cr.Date.now()},jr="Expected a function",Tr=Math.max,Rr=Math.min;function Dr(t,n,e){var r,i,o,a,u,s,l=0,c=!1,h=!1,f=!0;if("function"!=typeof t)throw new TypeError(jr);function d(n){var e=r,o=i;return r=i=void 0,l=n,a=t.apply(o,e)}function p(t){var e=t-s;return void 0===s||e>=n||e<0||h&&t-l>=o}function g(){var t=Pr();if(p(t))return y(t);u=setTimeout(g,function(t){var e=n-(t-s);return h?Rr(e,o-(t-l)):e}(t))}function y(t){return u=void 0,f&&r?d(t):(r=i=void 0,a)}function v(){var t=Pr(),e=p(t);if(r=arguments,i=this,s=t,e){if(void 0===u)return function(t){return l=t,u=setTimeout(g,n),c?d(t):a}(s);if(h)return clearTimeout(u),u=setTimeout(g,n),d(s)}return void 0===u&&(u=setTimeout(g,n)),a}return n=Nr(n)||0,zr(e)&&(c=!!e.leading,o=(h="maxWait"in e)?Tr(Nr(e.maxWait)||0,n):o,f="trailing"in e?!!e.trailing:f),v.cancel=function(){void 0!==u&&clearTimeout(u),l=0,r=s=i=u=void 0},v.flush=function(){return void 0===u?a:y(Pr())},v}var Ir=Object.freeze({Linear:Object.freeze({None:function(t){return t},In:function(t){return this.None(t)},Out:function(t){return this.None(t)},InOut:function(t){return this.None(t)}}),Quadratic:Object.freeze({In:function(t){return t*t},Out:function(t){return t*(2-t)},InOut:function(t){return(t*=2)<1?.5*t*t:-.5*(--t*(t-2)-1)}}),Cubic:Object.freeze({In:function(t){return t*t*t},Out:function(t){return--t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t:.5*((t-=2)*t*t+2)}}),Quartic:Object.freeze({In:function(t){return t*t*t*t},Out:function(t){return 1- --t*t*t*t},InOut:function(t){return(t*=2)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)}}),Quintic:Object.freeze({In:function(t){return t*t*t*t*t},Out:function(t){return--t*t*t*t*t+1},InOut:function(t){return(t*=2)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)}}),Sinusoidal:Object.freeze({In:function(t){return 1-Math.sin((1-t)*Math.PI/2)},Out:function(t){return Math.sin(t*Math.PI/2)},InOut:function(t){return.5*(1-Math.sin(Math.PI*(.5-t)))}}),Exponential:Object.freeze({In:function(t){return 0===t?0:Math.pow(1024,t-1)},Out:function(t){return 1===t?1:1-Math.pow(2,-10*t)},InOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?.5*Math.pow(1024,t-1):.5*(2-Math.pow(2,-10*(t-1)))}}),Circular:Object.freeze({In:function(t){return 1-Math.sqrt(1-t*t)},Out:function(t){return Math.sqrt(1- --t*t)},InOut:function(t){return(t*=2)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)}}),Elastic:Object.freeze({In:function(t){return 0===t?0:1===t?1:-Math.pow(2,10*(t-1))*Math.sin(5*(t-1.1)*Math.PI)},Out:function(t){return 0===t?0:1===t?1:Math.pow(2,-10*t)*Math.sin(5*(t-.1)*Math.PI)+1},InOut:function(t){return 0===t?0:1===t?1:(t*=2)<1?-.5*Math.pow(2,10*(t-1))*Math.sin(5*(t-1.1)*Math.PI):.5*Math.pow(2,-10*(t-1))*Math.sin(5*(t-1.1)*Math.PI)+1}}),Back:Object.freeze({In:function(t){var n=1.70158;return 1===t?1:t*t*((n+1)*t-n)},Out:function(t){var n=1.70158;return 0===t?0:--t*t*((n+1)*t+n)+1},InOut:function(t){var n=2.5949095;return(t*=2)<1?t*t*((n+1)*t-n)*.5:.5*((t-=2)*t*((n+1)*t+n)+2)}}),Bounce:Object.freeze({In:function(t){return 1-Ir.Bounce.Out(1-t)},Out:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},InOut:function(t){return t<.5?.5*Ir.Bounce.In(2*t):.5*Ir.Bounce.Out(2*t-1)+.5}}),generatePow:function(t){return void 0===t&&(t=4),t=(t=t1e4?1e4:t,{In:function(n){return Math.pow(n,t)},Out:function(n){return 1-Math.pow(1-n,t)},InOut:function(n){return n<.5?Math.pow(2*n,t)/2:(1-Math.pow(2-2*n,t))/2+.5}}}}),Ur=function(){return performance.now()},Fr=function(){function t(){this._tweens={},this._tweensAddedDuringUpdate={}}return t.prototype.getAll=function(){var t=this;return Object.keys(this._tweens).map((function(n){return t._tweens[n]}))},t.prototype.removeAll=function(){this._tweens={}},t.prototype.add=function(t){this._tweens[t.getId()]=t,this._tweensAddedDuringUpdate[t.getId()]=t},t.prototype.remove=function(t){delete this._tweens[t.getId()],delete this._tweensAddedDuringUpdate[t.getId()]},t.prototype.update=function(t,n){void 0===t&&(t=Ur()),void 0===n&&(n=!1);var e=Object.keys(this._tweens);if(0===e.length)return!1;for(;e.length>0;){this._tweensAddedDuringUpdate={};for(var r=0;r1?o(t[e],t[e-1],e-r):o(t[i],t[i+1>e?e:i+1],r-i)},Bezier:function(t,n){for(var e=0,r=t.length-1,i=Math.pow,o=Lr.Utils.Bernstein,a=0;a<=r;a++)e+=i(1-n,r-a)*i(n,a)*t[a]*o(r,a);return e},CatmullRom:function(t,n){var e=t.length-1,r=e*n,i=Math.floor(r),o=Lr.Utils.CatmullRom;return t[0]===t[e]?(n<0&&(i=Math.floor(r=e*(1+n))),o(t[(i-1+e)%e],t[i],t[(i+1)%e],t[(i+2)%e],r-i)):n<0?t[0]-(o(t[0],t[0],t[1],t[1],-r)-t[0]):n>1?t[e]-(o(t[e],t[e],t[e-1],t[e-1],r-e)-t[e]):o(t[i?i-1:0],t[i],t[e1;r--)e*=r;return t[n]=e,e}}(),CatmullRom:function(t,n,e,r,i){var o=.5*(e-t),a=.5*(r-n),u=i*i;return(2*n-2*e+o+a)*(i*u)+(-3*n+3*e-2*o-a)*u+o*i+n}}},qr=function(){function t(){}return t.nextId=function(){return t._nextId++},t._nextId=0,t}(),Br=new Fr,$r=function(){function t(t,n){void 0===n&&(n=Br),this._object=t,this._group=n,this._isPaused=!1,this._pauseStart=0,this._valuesStart={},this._valuesEnd={},this._valuesStartRepeat={},this._duration=1e3,this._isDynamic=!1,this._initialRepeat=0,this._repeat=0,this._yoyo=!1,this._isPlaying=!1,this._reversed=!1,this._delayTime=0,this._startTime=0,this._easingFunction=Ir.Linear.None,this._interpolationFunction=Lr.Linear,this._chainedTweens=[],this._onStartCallbackFired=!1,this._onEveryStartCallbackFired=!1,this._id=qr.nextId(),this._isChainStopped=!1,this._propertiesAreSetUp=!1,this._goToEnd=!1}return t.prototype.getId=function(){return this._id},t.prototype.isPlaying=function(){return this._isPlaying},t.prototype.isPaused=function(){return this._isPaused},t.prototype.to=function(t,n){if(void 0===n&&(n=1e3),this._isPlaying)throw new Error("Can not call Tween.to() while Tween is already started or paused. Stop the Tween first.");return this._valuesEnd=t,this._propertiesAreSetUp=!1,this._duration=n,this},t.prototype.duration=function(t){return void 0===t&&(t=1e3),this._duration=t,this},t.prototype.dynamic=function(t){return void 0===t&&(t=!1),this._isDynamic=t,this},t.prototype.start=function(t,n){if(void 0===t&&(t=Ur()),void 0===n&&(n=!1),this._isPlaying)return this;if(this._group&&this._group.add(this),this._repeat=this._initialRepeat,this._reversed)for(var e in this._reversed=!1,this._valuesStartRepeat)this._swapEndStartRepeatValues(e),this._valuesStart[e]=this._valuesStartRepeat[e];if(this._isPlaying=!0,this._isPaused=!1,this._onStartCallbackFired=!1,this._onEveryStartCallbackFired=!1,this._isChainStopped=!1,this._startTime=t,this._startTime+=this._delayTime,!this._propertiesAreSetUp||n){if(this._propertiesAreSetUp=!0,!this._isDynamic){var r={};for(var i in this._valuesEnd)r[i]=this._valuesEnd[i];this._valuesEnd=r}this._setupProperties(this._object,this._valuesStart,this._valuesEnd,this._valuesStartRepeat,n)}return this},t.prototype.startFromCurrentValues=function(t){return this.start(t,!0)},t.prototype._setupProperties=function(t,n,e,r,i){for(var o in e){var a=t[o],u=Array.isArray(a),s=u?"array":typeof a,l=!u&&Array.isArray(e[o]);if("undefined"!==s&&"function"!==s){if(l){if(0===(y=e[o]).length)continue;for(var c=[a],h=0,f=y.length;hi)return!1;n&&this.start(t,!0)}if(this._goToEnd=!1,t1?1:r;var o=this._easingFunction(r);if(this._updateProperties(this._object,this._valuesStart,this._valuesEnd,o),this._onUpdateCallback&&this._onUpdateCallback(this._object,r),1===r){if(this._repeat>0){for(e in isFinite(this._repeat)&&this._repeat--,this._valuesStartRepeat)this._yoyo||"string"!=typeof this._valuesEnd[e]||(this._valuesStartRepeat[e]=this._valuesStartRepeat[e]+parseFloat(this._valuesEnd[e])),this._yoyo&&this._swapEndStartRepeatValues(e),this._valuesStart[e]=this._valuesStartRepeat[e];return this._yoyo&&(this._reversed=!this._reversed),void 0!==this._repeatDelayTime?this._startTime=t+this._repeatDelayTime:this._startTime=t+this._delayTime,this._onRepeatCallback&&this._onRepeatCallback(this._object),this._onEveryStartCallbackFired=!1,!0}this._onCompleteCallback&&this._onCompleteCallback(this._object);for(var a=0,u=this._chainedTweens.length;at.length)&&(n=t.length);for(var e=0,r=new Array(n);e0&&void 0!==arguments[0]?arguments[0]:{},n=Object.assign({},e instanceof Function?e(t):e,{initialised:!1}),r={};function i(n){return o(n,t),u(),i}var o=function(t,e){c.call(i,t,n,e),n.initialised=!0},u=Dr((function(){n.initialised&&(f.call(i,n,r),r={})}),1);return d.forEach((function(t){i[t.name]=function(t){var e=t.name,o=t.triggerUpdate,a=void 0!==o&&o,s=t.onChange,l=void 0===s?function(t,n){}:s,c=t.defaultVal,h=void 0===c?null:c;return function(t){var o=n[e];if(!arguments.length)return o;var s=void 0===t?h:t;return n[e]=s,l.call(i,s,n,o),!r.hasOwnProperty(e)&&(r[e]=o),a&&u(),i}}(t)})),Object.keys(a).forEach((function(t){i[t]=function(){for(var e,r=arguments.length,o=new Array(r),u=0;u1&&(e-=1),e<1/6?t+6*(n-t)*e:e<.5?n:e<2/3?t+(n-t)*(2/3-e)*6:t}if(t=wi(t,360),n=wi(n,100),e=wi(e,100),0===n)r=i=o=e;else{var u=e<.5?e*(1+n):e+n-e*n,s=2*e-u;r=a(s,u,t+1/3),i=a(s,u,t),o=a(s,u,t-1/3)}return{r:255*r,g:255*i,b:255*o}}(t.h,r,o),a=!0,u="hsl"),t.hasOwnProperty("a")&&(e=t.a));var s,l,c;return e=bi(e),{ok:a,format:t.format||u,r:Math.min(255,Math.max(n.r,0)),g:Math.min(255,Math.max(n.g,0)),b:Math.min(255,Math.max(n.b,0)),a:e}}(t);this._originalInput=t,this._r=e.r,this._g=e.g,this._b=e.b,this._a=e.a,this._roundA=Math.round(100*this._a)/100,this._format=n.format||e.format,this._gradientType=n.gradientType,this._r<1&&(this._r=Math.round(this._r)),this._g<1&&(this._g=Math.round(this._g)),this._b<1&&(this._b=Math.round(this._b)),this._ok=e.ok}function ri(t,n,e){t=wi(t,255),n=wi(n,255),e=wi(e,255);var r,i,o=Math.max(t,n,e),a=Math.min(t,n,e),u=(o+a)/2;if(o==a)r=i=0;else{var s=o-a;switch(i=u>.5?s/(2-o-a):s/(o+a),o){case t:r=(n-e)/s+(n>1)+720)%360;--n;)r.h=(r.h+i)%360,o.push(ei(r));return o}function _i(t,n){n=n||6;for(var e=ei(t).toHsv(),r=e.h,i=e.s,o=e.v,a=[],u=1/n;n--;)a.push(ei({h:r,s:i,v:o})),o=(o+u)%1;return a}ei.prototype={isDark:function(){return this.getBrightness()<128},isLight:function(){return!this.isDark()},isValid:function(){return this._ok},getOriginalInput:function(){return this._originalInput},getFormat:function(){return this._format},getAlpha:function(){return this._a},getBrightness:function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},getLuminance:function(){var t,n,e,r=this.toRgb();return t=r.r/255,n=r.g/255,e=r.b/255,.2126*(t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4))+.7152*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))+.0722*(e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4))},setAlpha:function(t){return this._a=bi(t),this._roundA=Math.round(100*this._a)/100,this},toHsv:function(){var t=ii(this._r,this._g,this._b);return{h:360*t.h,s:t.s,v:t.v,a:this._a}},toHsvString:function(){var t=ii(this._r,this._g,this._b),n=Math.round(360*t.h),e=Math.round(100*t.s),r=Math.round(100*t.v);return 1==this._a?"hsv("+n+", "+e+"%, "+r+"%)":"hsva("+n+", "+e+"%, "+r+"%, "+this._roundA+")"},toHsl:function(){var t=ri(this._r,this._g,this._b);return{h:360*t.h,s:t.s,l:t.l,a:this._a}},toHslString:function(){var t=ri(this._r,this._g,this._b),n=Math.round(360*t.h),e=Math.round(100*t.s),r=Math.round(100*t.l);return 1==this._a?"hsl("+n+", "+e+"%, "+r+"%)":"hsla("+n+", "+e+"%, "+r+"%, "+this._roundA+")"},toHex:function(t){return oi(this._r,this._g,this._b,t)},toHexString:function(t){return"#"+this.toHex(t)},toHex8:function(t){return function(t,n,e,r,i){var o=[zi(Math.round(t).toString(16)),zi(Math.round(n).toString(16)),zi(Math.round(e).toString(16)),zi(Si(r))];if(i&&o[0].charAt(0)==o[0].charAt(1)&&o[1].charAt(0)==o[1].charAt(1)&&o[2].charAt(0)==o[2].charAt(1)&&o[3].charAt(0)==o[3].charAt(1))return o[0].charAt(0)+o[1].charAt(0)+o[2].charAt(0)+o[3].charAt(0);return o.join("")}(this._r,this._g,this._b,this._a,t)},toHex8String:function(t){return"#"+this.toHex8(t)},toRgb:function(){return{r:Math.round(this._r),g:Math.round(this._g),b:Math.round(this._b),a:this._a}},toRgbString:function(){return 1==this._a?"rgb("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+")":"rgba("+Math.round(this._r)+", "+Math.round(this._g)+", "+Math.round(this._b)+", "+this._roundA+")"},toPercentageRgb:function(){return{r:Math.round(100*wi(this._r,255))+"%",g:Math.round(100*wi(this._g,255))+"%",b:Math.round(100*wi(this._b,255))+"%",a:this._a}},toPercentageRgbString:function(){return 1==this._a?"rgb("+Math.round(100*wi(this._r,255))+"%, "+Math.round(100*wi(this._g,255))+"%, "+Math.round(100*wi(this._b,255))+"%)":"rgba("+Math.round(100*wi(this._r,255))+"%, "+Math.round(100*wi(this._g,255))+"%, "+Math.round(100*wi(this._b,255))+"%, "+this._roundA+")"},toName:function(){return 0===this._a?"transparent":!(this._a<1)&&(xi[oi(this._r,this._g,this._b,!0)]||!1)},toFilter:function(t){var n="#"+ai(this._r,this._g,this._b,this._a),e=n,r=this._gradientType?"GradientType = 1, ":"";if(t){var i=ei(t);e="#"+ai(i._r,i._g,i._b,i._a)}return"progid:DXImageTransform.Microsoft.gradient("+r+"startColorstr="+n+",endColorstr="+e+")"},toString:function(t){var n=!!t;t=t||this._format;var e=!1,r=this._a<1&&this._a>=0;return n||!r||"hex"!==t&&"hex6"!==t&&"hex3"!==t&&"hex4"!==t&&"hex8"!==t&&"name"!==t?("rgb"===t&&(e=this.toRgbString()),"prgb"===t&&(e=this.toPercentageRgbString()),"hex"!==t&&"hex6"!==t||(e=this.toHexString()),"hex3"===t&&(e=this.toHexString(!0)),"hex4"===t&&(e=this.toHex8String(!0)),"hex8"===t&&(e=this.toHex8String()),"name"===t&&(e=this.toName()),"hsl"===t&&(e=this.toHslString()),"hsv"===t&&(e=this.toHsvString()),e||this.toHexString()):"name"===t&&0===this._a?this.toName():this.toRgbString()},clone:function(){return ei(this.toString())},_applyModification:function(t,n){var e=t.apply(null,[this].concat([].slice.call(n)));return this._r=e._r,this._g=e._g,this._b=e._b,this.setAlpha(e._a),this},lighten:function(){return this._applyModification(ci,arguments)},brighten:function(){return this._applyModification(hi,arguments)},darken:function(){return this._applyModification(fi,arguments)},desaturate:function(){return this._applyModification(ui,arguments)},saturate:function(){return this._applyModification(si,arguments)},greyscale:function(){return this._applyModification(li,arguments)},spin:function(){return this._applyModification(di,arguments)},_applyCombination:function(t,n){return t.apply(null,[this].concat([].slice.call(n)))},analogous:function(){return this._applyCombination(vi,arguments)},complement:function(){return this._applyCombination(pi,arguments)},monochromatic:function(){return this._applyCombination(_i,arguments)},splitcomplement:function(){return this._applyCombination(yi,arguments)},triad:function(){return this._applyCombination(gi,[3])},tetrad:function(){return this._applyCombination(gi,[4])}},ei.fromRatio=function(t,n){if("object"==Jr(t)){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[r]="a"===r?t[r]:Ai(t[r]));t=e}return ei(t,n)},ei.equals=function(t,n){return!(!t||!n)&&ei(t).toRgbString()==ei(n).toRgbString()},ei.random=function(){return ei.fromRatio({r:Math.random(),g:Math.random(),b:Math.random()})},ei.mix=function(t,n,e){e=0===e?0:e||50;var r=ei(t).toRgb(),i=ei(n).toRgb(),o=e/100;return ei({r:(i.r-r.r)*o+r.r,g:(i.g-r.g)*o+r.g,b:(i.b-r.b)*o+r.b,a:(i.a-r.a)*o+r.a})}, -// =4.5;break;case"AAlarge":i=o>=3;break;case"AAAsmall":i=o>=7}return i},ei.mostReadable=function(t,n,e){var r,i,o,a,u=null,s=0;i=(e=e||{}).includeFallbackColors,o=e.level,a=e.size;for(var l=0;ls&&(s=r,u=ei(n[l]));return ei.isReadable(t,u,{level:o,size:a})||!i?u:(e.includeFallbackColors=!1,ei.mostReadable(t,["#fff","#000"],e))};var mi=ei.names={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"0ff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000",blanchedalmond:"ffebcd",blue:"00f",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",burntsienna:"ea7e5d",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"0ff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkgrey:"a9a9a9",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkslategrey:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dimgrey:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"f0f",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",grey:"808080",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgray:"d3d3d3",lightgreen:"90ee90",lightgrey:"d3d3d3",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"789",lightslategrey:"789",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"0f0",limegreen:"32cd32",linen:"faf0e6",magenta:"f0f",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370db",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"db7093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",rebeccapurple:"663399",red:"f00",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",slategrey:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"fff",whitesmoke:"f5f5f5",yellow:"ff0",yellowgreen:"9acd32"},xi=ei.hexNames=function(t){var n={};for(var e in t)t.hasOwnProperty(e)&&(n[t[e]]=e);return n}(mi);function bi(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function wi(t,n){(function(t){return"string"==typeof t&&-1!=t.indexOf(".")&&1===parseFloat(t)})(t)&&(t="100%");var e=function(t){return"string"==typeof t&&-1!=t.indexOf("%")}(t);return t=Math.min(n,Math.max(0,parseFloat(t))),e&&(t=parseInt(t*n,10)/100),Math.abs(t-n)<1e-6?1:t%n/parseFloat(n)}function ki(t){return Math.min(1,Math.max(0,t))}function Mi(t){return parseInt(t,16)}function zi(t){return 1==t.length?"0"+t:""+t}function Ai(t){return t<=1&&(t=100*t+"%"),t}function Si(t){return Math.round(255*parseFloat(t)).toString(16)}function Ci(t){return Mi(t)/255}var Ei,Oi,Ni,Pi=(Oi="[\\s|\\(]+("+(Ei="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)")+")[,|\\s]+("+Ei+")[,|\\s]+("+Ei+")\\s*\\)?",Ni="[\\s|\\(]+("+Ei+")[,|\\s]+("+Ei+")[,|\\s]+("+Ei+")[,|\\s]+("+Ei+")\\s*\\)?",{CSS_UNIT:new RegExp(Ei),rgb:new RegExp("rgb"+Oi),rgba:new RegExp("rgba"+Ni),hsl:new RegExp("hsl"+Oi),hsla:new RegExp("hsla"+Ni),hsv:new RegExp("hsv"+Oi),hsva:new RegExp("hsva"+Ni),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/});function ji(t){return!!Pi.CSS_UNIT.exec(t)}function Ti(t,n){for(var e=0;et.length)&&(n=t.length);for(var e=0,r=new Array(n);e0&&void 0!==arguments[0]?arguments[0]:6;!function(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}(this,t),this.csBits=n,this.registry=["__reserved for background__"]}var n,e,r;return n=t,e=[{key:"register",value:function(t){if(this.registry.length>=Math.pow(2,24-this.csBits))return null;var n,e=this.registry.length,r=Ui(e,this.csBits),i=(n=e+(r<<24-this.csBits),"#".concat(Math.min(n,Math.pow(2,24)).toString(16).padStart(6,"0")));return this.registry.push(t),i}},{key:"lookup",value:function(t){var n,e,r,i,o="string"==typeof t?(n=ei(t).toRgb(),e=n.r,r=n.g,i=n.b,Ii(e,r,i)):Ii.apply(void 0,Ri(t));if(!o)return null;var a=o&Math.pow(2,24-this.csBits)-1,u=o>>24-this.csBits&Math.pow(2,this.csBits)-1;return Ui(a,this.csBits)!==u||a>=this.registry.length?null:this.registry[a]}}],e&&Ti(n.prototype,e),r&&Ti(n,r),Object.defineProperty(n,"prototype",{writable:!1}),t}();function Li(t,n,e){var r,i=1;function o(){var o,a,u=r.length,s=0,l=0,c=0;for(o=0;o=(i=(h+f)/2))?h=i:f=i,r=l,!(l=l[u=+a]))return r[u]=c,t;if(n===(o=+t._x.call(null,l.data)))return c.next=l,r?r[u]=c:t._root=c,t;do{r=r?r[u]=new Array(2):t._root=new Array(2),(a=n>=(i=(h+f)/2))?h=i:f=i}while((u=+a)==(s=+(o>=i)));return r[s]=l,r[u]=c,t}function Bi(t,n,e){this.node=t,this.x0=n,this.x1=e}function $i(t){return t[0]}function Hi(t,n){var e=new Vi(null==n?$i:n,NaN,NaN);return null==t?e:e.addAll(t)}function Vi(t,n,e){this._x=t,this._x0=n,this._x1=e,this._root=void 0}function Xi(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Gi=Hi.prototype=Vi.prototype;function Yi(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,s,l,c,h,f,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((l=n>=(o=(g+v)/2))?g=o:v=o,(c=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[h=c<<1|l]))return i[h]=p,t;if(u=+t._x.call(null,d.data),s=+t._y.call(null,d.data),n===u&&e===s)return p.next=d,i?i[h]=p:t._root=p,t;do{i=i?i[h]=new Array(4):t._root=new Array(4),(l=n>=(o=(g+v)/2))?g=o:v=o,(c=e>=(a=(y+_)/2))?y=a:_=a}while((h=c<<1|l)==(f=(s>=a)<<1|u>=o));return i[f]=d,i[h]=p,t}function Wi(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Zi(t){return t[0]}function Qi(t){return t[1]}function Ki(t,n,e){var r=new Ji(null==n?Zi:n,null==e?Qi:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Ji(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function to(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}Gi.copy=function(){var t,n,e=new Vi(this._x,this._x0,this._x1),r=this._root;if(!r)return e;if(!r.length)return e._root=Xi(r),e;for(t=[{source:r,target:e._root=new Array(2)}];r=t.pop();)for(var i=0;i<2;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(2)}):r.target[i]=Xi(n));return e},Gi.add=function(t){const n=+this._x.call(null,t);return qi(this.cover(n),n,t)},Gi.addAll=function(t){Array.isArray(t)||(t=Array.from(t));const n=t.length,e=new Float64Array(n);let r=1/0,i=-1/0;for(let o,a=0;ai&&(i=o));if(r>i)return this;this.cover(r).cover(i);for(let r=0;rt||t>=e;)switch(i=+(ts||(i=o.x1)=h))&&(o=l[l.length-1],l[l.length-1]=l[l.length-1-a],l[l.length-1-a]=o)}else{var f=Math.abs(t-+this._x.call(null,c.data));f=(a=(h+f)/2))?h=a:f=a,n=c,!(c=c[s=+u]))return this;if(!c.length)break;n[s+1&1]&&(e=n,l=s)}for(;c.data!==t;)if(r=c,!(c=c.next))return this;return(i=c.next)&&delete c.next,r?(i?r.next=i:delete r.next,this):n?(i?n[s]=i:delete n[s],(c=n[0]||n[1])&&c===(n[1]||n[0])&&!c.length&&(e?e[l]=c:this._root=c),this):(this._root=i,this)},Gi.removeAll=function(t){for(var n=0,e=t.length;n=(a=(m+w)/2))?m=a:w=a,(d=e>=(u=(x+k)/2))?x=u:k=u,(p=r>=(s=(b+M)/2))?b=s:M=s,o=v,!(v=v[g=p<<2|d<<1|f]))return o[g]=_,t;if(l=+t._x.call(null,v.data),c=+t._y.call(null,v.data),h=+t._z.call(null,v.data),n===l&&e===c&&r===h)return _.next=v,o?o[g]=_:t._root=_,t;do{o=o?o[g]=new Array(8):t._root=new Array(8),(f=n>=(a=(m+w)/2))?m=a:w=a,(d=e>=(u=(x+k)/2))?x=u:k=u,(p=r>=(s=(b+M)/2))?b=s:M=s}while((g=p<<2|d<<1|f)==(y=(h>=s)<<2|(c>=u)<<1|l>=a));return o[y]=v,o[g]=_,t}function ro(t,n,e,r,i,o,a){this.node=t,this.x0=n,this.y0=e,this.z0=r,this.x1=i,this.y1=o,this.z1=a}function io(t){return t[0]}function oo(t){return t[1]}function ao(t){return t[2]}function uo(t,n,e,r){var i=new so(null==n?io:n,null==e?oo:e,null==r?ao:r,NaN,NaN,NaN,NaN,NaN,NaN);return null==t?i:i.addAll(t)}function so(t,n,e,r,i,o,a,u,s){this._x=t,this._y=n,this._z=e,this._x0=r,this._y0=i,this._z0=o,this._x1=a,this._y1=u,this._z1=s,this._root=void 0}function lo(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}no.copy=function(){var t,n,e=new Ji(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=to(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=to(n));return e},no.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return Yi(this.cover(n,e),n,e,t)},no.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),s=1/0,l=1/0,c=-1/0,h=-1/0;for(e=0;ec&&(c=r),ih&&(h=i));if(s>c||l>h)return this;for(this.cover(s,l).cover(c,h),e=0;et||t>=i||r>n||n>=o;)switch(u=(nf||(o=s.y0)>d||(a=s.x1)=v)<<1|t>=y)&&(s=p[p.length-1],p[p.length-1]=p[p.length-1-l],p[p.length-1-l]=s)}else{var _=t-+this._x.call(null,g.data),m=n-+this._y.call(null,g.data),x=_*_+m*m;if(x=(u=(p+y)/2))?p=u:y=u,(c=a>=(s=(g+v)/2))?g=s:v=s,n=d,!(d=d[h=c<<1|l]))return this;if(!d.length)break;(n[h+1&3]||n[h+2&3]||n[h+3&3])&&(e=n,f=h)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[h]=i:delete n[h],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[f]=d:this._root=d),this):(this._root=i,this)},no.removeAll=function(t){for(var n=0,e=t.length;n1&&(v=f.y+f.vy-c.y-c.vy||fo(u)),i>2&&(_=f.z+f.vz-c.z-c.vz||fo(u)),y*=d=((d=Math.sqrt(y*y+v*v+_*_))-e[g])/d*r*n[g],v*=d,_*=d,f.vx-=y*(p=a[g]),i>1&&(f.vy-=v*p),i>2&&(f.vz-=_*p),c.vx+=y*(p=1-p),i>1&&(c.vy+=v*p),i>2&&(c.vz+=_*p)}function d(){if(r){var i,u,l=r.length,c=t.length,h=new Map(r.map(((t,n)=>[s(t,n,r),t])));for(i=0,o=new Array(l);i"function"==typeof t))||Math.random,i=n.find((t=>[1,2,3].includes(t)))||2,d()},f.links=function(n){return arguments.length?(t=n,d(),f):t},f.id=function(t){return arguments.length?(s=t,f):s},f.iterations=function(t){return arguments.length?(h=+t,f):h},f.strength=function(t){return arguments.length?(l="function"==typeof t?t:ho(+t),p(),f):l},f.distance=function(t){return arguments.length?(c="function"==typeof t?t:ho(+t),g(),f):c},f}co.copy=function(){var t,n,e=new so(this._x,this._y,this._z,this._x0,this._y0,this._z0,this._x1,this._y1,this._z1),r=this._root;if(!r)return e;if(!r.length)return e._root=lo(r),e;for(t=[{source:r,target:e._root=new Array(8)}];r=t.pop();)for(var i=0;i<8;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(8)}):r.target[i]=lo(n));return e},co.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t),r=+this._z.call(null,t);return eo(this.cover(n,e,r),n,e,r,t)},co.addAll=function(t){Array.isArray(t)||(t=Array.from(t));const n=t.length,e=new Float64Array(n),r=new Float64Array(n),i=new Float64Array(n);let o=1/0,a=1/0,u=1/0,s=-1/0,l=-1/0,c=-1/0;for(let h,f,d,p,g=0;gs&&(s=f),dl&&(l=d),pc&&(c=p));if(o>s||a>l||u>c)return this;this.cover(o,a,u).cover(s,l,c);for(let o=0;ot||t>=a||i>n||n>=u||o>e||e>=s;)switch(c=(ey||(a=h.y0)>v||(u=h.z0)>_||(s=h.x1)=k)<<2|(n>=w)<<1|t>=b)&&(h=m[m.length-1],m[m.length-1]=m[m.length-1-f],m[m.length-1-f]=h)}else{var M=t-+this._x.call(null,x.data),z=n-+this._y.call(null,x.data),A=e-+this._z.call(null,x.data),S=M*M+z*z+A*A;if(S=(s=(v+x)/2))?v=s:x=s,(f=a>=(l=(_+b)/2))?_=l:b=l,(d=u>=(c=(m+w)/2))?m=c:w=c,n=y,!(y=y[p=d<<2|f<<1|h]))return this;if(!y.length)break;(n[p+1&7]||n[p+2&7]||n[p+3&7]||n[p+4&7]||n[p+5&7]||n[p+6&7]||n[p+7&7])&&(e=n,g=p)}for(;y.data!==t;)if(r=y,!(y=y.next))return this;return(i=y.next)&&delete y.next,r?(i?r.next=i:delete r.next,this):n?(i?n[p]=i:delete n[p],(y=n[0]||n[1]||n[2]||n[3]||n[4]||n[5]||n[6]||n[7])&&y===(n[7]||n[6]||n[5]||n[4]||n[3]||n[2]||n[1]||n[0])&&!y.length&&(e?e[g]=y:this._root=y),this):(this._root=i,this)},co.removeAll=function(t){for(var n=0,e=t.length;n(t=(vo*t+_o)%mo)/mo}();function d(){p(),h.call("tick",e),i1&&(null==c.fy?c.y+=c.vy*=s:(c.y=c.fy,c.vy=0)),r>2&&(null==c.fz?c.z+=c.vz*=s:(c.z=c.fz,c.vz=0));return e}function g(){for(var n,e=0,i=t.length;e1&&isNaN(n.y)||r>2&&isNaN(n.z)){var o=10*(r>2?Math.cbrt(.5+e):r>1?Math.sqrt(.5+e):e),a=e*ko,u=e*Mo;1===r?n.x=o:2===r?(n.x=o*Math.cos(a),n.y=o*Math.sin(a)):(n.x=o*Math.sin(a)*Math.cos(u),n.y=o*Math.cos(a),n.z=o*Math.sin(a)*Math.sin(u))}(isNaN(n.vx)||r>1&&isNaN(n.vy)||r>2&&isNaN(n.vz))&&(n.vx=0,r>1&&(n.vy=0),r>2&&(n.vz=0))}}function y(n){return n.initialize&&n.initialize(t,f,r),n}return null==t&&(t=[]),g(),e={tick:p,restart:function(){return c.restart(d),e},stop:function(){return c.stop(),e},numDimensions:function(t){return arguments.length?(r=Math.min(3,Math.max(1,Math.round(t))),l.forEach(y),e):r},nodes:function(n){return arguments.length?(t=n,g(),l.forEach(y),e):t},alpha:function(t){return arguments.length?(i=+t,e):i},alphaMin:function(t){return arguments.length?(o=+t,e):o},alphaDecay:function(t){return arguments.length?(a=+t,e):+a},alphaTarget:function(t){return arguments.length?(u=+t,e):u},velocityDecay:function(t){return arguments.length?(s=1-t,e):1-s},randomSource:function(t){return arguments.length?(f=t,l.forEach(y),e):f},force:function(t,n){return arguments.length>1?(null==n?l.delete(t):l.set(t,y(n)),e):l.get(t)},find:function(){var n,e,i,o,a,u,s=Array.prototype.slice.call(arguments),l=s.shift()||0,c=(r>1?s.shift():null)||0,h=(r>2?s.shift():null)||0,f=s.shift()||1/0,d=0,p=t.length;for(f*=f,d=0;d1?(h.on(t,n),e):h.on(t)}}}function Ao(){var t,n,e,r,i,o,a=ho(-30),u=1,s=1/0,l=.81;function c(r){var o,a=t.length,u=(1===n?Hi(t,xo):2===n?Ki(t,xo,bo):3===n?uo(t,xo,bo,wo):null).visitAfter(f);for(i=r,o=0;o1&&(t.y=a/c),n>2&&(t.z=u/c)}else{(e=t).x=e.data.x,n>1&&(e.y=e.data.y),n>2&&(e.z=e.data.z);do{l+=o[e.data.index]}while(e=e.next)}t.value=l}function d(t,a,c,h,f){if(!t.value)return!0;var d=[c,h,f][n-1],p=t.x-e.x,g=n>1?t.y-e.y:0,y=n>2?t.z-e.z:0,v=d-a,_=p*p+g*g+y*y;if(v*v/l<_)return _1&&0===g&&(_+=(g=fo(r))*g),n>2&&0===y&&(_+=(y=fo(r))*y),_1&&(e.vy+=g*t.value*i/_),n>2&&(e.vz+=y*t.value*i/_)),!0;if(!(t.length||_>=s)){(t.data!==e||t.next)&&(0===p&&(_+=(p=fo(r))*p),n>1&&0===g&&(_+=(g=fo(r))*g),n>2&&0===y&&(_+=(y=fo(r))*y),_1&&(e.vy+=g*v),n>2&&(e.vz+=y*v))}while(t=t.next)}}return c.initialize=function(e,...i){t=e,r=i.find((t=>"function"==typeof t))||Math.random,n=i.find((t=>[1,2,3].includes(t)))||2,h()},c.strength=function(t){return arguments.length?(a="function"==typeof t?t:ho(+t),h(),c):a},c.distanceMin=function(t){return arguments.length?(u=t*t,c):Math.sqrt(u)},c.distanceMax=function(t){return arguments.length?(s=t*t,c):Math.sqrt(s)},c.theta=function(t){return arguments.length?(l=t*t,c):Math.sqrt(l)},c}const{abs:So,cos:Co,sin:Eo,acos:Oo,atan2:No,sqrt:Po,pow:jo}=Math;function To(t){return t<0?-jo(-t,1/3):jo(t,1/3)}const Ro=Math.PI,Do=2*Ro,Io=Ro/2,Uo=Number.MAX_SAFE_INTEGER||9007199254740991,Fo=Number.MIN_SAFE_INTEGER||-9007199254740991,Lo={x:0,y:0,z:0},qo={Tvalues:[-.06405689286260563,.06405689286260563,-.1911188674736163,.1911188674736163,-.3150426796961634,.3150426796961634,-.4337935076260451,.4337935076260451,-.5454214713888396,.5454214713888396,-.6480936519369755,.6480936519369755,-.7401241915785544,.7401241915785544,-.820001985973903,.820001985973903,-.8864155270044011,.8864155270044011,-.9382745520027328,.9382745520027328,-.9747285559713095,.9747285559713095,-.9951872199970213,.9951872199970213],Cvalues:[.12793819534675216,.12793819534675216,.1258374563468283,.1258374563468283,.12167047292780339,.12167047292780339,.1155056680537256,.1155056680537256,.10744427011596563,.10744427011596563,.09761865210411388,.09761865210411388,.08619016153195327,.08619016153195327,.0733464814110803,.0733464814110803,.05929858491543678,.05929858491543678,.04427743881741981,.04427743881741981,.028531388628933663,.028531388628933663,.0123412297999872,.0123412297999872],arcfn:function(t,n){const e=n(t);let r=e.x*e.x+e.y*e.y;return void 0!==e.z&&(r+=e.z*e.z),Po(r)},compute:function(t,n,e){if(0===t)return n[0].t=0,n[0];const r=n.length-1;if(1===t)return n[r].t=1,n[r];const i=1-t;let o=n;if(0===r)return n[0].t=t,n[0];if(1===r){const n={x:i*o[0].x+t*o[1].x,y:i*o[0].y+t*o[1].y,t:t};return e&&(n.z=i*o[0].z+t*o[1].z),n}if(r<4){let n,a,u,s=i*i,l=t*t,c=0;2===r?(o=[o[0],o[1],o[2],Lo],n=s,a=i*t*2,u=l):3===r&&(n=s*i,a=s*t*3,u=i*l*3,c=t*l);const h={x:n*o[0].x+a*o[1].x+u*o[2].x+c*o[3].x,y:n*o[0].y+a*o[1].y+u*o[2].y+c*o[3].y,t:t};return e&&(h.z=n*o[0].z+a*o[1].z+u*o[2].z+c*o[3].z),h}const a=JSON.parse(JSON.stringify(n));for(;a.length>1;){for(let n=0;n1;i--,o--){const t=[];for(let e,i=0;io.x.min&&(n=o.x.min),e>o.y.min&&(e=o.y.min),r0&&(a.c1=n,a.c2=r,a.s1=t,a.s2=e,o.push(a))}))})),o},makeshape:function(t,n,e){const r=n.points.length,i=t.points.length,o=qo.makeline(n.points[r-1],t.points[0]),a=qo.makeline(t.points[i-1],n.points[0]),u={startcap:o,forward:t,back:n,endcap:a,bbox:qo.findbbox([o,t,n,a]),intersections:function(t){return qo.shapeintersections(u,u.bbox,t,t.bbox,e)}};return u},getminmax:function(t,n,e){if(!e)return{min:0,max:0};let r,i,o=Uo,a=Fo;-1===e.indexOf(0)&&(e=[0].concat(e)),-1===e.indexOf(1)&&e.push(1);for(let u=0,s=e.length;ua&&(a=i[n]);return{min:o,mid:(o+a)/2,max:a,size:a-o}},align:function(t,n){const e=n.p1.x,r=n.p1.y,i=-No(n.p2.y-r,n.p2.x-e);return t.map((function(t){return{x:(t.x-e)*Co(i)-(t.y-r)*Eo(i),y:(t.x-e)*Eo(i)+(t.y-r)*Co(i)}}))},roots:function(t,n){n=n||{p1:{x:0,y:0},p2:{x:1,y:0}};const e=t.length-1,r=qo.align(t,n),i=function(t){return 0<=t&&t<=1};if(2===e){const t=r[0].y,n=r[1].y,e=r[2].y,o=t-2*n+e;if(0!==o){const r=-Po(n*n-t*e),a=-t+n;return[-(r+a)/o,-(-r+a)/o].filter(i)}return n!==e&&0===o?[(2*n-e)/(2*n-2*e)].filter(i):[]}const o=r[0].y,a=r[1].y,u=r[2].y;let s=3*a-o-3*u+r[3].y,l=3*o-6*a+3*u,c=-3*o+3*a,h=o;if(qo.approximately(s,0)){if(qo.approximately(l,0))return qo.approximately(c,0)?[]:[-h/c].filter(i);const t=Po(c*c-4*l*h),n=2*l;return[(t-c)/n,(-c-t)/n].filter(i)}l/=s,c/=s,h/=s;const f=(3*c-l*l)/3,d=f/3,p=(2*l*l*l-9*l*c+27*h)/27,g=p/2,y=g*g+d*d*d;let v,_,m,x,b;if(y<0){const t=-f/3,n=Po(t*t*t),e=-p/(2*n),r=Oo(e<-1?-1:e>1?1:e),o=2*To(n);return m=o*Co(r/3)-l/3,x=o*Co((r+Do)/3)-l/3,b=o*Co((r+2*Do)/3)-l/3,[m,x,b].filter(i)}if(0===y)return v=g<0?To(-g):-To(g),m=2*v-l/3,x=-v-l/3,[m,x].filter(i);{const t=Po(y);return v=To(-g+t),_=To(g+t),[v-_-l/3].filter(i)}},droots:function(t){if(3===t.length){const n=t[0],e=t[1],r=t[2],i=n-2*e+r;if(0!==i){const t=-Po(e*e-n*r),o=-n+e;return[-(t+o)/i,-(-t+o)/i]}return e!==r&&0===i?[(2*e-r)/(2*(e-r))]:[]}if(2===t.length){const n=t[0],e=t[1];return n!==e?[n/(n-e)]:[]}return[]},curvature:function(t,n,e,r,i){let o,a,u,s,l=0,c=0;const h=qo.compute(t,n),f=qo.compute(t,e),d=h.x*h.x+h.y*h.y;if(r?(o=Po(jo(h.y*f.z-f.y*h.z,2)+jo(h.z*f.x-f.z*h.x,2)+jo(h.x*f.y-f.x*h.y,2)),a=jo(d+h.z*h.z,1.5)):(o=h.x*f.y-h.y*f.x,a=jo(d,1.5)),0===o||0===a)return{k:0,r:0};if(l=o/a,c=a/o,!i){const i=qo.curvature(t-.001,n,e,r,!0).k,o=qo.curvature(t+.001,n,e,r,!0).k;s=(o-l+(l-i))/2,u=(So(o-l)+So(l-i))/2}return{k:l,r:c,dk:s,adk:u}},inflections:function(t){if(t.length<4)return[];const n=qo.align(t,{p1:t[0],p2:t.slice(-1)[0]}),e=n[2].x*n[1].y,r=n[3].x*n[1].y,i=n[1].x*n[2].y,o=18*(-3*e+2*r+3*i-n[3].x*n[2].y),a=18*(3*e-r-3*i),u=18*(i-e);if(qo.approximately(o,0)){if(!qo.approximately(a,0)){let t=-u/a;if(0<=t&&t<=1)return[t]}return[]}const s=2*o;if(qo.approximately(s,0))return[];const l=a*a-4*o*u;if(l<0)return[];const c=Math.sqrt(l);return[(c-a)/s,-(a+c)/s].filter((function(t){return 0<=t&&t<=1}))},bboxoverlap:function(t,n){const e=["x","y"],r=e.length;for(let i,o,a,u,s=0;s=u)return!1;return!0},expandbox:function(t,n){n.x.mint.x.max&&(t.x.max=n.x.max),n.y.max>t.y.max&&(t.y.max=n.y.max),n.z&&n.z.max>t.z.max&&(t.z.max=n.z.max),t.x.mid=(t.x.min+t.x.max)/2,t.y.mid=(t.y.min+t.y.max)/2,t.z&&(t.z.mid=(t.z.min+t.z.max)/2),t.x.size=t.x.max-t.x.min,t.y.size=t.y.max-t.y.min,t.z&&(t.z.size=t.z.max-t.z.min)},pairiteration:function(t,n,e){const r=t.bbox(),i=n.bbox(),o=1e5,a=e||.5;if(r.x.size+r.y.sizek||k>M)&&(w+=Do),w>M&&(b=M,M=w,w=b)):M4){if(1!==arguments.length)throw new Error("Only new Bezier(point[]) is accepted for 4th and higher order curves");r=!0}}else if(6!==i&&8!==i&&9!==i&&12!==i&&1!==arguments.length)throw new Error("Only new Bezier(point[]) is accepted for 4th and higher order curves");const o=this._3d=!r&&(9===i||12===i)||t&&t[0]&&void 0!==t[0].z,a=this.points=[];for(let t=0,e=o?3:2;tt+$o(n.y)),0)0}length(){return qo.length(this.derivative.bind(this))}static getABC(t=2,n,e,r,i=.5){const o=qo.projectionratio(i,t),a=1-o,u={x:o*n.x+a*r.x,y:o*n.y+a*r.y},s=qo.abcratio(i,t);return{A:{x:e.x+(e.x-u.x)/s,y:e.y+(e.y-u.y)/s},B:e,C:u,S:n,E:r}}getABC(t,n){n=n||this.get(t);let e=this.points[0],r=this.points[this.order];return Qo.getABC(this.order,e,n,r,t)}getLUT(t){if(this.verify(),t=t||100,this._lut.length===t+1)return this._lut;this._lut=[],t++,this._lut=[];for(let n,e,r=0;r1?1:h,s=this.compute(h),s.t=h,s.d=l,s}get(t){return this.compute(t)}point(t){return this.points[t]}compute(t){return this.ratios?qo.computeWithRatios(t,this.points,this.ratios,this._3d):qo.compute(t,this.points,this._3d,this.ratios)}raise(){const t=this.points,n=[t[0]],e=t.length;for(let r,i,o=1;o1;){e=[];for(let o,a=0,u=n.length-1;a=0&&t<=1})),n=n.concat(t[e].sort(qo.numberSort))}.bind(this)),t.values=n.sort(qo.numberSort).filter((function(t,e){return n.indexOf(t)===e})),t}bbox(){const t=this.extrema(),n={};return this.dims.forEach(function(e){n[e]=qo.getminmax(this,e,t[e])}.bind(this)),n}overlaps(t){const n=this.bbox(),e=t.bbox();return qo.bboxoverlap(n,e)}offset(t,n){if(void 0!==n){const e=this.get(t),r=this.normal(t),i={c:e,n:r,x:e.x+r.x*n,y:e.y+r.y*n};return this._3d&&(i.z=e.z+r.z*n),i}if(this._linear){const n=this.normal(0),e=this.points.map((function(e){const r={x:e.x+t*n.x,y:e.y+t*n.y};return e.z&&n.z&&(r.z=e.z+t*n.z),r}));return[new Qo(e)]}return this.reduce().map((function(n){return n._linear?n.offset(t)[0]:n.scale(t)}))}simple(){if(3===this.order){const t=qo.angle(this.points[0],this.points[3],this.points[1]),n=qo.angle(this.points[0],this.points[3],this.points[2]);if(t>0&&n<0||t<0&&n>0)return!1}const t=this.normal(0),n=this.normal(1);let e=t.x*n.x+t.y*n.y;return this._3d&&(e+=t.z*n.z),$o(Yo(e))(1-i/r)*n+i/r*e));return new Qo(this.points.map(((n,e)=>({x:n.x+t.x*i[e],y:n.y+t.y*i[e]}))))}scale(t){const n=this.order;let e=!1;if("function"==typeof t&&(e=t),e&&2===n)return this.raise().scale(e);const r=this.clockwise,i=this.points;if(this._linear)return this.translate(this.normal(0),e?e(0):t,e?e(1):t);const o=e?e(0):t,a=e?e(1):t,u=[this.offset(0,10),this.offset(1,10)],s=[],l=qo.lli4(u[0],u[0].c,u[1],u[1].c);if(!l)throw new Error("cannot scale this curve. Try reducing it first.");return[0,1].forEach((function(t){const e=s[t*n]=qo.copy(i[t*n]);e.x+=(t?a:o)*u[t].n.x,e.y+=(t?a:o)*u[t].n.y})),e?([0,1].forEach((function(o){if(2!==n||!o){var a=i[o+1],u={x:a.x-l.x,y:a.y-l.y},c=e?e((o+1)/n):t;e&&!r&&(c=-c);var h=Wo(u.x*u.x+u.y*u.y);u.x/=h,u.y/=h,s[o+1]={x:a.x+c*u.x,y:a.y+c*u.y}}})),new Qo(s)):([0,1].forEach((t=>{if(2===n&&t)return;const e=s[t*n],r=this.derivative(t),o={x:e.x+r.x,y:e.y+r.y};s[t+1]=qo.lli4(e,o,l,i[t+1])})),new Qo(s))}outline(t,n,e,r){if(n=void 0===n?t:n,this._linear){const i=this.normal(0),o=this.points[0],a=this.points[this.points.length-1];let u,s,l;void 0===e&&(e=t,r=n),u={x:o.x+i.x*t,y:o.y+i.y*t},l={x:a.x+i.x*e,y:a.y+i.y*e},s={x:(u.x+l.x)/2,y:(u.y+l.y)/2};const c=[u,s,l];u={x:o.x-i.x*n,y:o.y-i.y*n},l={x:a.x-i.x*r,y:a.y-i.y*r},s={x:(u.x+l.x)/2,y:(u.y+l.y)/2};const h=[l,s,u],f=qo.makeline(h[2],c[0]),d=qo.makeline(c[2],h[0]),p=[f,new Qo(c),d,new Qo(h)];return new Bo(p)}const i=this.reduce(),o=i.length,a=[];let u,s=[],l=0,c=this.length();const h=void 0!==e&&void 0!==r;function f(t,n,e,r,i){return function(o){const a=r/e,u=(r+i)/e,s=n-t;return qo.map(o,0,1,t+a*s,t+u*s)}}i.forEach((function(i){const o=i.length();h?(a.push(i.scale(f(t,e,c,l,o))),s.push(i.scale(f(-n,-r,c,l,o)))):(a.push(i.scale(t)),s.push(i.scale(-n))),l+=o})),s=s.map((function(t){return u=t.points,u[3]?t.points=[u[3],u[2],u[1],u[0]]:t.points=[u[2],u[1],u[0]],t})).reverse();const d=a[0].points[0],p=a[o-1].points[a[o-1].points.length-1],g=s[o-1].points[s[o-1].points.length-1],y=s[0].points[0],v=qo.makeline(g,d),_=qo.makeline(p,y),m=[v].concat(a).concat([_]).concat(s);return new Bo(m)}outlineshapes(t,n,e){n=n||t;const r=this.outline(t,n).curves,i=[];for(let t=1,n=r.length;t1,o.endcap.virtual=t{var o=this.get(t);return qo.between(o.x,n,r)&&qo.between(o.y,e,i)}))}selfintersects(t){const n=this.reduce(),e=n.length-2,r=[];for(let i,o,a,u=0;u0&&(i=i.concat(n))})),i}arcs(t){return t=t||.5,this._iterate(t,[])}_error(t,n,e,r){const i=(r-e)/4,o=this.get(e+i),a=this.get(r-i),u=qo.dist(t,n),s=qo.dist(t,o),l=qo.dist(t,a);return $o(s-u)+$o(l-u)}_iterate(t,n){let e,r=0,i=1;do{e=0,i=1;let o,a,u,s,l,c=this.get(r),h=!1,f=!1,d=i,p=1;do{if(f=h,s=u,d=(r+i)/2,o=this.get(d),a=this.get(i),u=qo.getccenter(c,o,a),u.interval={start:r,end:i},h=this._error(u,c,r,i)<=t,l=f&&!h,l||(p=i),h){if(i>=1){if(u.interval.end=p=1,s=u,i>1){let t={x:u.x+u.r*Xo(u.e),y:u.y+u.r*Go(u.e)};u.e+=qo.angle({x:u.x,y:u.y},t,this.get(1))}break}i+=(i-r)/2}else i=d}while(!l&&e++<100);if(e>=100)break;s=s||u,n.push(s),r=p}while(i<1);return n}}function Ko(t,n){if(null==t)return{};var e,r,i=function(t,n){if(null==t)return{};var e,r,i={},o=Object.keys(t);for(r=0;r=0||(i[e]=t[e]);return i}(t,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,e)&&(i[e]=t[e])}return i}function Jo(t,n){return function(t){if(Array.isArray(t))return t}(t)||function(t,n){var e=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=e){var r,i,o,a,u=[],s=!0,l=!1;try{if(o=(e=e.call(t)).next,0===n){if(Object(e)!==e)return;s=!1}else for(;!(s=(r=o.call(e)).done)&&(u.push(r.value),u.length!==n);s=!0);}catch(t){l=!0,i=t}finally{try{if(!s&&null!=e.return&&(a=e.return(),Object(a)!==a))return}finally{if(l)throw i}}return u}}(t,n)||na(t,n)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function ta(t){return function(t){if(Array.isArray(t))return ea(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||na(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function na(t,n){if(t){if("string"==typeof t)return ea(t,n);var e=Object.prototype.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?ea(t,n):void 0}}function ea(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);et.cooldownTicks||new Date-t.startTickTime>t.cooldownTime||t.d3AlphaMin>0&&t.forceLayout.alpha()0){var a=Math.atan2(r.y-e.y,r.x-e.x),u=i*n,s={x:(e.x+r.x)/2+u*Math.cos(a-Math.PI/2),y:(e.y+r.y)/2+u*Math.sin(a-Math.PI/2)};t.__controlPoints=[s.x,s.y]}else{var l=70*n;t.__controlPoints=[r.x,r.y-l,r.x+l,r.y]}}));var f=[],d=[],p=h;if(t.linkCanvasObject){var g=[],y=[];h.forEach((function(t){return({before:f,after:d,replace:g}[a(t)]||y).push(t)})),p=[].concat(s(f),d,y),f=f.concat(g)}l.save(),f.forEach((function(n){return t.linkCanvasObject(n,l,t.globalScale)})),l.restore();var v=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],e=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],i=(n instanceof Array?n.length?n:[void 0]:[n]).map((function(t){return{keyAccessor:t,isProp:!(t instanceof Function)}})),o=t.reduce((function(t,n){var r=t,o=n;return i.forEach((function(t,n){var a,u=t.keyAccessor;if(t.isProp){var s=o,l=s[u],c=Ko(s,[u].map(ra));a=l,o=c}else a=u(o,n);n+11&&void 0!==arguments[1]?arguments[1]:1;r===i.length?Object.keys(n).forEach((function(t){return n[t]=e(n[t])})):Object.values(n).forEach((function(n){return t(n,r+1)}))}(o);var a=o;return r&&(a=[],function t(n){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];e.length===i.length?a.push({keys:e,vals:n}):Object.entries(n).forEach((function(n){var r=Jo(n,2),i=r[0],o=r[1];return t(o,[].concat(ta(e),[i]))}))}(o),n instanceof Array&&0===n.length&&1===a.length&&(a[0].keys=[])),a}(p,[e,r,i]);l.save(),Object.entries(v).forEach((function(n){var e=u(n,2),r=e[0],o=e[1],a=r&&"undefined"!==r?r:"rgba(0,0,0,0.15)";Object.entries(o).forEach((function(n){var e=u(n,2),r=e[0],o=e[1],h=(r||1)/t.globalScale+c;Object.entries(o).forEach((function(t){var n=u(t,2);n[0];var e=n[1],r=i(e[0]);l.beginPath(),e.forEach((function(t){var n=t.source,e=t.target;if(n&&e&&n.hasOwnProperty("x")&&e.hasOwnProperty("x")){l.moveTo(n.x,n.y);var r=t.__controlPoints;r?l[2===r.length?"quadraticCurveTo":"bezierCurveTo"].apply(l,s(r).concat([e.x,e.y])):l.lineTo(e.x,e.y)}})),l.strokeStyle=a,l.lineWidth=h,l.setLineDash(r||[]),l.stroke()}))}))})),l.restore(),l.save(),d.forEach((function(n){return t.linkCanvasObject(n,l,t.globalScale)})),l.restore()}(),!t.isShadow&&(n=Kr(t.linkDirectionalArrowLength),e=Kr(t.linkDirectionalArrowRelPos),r=Kr(t.linkVisibility),i=Kr(t.linkDirectionalArrowColor||t.linkColor),o=Kr(t.nodeVal),(l=t.ctx).save(),t.graphData.links.filter(r).forEach((function(r){var u=n(r);if(u&&!(u<0)){var c=r.source,h=r.target;if(c&&h&&c.hasOwnProperty("x")&&h.hasOwnProperty("x")){var f=Math.sqrt(Math.max(0,o(c)||1))*t.nodeRelSize,d=Math.sqrt(Math.max(0,o(h)||1))*t.nodeRelSize,p=Math.min(1,Math.max(0,e(r))),g=i(r)||"rgba(0,0,0,0.28)",y=u/1.6/2,v=r.__controlPoints&&a(Qo,[c.x,c.y].concat(s(r.__controlPoints),[h.x,h.y])),_=v?function(t){return v.get(t)}:function(t){return{x:c.x+(h.x-c.x)*t||0,y:c.y+(h.y-c.y)*t||0}},m=v?v.length():Math.sqrt(Math.pow(h.x-c.x,2)+Math.pow(h.y-c.y,2)),x=f+u+(m-f-d-u)*p,b=_(x/m),w=_((x-u)/m),k=_((x-.8*u)/m),M=Math.atan2(b.y-w.y,b.x-w.x)-Math.PI/2;l.beginPath(),l.moveTo(b.x,b.y),l.lineTo(w.x+y*Math.cos(M),w.y+y*Math.sin(M)),l.lineTo(k.x,k.y),l.lineTo(w.x-y*Math.cos(M),w.y-y*Math.sin(M)),l.fillStyle=g,l.fill()}}})),l.restore()),!t.isShadow&&function(){var n=Kr(t.linkDirectionalParticles),e=Kr(t.linkDirectionalParticleSpeed),r=Kr(t.linkDirectionalParticleWidth),i=Kr(t.linkVisibility),o=Kr(t.linkDirectionalParticleColor||t.linkColor),u=t.ctx;u.save(),t.graphData.links.filter(i).forEach((function(i){var l=n(i);if(i.hasOwnProperty("__photons")&&i.__photons.length){var c=i.source,h=i.target;if(c&&h&&c.hasOwnProperty("x")&&h.hasOwnProperty("x")){var f=e(i),d=i.__photons||[],p=Math.max(0,r(i)/2)/Math.sqrt(t.globalScale),g=o(i)||"rgba(0,0,0,0.28)";u.fillStyle=g;var y=i.__controlPoints?a(Qo,[c.x,c.y].concat(s(i.__controlPoints),[h.x,h.y])):null,v=0,_=!1;d.forEach((function(t){var n=!!t.__singleHop;if(t.hasOwnProperty("__progressRatio")||(t.__progressRatio=n?0:v/l),!n&&v++,t.__progressRatio+=f,t.__progressRatio>=1){if(n)return void(_=!0);t.__progressRatio=t.__progressRatio%1}var e=t.__progressRatio,r=y?y.get(e):{x:c.x+(h.x-c.x)*e||0,y:c.y+(h.y-c.y)*e||0};u.beginPath(),u.arc(r.x,r.y,p,0,2*Math.PI,!1),u.fill()})),_&&(i.__photons=i.__photons.filter((function(t){return!t.__singleHop||t.__progressRatio<=1})))}}})),u.restore()}(),function(){var n=Kr(t.nodeVisibility),e=Kr(t.nodeVal),r=Kr(t.nodeColor),i=Kr(t.nodeCanvasObjectMode),o=t.ctx,a=t.isShadow/t.globalScale,u=t.graphData.nodes.filter(n);o.save(),u.forEach((function(n){var u=i(n);if(!t.nodeCanvasObject||"before"!==u&&"replace"!==u||(t.nodeCanvasObject(n,o,t.globalScale),"replace"!==u)){var s=Math.sqrt(Math.max(0,e(n)||1))*t.nodeRelSize+a;o.beginPath(),o.arc(n.x,n.y,s,0,2*Math.PI,!1),o.fillStyle=r(n)||"rgba(31, 120, 180, 0.92)",o.fill(),t.nodeCanvasObject&&"after"===u&&t.nodeCanvasObject(n,t.ctx,t.globalScale)}else o.restore()})),o.restore()}(),this},emitParticle:function(t,n){return n&&(!n.__photons&&(n.__photons=[]),n.__photons.push({__singleHop:!0})),this}},stateInit:function(){return{forceLayout:zo().force("link",yo()).force("charge",Ao()).force("center",Li()).force("dagRadial",null).stop(),engineRunning:!1}},init:function(t,n){n.ctx=t},update:function(t){t.engineRunning=!1,t.onUpdate(),null!==t.nodeAutoColorBy&&sa(t.graphData.nodes,Kr(t.nodeAutoColorBy),t.nodeColor),null!==t.linkAutoColorBy&&sa(t.graphData.links,Kr(t.linkAutoColorBy),t.linkColor),t.graphData.links.forEach((function(n){n.source=n[t.linkSource],n.target=n[t.linkTarget]})),t.forceLayout.stop().alpha(1).nodes(t.graphData.nodes);var n=t.forceLayout.force("link");n&&n.id((function(n){return n[t.nodeId]})).links(t.graphData.links);var e=t.dagMode&&function(t,n){var e=t.nodes,o=t.links,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},l=a.nodeFilter,c=void 0===l?function(){return!0}:l,h=a.onLoopError,f=void 0===h?function(t){throw"Invalid DAG structure! Found cycle in node path: ".concat(t.join(" -> "),".")}:h,d={};e.forEach((function(t){return d[n(t)]={data:t,out:[],depth:-1,skip:!c(t)}})),o.forEach((function(t){var e=t.source,i=t.target,o=l(e),a=l(i);if(!d.hasOwnProperty(o))throw"Missing source node with id: ".concat(o);if(!d.hasOwnProperty(a))throw"Missing target node with id: ".concat(a);var u=d[o],s=d[a];function l(t){return"object"===r(t)?n(t):t}u.out.push(s)}));var p=[];return function t(e){for(var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,o=function(){var o=e[a];if(-1!==r.indexOf(o)){var u=[].concat(s(r.slice(r.indexOf(o))),[o]).map((function(t){return n(t.data)}));return p.some((function(t){return t.length===u.length&&t.every((function(t,n){return t===u[n]}))}))||(p.push(u),f(u)),1}i>o.depth&&(o.depth=i,t(o.out,[].concat(s(r),[o]),i+(o.skip?0:1)))},a=0,u=e.length;a1&&(c.vy+=f*g),o>2&&(c.vz+=d*g)}}function c(){if(i){var n,e=i.length;for(a=new Array(e),u=new Array(e),n=0;n[1,2,3].includes(t)))||2,c()},l.strength=function(t){return arguments.length?(s="function"==typeof t?t:ho(+t),c(),l):s},l.radius=function(n){return arguments.length?(t="function"==typeof n?n:ho(+n),c(),l):t},l.x=function(t){return arguments.length?(n=+t,l):n},l.y=function(t){return arguments.length?(e=+t,l):e},l.z=function(t){return arguments.length?(r=+t,l):r},l}((function(n){var r=e[n[t.nodeId]]||-1;return("radialin"===t.dagMode?o-r:r)*a})).strength((function(n){return t.dagNodeFilter(n)?1:0})):null);for(var f=0;f0&&t.forceLayout.alpha()1?r-1:0),o=1;o1&&void 0!==arguments[1]?arguments[1]:0,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,r=arguments.length,i=new Array(r>3?r-3:0),o=3;o1&&void 0!==arguments[1]?arguments[1]:function(){return!0},e=Kr(t.nodeVal),r=function(n){return Math.sqrt(Math.max(0,e(n)||1))*t.nodeRelSize},i=t.graphData.nodes.filter(n).map((function(t){return{x:t.x,y:t.y,r:r(t)}}));return i.length?{x:[ur(i,(function(t){return t.x-t.r})),ar(i,(function(t){return t.x+t.r}))],y:[ur(i,(function(t){return t.y-t.r})),ar(i,(function(t){return t.y+t.r}))]}:null},pauseAnimation:function(t){return t.animationFrameRequestId&&(cancelAnimationFrame(t.animationFrameRequestId),t.animationFrameRequestId=null),this},resumeAnimation:function(t){return t.animationFrameRequestId||this._animationCycle(),this},_destructor:function(){this.pauseAnimation(),this.graphData({nodes:[],links:[]})}},ya),stateInit:function(){return{lastSetZoom:1,zoom:er(),forceGraph:new ha,shadowGraph:(new ha).cooldownTicks(0).nodeColor("__indexColor").linkColor("__indexColor").isShadow(!0),colorTracker:new Fi}},init:function(t,n){var r=this;t.innerHTML="";var i=document.createElement("div");i.classList.add("force-graph-container"),i.style.position="relative",t.appendChild(i),n.canvas=document.createElement("canvas"),n.backgroundColor&&(n.canvas.style.background=n.backgroundColor),i.appendChild(n.canvas),n.shadowCanvas=document.createElement("canvas");var o=n.canvas.getContext("2d"),a=n.shadowCanvas.getContext("2d",{willReadFrequently:!0}),u={x:-1e12,y:-1e12},s=function(){var t=null,e=window.devicePixelRatio,r=u.x>0&&u.y>0?a.getImageData(u.x*e,u.y*e,1,1):null;return r&&(t=n.colorTracker.lookup(r.data)),t};kt(n.canvas).call(function(){var t,n,e,r,i=Ut,o=Ft,a=Lt,u=qt,s={},l=At("start","drag","end"),c=0,h=0;function f(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v,Ot).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var s=m(this,o.call(this,a,u),a,u,"mouse");s&&(kt(a.view).on("mousemove.drag",p,Nt).on("mouseup.drag",g,Nt),Tt(a.view),Pt(a),e=!1,t=a.clientX,n=a.clientY,s("start",a))}}function p(r){if(jt(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>h}s.mouse("drag",r)}function g(t){kt(t.view).on("mousemove.drag mouseup.drag",null),Rt(t.view,e),jt(t),s.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),s=a.length;for(e=0;e0||n.isPointerPressed)&&("touch"!==e.pointerType||void 0===e.movementX||[e.movementX,e.movementY].some((function(t){return Math.abs(t)>1})))&&(n.isPointerDragging=!0);var r,o,a,s=(r=i.getBoundingClientRect(),o=window.pageXOffset||document.documentElement.scrollLeft,a=window.pageYOffset||document.documentElement.scrollTop,{top:r.top+a,left:r.left+o});u.x=e.pageX-s.left,u.y=e.pageY-s.top,l.style.top="".concat(u.y,"px"),l.style.left="".concat(u.x,"px"),l.style.transform="translate(-".concat(u.x/n.width*100,"%, ").concat(n.height-u.y<100?"calc(-100% - 8px)":"21px",")")}),{passive:!0})})),i.addEventListener("pointerup",(function(t){if(n.isPointerPressed=!1,n.isPointerDragging)n.isPointerDragging=!1;else{var e=[t,n.pointerDownEvent];requestAnimationFrame((function(){if(0===t.button)if(n.hoverObj){var r=n["on".concat(n.hoverObj.type,"Click")];r&&r.apply(void 0,[n.hoverObj.d].concat(e))}else n.onBackgroundClick&&n.onBackgroundClick.apply(n,e);if(2===t.button)if(n.hoverObj){var i=n["on".concat(n.hoverObj.type,"RightClick")];i&&i.apply(void 0,[n.hoverObj.d].concat(e))}else n.onBackgroundRightClick&&n.onBackgroundRightClick.apply(n,e)}))}}),{passive:!0}),i.addEventListener("contextmenu",(function(t){return!(n.onBackgroundRightClick||n.onNodeRightClick||n.onLinkRightClick)||(t.preventDefault(),!1)})),n.forceGraph(o),n.shadowGraph(a);var c=function(t,n,e){var r=!0,i=!0;if("function"!=typeof t)throw new TypeError("Expected a function");return zr(e)&&(r="leading"in e?!!e.leading:r,i="trailing"in e?!!e.trailing:i),Dr(t,n,{leading:r,maxWait:n,trailing:i})}((function(){ma(a,n.width,n.height),n.shadowGraph.linkWidth((function(t){return Kr(n.linkWidth)(t)+n.linkHoverPrecision}));var t=Ge(n.canvas);n.shadowGraph.globalScale(t.k).tickFrame()}),800);n.flushShadowCanvas=c.flush,(this._animationCycle=function t(){var e=!n.autoPauseRedraw||!!n.needsRedraw||n.forceGraph.isEngineRunning()||n.graphData.links.some((function(t){return t.__photons&&t.__photons.length}));if(n.needsRedraw=!1,n.enablePointerInteraction){var r=n.isPointerDragging?null:s();if(r!==n.hoverObj){var i=n.hoverObj,a=i?i.type:null,u=r?r.type:null;if(a&&a!==u){var h=n["on".concat(a,"Hover")];h&&h(null,i.d)}if(u){var f=n["on".concat(u,"Hover")];f&&f(r.d,a===u?i.d:null)}var d=r&&Kr(n["".concat(r.type.toLowerCase(),"Label")])(r.d)||"";l.style.visibility=d?"visible":"hidden",l.innerHTML=d,n.canvas.classList[r&&n["on".concat(u,"Click")]||!r&&n.onBackgroundClick?"add":"remove"]("clickable"),n.hoverObj=r}e&&c()}if(e){ma(o,n.width,n.height);var p=Ge(n.canvas).k;n.onRenderFramePre&&n.onRenderFramePre(o,p),n.forceGraph.globalScale(p).tickFrame(),n.onRenderFramePost&&n.onRenderFramePost(o,p)}Vr(),n.animationFrameRequestId=requestAnimationFrame(t)})()},update:function(t){}});return xa})); diff --git a/hal-core/resources/web/js/lib/moment.LICENSE.txt b/hal-core/resources/web/js/lib/moment.LICENSE.txt deleted file mode 100644 index 8618b733..00000000 --- a/hal-core/resources/web/js/lib/moment.LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) JS Foundation and other contributors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/hal-core/resources/web/js/lib/svg.LICENSE b/hal-core/resources/web/js/lib/svg.LICENSE deleted file mode 100644 index 41b1b108..00000000 --- a/hal-core/resources/web/js/lib/svg.LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2012-2018 Wout Fierens -https://svgdotjs.github.io/ - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/hal-core/resources/web/js/lib/svg.draggable.LICENSE.txt b/hal-core/resources/web/js/lib/svg.draggable.LICENSE.txt deleted file mode 100644 index 83a83e25..00000000 --- a/hal-core/resources/web/js/lib/svg.draggable.LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Ulrich-Matthias - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/hal-core/resources/web/js/lib/svg.draggable.min.js b/hal-core/resources/web/js/lib/svg.draggable.min.js deleted file mode 100644 index 8381fccd..00000000 --- a/hal-core/resources/web/js/lib/svg.draggable.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! svg.draggable.js - v2.2.2 - 2019-01-08 -* https://github.com/svgdotjs/svg.draggable.js -* Copyright (c) 2019 Wout Fierens; Licensed MIT */ -(function(){function a(a){a.remember("_draggable",this),this.el=a}a.prototype.init=function(a,b){var c=this;this.constraint=a,this.value=b,this.el.on("mousedown.drag",function(a){c.start(a)}),this.el.on("touchstart.drag",function(a){c.start(a)})},a.prototype.transformPoint=function(a,b){a=a||window.event;var c=a.changedTouches&&a.changedTouches[0]||a;return this.p.x=c.clientX-(b||0),this.p.y=c.clientY,this.p.matrixTransform(this.m)},a.prototype.getBBox=function(){var a=this.el.bbox();return this.el instanceof SVG.Nested&&(a=this.el.rbox()),(this.el instanceof SVG.G||this.el instanceof SVG.Use||this.el instanceof SVG.Nested)&&(a.x=this.el.x(),a.y=this.el.y()),a},a.prototype.start=function(a){if("click"!=a.type&&"mousedown"!=a.type&&"mousemove"!=a.type||1==(a.which||a.buttons)){var b=this;if(this.el.fire("beforedrag",{event:a,handler:this}),!this.el.event().defaultPrevented){a.preventDefault(),a.stopPropagation(),this.parent=this.parent||this.el.parent(SVG.Nested)||this.el.parent(SVG.Doc),this.p=this.parent.node.createSVGPoint(),this.m=this.el.node.getScreenCTM().inverse();var c,d=this.getBBox();if(this.el instanceof SVG.Text)switch(c=this.el.node.getComputedTextLength(),this.el.attr("text-anchor")){case"middle":c/=2;break;case"start":c=0}this.startPoints={point:this.transformPoint(a,c),box:d,transform:this.el.transform()},SVG.on(window,"mousemove.drag",function(a){b.drag(a)}),SVG.on(window,"touchmove.drag",function(a){b.drag(a)}),SVG.on(window,"mouseup.drag",function(a){b.end(a)}),SVG.on(window,"touchend.drag",function(a){b.end(a)}),this.el.fire("dragstart",{event:a,p:this.startPoints.point,m:this.m,handler:this})}}},a.prototype.drag=function(a){var b=this.getBBox(),c=this.transformPoint(a),d=this.startPoints.box.x+c.x-this.startPoints.point.x,e=this.startPoints.box.y+c.y-this.startPoints.point.y,f=this.constraint,g=c.x-this.startPoints.point.x,h=c.y-this.startPoints.point.y;if(this.el.fire("dragmove",{event:a,p:c,m:this.m,handler:this}),this.el.event().defaultPrevented)return c;if("function"==typeof f){var i=f.call(this.el,d,e,this.m);"boolean"==typeof i&&(i={x:i,y:i}),i.x===!0?this.el.x(d):i.x!==!1&&this.el.x(i.x),i.y===!0?this.el.y(e):i.y!==!1&&this.el.y(i.y)}else"object"==typeof f&&(null!=f.minX&&df.maxX-b.width&&(d=f.maxX-b.width,g=d-this.startPoints.box.x),null!=f.minY&&ef.maxY-b.height&&(e=f.maxY-b.height,h=e-this.startPoints.box.y),null!=f.snapToGrid&&(d-=d%f.snapToGrid,e-=e%f.snapToGrid,g-=g%f.snapToGrid,h-=h%f.snapToGrid),this.el instanceof SVG.G?this.el.matrix(this.startPoints.transform).transform({x:g,y:h},!0):this.el.move(d,e));return c},a.prototype.end=function(a){var b=this.drag(a);this.el.fire("dragend",{event:a,p:b,m:this.m,handler:this}),SVG.off(window,"mousemove.drag"),SVG.off(window,"touchmove.drag"),SVG.off(window,"mouseup.drag"),SVG.off(window,"touchend.drag")},SVG.extend(SVG.Element,{draggable:function(b,c){("function"==typeof b||"object"==typeof b)&&(c=b,b=!0);var d=this.remember("_draggable")||new a(this);return b="undefined"==typeof b?!0:b,b?d.init(c||{},b):(this.off("mousedown.drag"),this.off("touchstart.drag")),this}})}).call(this); \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/svg.min.js b/hal-core/resources/web/js/lib/svg.min.js deleted file mode 100644 index d4b36bd4..00000000 --- a/hal-core/resources/web/js/lib/svg.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! svg.js v2.7.1 MIT*/;!function(t,e){"function"==typeof define&&define.amd?define(function(){return e(t,t.document)}):"object"==typeof exports?module.exports=t.document?e(t,t.document):function(t){return e(t,t.document)}:t.SVG=e(t,t.document)}("undefined"!=typeof window?window:this,function(t,e){function i(t,e,i,n){return i+n.replace(b.regex.dots," .")}function n(t){for(var e=t.slice(0),i=e.length;i--;)Array.isArray(e[i])&&(e[i]=n(e[i]));return e}function r(t,e){return t instanceof e}function s(t,e){return(t.matches||t.matchesSelector||t.msMatchesSelector||t.mozMatchesSelector||t.webkitMatchesSelector||t.oMatchesSelector).call(t,e)}function o(t){return t.toLowerCase().replace(/-(.)/g,function(t,e){return e.toUpperCase()})}function a(t){return t.charAt(0).toUpperCase()+t.slice(1)}function h(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t}function u(t){var e=t.toString(16);return 1==e.length?"0"+e:e}function l(t,e,i){if(null==e||null==i){var n=t.bbox();null==e?e=n.width/n.height*i:null==i&&(i=n.height/n.width*e)}return{width:e,height:i}}function c(t,e,i){return{x:e*t.a+i*t.c+0,y:e*t.b+i*t.d+0}}function f(t){return{a:t[0],b:t[1],c:t[2],d:t[3],e:t[4],f:t[5]}}function d(t){return t instanceof b.Matrix||(t=new b.Matrix(t)),t}function p(t,e){t.cx=null==t.cx?e.bbox().cx:t.cx,t.cy=null==t.cy?e.bbox().cy:t.cy}function m(t){for(var e=0,i=t.length,n="";e=0;i--)e.childNodes[i]instanceof t.SVGElement&&x(e.childNodes[i]);return b.adopt(e).id(b.eid(e.nodeName))}function y(t){return null==t.x&&(t.x=0,t.y=0,t.width=0,t.height=0),t.w=t.width,t.h=t.height,t.x2=t.x+t.width,t.y2=t.y+t.height,t.cx=t.x+t.width/2,t.cy=t.y+t.height/2,t}function v(t){var e=(t||"").toString().match(b.regex.reference);if(e)return e[1]}function g(t){return Math.abs(t)>1e-37?t:0}var w=void 0!==this?this:t,b=w.SVG=function(t){if(b.supported)return t=new b.Doc(t),b.parser.draw||b.prepare(),t};if(b.ns="http://www.w3.org/2000/svg",b.xmlns="http://www.w3.org/2000/xmlns/",b.xlink="http://www.w3.org/1999/xlink",b.svgjs="http://svgjs.com/svgjs",b.supported=function(){return!!e.createElementNS&&!!e.createElementNS(b.ns,"svg").createSVGRect}(),!b.supported)return!1;b.did=1e3,b.eid=function(t){return"Svgjs"+a(t)+b.did++},b.create=function(t){var i=e.createElementNS(this.ns,t);return i.setAttribute("id",this.eid(t)),i},b.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];b.Set&&b.Set.inherit&&b.Set.inherit()},b.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,b.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&b.extend(e,t.extend),t.construct&&b.extend(t.parent||b.Container,t.construct),e},b.adopt=function(e){if(!e)return null;if(e.instance)return e.instance;var i;return i="svg"==e.nodeName?e.parentNode instanceof t.SVGElement?new b.Nested:new b.Doc:"linearGradient"==e.nodeName?new b.Gradient("linear"):"radialGradient"==e.nodeName?new b.Gradient("radial"):b[a(e.nodeName)]?new(b[a(e.nodeName)]):new b.Element(e),i.type=e.nodeName,i.node=e,e.instance=i,i instanceof b.Doc&&i.namespace().defs(),i.setData(JSON.parse(e.getAttribute("svgjs:data"))||{}),i},b.prepare=function(){var t=e.getElementsByTagName("body")[0],i=(t?new b.Doc(t):b.adopt(e.documentElement).nested()).size(2,0);b.parser={body:t||e.documentElement,draw:i.style("opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden").attr("focusable","false").node,poly:i.polyline().node,path:i.path().node,native:b.create("svg")}},b.parser={native:b.create("svg")},e.addEventListener("DOMContentLoaded",function(){b.parser.draw||b.prepare()},!1),b.regex={numberAndUnit:/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,reference:/#([a-z0-9\-_]+)/i,transforms:/\)\s*,?\s*/,whitespace:/\s/g,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,delimiter:/[\s,]+/,hyphen:/([^e])\-/gi,pathLetters:/[MLHVCSQTAZ]/gi,isPathLetter:/[MLHVCSQTAZ]/i,numbersWithDots:/((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,dots:/\./g},b.utils={map:function(t,e){var i,n=t.length,r=[];for(i=0;i1?1:t,new b.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this}}),b.Color.test=function(t){return t+="",b.regex.isHex.test(t)||b.regex.isRgb.test(t)},b.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},b.Color.isColor=function(t){return b.Color.isRgb(t)||b.Color.test(t)},b.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},b.extend(b.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.length=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)n.width&&(this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x),n.height&&(this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y);return this},bbox:function(){return b.parser.poly.setAttribute("points",this.toString()),b.parser.poly.getBBox()}});for(var C={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],["M",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],["L",t[0],t[1]]},H:function(t,e){return e.x=t[0],["H",t[0]]},V:function(t,e){return e.y=t[0],["V",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],["C",t[0],t[1],t[2],t[3],t[4],t[5]]},S:function(t,e){return e.x=t[2],e.y=t[3],["S",t[0],t[1],t[2],t[3]]},Q:function(t,e){return e.x=t[2],e.y=t[3],["Q",t[0],t[1],t[2],t[3]]},T:function(t,e){return e.x=t[0],e.y=t[1],["T",t[0],t[1]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,["Z"]},A:function(t,e){return e.x=t[5],e.y=t[6],["A",t[0],t[1],t[2],t[3],t[4],t[5],t[6]]}},N="mlhvqtcsaz".split(""),A=0,P=N.length;A=0;r--)n=this.value[r][0],"M"==n||"L"==n||"T"==n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==n?this.value[r][1]+=t:"V"==n?this.value[r][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"==n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"==n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},equalCommands:function(t){var e,i,n;for(t=new b.PathArray(t),n=this.value.length===t.value.length,e=0,i=this.value.length;n&&ea);return n},bbox:function(){return b.parser.path.setAttribute("d",this.toString()),b.parser.path.getBBox()}}),b.Number=b.invent({create:function(t,e){this.value=0,this.unit=e||"","number"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-3.4e38:3.4e38:"string"==typeof t?(e=t.match(b.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),"%"==e[5]?this.value/=100:"s"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof b.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new b.Number(t),new b.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new b.Number(t),new b.Number(this-t,this.unit||t.unit)},times:function(t){return t=new b.Number(t),new b.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new b.Number(t),new b.Number(this/t,this.unit||t.unit)},to:function(t){var e=new b.Number(this);return"string"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new b.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new b.Number(this.destination).minus(this).times(t).plus(this):this}}}),b.Element=b.invent({create:function(t){this._stroke=b.defaults.attrs.stroke,this._event=null,this._events={},this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._events=t._events||{},this._stroke=t.getAttribute("stroke")||this._stroke)},extend:{x:function(t){return this.attr("x",t)},y:function(t){return this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=l(this,t,e);return this.width(new b.Number(i.width)).height(new b.Number(i.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr("id",t)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<([\w:-]+)([^<]+?)\/>/g,"<$1$2>")+"";for(var n=0,r=i.firstChild.childNodes.length;n":function(t){return-Math.cos(t*Math.PI)/2+.5},">":function(t){return Math.sin(t*Math.PI/2)},"<":function(t){return 1-Math.cos(t*Math.PI/2)}},b.morph=function(t){return function(e,i){return new b.MorphObj(e,i).at(t)}},b.Situation=b.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new b.Number(t.duration).valueOf(),this.delay=new b.Number(t.delay).valueOf(),this.start=+new Date+this.delay,this.finish=this.start+this.duration,this.ease=t.ease,this.loop=0,this.loops=!1,this.animations={},this.attrs={},this.styles={},this.transforms=[],this.once={}}}),b.FX=b.invent({create:function(t){this._target=t,this.situations=[],this.active=!1,this.situation=null,this.paused=!1,this.lastPos=0,this.pos=0,this.absPos=0,this._speed=1},extend:{animate:function(t,e,i){"object"==typeof t&&(e=t.ease,i=t.delay,t=t.duration);var n=new b.Situation({duration:t||1e3,delay:i||0,ease:b.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var e=new b.Situation({duration:t,delay:0,ease:b.easing["-"]});return this.queue(e)},target:function(t){return t&&t instanceof b.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof b.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof b.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e,i,n=this.situation;if(n.init)return this;for(t in n.animations)for(i=this.target()[t](),Array.isArray(i)||(i=[i]),Array.isArray(n.animations[t])||(n.animations[t]=[n.animations[t]]),e=i.length;e--;)n.animations[t][e]instanceof b.Number&&(i[e]=new b.Number(i[e])),n.animations[t][e]=i[e].morph(n.animations[t][e]);for(t in n.attrs)n.attrs[t]=new b.MorphObj(this.target().attr(t),n.attrs[t]);for(t in n.styles)n.styles[t]=new b.MorphObj(this.target().style(t),n.styles[t]);return n.initialTransformation=this.target().matrixify(),n.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.atStart()}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},atStart:function(){return this.at(0,!0)},atEnd:function(){return!0===this.situation.loops&&(this.situation.loops=this.situation.loop+1),"number"==typeof this.situation.loops?this.at(this.situation.loops,!0):this.at(1,!0)},at:function(t,e){var i=this.situation.duration/this._speed;return this.absPos=t,e||(this.situation.reversed&&(this.absPos=1-this.absPos),this.absPos+=this.situation.loop),this.situation.start=+new Date-this.absPos*i,this.situation.finish=this.situation.start+i,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.absPos,!0)):this._speed},loop:function(t,e){var i=this.last();return i.loops=null==t||t,i.loop=0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return e.reversed=void 0===t?!e.reversed:t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){var e=this.last(),i=function i(n){n.detail.situation==e&&(t.call(this,e),this.off("finished.fx",i))};return this.target().on("finished.fx",i),this._callStart()},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,b.morph(i.detail.pos),i.detail.eased,e)};return this.target().off("during.fx",i).on("during.fx",i),this.after(function(){this.off("during.fx",i)}),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this._callStart()},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,b.morph(e.detail.pos),e.detail.eased,e.detail.situation)};return this.target().off("during.fx",e).on("during.fx",e),this.afterAll(function(){this.off("during.fx",e)}),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,this._callStart()},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||ithis.lastPos&&s<=r&&(this.situation.once[s].call(this.target(),this.pos,r),delete this.situation.once[s]);return this.active&&this.target().fire("during",{pos:this.pos,eased:r,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.situations.length||(this.target().off(".fx"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=r,this):this},eachAt:function(){var t,e,i,n=this,r=this.target(),s=this.situation;for(t in s.animations)i=[].concat(s.animations[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r[t].apply(r,i);for(t in s.attrs)i=[t].concat(s.attrs[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r.attr.apply(r,i);for(t in s.styles)i=[t].concat(s.styles[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r.style.apply(r,i);if(s.transforms.length){for(i=s.initialTransformation,t=0,e=s.transforms.length;t=0;--e)this[k[e]]=null!=t[k[e]]?t[k[e]]:i[k[e]]},extend:{extract:function(){var t=c(this,0,1),e=c(this,1,0),i=180/Math.PI*Math.atan2(t.y,t.x)-90;return{x:this.e,y:this.f,transformedX:(this.e*Math.cos(i*Math.PI/180)+this.f*Math.sin(i*Math.PI/180))/Math.sqrt(this.a*this.a+this.b*this.b),transformedY:(this.f*Math.cos(i*Math.PI/180)+this.e*Math.sin(-i*Math.PI/180))/Math.sqrt(this.c*this.c+this.d*this.d),skewX:-i,skewY:180/Math.PI*Math.atan2(e.y,e.x),scaleX:Math.sqrt(this.a*this.a+this.b*this.b),scaleY:Math.sqrt(this.c*this.c+this.d*this.d),rotation:i,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f,matrix:new b.Matrix(this)}},clone:function(){return new b.Matrix(this)},morph:function(t){return this.destination=new b.Matrix(t),this},at:function(t){return this.destination?new b.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t}):this},multiply:function(t){return new b.Matrix(this.native().multiply(d(t).native()))},inverse:function(){return new b.Matrix(this.native().inverse())},translate:function(t,e){return new b.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),this.around(i,n,new b.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=b.utils.radians(t),this.around(e,i,new b.Matrix(Math.cos(t),Math.sin(t),-Math.sin(t),Math.cos(t),0,0))},flip:function(t,e){return"x"==t?this.scale(-1,1,e,0):"y"==t?this.scale(1,-1,0,e):this.scale(-1,-1,t,null!=e?e:t)},skew:function(t,e,i,n){ -return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),t=b.utils.radians(t),e=b.utils.radians(e),this.around(i,n,new b.Matrix(1,Math.tan(e),Math.tan(t),1,0,0))},skewX:function(t,e,i){return this.skew(t,0,e,i)},skewY:function(t,e,i){return this.skew(0,t,e,i)},around:function(t,e,i){return this.multiply(new b.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new b.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=b.parser.native.createSVGMatrix(),e=k.length-1;e>=0;e--)t[k[e]]=this[k[e]];return t},toString:function(){return"matrix("+g(this.a)+","+g(this.b)+","+g(this.c)+","+g(this.d)+","+g(this.e)+","+g(this.f)+")"}},parent:b.Element,construct:{ctm:function(){return new b.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof b.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new b.Matrix(e)}return new b.Matrix(this.node.getScreenCTM())}}}),b.Point=b.invent({create:function(t,e){var i,n={x:0,y:0};i=Array.isArray(t)?{x:t[0],y:t[1]}:"object"==typeof t?{x:t.x,y:t.y}:null!=t?{x:t,y:null!=e?e:t}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new b.Point(this)},morph:function(t,e){return this.destination=new b.Point(t,e),this},at:function(t){return this.destination?new b.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t}):this},native:function(){var t=b.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new b.Point(this.native().matrixTransform(t.native()))}}}),b.extend(b.Element,{point:function(t,e){return new b.Point(t,e).transform(this.screenCTM().inverse())}}),b.extend(b.Element,{attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=b.regex.isNumber.test(e[i].nodeValue)?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?b.defaults.attrs[t]:b.regex.isNumber.test(e)?parseFloat(e):e;"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),"fill"!=t&&"stroke"!=t||(b.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof b.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new b.Number(e):b.Color.isColor(e)?e=new b.Color(e):Array.isArray(e)&&(e=new b.Array(e)),"leading"==t?this.leading&&this.leading(e):"string"==typeof i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this}}),b.extend(b.Element,{transform:function(t,e){var i,n,r=this;if("object"!=typeof t)return i=new b.Matrix(r).extract(),"string"==typeof t?i[t]:i;if(i=new b.Matrix(r),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new b.Matrix(t)):new b.Matrix(t);else if(null!=t.rotation)p(t,r),i=e?i.rotate(t.rotation,t.cx,t.cy):i.rotate(t.rotation-i.extract().rotation,t.cx,t.cy);else if(null!=t.scale||null!=t.scaleX||null!=t.scaleY){if(p(t,r),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,!e){var s=i.extract();t.scaleX=1*t.scaleX/s.scaleX,t.scaleY=1*t.scaleY/s.scaleY}i=i.scale(t.scaleX,t.scaleY,t.cx,t.cy)}else if(null!=t.skew||null!=t.skewX||null!=t.skewY){if(p(t,r),t.skewX=null!=t.skew?t.skew:null!=t.skewX?t.skewX:0,t.skewY=null!=t.skew?t.skew:null!=t.skewY?t.skewY:0,!e){var s=i.extract();i=i.multiply((new b.Matrix).skew(s.skewX,s.skewY,t.cx,t.cy).inverse())}i=i.skew(t.skewX,t.skewY,t.cx,t.cy)}else t.flip?("x"==t.flip||"y"==t.flip?t.offset=null==t.offset?r.bbox()["c"+t.flip]:t.offset:null==t.offset?(n=r.bbox(),t.flip=n.cx,t.offset=n.cy):t.flip=t.offset,i=(new b.Matrix).flip(t.flip,t.offset)):null==t.x&&null==t.y||(e?i=i.translate(t.x,t.y):(null!=t.x&&(i.e=t.x),null!=t.y&&(i.f=t.y)));return this.attr("transform",i)}}),b.extend(b.FX,{transform:function(t,e){var i,n,r=this.target();return"object"!=typeof t?(i=new b.Matrix(r).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new b.Matrix(t):null!=t.rotation?(p(t,r),i=new b.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(p(t,r),t.scaleX=null!=t.scale?t.scale:null!=t.scaleX?t.scaleX:1,t.scaleY=null!=t.scale?t.scale:null!=t.scaleY?t.scaleY:1,i=new b.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(p(t,r),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new b.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?("x"==t.flip||"y"==t.flip?t.offset=null==t.offset?r.bbox()["c"+t.flip]:t.offset:null==t.offset?(n=r.bbox(),t.flip=n.cx,t.offset=n.cy):t.flip=t.offset,i=(new b.Matrix).flip(t.flip,t.offset)):null==t.x&&null==t.y||(i=new b.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),this._callStart()):this)}}),b.extend(b.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){return(this.attr("transform")||"").split(b.regex.transforms).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(b.regex.delimiter).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(f(e[1])):t[e[0]].apply(t,e[1])},new b.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),b.Transformation=b.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(t))for(var i=0,n=this.arguments.length;i=0},index:function(t){return[].slice.call(this.node.childNodes).indexOf(t.node)},get:function(t){return b.adopt(this.node.childNodes[t])},first:function(){return this.get(0)},last:function(){return this.get(this.node.childNodes.length-1)},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;in/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)}else t="string"==typeof t?t.match(c).map(function(t){return parseFloat(t)}):Array.isArray(t)?t:"object"==typeof t?[t.x,t.y,t.width,t.height]:4==arguments.length?[].slice.call(arguments):h,this.x=t[0],this.y=t[1],this.width=t[2],this.height=t[3]},extend:{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height},morph:function(t,e,i,n){return this.destination=new b.ViewBox(t,e,i,n),this},at:function(t){return this.destination?new b.ViewBox([this.x+(this.destination.x-this.x)*t,this.y+(this.destination.y-this.y)*t,this.width+(this.destination.width-this.width)*t,this.height+(this.destination.height-this.height)*t]):this}},parent:b.Container,construct:{viewbox:function(t,e,i,n){return 0==arguments.length?new b.ViewBox(this):this.attr("viewBox",new b.ViewBox(t,e,i,n))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){b.Element.prototype[t]=function(e){return null==e?b.off(this,t):b.on(this,t,e),this}}),b.listenerId=0,b.on=function(t,e,i,n,r){var s=i.bind(n||t),o=t instanceof b.Element?t.node:t;o.instance=o.instance||{_events:{}};var a=o.instance._events;i._svgjsListenerId||(i._svgjsListenerId=++b.listenerId),e.split(b.regex.delimiter).forEach(function(t){var e=t.split(".")[0],n=t.split(".")[1]||"*";a[e]=a[e]||{},a[e][n]=a[e][n]||{},a[e][n][i._svgjsListenerId]=s,o.addEventListener(e,s,r||!1)})},b.off=function(t,e,i,n){var r=t instanceof b.Element?t.node:t;if(r.instance&&("function"!=typeof i||(i=i._svgjsListenerId))){var s=r.instance._events;(e||"").split(b.regex.delimiter).forEach(function(t){var e,o,a=t&&t.split(".")[0],h=t&&t.split(".")[1];if(i)s[a]&&s[a][h||"*"]&&(r.removeEventListener(a,s[a][h||"*"][i],n||!1),delete s[a][h||"*"][i]);else if(a&&h){if(s[a]&&s[a][h]){for(o in s[a][h])b.off(r,[a,h].join("."),o);delete s[a][h]}}else if(h)for(t in s)for(e in s[t])h===e&&b.off(r,[t,h].join("."));else if(a){if(s[a]){for(e in s[a])b.off(r,[a,e].join("."));delete s[a]}}else{for(t in s)b.off(r,t);r.instance._events={}}})}},b.extend(b.Element,{on:function(t,e,i,n){return b.on(this,t,e,i,n),this},off:function(t,e){return b.off(this.node,t,e),this},fire:function(e,i){return e instanceof t.Event?this.node.dispatchEvent(e):this.node.dispatchEvent(e=new b.CustomEvent(e,{detail:i,cancelable:!0})),this._event=e,this},event:function(){return this._event}}),b.Defs=b.invent({create:"defs",inherit:b.Container}),b.G=b.invent({create:"g",inherit:b.Container,extend:{x:function(t){return null==t?this.transform("x"):this.transform({x:t-this.x()},!0)},y:function(t){return null==t?this.transform("y"):this.transform({y:t-this.y()},!0)},cx:function(t){return null==t?this.gbox().cx:this.x(t-this.gbox().width/2)},cy:function(t){return null==t?this.gbox().cy:this.y(t-this.gbox().height/2)},gbox:function(){var t=this.bbox(),e=this.transform();return t.x+=e.x,t.x2+=e.x,t.cx+=e.x,t.y+=e.y,t.y2+=e.y,t.cy+=e.y,t}},construct:{group:function(){return this.put(new b.G)}}}),b.Doc=b.invent({create:function(t){t&&(t="string"==typeof t?e.getElementById(t):t,"svg"==t.nodeName?this.constructor.call(this,t):(this.constructor.call(this,b.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:b.Container,extend:{namespace:function(){return this.attr({xmlns:b.ns,version:"1.1"}).attr("xmlns:xlink",b.xlink,b.xmlns).attr("xmlns:svgjs",b.svgjs,b.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=b.adopt(t):this._defs=new b.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return this.node.parentNode&&"#document"!=this.node.parentNode.nodeName&&"#document-fragment"!=this.node.parentNode.nodeName?this.node.parentNode:null},spof:function(){var t=this.node.getScreenCTM();return t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,b.parser.draw.parentNode||this.node.appendChild(b.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),b.extend(b.Element,{siblings:function(){return this.parent().children()},position:function(){return this.parent().index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position()+1,e=this.parent();return e.removeElement(this).add(this,t),e instanceof b.Doc&&e.node.appendChild(e.defs().node),this},backward:function(){var t=this.position();return t>0&&this.parent().removeElement(this).add(this,t-1),this},front:function(){var t=this.parent();return t.node.appendChild(this.node),t instanceof b.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),b.Mask=b.invent({create:function(){this.constructor.call(this,b.create("mask")),this.targets=[]},inherit:b.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],b.Element.prototype.remove.call(this),this}},construct:{mask:function(){return this.defs().put(new b.Mask)}}}),b.extend(b.Element,{maskWith:function(t){return this.masker=t instanceof b.Mask?t:this.parent().mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),b.ClipPath=b.invent({create:function(){this.constructor.call(this,b.create("clipPath")),this.targets=[]},inherit:b.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return this.targets=[],this.parent().removeElement(this),this}},construct:{clip:function(){return this.defs().put(new b.ClipPath)}}}),b.extend(b.Element,{clipWith:function(t){return this.clipper=t instanceof b.ClipPath?t:this.parent().clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),b.Gradient=b.invent({create:function(t){this.constructor.call(this,b.create(t+"Gradient")),this.type=t},inherit:b.Container,extend:{at:function(t,e,i){return this.put(new b.Stop).update(t,e,i)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.id()+")"},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="gradientTransform"),b.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),b.extend(b.Gradient,b.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new b.Number(t),fy:new b.Number(e)}):this.attr({x1:new b.Number(t),y1:new b.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new b.Number(t),cy:new b.Number(e)}):this.attr({x2:new b.Number(t),y2:new b.Number(e)})}}),b.extend(b.Defs,{gradient:function(t,e){return this.put(new b.Gradient(t)).update(e)}}),b.Stop=b.invent({create:"stop",inherit:b.Element,extend:{update:function(t){return("number"==typeof t||t instanceof b.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new b.Number(t.offset)),this}}}),b.Pattern=b.invent({create:"pattern",inherit:b.Container,extend:{fill:function(){return"url(#"+this.id()+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()},attr:function(t,e,i){return"transform"==t&&(t="patternTransform"),b.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),b.extend(b.Defs,{pattern:function(t,e,i){return this.put(new b.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),b.Shape=b.invent({create:function(t){this.constructor.call(this,t)},inherit:b.Element}),b.Bare=b.invent({create:function(t,e){if(this.constructor.call(this,b.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:b.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),b.extend(b.Parent,{element:function(t,e){return this.put(new b.Bare(t,e))}}),b.Symbol=b.invent({create:"symbol",inherit:b.Container,construct:{symbol:function(){return this.put(new b.Symbol)}}}),b.Use=b.invent({create:"use",inherit:b.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,b.xlink)}},construct:{use:function(t,e){return this.put(new b.Use).element(t,e)}}}),b.Rect=b.invent({create:"rect",inherit:b.Shape,construct:{rect:function(t,e){return this.put(new b.Rect).size(t,e)}}}),b.Circle=b.invent({create:"circle",inherit:b.Shape,construct:{circle:function(t){return this.put(new b.Circle).rx(new b.Number(t).divide(2)).move(0,0)}}}),b.extend(b.Circle,b.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),b.Ellipse=b.invent({create:"ellipse",inherit:b.Shape,construct:{ellipse:function(t,e){return this.put(new b.Ellipse).size(t,e).move(0,0)}}}),b.extend(b.Ellipse,b.Rect,b.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),b.extend(b.Circle,b.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new b.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new b.Number(t).divide(2))},size:function(t,e){var i=l(this,t,e);return this.rx(new b.Number(i.width).divide(2)).ry(new b.Number(i.height).divide(2))}}),b.Line=b.invent({create:"line",inherit:b.Shape,extend:{array:function(){return new b.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:n}:new b.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=l(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return b.Line.prototype.plot.apply(this.put(new b.Line),null!=t?[t,e,i,n]:[0,0,0,0])}}}),b.Polyline=b.invent({create:"polyline",inherit:b.Shape,construct:{polyline:function(t){return this.put(new b.Polyline).plot(t||new b.PointArray)}}}),b.Polygon=b.invent({create:"polygon",inherit:b.Shape,construct:{polygon:function(t){return this.put(new b.Polygon).plot(t||new b.PointArray)}}}),b.extend(b.Polyline,b.Polygon,{array:function(){return this._array||(this._array=new b.PointArray(this.attr("points")))},plot:function(t){return null==t?this.array():this.clear().attr("points","string"==typeof t?t:this._array=new b.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=l(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),b.extend(b.Line,b.Polyline,b.Polygon,{morphArray:b.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),b.Path=b.invent({create:"path",inherit:b.Shape,extend:{morphArray:b.PathArray,array:function(){return this._array||(this._array=new b.PathArray(this.attr("d")))},plot:function(t){return null==t?this.array():this.clear().attr("d","string"==typeof t?t:this._array=new b.PathArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=l(this,t,e);return this.attr("d",this.array().size(i.width,i.height))},width:function(t){return null==t?this.bbox().width:this.size(t,this.bbox().height)},height:function(t){return null==t?this.bbox().height:this.size(this.bbox().width,t)}},construct:{path:function(t){return this.put(new b.Path).plot(t||new b.PathArray)}}}),b.Image=b.invent({create:"image",inherit:b.Shape,extend:{load:function(e){if(!e)return this;var i=this,n=new t.Image;return b.on(n,"load",function(){b.off(n);var t=i.parent(b.Pattern);null!==t&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),t&&0==t.width()&&0==t.height()&&t.size(i.width(),i.height()),"function"==typeof i._loaded&&i._loaded.call(i,{width:n.width,height:n.height,ratio:n.width/n.height,url:e}))}),b.on(n,"error",function(t){b.off(n),"function"==typeof i._error&&i._error.call(i,t)}),this.attr("href",n.src=this.src=e,b.xlink)},loaded:function(t){return this._loaded=t,this},error:function(t){return this._error=t,this}},construct:{image:function(t,e,i){return this.put(new b.Image).load(t).size(e||0,i||e||0)}}}),b.Text=b.invent({create:function(){this.constructor.call(this,b.create("text")),this.dom.leading=new b.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",b.defaults.attrs["font-family"])},inherit:b.Shape,extend:{x:function(t){return null==t?this.attr("x"):this.attr("x",t)},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t.valueOf()?t+i:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if(void 0===t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[M[t][e]]&&this.attr(M.prefix(t,M[t][e]),i[M[t][e]]);return this},b.extend(b.Element,b.FX,i)}),b.extend(b.Element,b.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({skew:t,cx:e,cy:i}):this.transform({skewX:t,skewY:e,cx:i,cy:n})},scale:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({scale:t,cx:e,cy:i}):this.transform({scaleX:t,scaleY:e,cx:i,cy:n})},translate:function(t,e){return this.transform({x:t,y:e})},flip:function(t,e){return e="number"==typeof t?t:e,this.transform({flip:t||"both",offset:e})},matrix:function(t){return this.attr("transform",new b.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x(new b.Number(t).plus(this instanceof b.FX?0:this.x()),!0)},dy:function(t){return this.y(new b.Number(t).plus(this instanceof b.FX?0:this.y()),!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),b.extend(b.Rect,b.Ellipse,b.Circle,b.Gradient,b.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new b.Number(t)):this.rx(t).ry(null==e?t:e)}}),b.extend(b.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),b.extend(b.Parent,b.Text,b.Tspan,b.FX,{font:function(t,e){if("object"==typeof t)for(e in t)this.font(e,t[e]);return"leading"==t?this.leading(e):"anchor"==t?this.attr("text-anchor",e):"size"==t||"family"==t||"weight"==t||"stretch"==t||"variant"==t||"style"==t?this.attr("font-"+t,e):this.attr(t,e)}}),b.Set=b.invent({create:function(t){t instanceof b.Set?this.members=t.members.slice():Array.isArray(t)?this.members=t:this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;t-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;e=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},first:function(){return this.get(0)},last:function(){return this.get(this.members.length-1)},valueOf:function(){return this.members},bbox:function(){if(0==this.members.length)return new b.RBox;var t=this.members[0].rbox(this.members[0].doc());return this.each(function(){t=t.merge(this.rbox(this.doc()))}),t}},construct:{set:function(t){ -return new b.Set(t)}}}),b.FX.Set=b.invent({create:function(t){this.set=t}}),b.Set.inherit=function(){var t,e=[];for(var t in b.Shape.prototype)"function"==typeof b.Shape.prototype[t]&&"function"!=typeof b.Set.prototype[t]&&e.push(t);e.forEach(function(t){b.Set.prototype[t]=function(){for(var e=0,i=this.members.length;e=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),b.get=function(t){var i=e.getElementById(v(t)||t);return b.adopt(i)},b.select=function(t,i){return new b.Set(b.utils.map((i||e).querySelectorAll(t),function(t){return b.adopt(t)}))},b.extend(b.Parent,{select:function(t){return b.select(t,this.node)}});var k="abcdef".split("");if("function"!=typeof t.CustomEvent){var S=function(t,i){i=i||{bubbles:!1,cancelable:!1,detail:void 0};var n=e.createEvent("CustomEvent");return n.initCustomEvent(t,i.bubbles,i.cancelable,i.detail),n};S.prototype=t.Event.prototype,b.CustomEvent=S}else b.CustomEvent=t.CustomEvent;return function(e){for(var i=0,n=["moz","webkit"],r=0;r= this.resizeLimits.width) { - if (checkAspectRatio) { - snap = this.checkAspectRatio(snap, checkAspectRatioReverse); - } - - if (this.parameters.type === "text") { - if (resizeFont) { - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize - snap[0]); - } - return; - } - - this.el.width(this.parameters.box.width - snap[0]); - - if (updateOnlyChanges) { - this.el.x(this.parameters.box.x + snap[0]); - } else { - this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y); - } - } - }; - - this._resizeRight = function (snap, resizeFont, checkAspectRatio, checkAspectRatioReverse) { - if (this.parameters.box.width + snap[0] >= this.resizeLimits.width) { - if (checkAspectRatio) { - snap = this.checkAspectRatio(snap, checkAspectRatioReverse); - } - - if (this.parameters.type === "text") { - if (resizeFont) { - this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y); - this.el.attr("font-size", this.parameters.fontSize + snap[0]); - } - return; - } - - this.el.x(this.parameters.box.x).width(this.parameters.box.width + snap[0]); - } - }; - - this._resizeTop = function (snap, checkAspectRatio, checkAspectRatioReverse, updateOnlyChanges) { - if (this.parameters.box.height - snap[1] >= this.resizeLimits.height) { - if (checkAspectRatio) { - snap = this.checkAspectRatio(snap, checkAspectRatioReverse); - } - - // Disable the font-resizing if it is not from the corner of bounding-box - if (this.parameters.type === "text") { - return; - } - - this.el.height(this.parameters.box.height - snap[1]); - - if (updateOnlyChanges) { - this.el.y(this.parameters.box.y + snap[1]) - } else { - this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]); - } - } - }; - - this._resizeBottom = function (snap, checkAspectRatio, checkAspectRatioReverse) { - if (this.parameters.box.height + snap[1] >= this.resizeLimits.height) { - if (checkAspectRatio) { - snap = this.checkAspectRatio(snap, checkAspectRatioReverse); - } - - if (this.parameters.type === "text") { - return; - } - - this.el.y(this.parameters.box.y).height(this.parameters.box.height + snap[1]); - } - }; - - // Lets check which edge of the bounding-box was clicked and resize the this.el according to this - switch (event.type) { - - // Left-Top-Edge - case 'lt': - // We build a calculating function for every case which gives us the new position of the this.el - this.calc = function (diffX, diffY) { - // The procedure is always the same - // First we snap the edge to the given grid (snapping to 1px grid is normal resizing) - var snap = this.snapToGrid(diffX, diffY); - - this._resizeTop(snap, true, false, true); - this._resizeLeft(snap, true, true, false, true); - }; - break; - - // Right-Top - case 'rt': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1 << 1); - - this._resizeTop(snap, true, true, true); - this._resizeRight(snap, true, true, true); - }; - break; - - // Right-Bottom - case 'rb': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - - this._resizeBottom(snap, true); - this._resizeRight(snap, true, true); - }; - break; - - // Left-Bottom - case 'lb': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1); - - this._resizeBottom(snap, true, true); - this._resizeLeft(snap, true, true, true, true); - }; - break; - - // Top - case 't': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1 << 1); - - this._resizeTop(snap); - }; - break; - - // Right - case 'r': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - - this._resizeRight(snap); - }; - break; - - // Bottom - case 'b': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 0); - - this._resizeBottom(snap); - }; - break; - - // Left - case 'l': - // s.a. - this.calc = function (diffX, diffY) { - var snap = this.snapToGrid(diffX, diffY, 1); - - this._resizeLeft(snap); - }; - break; - - // Rotation - case 'rot': - // s.a. - this.calc = function (diffX, diffY) { - - // yes this is kinda stupid but we need the mouse coords back... - var current = {x: diffX + this.parameters.p.x, y: diffY + this.parameters.p.y}; - - // start minus middle - var sAngle = Math.atan2((this.parameters.p.y - this.parameters.box.y - this.parameters.box.height / 2), (this.parameters.p.x - this.parameters.box.x - this.parameters.box.width / 2)); - - // end minus middle - var pAngle = Math.atan2((current.y - this.parameters.box.y - this.parameters.box.height / 2), (current.x - this.parameters.box.x - this.parameters.box.width / 2)); - - var angle = this.parameters.rotation + (pAngle - sAngle) * 180 / Math.PI + this.options.snapToAngle / 2; - - // We have to move the element to the center of the box first and change the rotation afterwards - // because rotation always works around a rotation-center, which is changed when moving the element - // We also set the new rotation center to the center of the box. - this.el.center(this.parameters.box.cx, this.parameters.box.cy).rotate(angle - (angle % this.options.snapToAngle), this.parameters.box.cx, this.parameters.box.cy); - }; - break; - - // Moving one single Point (needed when an element is deepSelected which means you can move every single point of the object) - case 'point': - this.calc = function (diffX, diffY) { - - // Snapping the point to the grid - var snap = this.snapToGrid(diffX, diffY, this.parameters.pointCoords[0], this.parameters.pointCoords[1]); - - // Get the point array - var array = this.el.array().valueOf(); - - // Changing the moved point in the array - array[this.parameters.i][0] = this.parameters.pointCoords[0] + snap[0]; - array[this.parameters.i][1] = this.parameters.pointCoords[1] + snap[1]; - - // And plot the new this.el - this.el.plot(array); - }; - } - - this.el.fire('resizestart', {dx: this.parameters.x, dy: this.parameters.y, event: event}); - // When resizing started, we have to register events for... - // Touches. - SVG.on(window, 'touchmove.resize', function(e) { - _this.update(e || window.event); - }); - SVG.on(window, 'touchend.resize', function() { - _this.done(); - }); - // Mouse. - SVG.on(window, 'mousemove.resize', function (e) { - _this.update(e || window.event); - }); - SVG.on(window, 'mouseup.resize', function () { - _this.done(); - }); - - }; - - // The update-function redraws the element every time the mouse is moving - ResizeHandler.prototype.update = function (event) { - - if (!event) { - if (this.lastUpdateCall) { - this.calc(this.lastUpdateCall[0], this.lastUpdateCall[1]); - } - return; - } - - // Calculate the difference between the mouseposition at start and now - var txPt = this._extractPosition(event); - var p = this.transformPoint(txPt.x, txPt.y); - - var diffX = p.x - this.parameters.p.x, - diffY = p.y - this.parameters.p.y; - - this.lastUpdateCall = [diffX, diffY]; - - // Calculate the new position and height / width of the element - this.calc(diffX, diffY); - - // Emit an event to say we have changed. - this.el.fire('resizing', {dx: diffX, dy: diffY, event: event}); - }; - - // Is called on mouseup. - // Removes the update-function from the mousemove event - ResizeHandler.prototype.done = function () { - this.lastUpdateCall = null; - SVG.off(window, 'mousemove.resize'); - SVG.off(window, 'mouseup.resize'); - SVG.off(window, 'touchmove.resize'); - SVG.off(window, 'touchend.resize'); - this.el.fire('resizedone'); - }; - - // The flag is used to determine whether the resizing is used with a left-Point (first bit) and top-point (second bit) - // In this cases the temp-values are calculated differently - ResizeHandler.prototype.snapToGrid = function (diffX, diffY, flag, pointCoordsY) { - - var temp; - - // If `pointCoordsY` is given, a single Point has to be snapped (deepSelect). That's why we need a different temp-value - if (typeof pointCoordsY !== 'undefined') { - // Note that flag = pointCoordsX in this case - temp = [(flag + diffX) % this.options.snapToGrid, (pointCoordsY + diffY) % this.options.snapToGrid]; - } else { - // We check if the flag is set and if not we set a default-value (both bits set - which means upper-left-edge) - flag = flag == null ? 1 | 1 << 1 : flag; - temp = [(this.parameters.box.x + diffX + (flag & 1 ? 0 : this.parameters.box.width)) % this.options.snapToGrid, (this.parameters.box.y + diffY + (flag & (1 << 1) ? 0 : this.parameters.box.height)) % this.options.snapToGrid]; - } - - if(diffX < 0) { - temp[0] -= this.options.snapToGrid; - } - if(diffY < 0) { - temp[1] -= this.options.snapToGrid; - } - - diffX -= (Math.abs(temp[0]) < this.options.snapToGrid / 2 ? - temp[0] : - temp[0] - (diffX < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); - diffY -= (Math.abs(temp[1]) < this.options.snapToGrid / 2 ? - temp[1] : - temp[1] - (diffY < 0 ? -this.options.snapToGrid : this.options.snapToGrid)); - - return this.constraintToBox(diffX, diffY, flag, pointCoordsY); - - }; - - // keep element within constrained box - ResizeHandler.prototype.constraintToBox = function (diffX, diffY, flag, pointCoordsY) { - //return [diffX, diffY] - var c = this.options.constraint || {}; - var orgX, orgY; - - if (typeof pointCoordsY !== 'undefined') { - orgX = flag; - orgY = pointCoordsY; - } else { - orgX = this.parameters.box.x + (flag & 1 ? 0 : this.parameters.box.width); - orgY = this.parameters.box.y + (flag & (1<<1) ? 0 : this.parameters.box.height); - } - - if (typeof c.minX !== 'undefined' && orgX + diffX < c.minX) { - diffX = c.minX - orgX; - } - - if (typeof c.maxX !== 'undefined' && orgX + diffX > c.maxX) { - diffX = c.maxX - orgX; - } - - if (typeof c.minY !== 'undefined' && orgY + diffY < c.minY) { - diffY = c.minY - orgY; - } - - if (typeof c.maxY !== 'undefined' && orgY + diffY > c.maxY) { - diffY = c.maxY - orgY; - } - - return [diffX, diffY]; - }; - - ResizeHandler.prototype.checkAspectRatio = function (snap, isReverse) { - if (!this.options.saveAspectRatio) { - return snap; - } - - var updatedSnap = snap.slice(); - var aspectRatio = this.parameters.box.width / this.parameters.box.height; - var newW = this.parameters.box.width + snap[0]; - var newH = this.parameters.box.height - snap[1]; - var newAspectRatio = newW / newH; - - if (newAspectRatio < aspectRatio) { - // Height is too big. Adapt it - updatedSnap[1] = newW / aspectRatio - this.parameters.box.height; - isReverse && (updatedSnap[1] = -updatedSnap[1]); - } else if (newAspectRatio > aspectRatio) { - // Width is too big. Adapt it - updatedSnap[0] = this.parameters.box.width - newH * aspectRatio; - isReverse && (updatedSnap[0] = -updatedSnap[0]); - } - - return updatedSnap; - }; - - SVG.extend(SVG.Element, { - // Resize element with mouse - resize: function (options) { - - (this.remember('_resizeHandler') || new ResizeHandler(this)).init(options || {}); - - return this; - - } - - }); - - SVG.Element.prototype.resize.defaults = { - snapToAngle: 0.1, // Specifies the speed the rotation is happening when moving the mouse - snapToGrid: 1, // Snaps to a grid of `snapToGrid` Pixels - constraint: {}, // keep element within constrained box - resizeLimits: { width: 0, height: 0 }, // rect limit size on resize - saveAspectRatio: false // Save aspect ratio when resizing using lt, rt, rb or lb points - }; - -}).call(this); -}()); diff --git a/hal-core/resources/web/js/lib/svg.resize.min.js b/hal-core/resources/web/js/lib/svg.resize.min.js deleted file mode 100644 index aef5e88a..00000000 --- a/hal-core/resources/web/js/lib/svg.resize.min.js +++ /dev/null @@ -1 +0,0 @@ -/*! svg.resize.js v1.4.3 MIT*/;!function(){"use strict";(function(){function t(t){t.remember("_resizeHandler",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),"stop"!==t){this.options={};for(var i in this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on("lt.resize",function(t){e.resize(t||window.event)}),this.el.on("rt.resize",function(t){e.resize(t||window.event)}),this.el.on("rb.resize",function(t){e.resize(t||window.event)}),this.el.on("lb.resize",function(t){e.resize(t||window.event)}),this.el.on("t.resize",function(t){e.resize(t||window.event)}),this.el.on("r.resize",function(t){e.resize(t||window.event)}),this.el.on("b.resize",function(t){e.resize(t||window.event)}),this.el.on("l.resize",function(t){e.resize(t||window.event)}),this.el.on("rot.resize",function(t){e.resize(t||window.event)}),this.el.on("point.resize",function(t){e.resize(t||window.event)}),this.update()}},t.prototype.stop=function(){return this.el.off("lt.resize"),this.el.off("rt.resize"),this.el.off("rb.resize"),this.el.off("lb.resize"),this.el.off("t.resize"),this.el.off("r.resize"),this.el.off("b.resize"),this.el.off("l.resize"),this.el.off("rot.resize"),this.el.off("point.resize"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},this.resizeLimits=this.options.resizeLimits||this.resize.defaults.resizeLimits,"text"===this.el.type&&(this.parameters.fontSize=this.el.attr()["font-size"]),void 0!==t.detail.i){var s=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[s[t.detail.i][0],s[t.detail.i][1]]}switch(this._resizeLeft=function(t,e,i,s,r){if(this.parameters.box.width-t[0]>=this.resizeLimits.width){if(i&&(t=this.checkAspectRatio(t,s)),"text"===this.parameters.type)return void(e&&(this.el.move(this.parameters.box.x+t[0],this.parameters.box.y),this.el.attr("font-size",this.parameters.fontSize-t[0])));this.el.width(this.parameters.box.width-t[0]),r?this.el.x(this.parameters.box.x+t[0]):this.el.move(this.parameters.box.x+t[0],this.parameters.box.y)}},this._resizeRight=function(t,e,i,s){if(this.parameters.box.width+t[0]>=this.resizeLimits.width){if(i&&(t=this.checkAspectRatio(t,s)),"text"===this.parameters.type)return void(e&&(this.el.move(this.parameters.box.x-t[0],this.parameters.box.y),this.el.attr("font-size",this.parameters.fontSize+t[0])));this.el.x(this.parameters.box.x).width(this.parameters.box.width+t[0])}},this._resizeTop=function(t,e,i,s){if(this.parameters.box.height-t[1]>=this.resizeLimits.height){if(e&&(t=this.checkAspectRatio(t,i)),"text"===this.parameters.type)return;this.el.height(this.parameters.box.height-t[1]),s?this.el.y(this.parameters.box.y+t[1]):this.el.move(this.parameters.box.x,this.parameters.box.y+t[1])}},this._resizeBottom=function(t,e,i){if(this.parameters.box.height+t[1]>=this.resizeLimits.height){if(e&&(t=this.checkAspectRatio(t,i)),"text"===this.parameters.type)return;this.el.y(this.parameters.box.y).height(this.parameters.box.height+t[1])}},t.type){case"lt":this.calc=function(t,e){var i=this.snapToGrid(t,e);this._resizeTop(i,!0,!1,!0),this._resizeLeft(i,!0,!0,!1,!0)};break;case"rt":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);this._resizeTop(i,!0,!0,!0),this._resizeRight(i,!0,!0,!0)};break;case"rb":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);this._resizeBottom(i,!0),this._resizeRight(i,!0,!0)};break;case"lb":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);this._resizeBottom(i,!0,!0),this._resizeLeft(i,!0,!0,!0,!0)};break;case"t":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);this._resizeTop(i)};break;case"r":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);this._resizeRight(i)};break;case"b":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);this._resizeBottom(i)};break;case"l":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);this._resizeLeft(i)};break;case"rot":this.calc=function(t,e){var i={x:t+this.parameters.p.x,y:e+this.parameters.p.y},s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(i.y-this.parameters.box.y-this.parameters.box.height/2,i.x-this.parameters.box.x-this.parameters.box.width/2),o=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(o-o%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case"point":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),s=this.el.array().valueOf();s[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],s[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(s)}}this.el.fire("resizestart",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,"touchmove.resize",function(t){e.update(t||window.event)}),SVG.on(window,"touchend.resize",function(){e.done()}),SVG.on(window,"mousemove.resize",function(t){e.update(t||window.event)}),SVG.on(window,"mouseup.resize",function(){e.done()})},t.prototype.update=function(t){if(!t)return void(this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1]));var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),s=i.x-this.parameters.p.x,r=i.y-this.parameters.p.y;this.lastUpdateCall=[s,r],this.calc(s,r),this.el.fire("resizing",{dx:s,dy:r,event:t})},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,"mousemove.resize"),SVG.off(window,"mouseup.resize"),SVG.off(window,"touchmove.resize"),SVG.off(window,"touchend.resize"),this.el.fire("resizedone")},t.prototype.snapToGrid=function(t,e,i,s){var r;return void 0!==s?r=[(i+t)%this.options.snapToGrid,(s+e)%this.options.snapToGrid]:(i=null==i?3:i,r=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(r[0]-=this.options.snapToGrid),e<0&&(r[1]-=this.options.snapToGrid),t-=Math.abs(r[0])a.maxX&&(t=a.maxX-r),void 0!==a.minY&&o+ea.maxY&&(e=a.maxY-o),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),s=this.parameters.box.width/this.parameters.box.height,r=this.parameters.box.width+t[0],o=this.parameters.box.height-t[1],a=r/o;return as&&(i[0]=this.parameters.box.width-o*s,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember("_resizeHandler")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},resizeLimits:{width:0,height:0},saveAspectRatio:!1}}).call(this)}(); \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/svg.select.LICENSE.txt b/hal-core/resources/web/js/lib/svg.select.LICENSE.txt deleted file mode 100644 index ca43f864..00000000 --- a/hal-core/resources/web/js/lib/svg.select.LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Fuzzy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/svg.select.js b/hal-core/resources/web/js/lib/svg.select.js deleted file mode 100644 index cfd8acb5..00000000 --- a/hal-core/resources/web/js/lib/svg.select.js +++ /dev/null @@ -1,425 +0,0 @@ -/*! -* svg.select.js - An extension of svg.js which allows to select elements with mouse -* @version 3.0.1 -* https://github.com/svgdotjs/svg.select.js -* -* @copyright Ulrich-Matthias Schäfer -* @license MIT -*/; -;(function() { -"use strict"; - -function SelectHandler(el) { - - this.el = el; - el.remember('_selectHandler', this); - this.pointSelection = {isSelected: false}; - this.rectSelection = {isSelected: false}; - - // helper list with position settings of each type of point - this.pointsList = { - lt: [ 0, 0 ], - rt: [ 'width', 0 ], - rb: [ 'width', 'height' ], - lb: [ 0, 'height' ], - t: [ 'width', 0 ], - r: [ 'width', 'height' ], - b: [ 'width', 'height' ], - l: [ 0, 'height' ] - }; - - // helper function to get point coordinates based on settings above and an object (bbox in our case) - this.pointCoord = function (setting, object, isPointCentered) { - var coord = typeof setting !== 'string' ? setting : object[setting]; - // Top, bottom, right and left points are placed in the center of element width/height - return isPointCentered ? coord / 2 : coord - } - - this.pointCoords = function (point, object) { - var settings = this.pointsList[point]; - - return { - x: this.pointCoord(settings[0], object, (point === 't' || point === 'b')), - y: this.pointCoord(settings[1], object, (point === 'r' || point === 'l')) - } - } -} - -SelectHandler.prototype.init = function (value, options) { - - var bbox = this.el.bbox(); - this.options = {}; - - // store defaults list of points in order to verify users config - var points = this.el.selectize.defaults.points; - - // Merging the defaults and the options-object together - for (var i in this.el.selectize.defaults) { - this.options[i] = this.el.selectize.defaults[i]; - if (options[i] !== undefined) { - this.options[i] = options[i]; - } - } - - // prepare & validate list of points to be added (or excluded) - var pointsLists = ['points', 'pointsExclude']; - - for (var i in pointsLists) { - var option = this.options[pointsLists[i]]; - - if (typeof option === 'string') { - if (option.length > 0) { - // if set as comma separated string list => convert it into an array - option = option.split(/\s*,\s*/i); - } else { - option = []; - } - } else if (typeof option === 'boolean' && pointsLists[i] === 'points') { - // this is not needed, but let's have it for legacy support - option = option ? points : []; - } - - this.options[pointsLists[i]] = option; - } - - // intersect correct all points options with users config (exclude unwanted points) - // ES5 -> NO arrow functions nor Array.includes() - this.options.points = [ points, this.options.points ].reduce( - function (a, b) { - return a.filter( - function (c) { - return b.indexOf(c) > -1; - } - ) - } - ); - - // exclude pointsExclude, if wanted - this.options.points = [ this.options.points, this.options.pointsExclude ].reduce( - function (a, b) { - return a.filter( - function (c) { - return b.indexOf(c) < 0; - } - ) - } - ); - - this.parent = this.el.parent(); - this.nested = (this.nested || this.parent.group()); - this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); - - // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving - if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) { - this.selectPoints(value); - } else { - this.selectRect(value); - } - - this.observe(); - this.cleanup(); - -}; - -SelectHandler.prototype.selectPoints = function (value) { - - this.pointSelection.isSelected = value; - - // When set is already there we dont have to create one - if (this.pointSelection.set) { - return this; - } - - // Create our set of elements - this.pointSelection.set = this.parent.set(); - // draw the points and mark the element as selected - this.drawPoints(); - - return this; - -}; - -// create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon -SelectHandler.prototype.getPointArray = function () { - var bbox = this.el.bbox(); - - return this.el.array().valueOf().map(function (el) { - return [el[0] - bbox.x, el[1] - bbox.y]; - }); -}; - -// Draws a points -SelectHandler.prototype.drawPoints = function () { - - var _this = this, array = this.getPointArray(); - - // go through the array of points - for (var i = 0, len = array.length; i < len; ++i) { - - var curriedEvent = (function (k) { - return function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire('point', {x: x, y: y, i: k, event: ev}); - }; - })(i); - - // add every point to the set - // add css-classes and a touchstart-event which fires our event for moving points - var point = this.drawPoint(array[i][0], array[i][1]) - .addClass(this.options.classPoints) - .addClass(this.options.classPoints + '_point') - .on('touchstart', curriedEvent) - .on('mousedown', curriedEvent) - this.pointSelection.set.add(point); - } -}; - -// The function to draw single point -SelectHandler.prototype.drawPoint = function (cx, cy) { - var pointType = this.options.pointType; - - switch (pointType) { - case 'circle': - return this.drawCircle(cx, cy); - case 'rect': - return this.drawRect(cx, cy); - default: - if (typeof pointType === 'function') { - return pointType.call(this, cx, cy); - } - - throw new Error('Unknown ' + pointType + ' point type!'); - } -}; - -// The function to draw the circle point -SelectHandler.prototype.drawCircle = function (cx, cy) { - return this.nested.circle(this.options.pointSize) - .stroke(this.options.pointStroke) - .fill(this.options.pointFill) - .center(cx, cy); -}; - -// The function to draw the rect point -SelectHandler.prototype.drawRect = function (cx, cy) { - return this.nested.rect(this.options.pointSize, this.options.pointSize) - .stroke(this.options.pointStroke) - .fill(this.options.pointFill) - .center(cx, cy); -}; - -// every time a point is moved, we have to update the positions of our point -SelectHandler.prototype.updatePointSelection = function () { - var array = this.getPointArray(); - - this.pointSelection.set.each(function (i) { - if (this.cx() === array[i][0] && this.cy() === array[i][1]) { - return; - } - this.center(array[i][0], array[i][1]); - }); -}; - -SelectHandler.prototype.updateRectSelection = function () { - var _this = this, bbox = this.el.bbox(); - - this.rectSelection.set.get(0).attr({ - width: bbox.width, - height: bbox.height - }); - - // set.get(1) is always in the upper left corner. no need to move it - if (this.options.points.length) { - this.options.points.map(function (point, index) { - var coords = _this.pointCoords(point, bbox); - - _this.rectSelection.set.get(index + 1).center(coords.x, coords.y); - }); - } - - if (this.options.rotationPoint) { - var length = this.rectSelection.set.length(); - - this.rectSelection.set.get(length - 1).center(bbox.width / 2, 20); - } -}; - -SelectHandler.prototype.selectRect = function (value) { - - var _this = this, bbox = this.el.bbox(); - - this.rectSelection.isSelected = value; - - // when set is already p - this.rectSelection.set = this.rectSelection.set || this.parent.set(); - - // helperFunction to create a mouse-down function which triggers the event specified in `eventName` - function getMoseDownFunc(eventName) { - return function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire(eventName, {x: x, y: y, event: ev}); - }; - } - - // create the selection-rectangle and add the css-class - if (!this.rectSelection.set.get(0)) { - this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect)); - } - - // Draw Points at the edges, if enabled - if (this.options.points.length && this.rectSelection.set.length() < 2) { - var ename ="touchstart", mname = "mousedown"; - - this.options.points.map(function (point, index) { - var coords = _this.pointCoords(point, bbox); - - var pointElement = _this.drawPoint(coords.x, coords.y) - .attr('class', _this.options.classPoints + '_' + point) - .on(mname, getMoseDownFunc(point)) - .on(ename, getMoseDownFunc(point)); - _this.rectSelection.set.add(pointElement); - }); - - this.rectSelection.set.each(function () { - this.addClass(_this.options.classPoints); - }); - } - - // draw rotationPint, if enabled - if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) { - - var curriedEvent = function (ev) { - ev = ev || window.event; - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - ev.stopPropagation(); - - var x = ev.pageX || ev.touches[0].pageX; - var y = ev.pageY || ev.touches[0].pageY; - _this.el.fire('rot', {x: x, y: y, event: ev}); - }; - - var pointElement = this.drawPoint(bbox.width / 2, 20) - .attr('class', this.options.classPoints + '_rot') - .on("touchstart", curriedEvent) - .on("mousedown", curriedEvent); - this.rectSelection.set.add(pointElement); - } - -}; - -SelectHandler.prototype.handler = function () { - - var bbox = this.el.bbox(); - this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y)); - - if (this.rectSelection.isSelected) { - this.updateRectSelection(); - } - - if (this.pointSelection.isSelected) { - this.updatePointSelection(); - } - -}; - -SelectHandler.prototype.observe = function () { - var _this = this; - - if (MutationObserver) { - if (this.rectSelection.isSelected || this.pointSelection.isSelected) { - this.observerInst = this.observerInst || new MutationObserver(function () { - _this.handler(); - }); - this.observerInst.observe(this.el.node, {attributes: true}); - } else { - try { - this.observerInst.disconnect(); - delete this.observerInst; - } catch (e) { - } - } - } else { - this.el.off('DOMAttrModified.select'); - - if (this.rectSelection.isSelected || this.pointSelection.isSelected) { - this.el.on('DOMAttrModified.select', function () { - _this.handler(); - }); - } - } -}; - -SelectHandler.prototype.cleanup = function () { - - //var _this = this; - - if (!this.rectSelection.isSelected && this.rectSelection.set) { - // stop watching the element, remove the selection - this.rectSelection.set.each(function () { - this.remove(); - }); - - this.rectSelection.set.clear(); - delete this.rectSelection.set; - } - - if (!this.pointSelection.isSelected && this.pointSelection.set) { - // Remove all points, clear the set, stop watching the element - this.pointSelection.set.each(function () { - this.remove(); - }); - - this.pointSelection.set.clear(); - delete this.pointSelection.set; - } - - if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) { - this.nested.remove(); - delete this.nested; - - } -}; - - -SVG.extend(SVG.Element, { - // Select element with mouse - selectize: function (value, options) { - - // Check the parameters and reassign if needed - if (typeof value === 'object') { - options = value; - value = true; - } - - var selectHandler = this.remember('_selectHandler') || new SelectHandler(this); - - selectHandler.init(value === undefined ? true : value, options || {}); - - return this; - - } -}); - -SVG.Element.prototype.selectize.defaults = { - points: ['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l'], // which points to draw, default all - pointsExclude: [], // easier option if to exclude few than rewrite all - classRect: 'svg_select_boundingRect', // Css-class added to the rect - classPoints: 'svg_select_points', // Css-class added to the points - pointSize: 7, // size of point - rotationPoint: true, // If true, rotation point is drawn. Needed for rotation! - deepSelect: false, // If true, moving of single points is possible (only line, polyline, polyon) - pointType: 'circle', // Point type: circle or rect, default circle - pointFill: "#000", // Point fill color - pointStroke: { width: 1, color: "#000" } // Point stroke properties -}; -}()); \ No newline at end of file diff --git a/hal-core/resources/web/js/lib/svg.select.min.js b/hal-core/resources/web/js/lib/svg.select.min.js deleted file mode 100644 index 56e232fb..00000000 --- a/hal-core/resources/web/js/lib/svg.select.min.js +++ /dev/null @@ -1 +0,0 @@ -/*! svg.select.js v3.0.1 MIT*/;!function(){"use strict";function i(t){(this.el=t).remember("_selectHandler",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:["width",0],rb:["width","height"],lb:[0,"height"],t:["width",0],r:["width","height"],b:["width","height"],l:[0,"height"]},this.pointCoord=function(t,e,i){var o="string"!=typeof t?t:e[t];return i?o/2:o},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,"t"===t||"b"===t),y:this.pointCoord(i[1],e,"r"===t||"l"===t)}}}i.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var o=this.el.selectize.defaults.points;for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);var n=["points","pointsExclude"];for(var s in n){var r=this.options[n[s]];"string"==typeof r?r=0 - - - {{message}}   - -
- ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/AlertListComponent.js b/hal-core/resources/web/js/vue/components/AlertListComponent.js deleted file mode 100644 index d82edba4..00000000 --- a/hal-core/resources/web/js/vue/components/AlertListComponent.js +++ /dev/null @@ -1,29 +0,0 @@ -import Alert from 'AlertComponent' -import { alertStore } from 'AlertStore' - -export default { - data() { - return { - alertStore - } - }, - components: { - Alert, - }, - mounted() { - alertStore.enableAutoLoad(true); - }, - template: ` - - -
- -
` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/App.js b/hal-core/resources/web/js/vue/components/App.js deleted file mode 100644 index 6a2131c6..00000000 --- a/hal-core/resources/web/js/vue/components/App.js +++ /dev/null @@ -1,17 +0,0 @@ -import AlertList from 'AlertListComponent' - -export default { - data() { - return { - } - }, - components: { - AlertList, - }, - template: ` -
- - - -
` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/EventActionComponent.js b/hal-core/resources/web/js/vue/components/EventActionComponent.js deleted file mode 100644 index 43bf215f..00000000 --- a/hal-core/resources/web/js/vue/components/EventActionComponent.js +++ /dev/null @@ -1,49 +0,0 @@ -import {eventStore} from 'EventStore' -import { alertStore } from 'AlertStore' - -export default { - props: { - 'id': {default: 0}, - }, - data() { - let internalEvent = eventStore.getEvent(this.id); - return { - event: internalEvent, - checkboxChecked: internalEvent.data?.valueStr == 'ON', - } - }, - methods: { - changeState(changeEvent) { - fetch('/api/event?' + new URLSearchParams({ - id: this.event.id, - action: "modify", - data: changeEvent.target.type == "checkbox" ? this.checkboxChecked : changeEvent.target.value - }).toString(), { - method: 'PUT', - headers: { - 'Accept': 'application/json', - }, - }) - .then(response => response.json()) - .then(json => { - if (json['error'] != null) { - alertStore.alertError(json['error']); - return; - } - }) - } - }, - template: ` -
- - - -
- ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/EventDetailPageComponent.js b/hal-core/resources/web/js/vue/components/EventDetailPageComponent.js deleted file mode 100644 index 4d1a5446..00000000 --- a/hal-core/resources/web/js/vue/components/EventDetailPageComponent.js +++ /dev/null @@ -1,88 +0,0 @@ -import {eventStore} from 'EventStore' - -export default { - data() { - var id = this.$route.params.id; - return { - id: id, - event: eventStore.getEvent(id), - } - }, - template: ` -

Details for {{event.name}}

- -
-
-
Configuration
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event ID:{{event.id}
Name:{{event.name}}
Type:{{event.configType}}
Owner:{{event.owner}}

State: -
- - - -
- -
-
-
{{name}}:{{confValue}}
-
-
-
- -
-
-
History data
-
- - - - - - - - - - -
TimestampRaw Data
{{event.data.timestamp}}{{event.data.value}}
-
-
-
- - - ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js b/hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js deleted file mode 100644 index 2a05b6ca..00000000 --- a/hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js +++ /dev/null @@ -1,21 +0,0 @@ -import EventTable from 'EventTableComponent' - -export default { - props: { - }, - components: { - EventTable, - }, - template: ` -

Event Overview

- -
-
-
Local Events
-
- -
-
-
- ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/EventTableComponent.js b/hal-core/resources/web/js/vue/components/EventTableComponent.js deleted file mode 100644 index 18f62de9..00000000 --- a/hal-core/resources/web/js/vue/components/EventTableComponent.js +++ /dev/null @@ -1,34 +0,0 @@ -import {eventStore} from 'EventStore' -import EventTableRow from 'EventTableRowComponent' - -export default { - data() { - return { - eventStore, - } - }, - components: { - EventTableRow, - }, - mounted() { - eventStore.enableAutoLoad(true); - }, - unmounted() { - eventStore.enableAutoLoad(false); - }, - template: ` - - - - - - - - - - - - -
NameTypeDataLast UpdateActions
- ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/components/EventTableRowComponent.js b/hal-core/resources/web/js/vue/components/EventTableRowComponent.js deleted file mode 100644 index 0d7afc24..00000000 --- a/hal-core/resources/web/js/vue/components/EventTableRowComponent.js +++ /dev/null @@ -1,29 +0,0 @@ -import {eventStore} from 'EventStore' -import EventAction from 'EventActionComponent' - -export default { - props: { - 'id': {default: 0}, - }, - data() { - let event = eventStore.getEvent(this.id) - return { - event: event, - timestamp: getRelTimestamp(event.data?.timestamp) - } - }, - components: { - EventAction - }, - template: ` - - {{ event.name }} - {{ event.configType }} - {{ event.data?.valueStr }} - {{ timestamp }} - - - - - ` -} \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/stores/AlertStore.js b/hal-core/resources/web/js/vue/stores/AlertStore.js deleted file mode 100644 index 9bd2abde..00000000 --- a/hal-core/resources/web/js/vue/stores/AlertStore.js +++ /dev/null @@ -1,46 +0,0 @@ -import { reactive } from 'vue' - -export const alertStore = reactive({ - loading: false, - alerts: [], - pollTimer: null, - - enableAutoLoad(enabled = true) { - if (enabled && this.pollTimer !== null) { - this.pollTimer = setInterval(function() { - load(); - }, 3000); - } else { - clearInterval(this.timer); - this.pollTimer == null; - } - }, - - load() { - fetch('/api/alert?action=poll') - .then(response => response.json()) - .then(data => { - data.forEach(alert => { - alert.source = "server"; - alertStore.alerts.push(alert); - }); - }); - }, - - setLoading(l = true) { - this.loading = l; - }, - - addAlertInfo(message) { - this.alerts.push({source: "local", level: "info", message: message}); - }, - addAlertSuccess(message) { - this.alerts.push({source: "local", level: "success", message: message}); - }, - addAlertWarning(message) { - this.alerts.push({source: "local", level: "warning", message: message}); - }, - AddAlertError(message) { - this.alerts.push({source: "local", level: "danger", message: message}); - }, -}); \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/stores/EventStore.js b/hal-core/resources/web/js/vue/stores/EventStore.js deleted file mode 100644 index 93efe359..00000000 --- a/hal-core/resources/web/js/vue/stores/EventStore.js +++ /dev/null @@ -1,49 +0,0 @@ -import { reactive } from 'vue' -import { alertStore } from 'AlertStore' - -export const eventStore = reactive({ - events: [], - pollTimerId: null, - pollInterval: 10000, // 10 sec - - enableAutoLoad(enabled = true) { - if (enabled) { - if (this.pollTimerId !== null) - return; // Timer already initialized - eventStore.load(); - - this.pollTimerId = setInterval(function() { - eventStore.load(); - }, this.pollInterval); - } else { - clearInterval(this.pollTimerId); - this.pollTimerId == null; - } - }, - - load() { - alertStore.setLoading(true); - - fetch('/api/event', { - method: 'GET', - headers: { - 'Accept': 'application/json', - }, - }) - .then(response => response.json()) - .then(json => { - if (json['error'] != null) { - alertStore.alertError(json['error']); - return; - } - - eventStore.events = json; - alertStore.setLoading(false); - }) - }, - - getEvent(id) { - let event = eventStore.events.find(event => id == event.id); - return event; - } -}); \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/stores/SensorStore.js b/hal-core/resources/web/js/vue/stores/SensorStore.js deleted file mode 100644 index d57e91ff..00000000 --- a/hal-core/resources/web/js/vue/stores/SensorStore.js +++ /dev/null @@ -1,44 +0,0 @@ -import { reactive } from 'vue' -import { alertStore } from 'AlertStore' - -export const sensorStore = reactive({ - sensors: {}, - pollTimerId: null, - pollInterval: 10000, // 10 sec - - enableAutoLoad(enabled = true) { - if (enabled) { - if (this.pollTimerId !== null) - return; // Timer already initialized - sensorStore.load(); - - this.pollTimerId = setInterval(function() { - sensorStore.load(); - }, this.pollInterval); - } else { - clearInterval(this.pollTimerId); - this.pollTimerId == null; - } - }, - - load() { - alertStore.setLoading(true); - - fetch('/api/sensor', { - method: 'GET', - headers: { - 'Accept': 'application/json', - }, - }) - .then(response => response.json()) - .then(json => { - if (json['error'] != null) { - alertStore.alertError(json['error']); - return; - } - - sensorStore.sensors = json; - alertStore.setLoading(false); - }) - }, -}); \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/vue-router.esm-browser.js b/hal-core/resources/web/js/vue/vue-router.esm-browser.js deleted file mode 100644 index 64c8126c..00000000 --- a/hal-core/resources/web/js/vue/vue-router.esm-browser.js +++ /dev/null @@ -1,3615 +0,0 @@ -/*! - * vue-router v4.2.5 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */ -import { getCurrentInstance, inject, onUnmounted, onDeactivated, onActivated, computed, unref, watchEffect, defineComponent, reactive, h, provide, ref, watch, shallowRef, shallowReactive, nextTick } from 'vue'; -import { setupDevtoolsPlugin } from '@vue/devtools-api'; - -const isBrowser = typeof window !== 'undefined'; - -function isESModule(obj) { - return obj.__esModule || obj[Symbol.toStringTag] === 'Module'; -} -const assign = Object.assign; -function applyToParams(fn, params) { - const newParams = {}; - for (const key in params) { - const value = params[key]; - newParams[key] = isArray(value) - ? value.map(fn) - : fn(value); - } - return newParams; -} -const noop = () => { }; -/** - * Typesafe alternative to Array.isArray - * https://github.com/microsoft/TypeScript/pull/48228 - */ -const isArray = Array.isArray; - -function warn(msg) { - // avoid using ...args as it breaks in older Edge builds - const args = Array.from(arguments).slice(1); - console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args)); -} - -const TRAILING_SLASH_RE = /\/$/; -const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, ''); -/** - * Transforms a URI into a normalized history location - * - * @param parseQuery - * @param location - URI to normalize - * @param currentLocation - current absolute location. Allows resolving relative - * paths. Must start with `/`. Defaults to `/` - * @returns a normalized history location - */ -function parseURL(parseQuery, location, currentLocation = '/') { - let path, query = {}, searchString = '', hash = ''; - // Could use URL and URLSearchParams but IE 11 doesn't support it - // TODO: move to new URL() - const hashPos = location.indexOf('#'); - let searchPos = location.indexOf('?'); - // the hash appears before the search, so it's not part of the search string - if (hashPos < searchPos && hashPos >= 0) { - searchPos = -1; - } - if (searchPos > -1) { - path = location.slice(0, searchPos); - searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length); - query = parseQuery(searchString); - } - if (hashPos > -1) { - path = path || location.slice(0, hashPos); - // keep the # character - hash = location.slice(hashPos, location.length); - } - // no search and no query - path = resolveRelativePath(path != null ? path : location, currentLocation); - // empty path means a relative query or hash `?foo=f`, `#thing` - return { - fullPath: path + (searchString && '?') + searchString + hash, - path, - query, - hash, - }; -} -/** - * Stringifies a URL object - * - * @param stringifyQuery - * @param location - */ -function stringifyURL(stringifyQuery, location) { - const query = location.query ? stringifyQuery(location.query) : ''; - return location.path + (query && '?') + query + (location.hash || ''); -} -/** - * Strips off the base from the beginning of a location.pathname in a non-case-sensitive way. - * - * @param pathname - location.pathname - * @param base - base to strip off - */ -function stripBase(pathname, base) { - // no base or base is not found at the beginning - if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase())) - return pathname; - return pathname.slice(base.length) || '/'; -} -/** - * Checks if two RouteLocation are equal. This means that both locations are - * pointing towards the same {@link RouteRecord} and that all `params`, `query` - * parameters and `hash` are the same - * - * @param stringifyQuery - A function that takes a query object of type LocationQueryRaw and returns a string representation of it. - * @param a - first {@link RouteLocation} - * @param b - second {@link RouteLocation} - */ -function isSameRouteLocation(stringifyQuery, a, b) { - const aLastIndex = a.matched.length - 1; - const bLastIndex = b.matched.length - 1; - return (aLastIndex > -1 && - aLastIndex === bLastIndex && - isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) && - isSameRouteLocationParams(a.params, b.params) && - stringifyQuery(a.query) === stringifyQuery(b.query) && - a.hash === b.hash); -} -/** - * Check if two `RouteRecords` are equal. Takes into account aliases: they are - * considered equal to the `RouteRecord` they are aliasing. - * - * @param a - first {@link RouteRecord} - * @param b - second {@link RouteRecord} - */ -function isSameRouteRecord(a, b) { - // since the original record has an undefined value for aliasOf - // but all aliases point to the original record, this will always compare - // the original record - return (a.aliasOf || a) === (b.aliasOf || b); -} -function isSameRouteLocationParams(a, b) { - if (Object.keys(a).length !== Object.keys(b).length) - return false; - for (const key in a) { - if (!isSameRouteLocationParamsValue(a[key], b[key])) - return false; - } - return true; -} -function isSameRouteLocationParamsValue(a, b) { - return isArray(a) - ? isEquivalentArray(a, b) - : isArray(b) - ? isEquivalentArray(b, a) - : a === b; -} -/** - * Check if two arrays are the same or if an array with one single entry is the - * same as another primitive value. Used to check query and parameters - * - * @param a - array of values - * @param b - array of values or a single value - */ -function isEquivalentArray(a, b) { - return isArray(b) - ? a.length === b.length && a.every((value, i) => value === b[i]) - : a.length === 1 && a[0] === b; -} -/** - * Resolves a relative path that starts with `.`. - * - * @param to - path location we are resolving - * @param from - currentLocation.path, should start with `/` - */ -function resolveRelativePath(to, from) { - if (to.startsWith('/')) - return to; - if (!from.startsWith('/')) { - warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`); - return to; - } - if (!to) - return from; - const fromSegments = from.split('/'); - const toSegments = to.split('/'); - const lastToSegment = toSegments[toSegments.length - 1]; - // make . and ./ the same (../ === .., ../../ === ../..) - // this is the same behavior as new URL() - if (lastToSegment === '..' || lastToSegment === '.') { - toSegments.push(''); - } - let position = fromSegments.length - 1; - let toPosition; - let segment; - for (toPosition = 0; toPosition < toSegments.length; toPosition++) { - segment = toSegments[toPosition]; - // we stay on the same position - if (segment === '.') - continue; - // go up in the from array - if (segment === '..') { - // we can't go below zero, but we still need to increment toPosition - if (position > 1) - position--; - // continue - } - // we reached a non-relative path, we stop here - else - break; - } - return (fromSegments.slice(0, position).join('/') + - '/' + - toSegments - // ensure we use at least the last element in the toSegments - .slice(toPosition - (toPosition === toSegments.length ? 1 : 0)) - .join('/')); -} - -var NavigationType; -(function (NavigationType) { - NavigationType["pop"] = "pop"; - NavigationType["push"] = "push"; -})(NavigationType || (NavigationType = {})); -var NavigationDirection; -(function (NavigationDirection) { - NavigationDirection["back"] = "back"; - NavigationDirection["forward"] = "forward"; - NavigationDirection["unknown"] = ""; -})(NavigationDirection || (NavigationDirection = {})); -/** - * Starting location for Histories - */ -const START = ''; -// Generic utils -/** - * Normalizes a base by removing any trailing slash and reading the base tag if - * present. - * - * @param base - base to normalize - */ -function normalizeBase(base) { - if (!base) { - if (isBrowser) { - // respect tag - const baseEl = document.querySelector('base'); - base = (baseEl && baseEl.getAttribute('href')) || '/'; - // strip full URL origin - base = base.replace(/^\w+:\/\/[^\/]+/, ''); - } - else { - base = '/'; - } - } - // ensure leading slash when it was removed by the regex above avoid leading - // slash with hash because the file could be read from the disk like file:// - // and the leading slash would cause problems - if (base[0] !== '/' && base[0] !== '#') - base = '/' + base; - // remove the trailing slash so all other method can just do `base + fullPath` - // to build an href - return removeTrailingSlash(base); -} -// remove any character before the hash -const BEFORE_HASH_RE = /^[^#]+#/; -function createHref(base, location) { - return base.replace(BEFORE_HASH_RE, '#') + location; -} - -function getElementPosition(el, offset) { - const docRect = document.documentElement.getBoundingClientRect(); - const elRect = el.getBoundingClientRect(); - return { - behavior: offset.behavior, - left: elRect.left - docRect.left - (offset.left || 0), - top: elRect.top - docRect.top - (offset.top || 0), - }; -} -const computeScrollPosition = () => ({ - left: window.pageXOffset, - top: window.pageYOffset, -}); -function scrollToPosition(position) { - let scrollToOptions; - if ('el' in position) { - const positionEl = position.el; - const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#'); - /** - * `id`s can accept pretty much any characters, including CSS combinators - * like `>` or `~`. It's still possible to retrieve elements using - * `document.getElementById('~')` but it needs to be escaped when using - * `document.querySelector('#\\~')` for it to be valid. The only - * requirements for `id`s are them to be unique on the page and to not be - * empty (`id=""`). Because of that, when passing an id selector, it should - * be properly escaped for it to work with `querySelector`. We could check - * for the id selector to be simple (no CSS combinators `+ >~`) but that - * would make things inconsistent since they are valid characters for an - * `id` but would need to be escaped when using `querySelector`, breaking - * their usage and ending up in no selector returned. Selectors need to be - * escaped: - * - * - `#1-thing` becomes `#\31 -thing` - * - `#with~symbols` becomes `#with\\~symbols` - * - * - More information about the topic can be found at - * https://mathiasbynens.be/notes/html5-id-class. - * - Practical example: https://mathiasbynens.be/demo/html5-id - */ - if (typeof position.el === 'string') { - if (!isIdSelector || !document.getElementById(position.el.slice(1))) { - try { - const foundEl = document.querySelector(position.el); - if (isIdSelector && foundEl) { - warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`); - // return to avoid other warnings - return; - } - } - catch (err) { - warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`); - // return to avoid other warnings - return; - } - } - } - const el = typeof positionEl === 'string' - ? isIdSelector - ? document.getElementById(positionEl.slice(1)) - : document.querySelector(positionEl) - : positionEl; - if (!el) { - warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`); - return; - } - scrollToOptions = getElementPosition(el, position); - } - else { - scrollToOptions = position; - } - if ('scrollBehavior' in document.documentElement.style) - window.scrollTo(scrollToOptions); - else { - window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset); - } -} -function getScrollKey(path, delta) { - const position = history.state ? history.state.position - delta : -1; - return position + path; -} -const scrollPositions = new Map(); -function saveScrollPosition(key, scrollPosition) { - scrollPositions.set(key, scrollPosition); -} -function getSavedScrollPosition(key) { - const scroll = scrollPositions.get(key); - // consume it so it's not used again - scrollPositions.delete(key); - return scroll; -} -// TODO: RFC about how to save scroll position -/** - * ScrollBehavior instance used by the router to compute and restore the scroll - * position when navigating. - */ -// export interface ScrollHandler { -// // returns a scroll position that can be saved in history -// compute(): ScrollPositionEntry -// // can take an extended ScrollPositionEntry -// scroll(position: ScrollPosition): void -// } -// export const scrollHandler: ScrollHandler = { -// compute: computeScroll, -// scroll: scrollToPosition, -// } - -let createBaseLocation = () => location.protocol + '//' + location.host; -/** - * Creates a normalized history location from a window.location object - * @param base - The base path - * @param location - The window.location object - */ -function createCurrentLocation(base, location) { - const { pathname, search, hash } = location; - // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end - const hashPos = base.indexOf('#'); - if (hashPos > -1) { - let slicePos = hash.includes(base.slice(hashPos)) - ? base.slice(hashPos).length - : 1; - let pathFromHash = hash.slice(slicePos); - // prepend the starting slash to hash so the url starts with /# - if (pathFromHash[0] !== '/') - pathFromHash = '/' + pathFromHash; - return stripBase(pathFromHash, ''); - } - const path = stripBase(pathname, base); - return path + search + hash; -} -function useHistoryListeners(base, historyState, currentLocation, replace) { - let listeners = []; - let teardowns = []; - // TODO: should it be a stack? a Dict. Check if the popstate listener - // can trigger twice - let pauseState = null; - const popStateHandler = ({ state, }) => { - const to = createCurrentLocation(base, location); - const from = currentLocation.value; - const fromState = historyState.value; - let delta = 0; - if (state) { - currentLocation.value = to; - historyState.value = state; - // ignore the popstate and reset the pauseState - if (pauseState && pauseState === from) { - pauseState = null; - return; - } - delta = fromState ? state.position - fromState.position : 0; - } - else { - replace(to); - } - // Here we could also revert the navigation by calling history.go(-delta) - // this listener will have to be adapted to not trigger again and to wait for the url - // to be updated before triggering the listeners. Some kind of validation function would also - // need to be passed to the listeners so the navigation can be accepted - // call all listeners - listeners.forEach(listener => { - listener(currentLocation.value, from, { - delta, - type: NavigationType.pop, - direction: delta - ? delta > 0 - ? NavigationDirection.forward - : NavigationDirection.back - : NavigationDirection.unknown, - }); - }); - }; - function pauseListeners() { - pauseState = currentLocation.value; - } - function listen(callback) { - // set up the listener and prepare teardown callbacks - listeners.push(callback); - const teardown = () => { - const index = listeners.indexOf(callback); - if (index > -1) - listeners.splice(index, 1); - }; - teardowns.push(teardown); - return teardown; - } - function beforeUnloadListener() { - const { history } = window; - if (!history.state) - return; - history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), ''); - } - function destroy() { - for (const teardown of teardowns) - teardown(); - teardowns = []; - window.removeEventListener('popstate', popStateHandler); - window.removeEventListener('beforeunload', beforeUnloadListener); - } - // set up the listeners and prepare teardown callbacks - window.addEventListener('popstate', popStateHandler); - // TODO: could we use 'pagehide' or 'visibilitychange' instead? - // https://developer.chrome.com/blog/page-lifecycle-api/ - window.addEventListener('beforeunload', beforeUnloadListener, { - passive: true, - }); - return { - pauseListeners, - listen, - destroy, - }; -} -/** - * Creates a state object - */ -function buildState(back, current, forward, replaced = false, computeScroll = false) { - return { - back, - current, - forward, - replaced, - position: window.history.length, - scroll: computeScroll ? computeScrollPosition() : null, - }; -} -function useHistoryStateNavigation(base) { - const { history, location } = window; - // private variables - const currentLocation = { - value: createCurrentLocation(base, location), - }; - const historyState = { value: history.state }; - // build current history entry as this is a fresh navigation - if (!historyState.value) { - changeLocation(currentLocation.value, { - back: null, - current: currentLocation.value, - forward: null, - // the length is off by one, we need to decrease it - position: history.length - 1, - replaced: true, - // don't add a scroll as the user may have an anchor, and we want - // scrollBehavior to be triggered without a saved position - scroll: null, - }, true); - } - function changeLocation(to, state, replace) { - /** - * if a base tag is provided, and we are on a normal domain, we have to - * respect the provided `base` attribute because pushState() will use it and - * potentially erase anything before the `#` like at - * https://github.com/vuejs/router/issues/685 where a base of - * `/folder/#` but a base of `/` would erase the `/folder/` section. If - * there is no host, the `` tag makes no sense and if there isn't a - * base tag we can just use everything after the `#`. - */ - const hashIndex = base.indexOf('#'); - const url = hashIndex > -1 - ? (location.host && document.querySelector('base') - ? base - : base.slice(hashIndex)) + to - : createBaseLocation() + base + to; - try { - // BROWSER QUIRK - // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds - history[replace ? 'replaceState' : 'pushState'](state, '', url); - historyState.value = state; - } - catch (err) { - { - warn('Error with push/replace State', err); - } - // Force the navigation, this also resets the call count - location[replace ? 'replace' : 'assign'](url); - } - } - function replace(to, data) { - const state = assign({}, history.state, buildState(historyState.value.back, - // keep back and forward entries but override current position - to, historyState.value.forward, true), data, { position: historyState.value.position }); - changeLocation(to, state, true); - currentLocation.value = to; - } - function push(to, data) { - // Add to current entry the information of where we are going - // as well as saving the current position - const currentState = assign({}, - // use current history state to gracefully handle a wrong call to - // history.replaceState - // https://github.com/vuejs/router/issues/366 - historyState.value, history.state, { - forward: to, - scroll: computeScrollPosition(), - }); - if (!history.state) { - warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` + - `history.replaceState(history.state, '', url)\n\n` + - `You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`); - } - changeLocation(currentState.current, currentState, true); - const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data); - changeLocation(to, state, false); - currentLocation.value = to; - } - return { - location: currentLocation, - state: historyState, - push, - replace, - }; -} -/** - * Creates an HTML5 history. Most common history for single page applications. - * - * @param base - - */ -function createWebHistory(base) { - base = normalizeBase(base); - const historyNavigation = useHistoryStateNavigation(base); - const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace); - function go(delta, triggerListeners = true) { - if (!triggerListeners) - historyListeners.pauseListeners(); - history.go(delta); - } - const routerHistory = assign({ - // it's overridden right after - location: '', - base, - go, - createHref: createHref.bind(null, base), - }, historyNavigation, historyListeners); - Object.defineProperty(routerHistory, 'location', { - enumerable: true, - get: () => historyNavigation.location.value, - }); - Object.defineProperty(routerHistory, 'state', { - enumerable: true, - get: () => historyNavigation.state.value, - }); - return routerHistory; -} - -/** - * Creates an in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere. - * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`. - * - * @param base - Base applied to all urls, defaults to '/' - * @returns a history object that can be passed to the router constructor - */ -function createMemoryHistory(base = '') { - let listeners = []; - let queue = [START]; - let position = 0; - base = normalizeBase(base); - function setLocation(location) { - position++; - if (position !== queue.length) { - // we are in the middle, we remove everything from here in the queue - queue.splice(position); - } - queue.push(location); - } - function triggerListeners(to, from, { direction, delta }) { - const info = { - direction, - delta, - type: NavigationType.pop, - }; - for (const callback of listeners) { - callback(to, from, info); - } - } - const routerHistory = { - // rewritten by Object.defineProperty - location: START, - // TODO: should be kept in queue - state: {}, - base, - createHref: createHref.bind(null, base), - replace(to) { - // remove current entry and decrement position - queue.splice(position--, 1); - setLocation(to); - }, - push(to, data) { - setLocation(to); - }, - listen(callback) { - listeners.push(callback); - return () => { - const index = listeners.indexOf(callback); - if (index > -1) - listeners.splice(index, 1); - }; - }, - destroy() { - listeners = []; - queue = [START]; - position = 0; - }, - go(delta, shouldTrigger = true) { - const from = this.location; - const direction = - // we are considering delta === 0 going forward, but in abstract mode - // using 0 for the delta doesn't make sense like it does in html5 where - // it reloads the page - delta < 0 ? NavigationDirection.back : NavigationDirection.forward; - position = Math.max(0, Math.min(position + delta, queue.length - 1)); - if (shouldTrigger) { - triggerListeners(this.location, from, { - direction, - delta, - }); - } - }, - }; - Object.defineProperty(routerHistory, 'location', { - enumerable: true, - get: () => queue[position], - }); - return routerHistory; -} - -/** - * Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to - * handle any URL is not possible. - * - * @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `` tag - * in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState() - * calls**, meaning that if you use a `` tag, it's `href` value **has to match this parameter** (ignoring anything - * after the `#`). - * - * @example - * ```js - * // at https://example.com/folder - * createWebHashHistory() // gives a url of `https://example.com/folder#` - * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#` - * // if the `#` is provided in the base, it won't be added by `createWebHashHistory` - * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/` - * // you should avoid doing this because it changes the original url and breaks copying urls - * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#` - * - * // at file:///usr/etc/folder/index.html - * // for locations with no `host`, the base is ignored - * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#` - * ``` - */ -function createWebHashHistory(base) { - // Make sure this implementation is fine in terms of encoding, specially for IE11 - // for `file://`, directly use the pathname and ignore the base - // location.pathname contains an initial `/` even at the root: `https://example.com` - base = location.host ? base || location.pathname + location.search : ''; - // allow the user to provide a `#` in the middle: `/base/#/app` - if (!base.includes('#')) - base += '#'; - if (!base.endsWith('#/') && !base.endsWith('#')) { - warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`); - } - return createWebHistory(base); -} - -function isRouteLocation(route) { - return typeof route === 'string' || (route && typeof route === 'object'); -} -function isRouteName(name) { - return typeof name === 'string' || typeof name === 'symbol'; -} - -/** - * Initial route location where the router is. Can be used in navigation guards - * to differentiate the initial navigation. - * - * @example - * ```js - * import { START_LOCATION } from 'vue-router' - * - * router.beforeEach((to, from) => { - * if (from === START_LOCATION) { - * // initial navigation - * } - * }) - * ``` - */ -const START_LOCATION_NORMALIZED = { - path: '/', - name: undefined, - params: {}, - query: {}, - hash: '', - fullPath: '/', - matched: [], - meta: {}, - redirectedFrom: undefined, -}; - -const NavigationFailureSymbol = Symbol('navigation failure' ); -/** - * Enumeration with all possible types for navigation failures. Can be passed to - * {@link isNavigationFailure} to check for specific failures. - */ -var NavigationFailureType; -(function (NavigationFailureType) { - /** - * An aborted navigation is a navigation that failed because a navigation - * guard returned `false` or called `next(false)` - */ - NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted"; - /** - * A cancelled navigation is a navigation that failed because a more recent - * navigation finished started (not necessarily finished). - */ - NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled"; - /** - * A duplicated navigation is a navigation that failed because it was - * initiated while already being at the exact same location. - */ - NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated"; -})(NavigationFailureType || (NavigationFailureType = {})); -// DEV only debug messages -const ErrorTypeMessages = { - [1 /* ErrorTypes.MATCHER_NOT_FOUND */]({ location, currentLocation }) { - return `No match for\n ${JSON.stringify(location)}${currentLocation - ? '\nwhile being at\n' + JSON.stringify(currentLocation) - : ''}`; - }, - [2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */]({ from, to, }) { - return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`; - }, - [4 /* ErrorTypes.NAVIGATION_ABORTED */]({ from, to }) { - return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`; - }, - [8 /* ErrorTypes.NAVIGATION_CANCELLED */]({ from, to }) { - return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`; - }, - [16 /* ErrorTypes.NAVIGATION_DUPLICATED */]({ from, to }) { - return `Avoided redundant navigation to current location: "${from.fullPath}".`; - }, -}; -function createRouterError(type, params) { - // keep full error messages in cjs versions - { - return assign(new Error(ErrorTypeMessages[type](params)), { - type, - [NavigationFailureSymbol]: true, - }, params); - } -} -function isNavigationFailure(error, type) { - return (error instanceof Error && - NavigationFailureSymbol in error && - (type == null || !!(error.type & type))); -} -const propertiesToLog = ['params', 'query', 'hash']; -function stringifyRoute(to) { - if (typeof to === 'string') - return to; - if ('path' in to) - return to.path; - const location = {}; - for (const key of propertiesToLog) { - if (key in to) - location[key] = to[key]; - } - return JSON.stringify(location, null, 2); -} - -// default pattern for a param: non-greedy everything but / -const BASE_PARAM_PATTERN = '[^/]+?'; -const BASE_PATH_PARSER_OPTIONS = { - sensitive: false, - strict: false, - start: true, - end: true, -}; -// Special Regex characters that must be escaped in static tokens -const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g; -/** - * Creates a path parser from an array of Segments (a segment is an array of Tokens) - * - * @param segments - array of segments returned by tokenizePath - * @param extraOptions - optional options for the regexp - * @returns a PathParser - */ -function tokensToParser(segments, extraOptions) { - const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions); - // the amount of scores is the same as the length of segments except for the root segment "/" - const score = []; - // the regexp as a string - let pattern = options.start ? '^' : ''; - // extracted keys - const keys = []; - for (const segment of segments) { - // the root segment needs special treatment - const segmentScores = segment.length ? [] : [90 /* PathScore.Root */]; - // allow trailing slash - if (options.strict && !segment.length) - pattern += '/'; - for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) { - const token = segment[tokenIndex]; - // resets the score if we are inside a sub-segment /:a-other-:b - let subSegmentScore = 40 /* PathScore.Segment */ + - (options.sensitive ? 0.25 /* PathScore.BonusCaseSensitive */ : 0); - if (token.type === 0 /* TokenType.Static */) { - // prepend the slash if we are starting a new segment - if (!tokenIndex) - pattern += '/'; - pattern += token.value.replace(REGEX_CHARS_RE, '\\$&'); - subSegmentScore += 40 /* PathScore.Static */; - } - else if (token.type === 1 /* TokenType.Param */) { - const { value, repeatable, optional, regexp } = token; - keys.push({ - name: value, - repeatable, - optional, - }); - const re = regexp ? regexp : BASE_PARAM_PATTERN; - // the user provided a custom regexp /:id(\\d+) - if (re !== BASE_PARAM_PATTERN) { - subSegmentScore += 10 /* PathScore.BonusCustomRegExp */; - // make sure the regexp is valid before using it - try { - new RegExp(`(${re})`); - } - catch (err) { - throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` + - err.message); - } - } - // when we repeat we must take care of the repeating leading slash - let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`; - // prepend the slash if we are starting a new segment - if (!tokenIndex) - subPattern = - // avoid an optional / if there are more segments e.g. /:p?-static - // or /:p?-:p2 - optional && segment.length < 2 - ? `(?:/${subPattern})` - : '/' + subPattern; - if (optional) - subPattern += '?'; - pattern += subPattern; - subSegmentScore += 20 /* PathScore.Dynamic */; - if (optional) - subSegmentScore += -8 /* PathScore.BonusOptional */; - if (repeatable) - subSegmentScore += -20 /* PathScore.BonusRepeatable */; - if (re === '.*') - subSegmentScore += -50 /* PathScore.BonusWildcard */; - } - segmentScores.push(subSegmentScore); - } - // an empty array like /home/ -> [[{home}], []] - // if (!segment.length) pattern += '/' - score.push(segmentScores); - } - // only apply the strict bonus to the last score - if (options.strict && options.end) { - const i = score.length - 1; - score[i][score[i].length - 1] += 0.7000000000000001 /* PathScore.BonusStrict */; - } - // TODO: dev only warn double trailing slash - if (!options.strict) - pattern += '/?'; - if (options.end) - pattern += '$'; - // allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else - else if (options.strict) - pattern += '(?:/|$)'; - const re = new RegExp(pattern, options.sensitive ? '' : 'i'); - function parse(path) { - const match = path.match(re); - const params = {}; - if (!match) - return null; - for (let i = 1; i < match.length; i++) { - const value = match[i] || ''; - const key = keys[i - 1]; - params[key.name] = value && key.repeatable ? value.split('/') : value; - } - return params; - } - function stringify(params) { - let path = ''; - // for optional parameters to allow to be empty - let avoidDuplicatedSlash = false; - for (const segment of segments) { - if (!avoidDuplicatedSlash || !path.endsWith('/')) - path += '/'; - avoidDuplicatedSlash = false; - for (const token of segment) { - if (token.type === 0 /* TokenType.Static */) { - path += token.value; - } - else if (token.type === 1 /* TokenType.Param */) { - const { value, repeatable, optional } = token; - const param = value in params ? params[value] : ''; - if (isArray(param) && !repeatable) { - throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`); - } - const text = isArray(param) - ? param.join('/') - : param; - if (!text) { - if (optional) { - // if we have more than one optional param like /:a?-static we don't need to care about the optional param - if (segment.length < 2) { - // remove the last slash as we could be at the end - if (path.endsWith('/')) - path = path.slice(0, -1); - // do not append a slash on the next iteration - else - avoidDuplicatedSlash = true; - } - } - else - throw new Error(`Missing required param "${value}"`); - } - path += text; - } - } - } - // avoid empty path when we have multiple optional params - return path || '/'; - } - return { - re, - score, - keys, - parse, - stringify, - }; -} -/** - * Compares an array of numbers as used in PathParser.score and returns a - * number. This function can be used to `sort` an array - * - * @param a - first array of numbers - * @param b - second array of numbers - * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b - * should be sorted first - */ -function compareScoreArray(a, b) { - let i = 0; - while (i < a.length && i < b.length) { - const diff = b[i] - a[i]; - // only keep going if diff === 0 - if (diff) - return diff; - i++; - } - // if the last subsegment was Static, the shorter segments should be sorted first - // otherwise sort the longest segment first - if (a.length < b.length) { - return a.length === 1 && a[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */ - ? -1 - : 1; - } - else if (a.length > b.length) { - return b.length === 1 && b[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */ - ? 1 - : -1; - } - return 0; -} -/** - * Compare function that can be used with `sort` to sort an array of PathParser - * - * @param a - first PathParser - * @param b - second PathParser - * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b - */ -function comparePathParserScore(a, b) { - let i = 0; - const aScore = a.score; - const bScore = b.score; - while (i < aScore.length && i < bScore.length) { - const comp = compareScoreArray(aScore[i], bScore[i]); - // do not return if both are equal - if (comp) - return comp; - i++; - } - if (Math.abs(bScore.length - aScore.length) === 1) { - if (isLastScoreNegative(aScore)) - return 1; - if (isLastScoreNegative(bScore)) - return -1; - } - // if a and b share the same score entries but b has more, sort b first - return bScore.length - aScore.length; - // this is the ternary version - // return aScore.length < bScore.length - // ? 1 - // : aScore.length > bScore.length - // ? -1 - // : 0 -} -/** - * This allows detecting splats at the end of a path: /home/:id(.*)* - * - * @param score - score to check - * @returns true if the last entry is negative - */ -function isLastScoreNegative(score) { - const last = score[score.length - 1]; - return score.length > 0 && last[last.length - 1] < 0; -} - -const ROOT_TOKEN = { - type: 0 /* TokenType.Static */, - value: '', -}; -const VALID_PARAM_RE = /[a-zA-Z0-9_]/; -// After some profiling, the cache seems to be unnecessary because tokenizePath -// (the slowest part of adding a route) is very fast -// const tokenCache = new Map() -function tokenizePath(path) { - if (!path) - return [[]]; - if (path === '/') - return [[ROOT_TOKEN]]; - if (!path.startsWith('/')) { - throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".` - ); - } - // if (tokenCache.has(path)) return tokenCache.get(path)! - function crash(message) { - throw new Error(`ERR (${state})/"${buffer}": ${message}`); - } - let state = 0 /* TokenizerState.Static */; - let previousState = state; - const tokens = []; - // the segment will always be valid because we get into the initial state - // with the leading / - let segment; - function finalizeSegment() { - if (segment) - tokens.push(segment); - segment = []; - } - // index on the path - let i = 0; - // char at index - let char; - // buffer of the value read - let buffer = ''; - // custom regexp for a param - let customRe = ''; - function consumeBuffer() { - if (!buffer) - return; - if (state === 0 /* TokenizerState.Static */) { - segment.push({ - type: 0 /* TokenType.Static */, - value: buffer, - }); - } - else if (state === 1 /* TokenizerState.Param */ || - state === 2 /* TokenizerState.ParamRegExp */ || - state === 3 /* TokenizerState.ParamRegExpEnd */) { - if (segment.length > 1 && (char === '*' || char === '+')) - crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`); - segment.push({ - type: 1 /* TokenType.Param */, - value: buffer, - regexp: customRe, - repeatable: char === '*' || char === '+', - optional: char === '*' || char === '?', - }); - } - else { - crash('Invalid state to consume buffer'); - } - buffer = ''; - } - function addCharToBuffer() { - buffer += char; - } - while (i < path.length) { - char = path[i++]; - if (char === '\\' && state !== 2 /* TokenizerState.ParamRegExp */) { - previousState = state; - state = 4 /* TokenizerState.EscapeNext */; - continue; - } - switch (state) { - case 0 /* TokenizerState.Static */: - if (char === '/') { - if (buffer) { - consumeBuffer(); - } - finalizeSegment(); - } - else if (char === ':') { - consumeBuffer(); - state = 1 /* TokenizerState.Param */; - } - else { - addCharToBuffer(); - } - break; - case 4 /* TokenizerState.EscapeNext */: - addCharToBuffer(); - state = previousState; - break; - case 1 /* TokenizerState.Param */: - if (char === '(') { - state = 2 /* TokenizerState.ParamRegExp */; - } - else if (VALID_PARAM_RE.test(char)) { - addCharToBuffer(); - } - else { - consumeBuffer(); - state = 0 /* TokenizerState.Static */; - // go back one character if we were not modifying - if (char !== '*' && char !== '?' && char !== '+') - i--; - } - break; - case 2 /* TokenizerState.ParamRegExp */: - // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix) - // it already works by escaping the closing ) - // https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB# - // is this really something people need since you can also write - // /prefix_:p()_suffix - if (char === ')') { - // handle the escaped ) - if (customRe[customRe.length - 1] == '\\') - customRe = customRe.slice(0, -1) + char; - else - state = 3 /* TokenizerState.ParamRegExpEnd */; - } - else { - customRe += char; - } - break; - case 3 /* TokenizerState.ParamRegExpEnd */: - // same as finalizing a param - consumeBuffer(); - state = 0 /* TokenizerState.Static */; - // go back one character if we were not modifying - if (char !== '*' && char !== '?' && char !== '+') - i--; - customRe = ''; - break; - default: - crash('Unknown state'); - break; - } - } - if (state === 2 /* TokenizerState.ParamRegExp */) - crash(`Unfinished custom RegExp for param "${buffer}"`); - consumeBuffer(); - finalizeSegment(); - // tokenCache.set(path, tokens) - return tokens; -} - -function createRouteRecordMatcher(record, parent, options) { - const parser = tokensToParser(tokenizePath(record.path), options); - // warn against params with the same name - { - const existingKeys = new Set(); - for (const key of parser.keys) { - if (existingKeys.has(key.name)) - warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`); - existingKeys.add(key.name); - } - } - const matcher = assign(parser, { - record, - parent, - // these needs to be populated by the parent - children: [], - alias: [], - }); - if (parent) { - // both are aliases or both are not aliases - // we don't want to mix them because the order is used when - // passing originalRecord in Matcher.addRoute - if (!matcher.record.aliasOf === !parent.record.aliasOf) - parent.children.push(matcher); - } - return matcher; -} - -/** - * Creates a Router Matcher. - * - * @internal - * @param routes - array of initial routes - * @param globalOptions - global route options - */ -function createRouterMatcher(routes, globalOptions) { - // normalized ordered array of matchers - const matchers = []; - const matcherMap = new Map(); - globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions); - function getRecordMatcher(name) { - return matcherMap.get(name); - } - function addRoute(record, parent, originalRecord) { - // used later on to remove by name - const isRootAdd = !originalRecord; - const mainNormalizedRecord = normalizeRouteRecord(record); - { - checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent); - } - // we might be the child of an alias - mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record; - const options = mergeOptions(globalOptions, record); - // generate an array of records to correctly handle aliases - const normalizedRecords = [ - mainNormalizedRecord, - ]; - if ('alias' in record) { - const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias; - for (const alias of aliases) { - normalizedRecords.push(assign({}, mainNormalizedRecord, { - // this allows us to hold a copy of the `components` option - // so that async components cache is hold on the original record - components: originalRecord - ? originalRecord.record.components - : mainNormalizedRecord.components, - path: alias, - // we might be the child of an alias - aliasOf: originalRecord - ? originalRecord.record - : mainNormalizedRecord, - // the aliases are always of the same kind as the original since they - // are defined on the same record - })); - } - } - let matcher; - let originalMatcher; - for (const normalizedRecord of normalizedRecords) { - const { path } = normalizedRecord; - // Build up the path for nested routes if the child isn't an absolute - // route. Only add the / delimiter if the child path isn't empty and if the - // parent path doesn't have a trailing slash - if (parent && path[0] !== '/') { - const parentPath = parent.record.path; - const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/'; - normalizedRecord.path = - parent.record.path + (path && connectingSlash + path); - } - if (normalizedRecord.path === '*') { - throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' + - 'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.'); - } - // create the object beforehand, so it can be passed to children - matcher = createRouteRecordMatcher(normalizedRecord, parent, options); - if (parent && path[0] === '/') - checkMissingParamsInAbsolutePath(matcher, parent); - // if we are an alias we must tell the original record that we exist, - // so we can be removed - if (originalRecord) { - originalRecord.alias.push(matcher); - { - checkSameParams(originalRecord, matcher); - } - } - else { - // otherwise, the first record is the original and others are aliases - originalMatcher = originalMatcher || matcher; - if (originalMatcher !== matcher) - originalMatcher.alias.push(matcher); - // remove the route if named and only for the top record (avoid in nested calls) - // this works because the original record is the first one - if (isRootAdd && record.name && !isAliasRecord(matcher)) - removeRoute(record.name); - } - if (mainNormalizedRecord.children) { - const children = mainNormalizedRecord.children; - for (let i = 0; i < children.length; i++) { - addRoute(children[i], matcher, originalRecord && originalRecord.children[i]); - } - } - // if there was no original record, then the first one was not an alias and all - // other aliases (if any) need to reference this record when adding children - originalRecord = originalRecord || matcher; - // TODO: add normalized records for more flexibility - // if (parent && isAliasRecord(originalRecord)) { - // parent.children.push(originalRecord) - // } - // Avoid adding a record that doesn't display anything. This allows passing through records without a component to - // not be reached and pass through the catch all route - if ((matcher.record.components && - Object.keys(matcher.record.components).length) || - matcher.record.name || - matcher.record.redirect) { - insertMatcher(matcher); - } - } - return originalMatcher - ? () => { - // since other matchers are aliases, they should be removed by the original matcher - removeRoute(originalMatcher); - } - : noop; - } - function removeRoute(matcherRef) { - if (isRouteName(matcherRef)) { - const matcher = matcherMap.get(matcherRef); - if (matcher) { - matcherMap.delete(matcherRef); - matchers.splice(matchers.indexOf(matcher), 1); - matcher.children.forEach(removeRoute); - matcher.alias.forEach(removeRoute); - } - } - else { - const index = matchers.indexOf(matcherRef); - if (index > -1) { - matchers.splice(index, 1); - if (matcherRef.record.name) - matcherMap.delete(matcherRef.record.name); - matcherRef.children.forEach(removeRoute); - matcherRef.alias.forEach(removeRoute); - } - } - } - function getRoutes() { - return matchers; - } - function insertMatcher(matcher) { - let i = 0; - while (i < matchers.length && - comparePathParserScore(matcher, matchers[i]) >= 0 && - // Adding children with empty path should still appear before the parent - // https://github.com/vuejs/router/issues/1124 - (matcher.record.path !== matchers[i].record.path || - !isRecordChildOf(matcher, matchers[i]))) - i++; - matchers.splice(i, 0, matcher); - // only add the original record to the name map - if (matcher.record.name && !isAliasRecord(matcher)) - matcherMap.set(matcher.record.name, matcher); - } - function resolve(location, currentLocation) { - let matcher; - let params = {}; - let path; - let name; - if ('name' in location && location.name) { - matcher = matcherMap.get(location.name); - if (!matcher) - throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, { - location, - }); - // warn if the user is passing invalid params so they can debug it better when they get removed - { - const invalidParams = Object.keys(location.params || {}).filter(paramName => !matcher.keys.find(k => k.name === paramName)); - if (invalidParams.length) { - warn(`Discarded invalid param(s) "${invalidParams.join('", "')}" when navigating. See https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#414-2022-08-22 for more details.`); - } - } - name = matcher.record.name; - params = assign( - // paramsFromLocation is a new object - paramsFromLocation(currentLocation.params, - // only keep params that exist in the resolved location - // TODO: only keep optional params coming from a parent record - matcher.keys.filter(k => !k.optional).map(k => k.name)), - // discard any existing params in the current location that do not exist here - // #1497 this ensures better active/exact matching - location.params && - paramsFromLocation(location.params, matcher.keys.map(k => k.name))); - // throws if cannot be stringified - path = matcher.stringify(params); - } - else if ('path' in location) { - // no need to resolve the path with the matcher as it was provided - // this also allows the user to control the encoding - path = location.path; - if (!path.startsWith('/')) { - warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://github.com/vuejs/router/issues/new/choose.`); - } - matcher = matchers.find(m => m.re.test(path)); - // matcher should have a value after the loop - if (matcher) { - // we know the matcher works because we tested the regexp - params = matcher.parse(path); - name = matcher.record.name; - } - // location is a relative path - } - else { - // match by name or path of current route - matcher = currentLocation.name - ? matcherMap.get(currentLocation.name) - : matchers.find(m => m.re.test(currentLocation.path)); - if (!matcher) - throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, { - location, - currentLocation, - }); - name = matcher.record.name; - // since we are navigating to the same location, we don't need to pick the - // params like when `name` is provided - params = assign({}, currentLocation.params, location.params); - path = matcher.stringify(params); - } - const matched = []; - let parentMatcher = matcher; - while (parentMatcher) { - // reversed order so parents are at the beginning - matched.unshift(parentMatcher.record); - parentMatcher = parentMatcher.parent; - } - return { - name, - path, - params, - matched, - meta: mergeMetaFields(matched), - }; - } - // add initial routes - routes.forEach(route => addRoute(route)); - return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher }; -} -function paramsFromLocation(params, keys) { - const newParams = {}; - for (const key of keys) { - if (key in params) - newParams[key] = params[key]; - } - return newParams; -} -/** - * Normalizes a RouteRecordRaw. Creates a copy - * - * @param record - * @returns the normalized version - */ -function normalizeRouteRecord(record) { - return { - path: record.path, - redirect: record.redirect, - name: record.name, - meta: record.meta || {}, - aliasOf: undefined, - beforeEnter: record.beforeEnter, - props: normalizeRecordProps(record), - children: record.children || [], - instances: {}, - leaveGuards: new Set(), - updateGuards: new Set(), - enterCallbacks: {}, - components: 'components' in record - ? record.components || null - : record.component && { default: record.component }, - }; -} -/** - * Normalize the optional `props` in a record to always be an object similar to - * components. Also accept a boolean for components. - * @param record - */ -function normalizeRecordProps(record) { - const propsObject = {}; - // props does not exist on redirect records, but we can set false directly - const props = record.props || false; - if ('component' in record) { - propsObject.default = props; - } - else { - // NOTE: we could also allow a function to be applied to every component. - // Would need user feedback for use cases - for (const name in record.components) - propsObject[name] = typeof props === 'object' ? props[name] : props; - } - return propsObject; -} -/** - * Checks if a record or any of its parent is an alias - * @param record - */ -function isAliasRecord(record) { - while (record) { - if (record.record.aliasOf) - return true; - record = record.parent; - } - return false; -} -/** - * Merge meta fields of an array of records - * - * @param matched - array of matched records - */ -function mergeMetaFields(matched) { - return matched.reduce((meta, record) => assign(meta, record.meta), {}); -} -function mergeOptions(defaults, partialOptions) { - const options = {}; - for (const key in defaults) { - options[key] = key in partialOptions ? partialOptions[key] : defaults[key]; - } - return options; -} -function isSameParam(a, b) { - return (a.name === b.name && - a.optional === b.optional && - a.repeatable === b.repeatable); -} -/** - * Check if a path and its alias have the same required params - * - * @param a - original record - * @param b - alias record - */ -function checkSameParams(a, b) { - for (const key of a.keys) { - if (!key.optional && !b.keys.find(isSameParam.bind(null, key))) - return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`); - } - for (const key of b.keys) { - if (!key.optional && !a.keys.find(isSameParam.bind(null, key))) - return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`); - } -} -/** - * A route with a name and a child with an empty path without a name should warn when adding the route - * - * @param mainNormalizedRecord - RouteRecordNormalized - * @param parent - RouteRecordMatcher - */ -function checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent) { - if (parent && - parent.record.name && - !mainNormalizedRecord.name && - !mainNormalizedRecord.path) { - warn(`The route named "${String(parent.record.name)}" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.`); - } -} -function checkMissingParamsInAbsolutePath(record, parent) { - for (const key of parent.keys) { - if (!record.keys.find(isSameParam.bind(null, key))) - return warn(`Absolute path "${record.record.path}" must have the exact same param named "${key.name}" as its parent "${parent.record.path}".`); - } -} -function isRecordChildOf(record, parent) { - return parent.children.some(child => child === record || isRecordChildOf(record, child)); -} - -/** - * Encoding Rules ⣠= Space Path: ⣠" < > # ? { } Query: ⣠" < > # & = Hash: ⣠" - * < > ` - * - * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2) - * defines some extra characters to be encoded. Most browsers do not encode them - * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to - * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`) - * plus `-._~`. This extra safety should be applied to query by patching the - * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\` - * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\` - * into a `/` if directly typed in. The _backtick_ (`````) should also be - * encoded everywhere because some browsers like FF encode it when directly - * written while others don't. Safari and IE don't encode ``"<>{}``` in hash. - */ -// const EXTRA_RESERVED_RE = /[!'()*]/g -// const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16) -const HASH_RE = /#/g; // %23 -const AMPERSAND_RE = /&/g; // %26 -const SLASH_RE = /\//g; // %2F -const EQUAL_RE = /=/g; // %3D -const IM_RE = /\?/g; // %3F -const PLUS_RE = /\+/g; // %2B -/** - * NOTE: It's not clear to me if we should encode the + symbol in queries, it - * seems to be less flexible than not doing so and I can't find out the legacy - * systems requiring this for regular requests like text/html. In the standard, - * the encoding of the plus character is only mentioned for - * application/x-www-form-urlencoded - * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo - * leave the plus character as is in queries. To be more flexible, we allow the - * plus character on the query, but it can also be manually encoded by the user. - * - * Resources: - * - https://url.spec.whatwg.org/#urlencoded-parsing - * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20 - */ -const ENC_BRACKET_OPEN_RE = /%5B/g; // [ -const ENC_BRACKET_CLOSE_RE = /%5D/g; // ] -const ENC_CARET_RE = /%5E/g; // ^ -const ENC_BACKTICK_RE = /%60/g; // ` -const ENC_CURLY_OPEN_RE = /%7B/g; // { -const ENC_PIPE_RE = /%7C/g; // | -const ENC_CURLY_CLOSE_RE = /%7D/g; // } -const ENC_SPACE_RE = /%20/g; // } -/** - * Encode characters that need to be encoded on the path, search and hash - * sections of the URL. - * - * @internal - * @param text - string to encode - * @returns encoded string - */ -function commonEncode(text) { - return encodeURI('' + text) - .replace(ENC_PIPE_RE, '|') - .replace(ENC_BRACKET_OPEN_RE, '[') - .replace(ENC_BRACKET_CLOSE_RE, ']'); -} -/** - * Encode characters that need to be encoded on the hash section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeHash(text) { - return commonEncode(text) - .replace(ENC_CURLY_OPEN_RE, '{') - .replace(ENC_CURLY_CLOSE_RE, '}') - .replace(ENC_CARET_RE, '^'); -} -/** - * Encode characters that need to be encoded query values on the query - * section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeQueryValue(text) { - return (commonEncode(text) - // Encode the space as +, encode the + to differentiate it from the space - .replace(PLUS_RE, '%2B') - .replace(ENC_SPACE_RE, '+') - .replace(HASH_RE, '%23') - .replace(AMPERSAND_RE, '%26') - .replace(ENC_BACKTICK_RE, '`') - .replace(ENC_CURLY_OPEN_RE, '{') - .replace(ENC_CURLY_CLOSE_RE, '}') - .replace(ENC_CARET_RE, '^')); -} -/** - * Like `encodeQueryValue` but also encodes the `=` character. - * - * @param text - string to encode - */ -function encodeQueryKey(text) { - return encodeQueryValue(text).replace(EQUAL_RE, '%3D'); -} -/** - * Encode characters that need to be encoded on the path section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodePath(text) { - return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F'); -} -/** - * Encode characters that need to be encoded on the path section of the URL as a - * param. This function encodes everything {@link encodePath} does plus the - * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty - * string instead. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeParam(text) { - return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F'); -} -/** - * Decode text using `decodeURIComponent`. Returns the original text if it - * fails. - * - * @param text - string to decode - * @returns decoded string - */ -function decode(text) { - try { - return decodeURIComponent('' + text); - } - catch (err) { - warn(`Error decoding "${text}". Using original value`); - } - return '' + text; -} - -/** - * Transforms a queryString into a {@link LocationQuery} object. Accept both, a - * version with the leading `?` and without Should work as URLSearchParams - - * @internal - * - * @param search - search string to parse - * @returns a query object - */ -function parseQuery(search) { - const query = {}; - // avoid creating an object with an empty key and empty value - // because of split('&') - if (search === '' || search === '?') - return query; - const hasLeadingIM = search[0] === '?'; - const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&'); - for (let i = 0; i < searchParams.length; ++i) { - // pre decode the + into space - const searchParam = searchParams[i].replace(PLUS_RE, ' '); - // allow the = character - const eqPos = searchParam.indexOf('='); - const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos)); - const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1)); - if (key in query) { - // an extra variable for ts types - let currentValue = query[key]; - if (!isArray(currentValue)) { - currentValue = query[key] = [currentValue]; - } - currentValue.push(value); - } - else { - query[key] = value; - } - } - return query; -} -/** - * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it - * doesn't prepend a `?` - * - * @internal - * - * @param query - query object to stringify - * @returns string version of the query without the leading `?` - */ -function stringifyQuery(query) { - let search = ''; - for (let key in query) { - const value = query[key]; - key = encodeQueryKey(key); - if (value == null) { - // only null adds the value - if (value !== undefined) { - search += (search.length ? '&' : '') + key; - } - continue; - } - // keep null values - const values = isArray(value) - ? value.map(v => v && encodeQueryValue(v)) - : [value && encodeQueryValue(value)]; - values.forEach(value => { - // skip undefined values in arrays as if they were not present - // smaller code than using filter - if (value !== undefined) { - // only append & with non-empty search - search += (search.length ? '&' : '') + key; - if (value != null) - search += '=' + value; - } - }); - } - return search; -} -/** - * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting - * numbers into strings, removing keys with an undefined value and replacing - * undefined with null in arrays - * - * @param query - query object to normalize - * @returns a normalized query object - */ -function normalizeQuery(query) { - const normalizedQuery = {}; - for (const key in query) { - const value = query[key]; - if (value !== undefined) { - normalizedQuery[key] = isArray(value) - ? value.map(v => (v == null ? null : '' + v)) - : value == null - ? value - : '' + value; - } - } - return normalizedQuery; -} - -/** - * RouteRecord being rendered by the closest ancestor Router View. Used for - * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View - * Location Matched - * - * @internal - */ -const matchedRouteKey = Symbol('router view location matched' ); -/** - * Allows overriding the router view depth to control which component in - * `matched` is rendered. rvd stands for Router View Depth - * - * @internal - */ -const viewDepthKey = Symbol('router view depth' ); -/** - * Allows overriding the router instance returned by `useRouter` in tests. r - * stands for router - * - * @internal - */ -const routerKey = Symbol('router' ); -/** - * Allows overriding the current route returned by `useRoute` in tests. rl - * stands for route location - * - * @internal - */ -const routeLocationKey = Symbol('route location' ); -/** - * Allows overriding the current route used by router-view. Internally this is - * used when the `route` prop is passed. - * - * @internal - */ -const routerViewLocationKey = Symbol('router view location' ); - -/** - * Create a list of callbacks that can be reset. Used to create before and after navigation guards list - */ -function useCallbacks() { - let handlers = []; - function add(handler) { - handlers.push(handler); - return () => { - const i = handlers.indexOf(handler); - if (i > -1) - handlers.splice(i, 1); - }; - } - function reset() { - handlers = []; - } - return { - add, - list: () => handlers.slice(), - reset, - }; -} - -function registerGuard(record, name, guard) { - const removeFromList = () => { - record[name].delete(guard); - }; - onUnmounted(removeFromList); - onDeactivated(removeFromList); - onActivated(() => { - record[name].add(guard); - }); - record[name].add(guard); -} -/** - * Add a navigation guard that triggers whenever the component for the current - * location is about to be left. Similar to {@link beforeRouteLeave} but can be - * used in any component. The guard is removed when the component is unmounted. - * - * @param leaveGuard - {@link NavigationGuard} - */ -function onBeforeRouteLeave(leaveGuard) { - if (!getCurrentInstance()) { - warn('getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function'); - return; - } - const activeRecord = inject(matchedRouteKey, - // to avoid warning - {}).value; - if (!activeRecord) { - warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside a component child of . Maybe you called it inside of App.vue?'); - return; - } - registerGuard(activeRecord, 'leaveGuards', leaveGuard); -} -/** - * Add a navigation guard that triggers whenever the current location is about - * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any - * component. The guard is removed when the component is unmounted. - * - * @param updateGuard - {@link NavigationGuard} - */ -function onBeforeRouteUpdate(updateGuard) { - if (!getCurrentInstance()) { - warn('getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function'); - return; - } - const activeRecord = inject(matchedRouteKey, - // to avoid warning - {}).value; - if (!activeRecord) { - warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside a component child of . Maybe you called it inside of App.vue?'); - return; - } - registerGuard(activeRecord, 'updateGuards', updateGuard); -} -function guardToPromiseFn(guard, to, from, record, name) { - // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place - const enterCallbackArray = record && - // name is defined if record is because of the function overload - (record.enterCallbacks[name] = record.enterCallbacks[name] || []); - return () => new Promise((resolve, reject) => { - const next = (valid) => { - if (valid === false) { - reject(createRouterError(4 /* ErrorTypes.NAVIGATION_ABORTED */, { - from, - to, - })); - } - else if (valid instanceof Error) { - reject(valid); - } - else if (isRouteLocation(valid)) { - reject(createRouterError(2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */, { - from: to, - to: valid, - })); - } - else { - if (enterCallbackArray && - // since enterCallbackArray is truthy, both record and name also are - record.enterCallbacks[name] === enterCallbackArray && - typeof valid === 'function') { - enterCallbackArray.push(valid); - } - resolve(); - } - }; - // wrapping with Promise.resolve allows it to work with both async and sync guards - const guardReturn = guard.call(record && record.instances[name], to, from, canOnlyBeCalledOnce(next, to, from) ); - let guardCall = Promise.resolve(guardReturn); - if (guard.length < 3) - guardCall = guardCall.then(next); - if (guard.length > 2) { - const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`; - if (typeof guardReturn === 'object' && 'then' in guardReturn) { - guardCall = guardCall.then(resolvedValue => { - // @ts-expect-error: _called is added at canOnlyBeCalledOnce - if (!next._called) { - warn(message); - return Promise.reject(new Error('Invalid navigation guard')); - } - return resolvedValue; - }); - } - else if (guardReturn !== undefined) { - // @ts-expect-error: _called is added at canOnlyBeCalledOnce - if (!next._called) { - warn(message); - reject(new Error('Invalid navigation guard')); - return; - } - } - } - guardCall.catch(err => reject(err)); - }); -} -function canOnlyBeCalledOnce(next, to, from) { - let called = 0; - return function () { - if (called++ === 1) - warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`); - // @ts-expect-error: we put it in the original one because it's easier to check - next._called = true; - if (called === 1) - next.apply(null, arguments); - }; -} -function extractComponentsGuards(matched, guardType, to, from) { - const guards = []; - for (const record of matched) { - if (!record.components && !record.children.length) { - warn(`Record with path "${record.path}" is either missing a "component(s)"` + - ` or "children" property.`); - } - for (const name in record.components) { - let rawComponent = record.components[name]; - { - if (!rawComponent || - (typeof rawComponent !== 'object' && - typeof rawComponent !== 'function')) { - warn(`Component "${name}" in record with path "${record.path}" is not` + - ` a valid component. Received "${String(rawComponent)}".`); - // throw to ensure we stop here but warn to ensure the message isn't - // missed by the user - throw new Error('Invalid route component'); - } - else if ('then' in rawComponent) { - // warn if user wrote import('/component.vue') instead of () => - // import('./component.vue') - warn(`Component "${name}" in record with path "${record.path}" is a ` + - `Promise instead of a function that returns a Promise. Did you ` + - `write "import('./MyPage.vue')" instead of ` + - `"() => import('./MyPage.vue')" ? This will break in ` + - `production if not fixed.`); - const promise = rawComponent; - rawComponent = () => promise; - } - else if (rawComponent.__asyncLoader && - // warn only once per component - !rawComponent.__warnedDefineAsync) { - rawComponent.__warnedDefineAsync = true; - warn(`Component "${name}" in record with path "${record.path}" is defined ` + - `using "defineAsyncComponent()". ` + - `Write "() => import('./MyPage.vue')" instead of ` + - `"defineAsyncComponent(() => import('./MyPage.vue'))".`); - } - } - // skip update and leave guards if the route component is not mounted - if (guardType !== 'beforeRouteEnter' && !record.instances[name]) - continue; - if (isRouteComponent(rawComponent)) { - // __vccOpts is added by vue-class-component and contain the regular options - const options = rawComponent.__vccOpts || rawComponent; - const guard = options[guardType]; - guard && guards.push(guardToPromiseFn(guard, to, from, record, name)); - } - else { - // start requesting the chunk already - let componentPromise = rawComponent(); - if (!('catch' in componentPromise)) { - warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`); - componentPromise = Promise.resolve(componentPromise); - } - guards.push(() => componentPromise.then(resolved => { - if (!resolved) - return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`)); - const resolvedComponent = isESModule(resolved) - ? resolved.default - : resolved; - // replace the function with the resolved component - // cannot be null or undefined because we went into the for loop - record.components[name] = resolvedComponent; - // __vccOpts is added by vue-class-component and contain the regular options - const options = resolvedComponent.__vccOpts || resolvedComponent; - const guard = options[guardType]; - return guard && guardToPromiseFn(guard, to, from, record, name)(); - })); - } - } - } - return guards; -} -/** - * Allows differentiating lazy components from functional components and vue-class-component - * @internal - * - * @param component - */ -function isRouteComponent(component) { - return (typeof component === 'object' || - 'displayName' in component || - 'props' in component || - '__vccOpts' in component); -} -/** - * Ensures a route is loaded, so it can be passed as o prop to ``. - * - * @param route - resolved route to load - */ -function loadRouteLocation(route) { - return route.matched.every(record => record.redirect) - ? Promise.reject(new Error('Cannot load a route that redirects.')) - : Promise.all(route.matched.map(record => record.components && - Promise.all(Object.keys(record.components).reduce((promises, name) => { - const rawComponent = record.components[name]; - if (typeof rawComponent === 'function' && - !('displayName' in rawComponent)) { - promises.push(rawComponent().then(resolved => { - if (!resolved) - return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`)); - const resolvedComponent = isESModule(resolved) - ? resolved.default - : resolved; - // replace the function with the resolved component - // cannot be null or undefined because we went into the for loop - record.components[name] = resolvedComponent; - return; - })); - } - return promises; - }, [])))).then(() => route); -} - -// TODO: we could allow currentRoute as a prop to expose `isActive` and -// `isExactActive` behavior should go through an RFC -function useLink(props) { - const router = inject(routerKey); - const currentRoute = inject(routeLocationKey); - const route = computed(() => router.resolve(unref(props.to))); - const activeRecordIndex = computed(() => { - const { matched } = route.value; - const { length } = matched; - const routeMatched = matched[length - 1]; - const currentMatched = currentRoute.matched; - if (!routeMatched || !currentMatched.length) - return -1; - const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched)); - if (index > -1) - return index; - // possible parent record - const parentRecordPath = getOriginalPath(matched[length - 2]); - return ( - // we are dealing with nested routes - length > 1 && - // if the parent and matched route have the same path, this link is - // referring to the empty child. Or we currently are on a different - // child of the same parent - getOriginalPath(routeMatched) === parentRecordPath && - // avoid comparing the child with its parent - currentMatched[currentMatched.length - 1].path !== parentRecordPath - ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) - : index); - }); - const isActive = computed(() => activeRecordIndex.value > -1 && - includesParams(currentRoute.params, route.value.params)); - const isExactActive = computed(() => activeRecordIndex.value > -1 && - activeRecordIndex.value === currentRoute.matched.length - 1 && - isSameRouteLocationParams(currentRoute.params, route.value.params)); - function navigate(e = {}) { - if (guardEvent(e)) { - return router[unref(props.replace) ? 'replace' : 'push'](unref(props.to) - // avoid uncaught errors are they are logged anyway - ).catch(noop); - } - return Promise.resolve(); - } - // devtools only - if (isBrowser) { - const instance = getCurrentInstance(); - if (instance) { - const linkContextDevtools = { - route: route.value, - isActive: isActive.value, - isExactActive: isExactActive.value, - }; - // @ts-expect-error: this is internal - instance.__vrl_devtools = instance.__vrl_devtools || []; - // @ts-expect-error: this is internal - instance.__vrl_devtools.push(linkContextDevtools); - watchEffect(() => { - linkContextDevtools.route = route.value; - linkContextDevtools.isActive = isActive.value; - linkContextDevtools.isExactActive = isExactActive.value; - }, { flush: 'post' }); - } - } - /** - * NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this - */ - return { - route, - href: computed(() => route.value.href), - isActive, - isExactActive, - navigate, - }; -} -const RouterLinkImpl = /*#__PURE__*/ defineComponent({ - name: 'RouterLink', - compatConfig: { MODE: 3 }, - props: { - to: { - type: [String, Object], - required: true, - }, - replace: Boolean, - activeClass: String, - // inactiveClass: String, - exactActiveClass: String, - custom: Boolean, - ariaCurrentValue: { - type: String, - default: 'page', - }, - }, - useLink, - setup(props, { slots }) { - const link = reactive(useLink(props)); - const { options } = inject(routerKey); - const elClass = computed(() => ({ - [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive, - // [getLinkClass( - // props.inactiveClass, - // options.linkInactiveClass, - // 'router-link-inactive' - // )]: !link.isExactActive, - [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive, - })); - return () => { - const children = slots.default && slots.default(link); - return props.custom - ? children - : h('a', { - 'aria-current': link.isExactActive - ? props.ariaCurrentValue - : null, - href: link.href, - // this would override user added attrs but Vue will still add - // the listener, so we end up triggering both - onClick: link.navigate, - class: elClass.value, - }, children); - }; - }, -}); -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to render a link that triggers a navigation on click. - */ -const RouterLink = RouterLinkImpl; -function guardEvent(e) { - // don't redirect with control keys - if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) - return; - // don't redirect when preventDefault called - if (e.defaultPrevented) - return; - // don't redirect on right click - if (e.button !== undefined && e.button !== 0) - return; - // don't redirect if `target="_blank"` - // @ts-expect-error getAttribute does exist - if (e.currentTarget && e.currentTarget.getAttribute) { - // @ts-expect-error getAttribute exists - const target = e.currentTarget.getAttribute('target'); - if (/\b_blank\b/i.test(target)) - return; - } - // this may be a Weex event which doesn't have this method - if (e.preventDefault) - e.preventDefault(); - return true; -} -function includesParams(outer, inner) { - for (const key in inner) { - const innerValue = inner[key]; - const outerValue = outer[key]; - if (typeof innerValue === 'string') { - if (innerValue !== outerValue) - return false; - } - else { - if (!isArray(outerValue) || - outerValue.length !== innerValue.length || - innerValue.some((value, i) => value !== outerValue[i])) - return false; - } - } - return true; -} -/** - * Get the original path value of a record by following its aliasOf - * @param record - */ -function getOriginalPath(record) { - return record ? (record.aliasOf ? record.aliasOf.path : record.path) : ''; -} -/** - * Utility class to get the active class based on defaults. - * @param propClass - * @param globalClass - * @param defaultClass - */ -const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null - ? propClass - : globalClass != null - ? globalClass - : defaultClass; - -const RouterViewImpl = /*#__PURE__*/ defineComponent({ - name: 'RouterView', - // #674 we manually inherit them - inheritAttrs: false, - props: { - name: { - type: String, - default: 'default', - }, - route: Object, - }, - // Better compat for @vue/compat users - // https://github.com/vuejs/router/issues/1315 - compatConfig: { MODE: 3 }, - setup(props, { attrs, slots }) { - warnDeprecatedUsage(); - const injectedRoute = inject(routerViewLocationKey); - const routeToDisplay = computed(() => props.route || injectedRoute.value); - const injectedDepth = inject(viewDepthKey, 0); - // The depth changes based on empty components option, which allows passthrough routes e.g. routes with children - // that are used to reuse the `path` property - const depth = computed(() => { - let initialDepth = unref(injectedDepth); - const { matched } = routeToDisplay.value; - let matchedRoute; - while ((matchedRoute = matched[initialDepth]) && - !matchedRoute.components) { - initialDepth++; - } - return initialDepth; - }); - const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth.value]); - provide(viewDepthKey, computed(() => depth.value + 1)); - provide(matchedRouteKey, matchedRouteRef); - provide(routerViewLocationKey, routeToDisplay); - const viewRef = ref(); - // watch at the same time the component instance, the route record we are - // rendering, and the name - watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => { - // copy reused instances - if (to) { - // this will update the instance for new instances as well as reused - // instances when navigating to a new route - to.instances[name] = instance; - // the component instance is reused for a different route or name, so - // we copy any saved update or leave guards. With async setup, the - // mounting component will mount before the matchedRoute changes, - // making instance === oldInstance, so we check if guards have been - // added before. This works because we remove guards when - // unmounting/deactivating components - if (from && from !== to && instance && instance === oldInstance) { - if (!to.leaveGuards.size) { - to.leaveGuards = from.leaveGuards; - } - if (!to.updateGuards.size) { - to.updateGuards = from.updateGuards; - } - } - } - // trigger beforeRouteEnter next callbacks - if (instance && - to && - // if there is no instance but to and from are the same this might be - // the first visit - (!from || !isSameRouteRecord(to, from) || !oldInstance)) { - (to.enterCallbacks[name] || []).forEach(callback => callback(instance)); - } - }, { flush: 'post' }); - return () => { - const route = routeToDisplay.value; - // we need the value at the time we render because when we unmount, we - // navigated to a different location so the value is different - const currentName = props.name; - const matchedRoute = matchedRouteRef.value; - const ViewComponent = matchedRoute && matchedRoute.components[currentName]; - if (!ViewComponent) { - return normalizeSlot(slots.default, { Component: ViewComponent, route }); - } - // props from route configuration - const routePropsOption = matchedRoute.props[currentName]; - const routeProps = routePropsOption - ? routePropsOption === true - ? route.params - : typeof routePropsOption === 'function' - ? routePropsOption(route) - : routePropsOption - : null; - const onVnodeUnmounted = vnode => { - // remove the instance reference to prevent leak - if (vnode.component.isUnmounted) { - matchedRoute.instances[currentName] = null; - } - }; - const component = h(ViewComponent, assign({}, routeProps, attrs, { - onVnodeUnmounted, - ref: viewRef, - })); - if (isBrowser && - component.ref) { - // TODO: can display if it's an alias, its props - const info = { - depth: depth.value, - name: matchedRoute.name, - path: matchedRoute.path, - meta: matchedRoute.meta, - }; - const internalInstances = isArray(component.ref) - ? component.ref.map(r => r.i) - : [component.ref.i]; - internalInstances.forEach(instance => { - // @ts-expect-error - instance.__vrv_devtools = info; - }); - } - return ( - // pass the vnode to the slot as a prop. - // h and both accept vnodes - normalizeSlot(slots.default, { Component: component, route }) || - component); - }; - }, -}); -function normalizeSlot(slot, data) { - if (!slot) - return null; - const slotContent = slot(data); - return slotContent.length === 1 ? slotContent[0] : slotContent; -} -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to display the current route the user is at. - */ -const RouterView = RouterViewImpl; -// warn against deprecated usage with & -// due to functional component being no longer eager in Vue 3 -function warnDeprecatedUsage() { - const instance = getCurrentInstance(); - const parentName = instance.parent && instance.parent.type.name; - const parentSubTreeType = instance.parent && instance.parent.subTree && instance.parent.subTree.type; - if (parentName && - (parentName === 'KeepAlive' || parentName.includes('Transition')) && - typeof parentSubTreeType === 'object' && - parentSubTreeType.name === 'RouterView') { - const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition'; - warn(` can no longer be used directly inside or .\n` + - `Use slot props instead:\n\n` + - `\n` + - ` <${comp}>\n` + - ` \n` + - ` \n` + - ``); - } -} - -/** - * Copies a route location and removes any problematic properties that cannot be shown in devtools (e.g. Vue instances). - * - * @param routeLocation - routeLocation to format - * @param tooltip - optional tooltip - * @returns a copy of the routeLocation - */ -function formatRouteLocation(routeLocation, tooltip) { - const copy = assign({}, routeLocation, { - // remove variables that can contain vue instances - matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children', 'aliasOf'])), - }); - return { - _custom: { - type: null, - readOnly: true, - display: routeLocation.fullPath, - tooltip, - value: copy, - }, - }; -} -function formatDisplay(display) { - return { - _custom: { - display, - }, - }; -} -// to support multiple router instances -let routerId = 0; -function addDevtools(app, router, matcher) { - // Take over router.beforeEach and afterEach - // make sure we are not registering the devtool twice - if (router.__hasDevtools) - return; - router.__hasDevtools = true; - // increment to support multiple router instances - const id = routerId++; - setupDevtoolsPlugin({ - id: 'org.vuejs.router' + (id ? '.' + id : ''), - label: 'Vue Router', - packageName: 'vue-router', - homepage: 'https://router.vuejs.org', - logo: 'https://router.vuejs.org/logo.png', - componentStateTypes: ['Routing'], - app, - }, api => { - if (typeof api.now !== 'function') { - console.warn('[Vue Router]: You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); - } - // display state added by the router - api.on.inspectComponent((payload, ctx) => { - if (payload.instanceData) { - payload.instanceData.state.push({ - type: 'Routing', - key: '$route', - editable: false, - value: formatRouteLocation(router.currentRoute.value, 'Current Route'), - }); - } - }); - // mark router-link as active and display tags on router views - api.on.visitComponentTree(({ treeNode: node, componentInstance }) => { - if (componentInstance.__vrv_devtools) { - const info = componentInstance.__vrv_devtools; - node.tags.push({ - label: (info.name ? `${info.name.toString()}: ` : '') + info.path, - textColor: 0, - tooltip: 'This component is rendered by <router-view>', - backgroundColor: PINK_500, - }); - } - // if multiple useLink are used - if (isArray(componentInstance.__vrl_devtools)) { - componentInstance.__devtoolsApi = api; - componentInstance.__vrl_devtools.forEach(devtoolsData => { - let backgroundColor = ORANGE_400; - let tooltip = ''; - if (devtoolsData.isExactActive) { - backgroundColor = LIME_500; - tooltip = 'This is exactly active'; - } - else if (devtoolsData.isActive) { - backgroundColor = BLUE_600; - tooltip = 'This link is active'; - } - node.tags.push({ - label: devtoolsData.route.path, - textColor: 0, - tooltip, - backgroundColor, - }); - }); - } - }); - watch(router.currentRoute, () => { - // refresh active state - refreshRoutesView(); - api.notifyComponentUpdate(); - api.sendInspectorTree(routerInspectorId); - api.sendInspectorState(routerInspectorId); - }); - const navigationsLayerId = 'router:navigations:' + id; - api.addTimelineLayer({ - id: navigationsLayerId, - label: `Router${id ? ' ' + id : ''} Navigations`, - color: 0x40a8c4, - }); - // const errorsLayerId = 'router:errors' - // api.addTimelineLayer({ - // id: errorsLayerId, - // label: 'Router Errors', - // color: 0xea5455, - // }) - router.onError((error, to) => { - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - title: 'Error during Navigation', - subtitle: to.fullPath, - logType: 'error', - time: api.now(), - data: { error }, - groupId: to.meta.__navigationId, - }, - }); - }); - // attached to `meta` and used to group events - let navigationId = 0; - router.beforeEach((to, from) => { - const data = { - guard: formatDisplay('beforeEach'), - from: formatRouteLocation(from, 'Current Location during this navigation'), - to: formatRouteLocation(to, 'Target location'), - }; - // Used to group navigations together, hide from devtools - Object.defineProperty(to.meta, '__navigationId', { - value: navigationId++, - }); - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - time: api.now(), - title: 'Start of navigation', - subtitle: to.fullPath, - data, - groupId: to.meta.__navigationId, - }, - }); - }); - router.afterEach((to, from, failure) => { - const data = { - guard: formatDisplay('afterEach'), - }; - if (failure) { - data.failure = { - _custom: { - type: Error, - readOnly: true, - display: failure ? failure.message : '', - tooltip: 'Navigation Failure', - value: failure, - }, - }; - data.status = formatDisplay('âŒ'); - } - else { - data.status = formatDisplay('✅'); - } - // we set here to have the right order - data.from = formatRouteLocation(from, 'Current Location during this navigation'); - data.to = formatRouteLocation(to, 'Target location'); - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - title: 'End of navigation', - subtitle: to.fullPath, - time: api.now(), - data, - logType: failure ? 'warning' : 'default', - groupId: to.meta.__navigationId, - }, - }); - }); - /** - * Inspector of Existing routes - */ - const routerInspectorId = 'router-inspector:' + id; - api.addInspector({ - id: routerInspectorId, - label: 'Routes' + (id ? ' ' + id : ''), - icon: 'book', - treeFilterPlaceholder: 'Search routes', - }); - function refreshRoutesView() { - // the routes view isn't active - if (!activeRoutesPayload) - return; - const payload = activeRoutesPayload; - // children routes will appear as nested - let routes = matcher.getRoutes().filter(route => !route.parent || - // these routes have a parent with no component which will not appear in the view - // therefore we still need to include them - !route.parent.record.components); - // reset match state to false - routes.forEach(resetMatchStateOnRouteRecord); - // apply a match state if there is a payload - if (payload.filter) { - routes = routes.filter(route => - // save matches state based on the payload - isRouteMatching(route, payload.filter.toLowerCase())); - } - // mark active routes - routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value)); - payload.rootNodes = routes.map(formatRouteRecordForInspector); - } - let activeRoutesPayload; - api.on.getInspectorTree(payload => { - activeRoutesPayload = payload; - if (payload.app === app && payload.inspectorId === routerInspectorId) { - refreshRoutesView(); - } - }); - /** - * Display information about the currently selected route record - */ - api.on.getInspectorState(payload => { - if (payload.app === app && payload.inspectorId === routerInspectorId) { - const routes = matcher.getRoutes(); - const route = routes.find(route => route.record.__vd_id === payload.nodeId); - if (route) { - payload.state = { - options: formatRouteRecordMatcherForStateInspector(route), - }; - } - } - }); - api.sendInspectorTree(routerInspectorId); - api.sendInspectorState(routerInspectorId); - }); -} -function modifierForKey(key) { - if (key.optional) { - return key.repeatable ? '*' : '?'; - } - else { - return key.repeatable ? '+' : ''; - } -} -function formatRouteRecordMatcherForStateInspector(route) { - const { record } = route; - const fields = [ - { editable: false, key: 'path', value: record.path }, - ]; - if (record.name != null) { - fields.push({ - editable: false, - key: 'name', - value: record.name, - }); - } - fields.push({ editable: false, key: 'regexp', value: route.re }); - if (route.keys.length) { - fields.push({ - editable: false, - key: 'keys', - value: { - _custom: { - type: null, - readOnly: true, - display: route.keys - .map(key => `${key.name}${modifierForKey(key)}`) - .join(' '), - tooltip: 'Param keys', - value: route.keys, - }, - }, - }); - } - if (record.redirect != null) { - fields.push({ - editable: false, - key: 'redirect', - value: record.redirect, - }); - } - if (route.alias.length) { - fields.push({ - editable: false, - key: 'aliases', - value: route.alias.map(alias => alias.record.path), - }); - } - if (Object.keys(route.record.meta).length) { - fields.push({ - editable: false, - key: 'meta', - value: route.record.meta, - }); - } - fields.push({ - key: 'score', - editable: false, - value: { - _custom: { - type: null, - readOnly: true, - display: route.score.map(score => score.join(', ')).join(' | '), - tooltip: 'Score used to sort routes', - value: route.score, - }, - }, - }); - return fields; -} -/** - * Extracted from tailwind palette - */ -const PINK_500 = 0xec4899; -const BLUE_600 = 0x2563eb; -const LIME_500 = 0x84cc16; -const CYAN_400 = 0x22d3ee; -const ORANGE_400 = 0xfb923c; -// const GRAY_100 = 0xf4f4f5 -const DARK = 0x666666; -function formatRouteRecordForInspector(route) { - const tags = []; - const { record } = route; - if (record.name != null) { - tags.push({ - label: String(record.name), - textColor: 0, - backgroundColor: CYAN_400, - }); - } - if (record.aliasOf) { - tags.push({ - label: 'alias', - textColor: 0, - backgroundColor: ORANGE_400, - }); - } - if (route.__vd_match) { - tags.push({ - label: 'matches', - textColor: 0, - backgroundColor: PINK_500, - }); - } - if (route.__vd_exactActive) { - tags.push({ - label: 'exact', - textColor: 0, - backgroundColor: LIME_500, - }); - } - if (route.__vd_active) { - tags.push({ - label: 'active', - textColor: 0, - backgroundColor: BLUE_600, - }); - } - if (record.redirect) { - tags.push({ - label: typeof record.redirect === 'string' - ? `redirect: ${record.redirect}` - : 'redirects', - textColor: 0xffffff, - backgroundColor: DARK, - }); - } - // add an id to be able to select it. Using the `path` is not possible because - // empty path children would collide with their parents - let id = record.__vd_id; - if (id == null) { - id = String(routeRecordId++); - record.__vd_id = id; - } - return { - id, - label: record.path, - tags, - children: route.children.map(formatRouteRecordForInspector), - }; -} -// incremental id for route records and inspector state -let routeRecordId = 0; -const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/; -function markRouteRecordActive(route, currentRoute) { - // no route will be active if matched is empty - // reset the matching state - const isExactActive = currentRoute.matched.length && - isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record); - route.__vd_exactActive = route.__vd_active = isExactActive; - if (!isExactActive) { - route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record)); - } - route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute)); -} -function resetMatchStateOnRouteRecord(route) { - route.__vd_match = false; - route.children.forEach(resetMatchStateOnRouteRecord); -} -function isRouteMatching(route, filter) { - const found = String(route.re).match(EXTRACT_REGEXP_RE); - route.__vd_match = false; - if (!found || found.length < 3) { - return false; - } - // use a regexp without $ at the end to match nested routes better - const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]); - if (nonEndingRE.test(filter)) { - // mark children as matches - route.children.forEach(child => isRouteMatching(child, filter)); - // exception case: `/` - if (route.record.path !== '/' || filter === '/') { - route.__vd_match = route.re.test(filter); - return true; - } - // hide the / route - return false; - } - const path = route.record.path.toLowerCase(); - const decodedPath = decode(path); - // also allow partial matching on the path - if (!filter.startsWith('/') && - (decodedPath.includes(filter) || path.includes(filter))) - return true; - if (decodedPath.startsWith(filter) || path.startsWith(filter)) - return true; - if (route.record.name && String(route.record.name).includes(filter)) - return true; - return route.children.some(child => isRouteMatching(child, filter)); -} -function omit(obj, keys) { - const ret = {}; - for (const key in obj) { - if (!keys.includes(key)) { - // @ts-expect-error - ret[key] = obj[key]; - } - } - return ret; -} - -/** - * Creates a Router instance that can be used by a Vue app. - * - * @param options - {@link RouterOptions} - */ -function createRouter(options) { - const matcher = createRouterMatcher(options.routes, options); - const parseQuery$1 = options.parseQuery || parseQuery; - const stringifyQuery$1 = options.stringifyQuery || stringifyQuery; - const routerHistory = options.history; - if (!routerHistory) - throw new Error('Provide the "history" option when calling "createRouter()":' + - ' https://next.router.vuejs.org/api/#history.'); - const beforeGuards = useCallbacks(); - const beforeResolveGuards = useCallbacks(); - const afterGuards = useCallbacks(); - const currentRoute = shallowRef(START_LOCATION_NORMALIZED); - let pendingLocation = START_LOCATION_NORMALIZED; - // leave the scrollRestoration if no scrollBehavior is provided - if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) { - history.scrollRestoration = 'manual'; - } - const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue); - const encodeParams = applyToParams.bind(null, encodeParam); - const decodeParams = - // @ts-expect-error: intentionally avoid the type check - applyToParams.bind(null, decode); - function addRoute(parentOrRoute, route) { - let parent; - let record; - if (isRouteName(parentOrRoute)) { - parent = matcher.getRecordMatcher(parentOrRoute); - record = route; - } - else { - record = parentOrRoute; - } - return matcher.addRoute(record, parent); - } - function removeRoute(name) { - const recordMatcher = matcher.getRecordMatcher(name); - if (recordMatcher) { - matcher.removeRoute(recordMatcher); - } - else { - warn(`Cannot remove non-existent route "${String(name)}"`); - } - } - function getRoutes() { - return matcher.getRoutes().map(routeMatcher => routeMatcher.record); - } - function hasRoute(name) { - return !!matcher.getRecordMatcher(name); - } - function resolve(rawLocation, currentLocation) { - // const objectLocation = routerLocationAsObject(rawLocation) - // we create a copy to modify it later - currentLocation = assign({}, currentLocation || currentRoute.value); - if (typeof rawLocation === 'string') { - const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path); - const matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation); - const href = routerHistory.createHref(locationNormalized.fullPath); - { - if (href.startsWith('//')) - warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`); - else if (!matchedRoute.matched.length) { - warn(`No match found for location with path "${rawLocation}"`); - } - } - // locationNormalized is always a new object - return assign(locationNormalized, matchedRoute, { - params: decodeParams(matchedRoute.params), - hash: decode(locationNormalized.hash), - redirectedFrom: undefined, - href, - }); - } - let matcherLocation; - // path could be relative in object as well - if ('path' in rawLocation) { - if ('params' in rawLocation && - !('name' in rawLocation) && - // @ts-expect-error: the type is never - Object.keys(rawLocation.params).length) { - warn(`Path "${rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`); - } - matcherLocation = assign({}, rawLocation, { - path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path, - }); - } - else { - // remove any nullish param - const targetParams = assign({}, rawLocation.params); - for (const key in targetParams) { - if (targetParams[key] == null) { - delete targetParams[key]; - } - } - // pass encoded values to the matcher, so it can produce encoded path and fullPath - matcherLocation = assign({}, rawLocation, { - params: encodeParams(targetParams), - }); - // current location params are decoded, we need to encode them in case the - // matcher merges the params - currentLocation.params = encodeParams(currentLocation.params); - } - const matchedRoute = matcher.resolve(matcherLocation, currentLocation); - const hash = rawLocation.hash || ''; - if (hash && !hash.startsWith('#')) { - warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`); - } - // the matcher might have merged current location params, so - // we need to run the decoding again - matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params)); - const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, { - hash: encodeHash(hash), - path: matchedRoute.path, - })); - const href = routerHistory.createHref(fullPath); - { - if (href.startsWith('//')) { - warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`); - } - else if (!matchedRoute.matched.length) { - warn(`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`); - } - } - return assign({ - fullPath, - // keep the hash encoded so fullPath is effectively path + encodedQuery + - // hash - hash, - query: - // if the user is using a custom query lib like qs, we might have - // nested objects, so we keep the query as is, meaning it can contain - // numbers at `$route.query`, but at the point, the user will have to - // use their own type anyway. - // https://github.com/vuejs/router/issues/328#issuecomment-649481567 - stringifyQuery$1 === stringifyQuery - ? normalizeQuery(rawLocation.query) - : (rawLocation.query || {}), - }, matchedRoute, { - redirectedFrom: undefined, - href, - }); - } - function locationAsObject(to) { - return typeof to === 'string' - ? parseURL(parseQuery$1, to, currentRoute.value.path) - : assign({}, to); - } - function checkCanceledNavigation(to, from) { - if (pendingLocation !== to) { - return createRouterError(8 /* ErrorTypes.NAVIGATION_CANCELLED */, { - from, - to, - }); - } - } - function push(to) { - return pushWithRedirect(to); - } - function replace(to) { - return push(assign(locationAsObject(to), { replace: true })); - } - function handleRedirectRecord(to) { - const lastMatched = to.matched[to.matched.length - 1]; - if (lastMatched && lastMatched.redirect) { - const { redirect } = lastMatched; - let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect; - if (typeof newTargetLocation === 'string') { - newTargetLocation = - newTargetLocation.includes('?') || newTargetLocation.includes('#') - ? (newTargetLocation = locationAsObject(newTargetLocation)) - : // force empty params - { path: newTargetLocation }; - // @ts-expect-error: force empty params when a string is passed to let - // the router parse them again - newTargetLocation.params = {}; - } - if (!('path' in newTargetLocation) && - !('name' in newTargetLocation)) { - warn(`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`); - throw new Error('Invalid redirect'); - } - return assign({ - query: to.query, - hash: to.hash, - // avoid transferring params if the redirect has a path - params: 'path' in newTargetLocation ? {} : to.params, - }, newTargetLocation); - } - } - function pushWithRedirect(to, redirectedFrom) { - const targetLocation = (pendingLocation = resolve(to)); - const from = currentRoute.value; - const data = to.state; - const force = to.force; - // to could be a string where `replace` is a function - const replace = to.replace === true; - const shouldRedirect = handleRedirectRecord(targetLocation); - if (shouldRedirect) - return pushWithRedirect(assign(locationAsObject(shouldRedirect), { - state: typeof shouldRedirect === 'object' - ? assign({}, data, shouldRedirect.state) - : data, - force, - replace, - }), - // keep original redirectedFrom if it exists - redirectedFrom || targetLocation); - // if it was a redirect we already called `pushWithRedirect` above - const toLocation = targetLocation; - toLocation.redirectedFrom = redirectedFrom; - let failure; - if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) { - failure = createRouterError(16 /* ErrorTypes.NAVIGATION_DUPLICATED */, { to: toLocation, from }); - // trigger scroll to allow scrolling to the same anchor - handleScroll(from, from, - // this is a push, the only way for it to be triggered from a - // history.listen is with a redirect, which makes it become a push - true, - // This cannot be the first navigation because the initial location - // cannot be manually navigated to - false); - } - return (failure ? Promise.resolve(failure) : navigate(toLocation, from)) - .catch((error) => isNavigationFailure(error) - ? // navigation redirects still mark the router as ready - isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */) - ? error - : markAsReady(error) // also returns the error - : // reject any unknown error - triggerError(error, toLocation, from)) - .then((failure) => { - if (failure) { - if (isNavigationFailure(failure, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) { - if (// we are redirecting to the same location we were already at - isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) && - // and we have done it a couple of times - redirectedFrom && - // @ts-expect-error: added only in dev - (redirectedFrom._count = redirectedFrom._count - ? // @ts-expect-error - redirectedFrom._count + 1 - : 1) > 30) { - warn(`Detected a possibly infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow.\n Are you always returning a new location within a navigation guard? That would lead to this error. Only return when redirecting or aborting, that should fix this. This might break in production if not fixed.`); - return Promise.reject(new Error('Infinite redirect in navigation guard')); - } - return pushWithRedirect( - // keep options - assign({ - // preserve an existing replacement but allow the redirect to override it - replace, - }, locationAsObject(failure.to), { - state: typeof failure.to === 'object' - ? assign({}, data, failure.to.state) - : data, - force, - }), - // preserve the original redirectedFrom if any - redirectedFrom || toLocation); - } - } - else { - // if we fail we don't finalize the navigation - failure = finalizeNavigation(toLocation, from, true, replace, data); - } - triggerAfterEach(toLocation, from, failure); - return failure; - }); - } - /** - * Helper to reject and skip all navigation guards if a new navigation happened - * @param to - * @param from - */ - function checkCanceledNavigationAndReject(to, from) { - const error = checkCanceledNavigation(to, from); - return error ? Promise.reject(error) : Promise.resolve(); - } - function runWithContext(fn) { - const app = installedApps.values().next().value; - // support Vue < 3.3 - return app && typeof app.runWithContext === 'function' - ? app.runWithContext(fn) - : fn(); - } - // TODO: refactor the whole before guards by internally using router.beforeEach - function navigate(to, from) { - let guards; - const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from); - // all components here have been resolved once because we are leaving - guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from); - // leavingRecords is already reversed - for (const record of leavingRecords) { - record.leaveGuards.forEach(guard => { - guards.push(guardToPromiseFn(guard, to, from)); - }); - } - const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from); - guards.push(canceledNavigationCheck); - // run the queue of per route beforeRouteLeave guards - return (runGuardQueue(guards) - .then(() => { - // check global guards beforeEach - guards = []; - for (const guard of beforeGuards.list()) { - guards.push(guardToPromiseFn(guard, to, from)); - } - guards.push(canceledNavigationCheck); - return runGuardQueue(guards); - }) - .then(() => { - // check in components beforeRouteUpdate - guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from); - for (const record of updatingRecords) { - record.updateGuards.forEach(guard => { - guards.push(guardToPromiseFn(guard, to, from)); - }); - } - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // check the route beforeEnter - guards = []; - for (const record of enteringRecords) { - // do not trigger beforeEnter on reused views - if (record.beforeEnter) { - if (isArray(record.beforeEnter)) { - for (const beforeEnter of record.beforeEnter) - guards.push(guardToPromiseFn(beforeEnter, to, from)); - } - else { - guards.push(guardToPromiseFn(record.beforeEnter, to, from)); - } - } - } - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // NOTE: at this point to.matched is normalized and does not contain any () => Promise - // clear existing enterCallbacks, these are added by extractComponentsGuards - to.matched.forEach(record => (record.enterCallbacks = {})); - // check in-component beforeRouteEnter - guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from); - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // check global guards beforeResolve - guards = []; - for (const guard of beforeResolveGuards.list()) { - guards.push(guardToPromiseFn(guard, to, from)); - } - guards.push(canceledNavigationCheck); - return runGuardQueue(guards); - }) - // catch any navigation canceled - .catch(err => isNavigationFailure(err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */) - ? err - : Promise.reject(err))); - } - function triggerAfterEach(to, from, failure) { - // navigation is confirmed, call afterGuards - // TODO: wrap with error handlers - afterGuards - .list() - .forEach(guard => runWithContext(() => guard(to, from, failure))); - } - /** - * - Cleans up any navigation guards - * - Changes the url if necessary - * - Calls the scrollBehavior - */ - function finalizeNavigation(toLocation, from, isPush, replace, data) { - // a more recent navigation took place - const error = checkCanceledNavigation(toLocation, from); - if (error) - return error; - // only consider as push if it's not the first navigation - const isFirstNavigation = from === START_LOCATION_NORMALIZED; - const state = !isBrowser ? {} : history.state; - // change URL only if the user did a push/replace and if it's not the initial navigation because - // it's just reflecting the url - if (isPush) { - // on the initial navigation, we want to reuse the scroll position from - // history state if it exists - if (replace || isFirstNavigation) - routerHistory.replace(toLocation.fullPath, assign({ - scroll: isFirstNavigation && state && state.scroll, - }, data)); - else - routerHistory.push(toLocation.fullPath, data); - } - // accept current navigation - currentRoute.value = toLocation; - handleScroll(toLocation, from, isPush, isFirstNavigation); - markAsReady(); - } - let removeHistoryListener; - // attach listener to history to trigger navigations - function setupListeners() { - // avoid setting up listeners twice due to an invalid first navigation - if (removeHistoryListener) - return; - removeHistoryListener = routerHistory.listen((to, _from, info) => { - if (!router.listening) - return; - // cannot be a redirect route because it was in history - const toLocation = resolve(to); - // due to dynamic routing, and to hash history with manual navigation - // (manually changing the url or calling history.hash = '#/somewhere'), - // there could be a redirect record in history - const shouldRedirect = handleRedirectRecord(toLocation); - if (shouldRedirect) { - pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop); - return; - } - pendingLocation = toLocation; - const from = currentRoute.value; - // TODO: should be moved to web history? - if (isBrowser) { - saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition()); - } - navigate(toLocation, from) - .catch((error) => { - if (isNavigationFailure(error, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) { - return error; - } - if (isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) { - // Here we could call if (info.delta) routerHistory.go(-info.delta, - // false) but this is bug prone as we have no way to wait the - // navigation to be finished before calling pushWithRedirect. Using - // a setTimeout of 16ms seems to work but there is no guarantee for - // it to work on every browser. So instead we do not restore the - // history entry and trigger a new navigation as requested by the - // navigation guard. - // the error is already handled by router.push we just want to avoid - // logging the error - pushWithRedirect(error.to, toLocation - // avoid an uncaught rejection, let push call triggerError - ) - .then(failure => { - // manual change in hash history #916 ending up in the URL not - // changing, but it was changed by the manual url change, so we - // need to manually change it ourselves - if (isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | - 16 /* ErrorTypes.NAVIGATION_DUPLICATED */) && - !info.delta && - info.type === NavigationType.pop) { - routerHistory.go(-1, false); - } - }) - .catch(noop); - // avoid the then branch - return Promise.reject(); - } - // do not restore history on unknown direction - if (info.delta) { - routerHistory.go(-info.delta, false); - } - // unrecognized error, transfer to the global handler - return triggerError(error, toLocation, from); - }) - .then((failure) => { - failure = - failure || - finalizeNavigation( - // after navigation, all matched components are resolved - toLocation, from, false); - // revert the navigation - if (failure) { - if (info.delta && - // a new navigation has been triggered, so we do not want to revert, that will change the current history - // entry while a different route is displayed - !isNavigationFailure(failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) { - routerHistory.go(-info.delta, false); - } - else if (info.type === NavigationType.pop && - isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 16 /* ErrorTypes.NAVIGATION_DUPLICATED */)) { - // manual change in hash history #916 - // it's like a push but lacks the information of the direction - routerHistory.go(-1, false); - } - } - triggerAfterEach(toLocation, from, failure); - }) - // avoid warnings in the console about uncaught rejections, they are logged by triggerErrors - .catch(noop); - }); - } - // Initialization and Errors - let readyHandlers = useCallbacks(); - let errorListeners = useCallbacks(); - let ready; - /** - * Trigger errorListeners added via onError and throws the error as well - * - * @param error - error to throw - * @param to - location we were navigating to when the error happened - * @param from - location we were navigating from when the error happened - * @returns the error as a rejected promise - */ - function triggerError(error, to, from) { - markAsReady(error); - const list = errorListeners.list(); - if (list.length) { - list.forEach(handler => handler(error, to, from)); - } - else { - { - warn('uncaught error during route navigation:'); - } - console.error(error); - } - // reject the error no matter there were error listeners or not - return Promise.reject(error); - } - function isReady() { - if (ready && currentRoute.value !== START_LOCATION_NORMALIZED) - return Promise.resolve(); - return new Promise((resolve, reject) => { - readyHandlers.add([resolve, reject]); - }); - } - function markAsReady(err) { - if (!ready) { - // still not ready if an error happened - ready = !err; - setupListeners(); - readyHandlers - .list() - .forEach(([resolve, reject]) => (err ? reject(err) : resolve())); - readyHandlers.reset(); - } - return err; - } - // Scroll behavior - function handleScroll(to, from, isPush, isFirstNavigation) { - const { scrollBehavior } = options; - if (!isBrowser || !scrollBehavior) - return Promise.resolve(); - const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) || - ((isFirstNavigation || !isPush) && - history.state && - history.state.scroll) || - null; - return nextTick() - .then(() => scrollBehavior(to, from, scrollPosition)) - .then(position => position && scrollToPosition(position)) - .catch(err => triggerError(err, to, from)); - } - const go = (delta) => routerHistory.go(delta); - let started; - const installedApps = new Set(); - const router = { - currentRoute, - listening: true, - addRoute, - removeRoute, - hasRoute, - getRoutes, - resolve, - options, - push, - replace, - go, - back: () => go(-1), - forward: () => go(1), - beforeEach: beforeGuards.add, - beforeResolve: beforeResolveGuards.add, - afterEach: afterGuards.add, - onError: errorListeners.add, - isReady, - install(app) { - const router = this; - app.component('RouterLink', RouterLink); - app.component('RouterView', RouterView); - app.config.globalProperties.$router = router; - Object.defineProperty(app.config.globalProperties, '$route', { - enumerable: true, - get: () => unref(currentRoute), - }); - // this initial navigation is only necessary on client, on server it doesn't - // make sense because it will create an extra unnecessary navigation and could - // lead to problems - if (isBrowser && - // used for the initial navigation client side to avoid pushing - // multiple times when the router is used in multiple apps - !started && - currentRoute.value === START_LOCATION_NORMALIZED) { - // see above - started = true; - push(routerHistory.location).catch(err => { - warn('Unexpected error when starting the router:', err); - }); - } - const reactiveRoute = {}; - for (const key in START_LOCATION_NORMALIZED) { - Object.defineProperty(reactiveRoute, key, { - get: () => currentRoute.value[key], - enumerable: true, - }); - } - app.provide(routerKey, router); - app.provide(routeLocationKey, shallowReactive(reactiveRoute)); - app.provide(routerViewLocationKey, currentRoute); - const unmountApp = app.unmount; - installedApps.add(app); - app.unmount = function () { - installedApps.delete(app); - // the router is not attached to an app anymore - if (installedApps.size < 1) { - // invalidate the current navigation - pendingLocation = START_LOCATION_NORMALIZED; - removeHistoryListener && removeHistoryListener(); - removeHistoryListener = null; - currentRoute.value = START_LOCATION_NORMALIZED; - started = false; - ready = false; - } - unmountApp(); - }; - // TODO: this probably needs to be updated so it can be used by vue-termui - if (isBrowser) { - addDevtools(app, router, matcher); - } - }, - }; - // TODO: type this as NavigationGuardReturn or similar instead of any - function runGuardQueue(guards) { - return guards.reduce((promise, guard) => promise.then(() => runWithContext(guard)), Promise.resolve()); - } - return router; -} -function extractChangingRecords(to, from) { - const leavingRecords = []; - const updatingRecords = []; - const enteringRecords = []; - const len = Math.max(from.matched.length, to.matched.length); - for (let i = 0; i < len; i++) { - const recordFrom = from.matched[i]; - if (recordFrom) { - if (to.matched.find(record => isSameRouteRecord(record, recordFrom))) - updatingRecords.push(recordFrom); - else - leavingRecords.push(recordFrom); - } - const recordTo = to.matched[i]; - if (recordTo) { - // the type doesn't matter because we are comparing per reference - if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) { - enteringRecords.push(recordTo); - } - } - } - return [leavingRecords, updatingRecords, enteringRecords]; -} - -/** - * Returns the router instance. Equivalent to using `$router` inside - * templates. - */ -function useRouter() { - return inject(routerKey); -} -/** - * Returns the current route location. Equivalent to using `$route` inside - * templates. - */ -function useRoute() { - return inject(routeLocationKey); -} - -export { NavigationFailureType, RouterLink, RouterView, START_LOCATION_NORMALIZED as START_LOCATION, createMemoryHistory, createRouter, createRouterMatcher, createWebHashHistory, createWebHistory, isNavigationFailure, loadRouteLocation, matchedRouteKey, onBeforeRouteLeave, onBeforeRouteUpdate, parseQuery, routeLocationKey, routerKey, routerViewLocationKey, stringifyQuery, useLink, useRoute, useRouter, viewDepthKey }; \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js b/hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js deleted file mode 100644 index 64c8126c..00000000 --- a/hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js +++ /dev/null @@ -1,3615 +0,0 @@ -/*! - * vue-router v4.2.5 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */ -import { getCurrentInstance, inject, onUnmounted, onDeactivated, onActivated, computed, unref, watchEffect, defineComponent, reactive, h, provide, ref, watch, shallowRef, shallowReactive, nextTick } from 'vue'; -import { setupDevtoolsPlugin } from '@vue/devtools-api'; - -const isBrowser = typeof window !== 'undefined'; - -function isESModule(obj) { - return obj.__esModule || obj[Symbol.toStringTag] === 'Module'; -} -const assign = Object.assign; -function applyToParams(fn, params) { - const newParams = {}; - for (const key in params) { - const value = params[key]; - newParams[key] = isArray(value) - ? value.map(fn) - : fn(value); - } - return newParams; -} -const noop = () => { }; -/** - * Typesafe alternative to Array.isArray - * https://github.com/microsoft/TypeScript/pull/48228 - */ -const isArray = Array.isArray; - -function warn(msg) { - // avoid using ...args as it breaks in older Edge builds - const args = Array.from(arguments).slice(1); - console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args)); -} - -const TRAILING_SLASH_RE = /\/$/; -const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, ''); -/** - * Transforms a URI into a normalized history location - * - * @param parseQuery - * @param location - URI to normalize - * @param currentLocation - current absolute location. Allows resolving relative - * paths. Must start with `/`. Defaults to `/` - * @returns a normalized history location - */ -function parseURL(parseQuery, location, currentLocation = '/') { - let path, query = {}, searchString = '', hash = ''; - // Could use URL and URLSearchParams but IE 11 doesn't support it - // TODO: move to new URL() - const hashPos = location.indexOf('#'); - let searchPos = location.indexOf('?'); - // the hash appears before the search, so it's not part of the search string - if (hashPos < searchPos && hashPos >= 0) { - searchPos = -1; - } - if (searchPos > -1) { - path = location.slice(0, searchPos); - searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length); - query = parseQuery(searchString); - } - if (hashPos > -1) { - path = path || location.slice(0, hashPos); - // keep the # character - hash = location.slice(hashPos, location.length); - } - // no search and no query - path = resolveRelativePath(path != null ? path : location, currentLocation); - // empty path means a relative query or hash `?foo=f`, `#thing` - return { - fullPath: path + (searchString && '?') + searchString + hash, - path, - query, - hash, - }; -} -/** - * Stringifies a URL object - * - * @param stringifyQuery - * @param location - */ -function stringifyURL(stringifyQuery, location) { - const query = location.query ? stringifyQuery(location.query) : ''; - return location.path + (query && '?') + query + (location.hash || ''); -} -/** - * Strips off the base from the beginning of a location.pathname in a non-case-sensitive way. - * - * @param pathname - location.pathname - * @param base - base to strip off - */ -function stripBase(pathname, base) { - // no base or base is not found at the beginning - if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase())) - return pathname; - return pathname.slice(base.length) || '/'; -} -/** - * Checks if two RouteLocation are equal. This means that both locations are - * pointing towards the same {@link RouteRecord} and that all `params`, `query` - * parameters and `hash` are the same - * - * @param stringifyQuery - A function that takes a query object of type LocationQueryRaw and returns a string representation of it. - * @param a - first {@link RouteLocation} - * @param b - second {@link RouteLocation} - */ -function isSameRouteLocation(stringifyQuery, a, b) { - const aLastIndex = a.matched.length - 1; - const bLastIndex = b.matched.length - 1; - return (aLastIndex > -1 && - aLastIndex === bLastIndex && - isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) && - isSameRouteLocationParams(a.params, b.params) && - stringifyQuery(a.query) === stringifyQuery(b.query) && - a.hash === b.hash); -} -/** - * Check if two `RouteRecords` are equal. Takes into account aliases: they are - * considered equal to the `RouteRecord` they are aliasing. - * - * @param a - first {@link RouteRecord} - * @param b - second {@link RouteRecord} - */ -function isSameRouteRecord(a, b) { - // since the original record has an undefined value for aliasOf - // but all aliases point to the original record, this will always compare - // the original record - return (a.aliasOf || a) === (b.aliasOf || b); -} -function isSameRouteLocationParams(a, b) { - if (Object.keys(a).length !== Object.keys(b).length) - return false; - for (const key in a) { - if (!isSameRouteLocationParamsValue(a[key], b[key])) - return false; - } - return true; -} -function isSameRouteLocationParamsValue(a, b) { - return isArray(a) - ? isEquivalentArray(a, b) - : isArray(b) - ? isEquivalentArray(b, a) - : a === b; -} -/** - * Check if two arrays are the same or if an array with one single entry is the - * same as another primitive value. Used to check query and parameters - * - * @param a - array of values - * @param b - array of values or a single value - */ -function isEquivalentArray(a, b) { - return isArray(b) - ? a.length === b.length && a.every((value, i) => value === b[i]) - : a.length === 1 && a[0] === b; -} -/** - * Resolves a relative path that starts with `.`. - * - * @param to - path location we are resolving - * @param from - currentLocation.path, should start with `/` - */ -function resolveRelativePath(to, from) { - if (to.startsWith('/')) - return to; - if (!from.startsWith('/')) { - warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`); - return to; - } - if (!to) - return from; - const fromSegments = from.split('/'); - const toSegments = to.split('/'); - const lastToSegment = toSegments[toSegments.length - 1]; - // make . and ./ the same (../ === .., ../../ === ../..) - // this is the same behavior as new URL() - if (lastToSegment === '..' || lastToSegment === '.') { - toSegments.push(''); - } - let position = fromSegments.length - 1; - let toPosition; - let segment; - for (toPosition = 0; toPosition < toSegments.length; toPosition++) { - segment = toSegments[toPosition]; - // we stay on the same position - if (segment === '.') - continue; - // go up in the from array - if (segment === '..') { - // we can't go below zero, but we still need to increment toPosition - if (position > 1) - position--; - // continue - } - // we reached a non-relative path, we stop here - else - break; - } - return (fromSegments.slice(0, position).join('/') + - '/' + - toSegments - // ensure we use at least the last element in the toSegments - .slice(toPosition - (toPosition === toSegments.length ? 1 : 0)) - .join('/')); -} - -var NavigationType; -(function (NavigationType) { - NavigationType["pop"] = "pop"; - NavigationType["push"] = "push"; -})(NavigationType || (NavigationType = {})); -var NavigationDirection; -(function (NavigationDirection) { - NavigationDirection["back"] = "back"; - NavigationDirection["forward"] = "forward"; - NavigationDirection["unknown"] = ""; -})(NavigationDirection || (NavigationDirection = {})); -/** - * Starting location for Histories - */ -const START = ''; -// Generic utils -/** - * Normalizes a base by removing any trailing slash and reading the base tag if - * present. - * - * @param base - base to normalize - */ -function normalizeBase(base) { - if (!base) { - if (isBrowser) { - // respect tag - const baseEl = document.querySelector('base'); - base = (baseEl && baseEl.getAttribute('href')) || '/'; - // strip full URL origin - base = base.replace(/^\w+:\/\/[^\/]+/, ''); - } - else { - base = '/'; - } - } - // ensure leading slash when it was removed by the regex above avoid leading - // slash with hash because the file could be read from the disk like file:// - // and the leading slash would cause problems - if (base[0] !== '/' && base[0] !== '#') - base = '/' + base; - // remove the trailing slash so all other method can just do `base + fullPath` - // to build an href - return removeTrailingSlash(base); -} -// remove any character before the hash -const BEFORE_HASH_RE = /^[^#]+#/; -function createHref(base, location) { - return base.replace(BEFORE_HASH_RE, '#') + location; -} - -function getElementPosition(el, offset) { - const docRect = document.documentElement.getBoundingClientRect(); - const elRect = el.getBoundingClientRect(); - return { - behavior: offset.behavior, - left: elRect.left - docRect.left - (offset.left || 0), - top: elRect.top - docRect.top - (offset.top || 0), - }; -} -const computeScrollPosition = () => ({ - left: window.pageXOffset, - top: window.pageYOffset, -}); -function scrollToPosition(position) { - let scrollToOptions; - if ('el' in position) { - const positionEl = position.el; - const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#'); - /** - * `id`s can accept pretty much any characters, including CSS combinators - * like `>` or `~`. It's still possible to retrieve elements using - * `document.getElementById('~')` but it needs to be escaped when using - * `document.querySelector('#\\~')` for it to be valid. The only - * requirements for `id`s are them to be unique on the page and to not be - * empty (`id=""`). Because of that, when passing an id selector, it should - * be properly escaped for it to work with `querySelector`. We could check - * for the id selector to be simple (no CSS combinators `+ >~`) but that - * would make things inconsistent since they are valid characters for an - * `id` but would need to be escaped when using `querySelector`, breaking - * their usage and ending up in no selector returned. Selectors need to be - * escaped: - * - * - `#1-thing` becomes `#\31 -thing` - * - `#with~symbols` becomes `#with\\~symbols` - * - * - More information about the topic can be found at - * https://mathiasbynens.be/notes/html5-id-class. - * - Practical example: https://mathiasbynens.be/demo/html5-id - */ - if (typeof position.el === 'string') { - if (!isIdSelector || !document.getElementById(position.el.slice(1))) { - try { - const foundEl = document.querySelector(position.el); - if (isIdSelector && foundEl) { - warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`); - // return to avoid other warnings - return; - } - } - catch (err) { - warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`); - // return to avoid other warnings - return; - } - } - } - const el = typeof positionEl === 'string' - ? isIdSelector - ? document.getElementById(positionEl.slice(1)) - : document.querySelector(positionEl) - : positionEl; - if (!el) { - warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`); - return; - } - scrollToOptions = getElementPosition(el, position); - } - else { - scrollToOptions = position; - } - if ('scrollBehavior' in document.documentElement.style) - window.scrollTo(scrollToOptions); - else { - window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset); - } -} -function getScrollKey(path, delta) { - const position = history.state ? history.state.position - delta : -1; - return position + path; -} -const scrollPositions = new Map(); -function saveScrollPosition(key, scrollPosition) { - scrollPositions.set(key, scrollPosition); -} -function getSavedScrollPosition(key) { - const scroll = scrollPositions.get(key); - // consume it so it's not used again - scrollPositions.delete(key); - return scroll; -} -// TODO: RFC about how to save scroll position -/** - * ScrollBehavior instance used by the router to compute and restore the scroll - * position when navigating. - */ -// export interface ScrollHandler { -// // returns a scroll position that can be saved in history -// compute(): ScrollPositionEntry -// // can take an extended ScrollPositionEntry -// scroll(position: ScrollPosition): void -// } -// export const scrollHandler: ScrollHandler = { -// compute: computeScroll, -// scroll: scrollToPosition, -// } - -let createBaseLocation = () => location.protocol + '//' + location.host; -/** - * Creates a normalized history location from a window.location object - * @param base - The base path - * @param location - The window.location object - */ -function createCurrentLocation(base, location) { - const { pathname, search, hash } = location; - // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end - const hashPos = base.indexOf('#'); - if (hashPos > -1) { - let slicePos = hash.includes(base.slice(hashPos)) - ? base.slice(hashPos).length - : 1; - let pathFromHash = hash.slice(slicePos); - // prepend the starting slash to hash so the url starts with /# - if (pathFromHash[0] !== '/') - pathFromHash = '/' + pathFromHash; - return stripBase(pathFromHash, ''); - } - const path = stripBase(pathname, base); - return path + search + hash; -} -function useHistoryListeners(base, historyState, currentLocation, replace) { - let listeners = []; - let teardowns = []; - // TODO: should it be a stack? a Dict. Check if the popstate listener - // can trigger twice - let pauseState = null; - const popStateHandler = ({ state, }) => { - const to = createCurrentLocation(base, location); - const from = currentLocation.value; - const fromState = historyState.value; - let delta = 0; - if (state) { - currentLocation.value = to; - historyState.value = state; - // ignore the popstate and reset the pauseState - if (pauseState && pauseState === from) { - pauseState = null; - return; - } - delta = fromState ? state.position - fromState.position : 0; - } - else { - replace(to); - } - // Here we could also revert the navigation by calling history.go(-delta) - // this listener will have to be adapted to not trigger again and to wait for the url - // to be updated before triggering the listeners. Some kind of validation function would also - // need to be passed to the listeners so the navigation can be accepted - // call all listeners - listeners.forEach(listener => { - listener(currentLocation.value, from, { - delta, - type: NavigationType.pop, - direction: delta - ? delta > 0 - ? NavigationDirection.forward - : NavigationDirection.back - : NavigationDirection.unknown, - }); - }); - }; - function pauseListeners() { - pauseState = currentLocation.value; - } - function listen(callback) { - // set up the listener and prepare teardown callbacks - listeners.push(callback); - const teardown = () => { - const index = listeners.indexOf(callback); - if (index > -1) - listeners.splice(index, 1); - }; - teardowns.push(teardown); - return teardown; - } - function beforeUnloadListener() { - const { history } = window; - if (!history.state) - return; - history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), ''); - } - function destroy() { - for (const teardown of teardowns) - teardown(); - teardowns = []; - window.removeEventListener('popstate', popStateHandler); - window.removeEventListener('beforeunload', beforeUnloadListener); - } - // set up the listeners and prepare teardown callbacks - window.addEventListener('popstate', popStateHandler); - // TODO: could we use 'pagehide' or 'visibilitychange' instead? - // https://developer.chrome.com/blog/page-lifecycle-api/ - window.addEventListener('beforeunload', beforeUnloadListener, { - passive: true, - }); - return { - pauseListeners, - listen, - destroy, - }; -} -/** - * Creates a state object - */ -function buildState(back, current, forward, replaced = false, computeScroll = false) { - return { - back, - current, - forward, - replaced, - position: window.history.length, - scroll: computeScroll ? computeScrollPosition() : null, - }; -} -function useHistoryStateNavigation(base) { - const { history, location } = window; - // private variables - const currentLocation = { - value: createCurrentLocation(base, location), - }; - const historyState = { value: history.state }; - // build current history entry as this is a fresh navigation - if (!historyState.value) { - changeLocation(currentLocation.value, { - back: null, - current: currentLocation.value, - forward: null, - // the length is off by one, we need to decrease it - position: history.length - 1, - replaced: true, - // don't add a scroll as the user may have an anchor, and we want - // scrollBehavior to be triggered without a saved position - scroll: null, - }, true); - } - function changeLocation(to, state, replace) { - /** - * if a base tag is provided, and we are on a normal domain, we have to - * respect the provided `base` attribute because pushState() will use it and - * potentially erase anything before the `#` like at - * https://github.com/vuejs/router/issues/685 where a base of - * `/folder/#` but a base of `/` would erase the `/folder/` section. If - * there is no host, the `` tag makes no sense and if there isn't a - * base tag we can just use everything after the `#`. - */ - const hashIndex = base.indexOf('#'); - const url = hashIndex > -1 - ? (location.host && document.querySelector('base') - ? base - : base.slice(hashIndex)) + to - : createBaseLocation() + base + to; - try { - // BROWSER QUIRK - // NOTE: Safari throws a SecurityError when calling this function 100 times in 30 seconds - history[replace ? 'replaceState' : 'pushState'](state, '', url); - historyState.value = state; - } - catch (err) { - { - warn('Error with push/replace State', err); - } - // Force the navigation, this also resets the call count - location[replace ? 'replace' : 'assign'](url); - } - } - function replace(to, data) { - const state = assign({}, history.state, buildState(historyState.value.back, - // keep back and forward entries but override current position - to, historyState.value.forward, true), data, { position: historyState.value.position }); - changeLocation(to, state, true); - currentLocation.value = to; - } - function push(to, data) { - // Add to current entry the information of where we are going - // as well as saving the current position - const currentState = assign({}, - // use current history state to gracefully handle a wrong call to - // history.replaceState - // https://github.com/vuejs/router/issues/366 - historyState.value, history.state, { - forward: to, - scroll: computeScrollPosition(), - }); - if (!history.state) { - warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` + - `history.replaceState(history.state, '', url)\n\n` + - `You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`); - } - changeLocation(currentState.current, currentState, true); - const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data); - changeLocation(to, state, false); - currentLocation.value = to; - } - return { - location: currentLocation, - state: historyState, - push, - replace, - }; -} -/** - * Creates an HTML5 history. Most common history for single page applications. - * - * @param base - - */ -function createWebHistory(base) { - base = normalizeBase(base); - const historyNavigation = useHistoryStateNavigation(base); - const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace); - function go(delta, triggerListeners = true) { - if (!triggerListeners) - historyListeners.pauseListeners(); - history.go(delta); - } - const routerHistory = assign({ - // it's overridden right after - location: '', - base, - go, - createHref: createHref.bind(null, base), - }, historyNavigation, historyListeners); - Object.defineProperty(routerHistory, 'location', { - enumerable: true, - get: () => historyNavigation.location.value, - }); - Object.defineProperty(routerHistory, 'state', { - enumerable: true, - get: () => historyNavigation.state.value, - }); - return routerHistory; -} - -/** - * Creates an in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere. - * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`. - * - * @param base - Base applied to all urls, defaults to '/' - * @returns a history object that can be passed to the router constructor - */ -function createMemoryHistory(base = '') { - let listeners = []; - let queue = [START]; - let position = 0; - base = normalizeBase(base); - function setLocation(location) { - position++; - if (position !== queue.length) { - // we are in the middle, we remove everything from here in the queue - queue.splice(position); - } - queue.push(location); - } - function triggerListeners(to, from, { direction, delta }) { - const info = { - direction, - delta, - type: NavigationType.pop, - }; - for (const callback of listeners) { - callback(to, from, info); - } - } - const routerHistory = { - // rewritten by Object.defineProperty - location: START, - // TODO: should be kept in queue - state: {}, - base, - createHref: createHref.bind(null, base), - replace(to) { - // remove current entry and decrement position - queue.splice(position--, 1); - setLocation(to); - }, - push(to, data) { - setLocation(to); - }, - listen(callback) { - listeners.push(callback); - return () => { - const index = listeners.indexOf(callback); - if (index > -1) - listeners.splice(index, 1); - }; - }, - destroy() { - listeners = []; - queue = [START]; - position = 0; - }, - go(delta, shouldTrigger = true) { - const from = this.location; - const direction = - // we are considering delta === 0 going forward, but in abstract mode - // using 0 for the delta doesn't make sense like it does in html5 where - // it reloads the page - delta < 0 ? NavigationDirection.back : NavigationDirection.forward; - position = Math.max(0, Math.min(position + delta, queue.length - 1)); - if (shouldTrigger) { - triggerListeners(this.location, from, { - direction, - delta, - }); - } - }, - }; - Object.defineProperty(routerHistory, 'location', { - enumerable: true, - get: () => queue[position], - }); - return routerHistory; -} - -/** - * Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to - * handle any URL is not possible. - * - * @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `` tag - * in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState() - * calls**, meaning that if you use a `` tag, it's `href` value **has to match this parameter** (ignoring anything - * after the `#`). - * - * @example - * ```js - * // at https://example.com/folder - * createWebHashHistory() // gives a url of `https://example.com/folder#` - * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#` - * // if the `#` is provided in the base, it won't be added by `createWebHashHistory` - * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/` - * // you should avoid doing this because it changes the original url and breaks copying urls - * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#` - * - * // at file:///usr/etc/folder/index.html - * // for locations with no `host`, the base is ignored - * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#` - * ``` - */ -function createWebHashHistory(base) { - // Make sure this implementation is fine in terms of encoding, specially for IE11 - // for `file://`, directly use the pathname and ignore the base - // location.pathname contains an initial `/` even at the root: `https://example.com` - base = location.host ? base || location.pathname + location.search : ''; - // allow the user to provide a `#` in the middle: `/base/#/app` - if (!base.includes('#')) - base += '#'; - if (!base.endsWith('#/') && !base.endsWith('#')) { - warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`); - } - return createWebHistory(base); -} - -function isRouteLocation(route) { - return typeof route === 'string' || (route && typeof route === 'object'); -} -function isRouteName(name) { - return typeof name === 'string' || typeof name === 'symbol'; -} - -/** - * Initial route location where the router is. Can be used in navigation guards - * to differentiate the initial navigation. - * - * @example - * ```js - * import { START_LOCATION } from 'vue-router' - * - * router.beforeEach((to, from) => { - * if (from === START_LOCATION) { - * // initial navigation - * } - * }) - * ``` - */ -const START_LOCATION_NORMALIZED = { - path: '/', - name: undefined, - params: {}, - query: {}, - hash: '', - fullPath: '/', - matched: [], - meta: {}, - redirectedFrom: undefined, -}; - -const NavigationFailureSymbol = Symbol('navigation failure' ); -/** - * Enumeration with all possible types for navigation failures. Can be passed to - * {@link isNavigationFailure} to check for specific failures. - */ -var NavigationFailureType; -(function (NavigationFailureType) { - /** - * An aborted navigation is a navigation that failed because a navigation - * guard returned `false` or called `next(false)` - */ - NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted"; - /** - * A cancelled navigation is a navigation that failed because a more recent - * navigation finished started (not necessarily finished). - */ - NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled"; - /** - * A duplicated navigation is a navigation that failed because it was - * initiated while already being at the exact same location. - */ - NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated"; -})(NavigationFailureType || (NavigationFailureType = {})); -// DEV only debug messages -const ErrorTypeMessages = { - [1 /* ErrorTypes.MATCHER_NOT_FOUND */]({ location, currentLocation }) { - return `No match for\n ${JSON.stringify(location)}${currentLocation - ? '\nwhile being at\n' + JSON.stringify(currentLocation) - : ''}`; - }, - [2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */]({ from, to, }) { - return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`; - }, - [4 /* ErrorTypes.NAVIGATION_ABORTED */]({ from, to }) { - return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`; - }, - [8 /* ErrorTypes.NAVIGATION_CANCELLED */]({ from, to }) { - return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`; - }, - [16 /* ErrorTypes.NAVIGATION_DUPLICATED */]({ from, to }) { - return `Avoided redundant navigation to current location: "${from.fullPath}".`; - }, -}; -function createRouterError(type, params) { - // keep full error messages in cjs versions - { - return assign(new Error(ErrorTypeMessages[type](params)), { - type, - [NavigationFailureSymbol]: true, - }, params); - } -} -function isNavigationFailure(error, type) { - return (error instanceof Error && - NavigationFailureSymbol in error && - (type == null || !!(error.type & type))); -} -const propertiesToLog = ['params', 'query', 'hash']; -function stringifyRoute(to) { - if (typeof to === 'string') - return to; - if ('path' in to) - return to.path; - const location = {}; - for (const key of propertiesToLog) { - if (key in to) - location[key] = to[key]; - } - return JSON.stringify(location, null, 2); -} - -// default pattern for a param: non-greedy everything but / -const BASE_PARAM_PATTERN = '[^/]+?'; -const BASE_PATH_PARSER_OPTIONS = { - sensitive: false, - strict: false, - start: true, - end: true, -}; -// Special Regex characters that must be escaped in static tokens -const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g; -/** - * Creates a path parser from an array of Segments (a segment is an array of Tokens) - * - * @param segments - array of segments returned by tokenizePath - * @param extraOptions - optional options for the regexp - * @returns a PathParser - */ -function tokensToParser(segments, extraOptions) { - const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions); - // the amount of scores is the same as the length of segments except for the root segment "/" - const score = []; - // the regexp as a string - let pattern = options.start ? '^' : ''; - // extracted keys - const keys = []; - for (const segment of segments) { - // the root segment needs special treatment - const segmentScores = segment.length ? [] : [90 /* PathScore.Root */]; - // allow trailing slash - if (options.strict && !segment.length) - pattern += '/'; - for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) { - const token = segment[tokenIndex]; - // resets the score if we are inside a sub-segment /:a-other-:b - let subSegmentScore = 40 /* PathScore.Segment */ + - (options.sensitive ? 0.25 /* PathScore.BonusCaseSensitive */ : 0); - if (token.type === 0 /* TokenType.Static */) { - // prepend the slash if we are starting a new segment - if (!tokenIndex) - pattern += '/'; - pattern += token.value.replace(REGEX_CHARS_RE, '\\$&'); - subSegmentScore += 40 /* PathScore.Static */; - } - else if (token.type === 1 /* TokenType.Param */) { - const { value, repeatable, optional, regexp } = token; - keys.push({ - name: value, - repeatable, - optional, - }); - const re = regexp ? regexp : BASE_PARAM_PATTERN; - // the user provided a custom regexp /:id(\\d+) - if (re !== BASE_PARAM_PATTERN) { - subSegmentScore += 10 /* PathScore.BonusCustomRegExp */; - // make sure the regexp is valid before using it - try { - new RegExp(`(${re})`); - } - catch (err) { - throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` + - err.message); - } - } - // when we repeat we must take care of the repeating leading slash - let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`; - // prepend the slash if we are starting a new segment - if (!tokenIndex) - subPattern = - // avoid an optional / if there are more segments e.g. /:p?-static - // or /:p?-:p2 - optional && segment.length < 2 - ? `(?:/${subPattern})` - : '/' + subPattern; - if (optional) - subPattern += '?'; - pattern += subPattern; - subSegmentScore += 20 /* PathScore.Dynamic */; - if (optional) - subSegmentScore += -8 /* PathScore.BonusOptional */; - if (repeatable) - subSegmentScore += -20 /* PathScore.BonusRepeatable */; - if (re === '.*') - subSegmentScore += -50 /* PathScore.BonusWildcard */; - } - segmentScores.push(subSegmentScore); - } - // an empty array like /home/ -> [[{home}], []] - // if (!segment.length) pattern += '/' - score.push(segmentScores); - } - // only apply the strict bonus to the last score - if (options.strict && options.end) { - const i = score.length - 1; - score[i][score[i].length - 1] += 0.7000000000000001 /* PathScore.BonusStrict */; - } - // TODO: dev only warn double trailing slash - if (!options.strict) - pattern += '/?'; - if (options.end) - pattern += '$'; - // allow paths like /dynamic to only match dynamic or dynamic/... but not dynamic_something_else - else if (options.strict) - pattern += '(?:/|$)'; - const re = new RegExp(pattern, options.sensitive ? '' : 'i'); - function parse(path) { - const match = path.match(re); - const params = {}; - if (!match) - return null; - for (let i = 1; i < match.length; i++) { - const value = match[i] || ''; - const key = keys[i - 1]; - params[key.name] = value && key.repeatable ? value.split('/') : value; - } - return params; - } - function stringify(params) { - let path = ''; - // for optional parameters to allow to be empty - let avoidDuplicatedSlash = false; - for (const segment of segments) { - if (!avoidDuplicatedSlash || !path.endsWith('/')) - path += '/'; - avoidDuplicatedSlash = false; - for (const token of segment) { - if (token.type === 0 /* TokenType.Static */) { - path += token.value; - } - else if (token.type === 1 /* TokenType.Param */) { - const { value, repeatable, optional } = token; - const param = value in params ? params[value] : ''; - if (isArray(param) && !repeatable) { - throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`); - } - const text = isArray(param) - ? param.join('/') - : param; - if (!text) { - if (optional) { - // if we have more than one optional param like /:a?-static we don't need to care about the optional param - if (segment.length < 2) { - // remove the last slash as we could be at the end - if (path.endsWith('/')) - path = path.slice(0, -1); - // do not append a slash on the next iteration - else - avoidDuplicatedSlash = true; - } - } - else - throw new Error(`Missing required param "${value}"`); - } - path += text; - } - } - } - // avoid empty path when we have multiple optional params - return path || '/'; - } - return { - re, - score, - keys, - parse, - stringify, - }; -} -/** - * Compares an array of numbers as used in PathParser.score and returns a - * number. This function can be used to `sort` an array - * - * @param a - first array of numbers - * @param b - second array of numbers - * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b - * should be sorted first - */ -function compareScoreArray(a, b) { - let i = 0; - while (i < a.length && i < b.length) { - const diff = b[i] - a[i]; - // only keep going if diff === 0 - if (diff) - return diff; - i++; - } - // if the last subsegment was Static, the shorter segments should be sorted first - // otherwise sort the longest segment first - if (a.length < b.length) { - return a.length === 1 && a[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */ - ? -1 - : 1; - } - else if (a.length > b.length) { - return b.length === 1 && b[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */ - ? 1 - : -1; - } - return 0; -} -/** - * Compare function that can be used with `sort` to sort an array of PathParser - * - * @param a - first PathParser - * @param b - second PathParser - * @returns 0 if both are equal, < 0 if a should be sorted first, > 0 if b - */ -function comparePathParserScore(a, b) { - let i = 0; - const aScore = a.score; - const bScore = b.score; - while (i < aScore.length && i < bScore.length) { - const comp = compareScoreArray(aScore[i], bScore[i]); - // do not return if both are equal - if (comp) - return comp; - i++; - } - if (Math.abs(bScore.length - aScore.length) === 1) { - if (isLastScoreNegative(aScore)) - return 1; - if (isLastScoreNegative(bScore)) - return -1; - } - // if a and b share the same score entries but b has more, sort b first - return bScore.length - aScore.length; - // this is the ternary version - // return aScore.length < bScore.length - // ? 1 - // : aScore.length > bScore.length - // ? -1 - // : 0 -} -/** - * This allows detecting splats at the end of a path: /home/:id(.*)* - * - * @param score - score to check - * @returns true if the last entry is negative - */ -function isLastScoreNegative(score) { - const last = score[score.length - 1]; - return score.length > 0 && last[last.length - 1] < 0; -} - -const ROOT_TOKEN = { - type: 0 /* TokenType.Static */, - value: '', -}; -const VALID_PARAM_RE = /[a-zA-Z0-9_]/; -// After some profiling, the cache seems to be unnecessary because tokenizePath -// (the slowest part of adding a route) is very fast -// const tokenCache = new Map() -function tokenizePath(path) { - if (!path) - return [[]]; - if (path === '/') - return [[ROOT_TOKEN]]; - if (!path.startsWith('/')) { - throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".` - ); - } - // if (tokenCache.has(path)) return tokenCache.get(path)! - function crash(message) { - throw new Error(`ERR (${state})/"${buffer}": ${message}`); - } - let state = 0 /* TokenizerState.Static */; - let previousState = state; - const tokens = []; - // the segment will always be valid because we get into the initial state - // with the leading / - let segment; - function finalizeSegment() { - if (segment) - tokens.push(segment); - segment = []; - } - // index on the path - let i = 0; - // char at index - let char; - // buffer of the value read - let buffer = ''; - // custom regexp for a param - let customRe = ''; - function consumeBuffer() { - if (!buffer) - return; - if (state === 0 /* TokenizerState.Static */) { - segment.push({ - type: 0 /* TokenType.Static */, - value: buffer, - }); - } - else if (state === 1 /* TokenizerState.Param */ || - state === 2 /* TokenizerState.ParamRegExp */ || - state === 3 /* TokenizerState.ParamRegExpEnd */) { - if (segment.length > 1 && (char === '*' || char === '+')) - crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`); - segment.push({ - type: 1 /* TokenType.Param */, - value: buffer, - regexp: customRe, - repeatable: char === '*' || char === '+', - optional: char === '*' || char === '?', - }); - } - else { - crash('Invalid state to consume buffer'); - } - buffer = ''; - } - function addCharToBuffer() { - buffer += char; - } - while (i < path.length) { - char = path[i++]; - if (char === '\\' && state !== 2 /* TokenizerState.ParamRegExp */) { - previousState = state; - state = 4 /* TokenizerState.EscapeNext */; - continue; - } - switch (state) { - case 0 /* TokenizerState.Static */: - if (char === '/') { - if (buffer) { - consumeBuffer(); - } - finalizeSegment(); - } - else if (char === ':') { - consumeBuffer(); - state = 1 /* TokenizerState.Param */; - } - else { - addCharToBuffer(); - } - break; - case 4 /* TokenizerState.EscapeNext */: - addCharToBuffer(); - state = previousState; - break; - case 1 /* TokenizerState.Param */: - if (char === '(') { - state = 2 /* TokenizerState.ParamRegExp */; - } - else if (VALID_PARAM_RE.test(char)) { - addCharToBuffer(); - } - else { - consumeBuffer(); - state = 0 /* TokenizerState.Static */; - // go back one character if we were not modifying - if (char !== '*' && char !== '?' && char !== '+') - i--; - } - break; - case 2 /* TokenizerState.ParamRegExp */: - // TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix) - // it already works by escaping the closing ) - // https://paths.esm.dev/?p=AAMeJbiAwQEcDKbAoAAkP60PG2R6QAvgNaA6AFACM2ABuQBB# - // is this really something people need since you can also write - // /prefix_:p()_suffix - if (char === ')') { - // handle the escaped ) - if (customRe[customRe.length - 1] == '\\') - customRe = customRe.slice(0, -1) + char; - else - state = 3 /* TokenizerState.ParamRegExpEnd */; - } - else { - customRe += char; - } - break; - case 3 /* TokenizerState.ParamRegExpEnd */: - // same as finalizing a param - consumeBuffer(); - state = 0 /* TokenizerState.Static */; - // go back one character if we were not modifying - if (char !== '*' && char !== '?' && char !== '+') - i--; - customRe = ''; - break; - default: - crash('Unknown state'); - break; - } - } - if (state === 2 /* TokenizerState.ParamRegExp */) - crash(`Unfinished custom RegExp for param "${buffer}"`); - consumeBuffer(); - finalizeSegment(); - // tokenCache.set(path, tokens) - return tokens; -} - -function createRouteRecordMatcher(record, parent, options) { - const parser = tokensToParser(tokenizePath(record.path), options); - // warn against params with the same name - { - const existingKeys = new Set(); - for (const key of parser.keys) { - if (existingKeys.has(key.name)) - warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`); - existingKeys.add(key.name); - } - } - const matcher = assign(parser, { - record, - parent, - // these needs to be populated by the parent - children: [], - alias: [], - }); - if (parent) { - // both are aliases or both are not aliases - // we don't want to mix them because the order is used when - // passing originalRecord in Matcher.addRoute - if (!matcher.record.aliasOf === !parent.record.aliasOf) - parent.children.push(matcher); - } - return matcher; -} - -/** - * Creates a Router Matcher. - * - * @internal - * @param routes - array of initial routes - * @param globalOptions - global route options - */ -function createRouterMatcher(routes, globalOptions) { - // normalized ordered array of matchers - const matchers = []; - const matcherMap = new Map(); - globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions); - function getRecordMatcher(name) { - return matcherMap.get(name); - } - function addRoute(record, parent, originalRecord) { - // used later on to remove by name - const isRootAdd = !originalRecord; - const mainNormalizedRecord = normalizeRouteRecord(record); - { - checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent); - } - // we might be the child of an alias - mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record; - const options = mergeOptions(globalOptions, record); - // generate an array of records to correctly handle aliases - const normalizedRecords = [ - mainNormalizedRecord, - ]; - if ('alias' in record) { - const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias; - for (const alias of aliases) { - normalizedRecords.push(assign({}, mainNormalizedRecord, { - // this allows us to hold a copy of the `components` option - // so that async components cache is hold on the original record - components: originalRecord - ? originalRecord.record.components - : mainNormalizedRecord.components, - path: alias, - // we might be the child of an alias - aliasOf: originalRecord - ? originalRecord.record - : mainNormalizedRecord, - // the aliases are always of the same kind as the original since they - // are defined on the same record - })); - } - } - let matcher; - let originalMatcher; - for (const normalizedRecord of normalizedRecords) { - const { path } = normalizedRecord; - // Build up the path for nested routes if the child isn't an absolute - // route. Only add the / delimiter if the child path isn't empty and if the - // parent path doesn't have a trailing slash - if (parent && path[0] !== '/') { - const parentPath = parent.record.path; - const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/'; - normalizedRecord.path = - parent.record.path + (path && connectingSlash + path); - } - if (normalizedRecord.path === '*') { - throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' + - 'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.'); - } - // create the object beforehand, so it can be passed to children - matcher = createRouteRecordMatcher(normalizedRecord, parent, options); - if (parent && path[0] === '/') - checkMissingParamsInAbsolutePath(matcher, parent); - // if we are an alias we must tell the original record that we exist, - // so we can be removed - if (originalRecord) { - originalRecord.alias.push(matcher); - { - checkSameParams(originalRecord, matcher); - } - } - else { - // otherwise, the first record is the original and others are aliases - originalMatcher = originalMatcher || matcher; - if (originalMatcher !== matcher) - originalMatcher.alias.push(matcher); - // remove the route if named and only for the top record (avoid in nested calls) - // this works because the original record is the first one - if (isRootAdd && record.name && !isAliasRecord(matcher)) - removeRoute(record.name); - } - if (mainNormalizedRecord.children) { - const children = mainNormalizedRecord.children; - for (let i = 0; i < children.length; i++) { - addRoute(children[i], matcher, originalRecord && originalRecord.children[i]); - } - } - // if there was no original record, then the first one was not an alias and all - // other aliases (if any) need to reference this record when adding children - originalRecord = originalRecord || matcher; - // TODO: add normalized records for more flexibility - // if (parent && isAliasRecord(originalRecord)) { - // parent.children.push(originalRecord) - // } - // Avoid adding a record that doesn't display anything. This allows passing through records without a component to - // not be reached and pass through the catch all route - if ((matcher.record.components && - Object.keys(matcher.record.components).length) || - matcher.record.name || - matcher.record.redirect) { - insertMatcher(matcher); - } - } - return originalMatcher - ? () => { - // since other matchers are aliases, they should be removed by the original matcher - removeRoute(originalMatcher); - } - : noop; - } - function removeRoute(matcherRef) { - if (isRouteName(matcherRef)) { - const matcher = matcherMap.get(matcherRef); - if (matcher) { - matcherMap.delete(matcherRef); - matchers.splice(matchers.indexOf(matcher), 1); - matcher.children.forEach(removeRoute); - matcher.alias.forEach(removeRoute); - } - } - else { - const index = matchers.indexOf(matcherRef); - if (index > -1) { - matchers.splice(index, 1); - if (matcherRef.record.name) - matcherMap.delete(matcherRef.record.name); - matcherRef.children.forEach(removeRoute); - matcherRef.alias.forEach(removeRoute); - } - } - } - function getRoutes() { - return matchers; - } - function insertMatcher(matcher) { - let i = 0; - while (i < matchers.length && - comparePathParserScore(matcher, matchers[i]) >= 0 && - // Adding children with empty path should still appear before the parent - // https://github.com/vuejs/router/issues/1124 - (matcher.record.path !== matchers[i].record.path || - !isRecordChildOf(matcher, matchers[i]))) - i++; - matchers.splice(i, 0, matcher); - // only add the original record to the name map - if (matcher.record.name && !isAliasRecord(matcher)) - matcherMap.set(matcher.record.name, matcher); - } - function resolve(location, currentLocation) { - let matcher; - let params = {}; - let path; - let name; - if ('name' in location && location.name) { - matcher = matcherMap.get(location.name); - if (!matcher) - throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, { - location, - }); - // warn if the user is passing invalid params so they can debug it better when they get removed - { - const invalidParams = Object.keys(location.params || {}).filter(paramName => !matcher.keys.find(k => k.name === paramName)); - if (invalidParams.length) { - warn(`Discarded invalid param(s) "${invalidParams.join('", "')}" when navigating. See https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#414-2022-08-22 for more details.`); - } - } - name = matcher.record.name; - params = assign( - // paramsFromLocation is a new object - paramsFromLocation(currentLocation.params, - // only keep params that exist in the resolved location - // TODO: only keep optional params coming from a parent record - matcher.keys.filter(k => !k.optional).map(k => k.name)), - // discard any existing params in the current location that do not exist here - // #1497 this ensures better active/exact matching - location.params && - paramsFromLocation(location.params, matcher.keys.map(k => k.name))); - // throws if cannot be stringified - path = matcher.stringify(params); - } - else if ('path' in location) { - // no need to resolve the path with the matcher as it was provided - // this also allows the user to control the encoding - path = location.path; - if (!path.startsWith('/')) { - warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://github.com/vuejs/router/issues/new/choose.`); - } - matcher = matchers.find(m => m.re.test(path)); - // matcher should have a value after the loop - if (matcher) { - // we know the matcher works because we tested the regexp - params = matcher.parse(path); - name = matcher.record.name; - } - // location is a relative path - } - else { - // match by name or path of current route - matcher = currentLocation.name - ? matcherMap.get(currentLocation.name) - : matchers.find(m => m.re.test(currentLocation.path)); - if (!matcher) - throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, { - location, - currentLocation, - }); - name = matcher.record.name; - // since we are navigating to the same location, we don't need to pick the - // params like when `name` is provided - params = assign({}, currentLocation.params, location.params); - path = matcher.stringify(params); - } - const matched = []; - let parentMatcher = matcher; - while (parentMatcher) { - // reversed order so parents are at the beginning - matched.unshift(parentMatcher.record); - parentMatcher = parentMatcher.parent; - } - return { - name, - path, - params, - matched, - meta: mergeMetaFields(matched), - }; - } - // add initial routes - routes.forEach(route => addRoute(route)); - return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher }; -} -function paramsFromLocation(params, keys) { - const newParams = {}; - for (const key of keys) { - if (key in params) - newParams[key] = params[key]; - } - return newParams; -} -/** - * Normalizes a RouteRecordRaw. Creates a copy - * - * @param record - * @returns the normalized version - */ -function normalizeRouteRecord(record) { - return { - path: record.path, - redirect: record.redirect, - name: record.name, - meta: record.meta || {}, - aliasOf: undefined, - beforeEnter: record.beforeEnter, - props: normalizeRecordProps(record), - children: record.children || [], - instances: {}, - leaveGuards: new Set(), - updateGuards: new Set(), - enterCallbacks: {}, - components: 'components' in record - ? record.components || null - : record.component && { default: record.component }, - }; -} -/** - * Normalize the optional `props` in a record to always be an object similar to - * components. Also accept a boolean for components. - * @param record - */ -function normalizeRecordProps(record) { - const propsObject = {}; - // props does not exist on redirect records, but we can set false directly - const props = record.props || false; - if ('component' in record) { - propsObject.default = props; - } - else { - // NOTE: we could also allow a function to be applied to every component. - // Would need user feedback for use cases - for (const name in record.components) - propsObject[name] = typeof props === 'object' ? props[name] : props; - } - return propsObject; -} -/** - * Checks if a record or any of its parent is an alias - * @param record - */ -function isAliasRecord(record) { - while (record) { - if (record.record.aliasOf) - return true; - record = record.parent; - } - return false; -} -/** - * Merge meta fields of an array of records - * - * @param matched - array of matched records - */ -function mergeMetaFields(matched) { - return matched.reduce((meta, record) => assign(meta, record.meta), {}); -} -function mergeOptions(defaults, partialOptions) { - const options = {}; - for (const key in defaults) { - options[key] = key in partialOptions ? partialOptions[key] : defaults[key]; - } - return options; -} -function isSameParam(a, b) { - return (a.name === b.name && - a.optional === b.optional && - a.repeatable === b.repeatable); -} -/** - * Check if a path and its alias have the same required params - * - * @param a - original record - * @param b - alias record - */ -function checkSameParams(a, b) { - for (const key of a.keys) { - if (!key.optional && !b.keys.find(isSameParam.bind(null, key))) - return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`); - } - for (const key of b.keys) { - if (!key.optional && !a.keys.find(isSameParam.bind(null, key))) - return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`); - } -} -/** - * A route with a name and a child with an empty path without a name should warn when adding the route - * - * @param mainNormalizedRecord - RouteRecordNormalized - * @param parent - RouteRecordMatcher - */ -function checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent) { - if (parent && - parent.record.name && - !mainNormalizedRecord.name && - !mainNormalizedRecord.path) { - warn(`The route named "${String(parent.record.name)}" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.`); - } -} -function checkMissingParamsInAbsolutePath(record, parent) { - for (const key of parent.keys) { - if (!record.keys.find(isSameParam.bind(null, key))) - return warn(`Absolute path "${record.record.path}" must have the exact same param named "${key.name}" as its parent "${parent.record.path}".`); - } -} -function isRecordChildOf(record, parent) { - return parent.children.some(child => child === record || isRecordChildOf(record, child)); -} - -/** - * Encoding Rules ⣠= Space Path: ⣠" < > # ? { } Query: ⣠" < > # & = Hash: ⣠" - * < > ` - * - * On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2) - * defines some extra characters to be encoded. Most browsers do not encode them - * in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to - * also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`) - * plus `-._~`. This extra safety should be applied to query by patching the - * string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\` - * should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\` - * into a `/` if directly typed in. The _backtick_ (`````) should also be - * encoded everywhere because some browsers like FF encode it when directly - * written while others don't. Safari and IE don't encode ``"<>{}``` in hash. - */ -// const EXTRA_RESERVED_RE = /[!'()*]/g -// const encodeReservedReplacer = (c: string) => '%' + c.charCodeAt(0).toString(16) -const HASH_RE = /#/g; // %23 -const AMPERSAND_RE = /&/g; // %26 -const SLASH_RE = /\//g; // %2F -const EQUAL_RE = /=/g; // %3D -const IM_RE = /\?/g; // %3F -const PLUS_RE = /\+/g; // %2B -/** - * NOTE: It's not clear to me if we should encode the + symbol in queries, it - * seems to be less flexible than not doing so and I can't find out the legacy - * systems requiring this for regular requests like text/html. In the standard, - * the encoding of the plus character is only mentioned for - * application/x-www-form-urlencoded - * (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo - * leave the plus character as is in queries. To be more flexible, we allow the - * plus character on the query, but it can also be manually encoded by the user. - * - * Resources: - * - https://url.spec.whatwg.org/#urlencoded-parsing - * - https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20 - */ -const ENC_BRACKET_OPEN_RE = /%5B/g; // [ -const ENC_BRACKET_CLOSE_RE = /%5D/g; // ] -const ENC_CARET_RE = /%5E/g; // ^ -const ENC_BACKTICK_RE = /%60/g; // ` -const ENC_CURLY_OPEN_RE = /%7B/g; // { -const ENC_PIPE_RE = /%7C/g; // | -const ENC_CURLY_CLOSE_RE = /%7D/g; // } -const ENC_SPACE_RE = /%20/g; // } -/** - * Encode characters that need to be encoded on the path, search and hash - * sections of the URL. - * - * @internal - * @param text - string to encode - * @returns encoded string - */ -function commonEncode(text) { - return encodeURI('' + text) - .replace(ENC_PIPE_RE, '|') - .replace(ENC_BRACKET_OPEN_RE, '[') - .replace(ENC_BRACKET_CLOSE_RE, ']'); -} -/** - * Encode characters that need to be encoded on the hash section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeHash(text) { - return commonEncode(text) - .replace(ENC_CURLY_OPEN_RE, '{') - .replace(ENC_CURLY_CLOSE_RE, '}') - .replace(ENC_CARET_RE, '^'); -} -/** - * Encode characters that need to be encoded query values on the query - * section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeQueryValue(text) { - return (commonEncode(text) - // Encode the space as +, encode the + to differentiate it from the space - .replace(PLUS_RE, '%2B') - .replace(ENC_SPACE_RE, '+') - .replace(HASH_RE, '%23') - .replace(AMPERSAND_RE, '%26') - .replace(ENC_BACKTICK_RE, '`') - .replace(ENC_CURLY_OPEN_RE, '{') - .replace(ENC_CURLY_CLOSE_RE, '}') - .replace(ENC_CARET_RE, '^')); -} -/** - * Like `encodeQueryValue` but also encodes the `=` character. - * - * @param text - string to encode - */ -function encodeQueryKey(text) { - return encodeQueryValue(text).replace(EQUAL_RE, '%3D'); -} -/** - * Encode characters that need to be encoded on the path section of the URL. - * - * @param text - string to encode - * @returns encoded string - */ -function encodePath(text) { - return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F'); -} -/** - * Encode characters that need to be encoded on the path section of the URL as a - * param. This function encodes everything {@link encodePath} does plus the - * slash (`/`) character. If `text` is `null` or `undefined`, returns an empty - * string instead. - * - * @param text - string to encode - * @returns encoded string - */ -function encodeParam(text) { - return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F'); -} -/** - * Decode text using `decodeURIComponent`. Returns the original text if it - * fails. - * - * @param text - string to decode - * @returns decoded string - */ -function decode(text) { - try { - return decodeURIComponent('' + text); - } - catch (err) { - warn(`Error decoding "${text}". Using original value`); - } - return '' + text; -} - -/** - * Transforms a queryString into a {@link LocationQuery} object. Accept both, a - * version with the leading `?` and without Should work as URLSearchParams - - * @internal - * - * @param search - search string to parse - * @returns a query object - */ -function parseQuery(search) { - const query = {}; - // avoid creating an object with an empty key and empty value - // because of split('&') - if (search === '' || search === '?') - return query; - const hasLeadingIM = search[0] === '?'; - const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&'); - for (let i = 0; i < searchParams.length; ++i) { - // pre decode the + into space - const searchParam = searchParams[i].replace(PLUS_RE, ' '); - // allow the = character - const eqPos = searchParam.indexOf('='); - const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos)); - const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1)); - if (key in query) { - // an extra variable for ts types - let currentValue = query[key]; - if (!isArray(currentValue)) { - currentValue = query[key] = [currentValue]; - } - currentValue.push(value); - } - else { - query[key] = value; - } - } - return query; -} -/** - * Stringifies a {@link LocationQueryRaw} object. Like `URLSearchParams`, it - * doesn't prepend a `?` - * - * @internal - * - * @param query - query object to stringify - * @returns string version of the query without the leading `?` - */ -function stringifyQuery(query) { - let search = ''; - for (let key in query) { - const value = query[key]; - key = encodeQueryKey(key); - if (value == null) { - // only null adds the value - if (value !== undefined) { - search += (search.length ? '&' : '') + key; - } - continue; - } - // keep null values - const values = isArray(value) - ? value.map(v => v && encodeQueryValue(v)) - : [value && encodeQueryValue(value)]; - values.forEach(value => { - // skip undefined values in arrays as if they were not present - // smaller code than using filter - if (value !== undefined) { - // only append & with non-empty search - search += (search.length ? '&' : '') + key; - if (value != null) - search += '=' + value; - } - }); - } - return search; -} -/** - * Transforms a {@link LocationQueryRaw} into a {@link LocationQuery} by casting - * numbers into strings, removing keys with an undefined value and replacing - * undefined with null in arrays - * - * @param query - query object to normalize - * @returns a normalized query object - */ -function normalizeQuery(query) { - const normalizedQuery = {}; - for (const key in query) { - const value = query[key]; - if (value !== undefined) { - normalizedQuery[key] = isArray(value) - ? value.map(v => (v == null ? null : '' + v)) - : value == null - ? value - : '' + value; - } - } - return normalizedQuery; -} - -/** - * RouteRecord being rendered by the closest ancestor Router View. Used for - * `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View - * Location Matched - * - * @internal - */ -const matchedRouteKey = Symbol('router view location matched' ); -/** - * Allows overriding the router view depth to control which component in - * `matched` is rendered. rvd stands for Router View Depth - * - * @internal - */ -const viewDepthKey = Symbol('router view depth' ); -/** - * Allows overriding the router instance returned by `useRouter` in tests. r - * stands for router - * - * @internal - */ -const routerKey = Symbol('router' ); -/** - * Allows overriding the current route returned by `useRoute` in tests. rl - * stands for route location - * - * @internal - */ -const routeLocationKey = Symbol('route location' ); -/** - * Allows overriding the current route used by router-view. Internally this is - * used when the `route` prop is passed. - * - * @internal - */ -const routerViewLocationKey = Symbol('router view location' ); - -/** - * Create a list of callbacks that can be reset. Used to create before and after navigation guards list - */ -function useCallbacks() { - let handlers = []; - function add(handler) { - handlers.push(handler); - return () => { - const i = handlers.indexOf(handler); - if (i > -1) - handlers.splice(i, 1); - }; - } - function reset() { - handlers = []; - } - return { - add, - list: () => handlers.slice(), - reset, - }; -} - -function registerGuard(record, name, guard) { - const removeFromList = () => { - record[name].delete(guard); - }; - onUnmounted(removeFromList); - onDeactivated(removeFromList); - onActivated(() => { - record[name].add(guard); - }); - record[name].add(guard); -} -/** - * Add a navigation guard that triggers whenever the component for the current - * location is about to be left. Similar to {@link beforeRouteLeave} but can be - * used in any component. The guard is removed when the component is unmounted. - * - * @param leaveGuard - {@link NavigationGuard} - */ -function onBeforeRouteLeave(leaveGuard) { - if (!getCurrentInstance()) { - warn('getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function'); - return; - } - const activeRecord = inject(matchedRouteKey, - // to avoid warning - {}).value; - if (!activeRecord) { - warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside a component child of . Maybe you called it inside of App.vue?'); - return; - } - registerGuard(activeRecord, 'leaveGuards', leaveGuard); -} -/** - * Add a navigation guard that triggers whenever the current location is about - * to be updated. Similar to {@link beforeRouteUpdate} but can be used in any - * component. The guard is removed when the component is unmounted. - * - * @param updateGuard - {@link NavigationGuard} - */ -function onBeforeRouteUpdate(updateGuard) { - if (!getCurrentInstance()) { - warn('getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function'); - return; - } - const activeRecord = inject(matchedRouteKey, - // to avoid warning - {}).value; - if (!activeRecord) { - warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside a component child of . Maybe you called it inside of App.vue?'); - return; - } - registerGuard(activeRecord, 'updateGuards', updateGuard); -} -function guardToPromiseFn(guard, to, from, record, name) { - // keep a reference to the enterCallbackArray to prevent pushing callbacks if a new navigation took place - const enterCallbackArray = record && - // name is defined if record is because of the function overload - (record.enterCallbacks[name] = record.enterCallbacks[name] || []); - return () => new Promise((resolve, reject) => { - const next = (valid) => { - if (valid === false) { - reject(createRouterError(4 /* ErrorTypes.NAVIGATION_ABORTED */, { - from, - to, - })); - } - else if (valid instanceof Error) { - reject(valid); - } - else if (isRouteLocation(valid)) { - reject(createRouterError(2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */, { - from: to, - to: valid, - })); - } - else { - if (enterCallbackArray && - // since enterCallbackArray is truthy, both record and name also are - record.enterCallbacks[name] === enterCallbackArray && - typeof valid === 'function') { - enterCallbackArray.push(valid); - } - resolve(); - } - }; - // wrapping with Promise.resolve allows it to work with both async and sync guards - const guardReturn = guard.call(record && record.instances[name], to, from, canOnlyBeCalledOnce(next, to, from) ); - let guardCall = Promise.resolve(guardReturn); - if (guard.length < 3) - guardCall = guardCall.then(next); - if (guard.length > 2) { - const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`; - if (typeof guardReturn === 'object' && 'then' in guardReturn) { - guardCall = guardCall.then(resolvedValue => { - // @ts-expect-error: _called is added at canOnlyBeCalledOnce - if (!next._called) { - warn(message); - return Promise.reject(new Error('Invalid navigation guard')); - } - return resolvedValue; - }); - } - else if (guardReturn !== undefined) { - // @ts-expect-error: _called is added at canOnlyBeCalledOnce - if (!next._called) { - warn(message); - reject(new Error('Invalid navigation guard')); - return; - } - } - } - guardCall.catch(err => reject(err)); - }); -} -function canOnlyBeCalledOnce(next, to, from) { - let called = 0; - return function () { - if (called++ === 1) - warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`); - // @ts-expect-error: we put it in the original one because it's easier to check - next._called = true; - if (called === 1) - next.apply(null, arguments); - }; -} -function extractComponentsGuards(matched, guardType, to, from) { - const guards = []; - for (const record of matched) { - if (!record.components && !record.children.length) { - warn(`Record with path "${record.path}" is either missing a "component(s)"` + - ` or "children" property.`); - } - for (const name in record.components) { - let rawComponent = record.components[name]; - { - if (!rawComponent || - (typeof rawComponent !== 'object' && - typeof rawComponent !== 'function')) { - warn(`Component "${name}" in record with path "${record.path}" is not` + - ` a valid component. Received "${String(rawComponent)}".`); - // throw to ensure we stop here but warn to ensure the message isn't - // missed by the user - throw new Error('Invalid route component'); - } - else if ('then' in rawComponent) { - // warn if user wrote import('/component.vue') instead of () => - // import('./component.vue') - warn(`Component "${name}" in record with path "${record.path}" is a ` + - `Promise instead of a function that returns a Promise. Did you ` + - `write "import('./MyPage.vue')" instead of ` + - `"() => import('./MyPage.vue')" ? This will break in ` + - `production if not fixed.`); - const promise = rawComponent; - rawComponent = () => promise; - } - else if (rawComponent.__asyncLoader && - // warn only once per component - !rawComponent.__warnedDefineAsync) { - rawComponent.__warnedDefineAsync = true; - warn(`Component "${name}" in record with path "${record.path}" is defined ` + - `using "defineAsyncComponent()". ` + - `Write "() => import('./MyPage.vue')" instead of ` + - `"defineAsyncComponent(() => import('./MyPage.vue'))".`); - } - } - // skip update and leave guards if the route component is not mounted - if (guardType !== 'beforeRouteEnter' && !record.instances[name]) - continue; - if (isRouteComponent(rawComponent)) { - // __vccOpts is added by vue-class-component and contain the regular options - const options = rawComponent.__vccOpts || rawComponent; - const guard = options[guardType]; - guard && guards.push(guardToPromiseFn(guard, to, from, record, name)); - } - else { - // start requesting the chunk already - let componentPromise = rawComponent(); - if (!('catch' in componentPromise)) { - warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`); - componentPromise = Promise.resolve(componentPromise); - } - guards.push(() => componentPromise.then(resolved => { - if (!resolved) - return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`)); - const resolvedComponent = isESModule(resolved) - ? resolved.default - : resolved; - // replace the function with the resolved component - // cannot be null or undefined because we went into the for loop - record.components[name] = resolvedComponent; - // __vccOpts is added by vue-class-component and contain the regular options - const options = resolvedComponent.__vccOpts || resolvedComponent; - const guard = options[guardType]; - return guard && guardToPromiseFn(guard, to, from, record, name)(); - })); - } - } - } - return guards; -} -/** - * Allows differentiating lazy components from functional components and vue-class-component - * @internal - * - * @param component - */ -function isRouteComponent(component) { - return (typeof component === 'object' || - 'displayName' in component || - 'props' in component || - '__vccOpts' in component); -} -/** - * Ensures a route is loaded, so it can be passed as o prop to ``. - * - * @param route - resolved route to load - */ -function loadRouteLocation(route) { - return route.matched.every(record => record.redirect) - ? Promise.reject(new Error('Cannot load a route that redirects.')) - : Promise.all(route.matched.map(record => record.components && - Promise.all(Object.keys(record.components).reduce((promises, name) => { - const rawComponent = record.components[name]; - if (typeof rawComponent === 'function' && - !('displayName' in rawComponent)) { - promises.push(rawComponent().then(resolved => { - if (!resolved) - return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`)); - const resolvedComponent = isESModule(resolved) - ? resolved.default - : resolved; - // replace the function with the resolved component - // cannot be null or undefined because we went into the for loop - record.components[name] = resolvedComponent; - return; - })); - } - return promises; - }, [])))).then(() => route); -} - -// TODO: we could allow currentRoute as a prop to expose `isActive` and -// `isExactActive` behavior should go through an RFC -function useLink(props) { - const router = inject(routerKey); - const currentRoute = inject(routeLocationKey); - const route = computed(() => router.resolve(unref(props.to))); - const activeRecordIndex = computed(() => { - const { matched } = route.value; - const { length } = matched; - const routeMatched = matched[length - 1]; - const currentMatched = currentRoute.matched; - if (!routeMatched || !currentMatched.length) - return -1; - const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched)); - if (index > -1) - return index; - // possible parent record - const parentRecordPath = getOriginalPath(matched[length - 2]); - return ( - // we are dealing with nested routes - length > 1 && - // if the parent and matched route have the same path, this link is - // referring to the empty child. Or we currently are on a different - // child of the same parent - getOriginalPath(routeMatched) === parentRecordPath && - // avoid comparing the child with its parent - currentMatched[currentMatched.length - 1].path !== parentRecordPath - ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2])) - : index); - }); - const isActive = computed(() => activeRecordIndex.value > -1 && - includesParams(currentRoute.params, route.value.params)); - const isExactActive = computed(() => activeRecordIndex.value > -1 && - activeRecordIndex.value === currentRoute.matched.length - 1 && - isSameRouteLocationParams(currentRoute.params, route.value.params)); - function navigate(e = {}) { - if (guardEvent(e)) { - return router[unref(props.replace) ? 'replace' : 'push'](unref(props.to) - // avoid uncaught errors are they are logged anyway - ).catch(noop); - } - return Promise.resolve(); - } - // devtools only - if (isBrowser) { - const instance = getCurrentInstance(); - if (instance) { - const linkContextDevtools = { - route: route.value, - isActive: isActive.value, - isExactActive: isExactActive.value, - }; - // @ts-expect-error: this is internal - instance.__vrl_devtools = instance.__vrl_devtools || []; - // @ts-expect-error: this is internal - instance.__vrl_devtools.push(linkContextDevtools); - watchEffect(() => { - linkContextDevtools.route = route.value; - linkContextDevtools.isActive = isActive.value; - linkContextDevtools.isExactActive = isExactActive.value; - }, { flush: 'post' }); - } - } - /** - * NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this - */ - return { - route, - href: computed(() => route.value.href), - isActive, - isExactActive, - navigate, - }; -} -const RouterLinkImpl = /*#__PURE__*/ defineComponent({ - name: 'RouterLink', - compatConfig: { MODE: 3 }, - props: { - to: { - type: [String, Object], - required: true, - }, - replace: Boolean, - activeClass: String, - // inactiveClass: String, - exactActiveClass: String, - custom: Boolean, - ariaCurrentValue: { - type: String, - default: 'page', - }, - }, - useLink, - setup(props, { slots }) { - const link = reactive(useLink(props)); - const { options } = inject(routerKey); - const elClass = computed(() => ({ - [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive, - // [getLinkClass( - // props.inactiveClass, - // options.linkInactiveClass, - // 'router-link-inactive' - // )]: !link.isExactActive, - [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive, - })); - return () => { - const children = slots.default && slots.default(link); - return props.custom - ? children - : h('a', { - 'aria-current': link.isExactActive - ? props.ariaCurrentValue - : null, - href: link.href, - // this would override user added attrs but Vue will still add - // the listener, so we end up triggering both - onClick: link.navigate, - class: elClass.value, - }, children); - }; - }, -}); -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to render a link that triggers a navigation on click. - */ -const RouterLink = RouterLinkImpl; -function guardEvent(e) { - // don't redirect with control keys - if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) - return; - // don't redirect when preventDefault called - if (e.defaultPrevented) - return; - // don't redirect on right click - if (e.button !== undefined && e.button !== 0) - return; - // don't redirect if `target="_blank"` - // @ts-expect-error getAttribute does exist - if (e.currentTarget && e.currentTarget.getAttribute) { - // @ts-expect-error getAttribute exists - const target = e.currentTarget.getAttribute('target'); - if (/\b_blank\b/i.test(target)) - return; - } - // this may be a Weex event which doesn't have this method - if (e.preventDefault) - e.preventDefault(); - return true; -} -function includesParams(outer, inner) { - for (const key in inner) { - const innerValue = inner[key]; - const outerValue = outer[key]; - if (typeof innerValue === 'string') { - if (innerValue !== outerValue) - return false; - } - else { - if (!isArray(outerValue) || - outerValue.length !== innerValue.length || - innerValue.some((value, i) => value !== outerValue[i])) - return false; - } - } - return true; -} -/** - * Get the original path value of a record by following its aliasOf - * @param record - */ -function getOriginalPath(record) { - return record ? (record.aliasOf ? record.aliasOf.path : record.path) : ''; -} -/** - * Utility class to get the active class based on defaults. - * @param propClass - * @param globalClass - * @param defaultClass - */ -const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null - ? propClass - : globalClass != null - ? globalClass - : defaultClass; - -const RouterViewImpl = /*#__PURE__*/ defineComponent({ - name: 'RouterView', - // #674 we manually inherit them - inheritAttrs: false, - props: { - name: { - type: String, - default: 'default', - }, - route: Object, - }, - // Better compat for @vue/compat users - // https://github.com/vuejs/router/issues/1315 - compatConfig: { MODE: 3 }, - setup(props, { attrs, slots }) { - warnDeprecatedUsage(); - const injectedRoute = inject(routerViewLocationKey); - const routeToDisplay = computed(() => props.route || injectedRoute.value); - const injectedDepth = inject(viewDepthKey, 0); - // The depth changes based on empty components option, which allows passthrough routes e.g. routes with children - // that are used to reuse the `path` property - const depth = computed(() => { - let initialDepth = unref(injectedDepth); - const { matched } = routeToDisplay.value; - let matchedRoute; - while ((matchedRoute = matched[initialDepth]) && - !matchedRoute.components) { - initialDepth++; - } - return initialDepth; - }); - const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth.value]); - provide(viewDepthKey, computed(() => depth.value + 1)); - provide(matchedRouteKey, matchedRouteRef); - provide(routerViewLocationKey, routeToDisplay); - const viewRef = ref(); - // watch at the same time the component instance, the route record we are - // rendering, and the name - watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => { - // copy reused instances - if (to) { - // this will update the instance for new instances as well as reused - // instances when navigating to a new route - to.instances[name] = instance; - // the component instance is reused for a different route or name, so - // we copy any saved update or leave guards. With async setup, the - // mounting component will mount before the matchedRoute changes, - // making instance === oldInstance, so we check if guards have been - // added before. This works because we remove guards when - // unmounting/deactivating components - if (from && from !== to && instance && instance === oldInstance) { - if (!to.leaveGuards.size) { - to.leaveGuards = from.leaveGuards; - } - if (!to.updateGuards.size) { - to.updateGuards = from.updateGuards; - } - } - } - // trigger beforeRouteEnter next callbacks - if (instance && - to && - // if there is no instance but to and from are the same this might be - // the first visit - (!from || !isSameRouteRecord(to, from) || !oldInstance)) { - (to.enterCallbacks[name] || []).forEach(callback => callback(instance)); - } - }, { flush: 'post' }); - return () => { - const route = routeToDisplay.value; - // we need the value at the time we render because when we unmount, we - // navigated to a different location so the value is different - const currentName = props.name; - const matchedRoute = matchedRouteRef.value; - const ViewComponent = matchedRoute && matchedRoute.components[currentName]; - if (!ViewComponent) { - return normalizeSlot(slots.default, { Component: ViewComponent, route }); - } - // props from route configuration - const routePropsOption = matchedRoute.props[currentName]; - const routeProps = routePropsOption - ? routePropsOption === true - ? route.params - : typeof routePropsOption === 'function' - ? routePropsOption(route) - : routePropsOption - : null; - const onVnodeUnmounted = vnode => { - // remove the instance reference to prevent leak - if (vnode.component.isUnmounted) { - matchedRoute.instances[currentName] = null; - } - }; - const component = h(ViewComponent, assign({}, routeProps, attrs, { - onVnodeUnmounted, - ref: viewRef, - })); - if (isBrowser && - component.ref) { - // TODO: can display if it's an alias, its props - const info = { - depth: depth.value, - name: matchedRoute.name, - path: matchedRoute.path, - meta: matchedRoute.meta, - }; - const internalInstances = isArray(component.ref) - ? component.ref.map(r => r.i) - : [component.ref.i]; - internalInstances.forEach(instance => { - // @ts-expect-error - instance.__vrv_devtools = info; - }); - } - return ( - // pass the vnode to the slot as a prop. - // h and both accept vnodes - normalizeSlot(slots.default, { Component: component, route }) || - component); - }; - }, -}); -function normalizeSlot(slot, data) { - if (!slot) - return null; - const slotContent = slot(data); - return slotContent.length === 1 ? slotContent[0] : slotContent; -} -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to display the current route the user is at. - */ -const RouterView = RouterViewImpl; -// warn against deprecated usage with & -// due to functional component being no longer eager in Vue 3 -function warnDeprecatedUsage() { - const instance = getCurrentInstance(); - const parentName = instance.parent && instance.parent.type.name; - const parentSubTreeType = instance.parent && instance.parent.subTree && instance.parent.subTree.type; - if (parentName && - (parentName === 'KeepAlive' || parentName.includes('Transition')) && - typeof parentSubTreeType === 'object' && - parentSubTreeType.name === 'RouterView') { - const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition'; - warn(` can no longer be used directly inside or .\n` + - `Use slot props instead:\n\n` + - `\n` + - ` <${comp}>\n` + - ` \n` + - ` \n` + - ``); - } -} - -/** - * Copies a route location and removes any problematic properties that cannot be shown in devtools (e.g. Vue instances). - * - * @param routeLocation - routeLocation to format - * @param tooltip - optional tooltip - * @returns a copy of the routeLocation - */ -function formatRouteLocation(routeLocation, tooltip) { - const copy = assign({}, routeLocation, { - // remove variables that can contain vue instances - matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children', 'aliasOf'])), - }); - return { - _custom: { - type: null, - readOnly: true, - display: routeLocation.fullPath, - tooltip, - value: copy, - }, - }; -} -function formatDisplay(display) { - return { - _custom: { - display, - }, - }; -} -// to support multiple router instances -let routerId = 0; -function addDevtools(app, router, matcher) { - // Take over router.beforeEach and afterEach - // make sure we are not registering the devtool twice - if (router.__hasDevtools) - return; - router.__hasDevtools = true; - // increment to support multiple router instances - const id = routerId++; - setupDevtoolsPlugin({ - id: 'org.vuejs.router' + (id ? '.' + id : ''), - label: 'Vue Router', - packageName: 'vue-router', - homepage: 'https://router.vuejs.org', - logo: 'https://router.vuejs.org/logo.png', - componentStateTypes: ['Routing'], - app, - }, api => { - if (typeof api.now !== 'function') { - console.warn('[Vue Router]: You seem to be using an outdated version of Vue Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://devtools.vuejs.org/guide/installation.html.'); - } - // display state added by the router - api.on.inspectComponent((payload, ctx) => { - if (payload.instanceData) { - payload.instanceData.state.push({ - type: 'Routing', - key: '$route', - editable: false, - value: formatRouteLocation(router.currentRoute.value, 'Current Route'), - }); - } - }); - // mark router-link as active and display tags on router views - api.on.visitComponentTree(({ treeNode: node, componentInstance }) => { - if (componentInstance.__vrv_devtools) { - const info = componentInstance.__vrv_devtools; - node.tags.push({ - label: (info.name ? `${info.name.toString()}: ` : '') + info.path, - textColor: 0, - tooltip: 'This component is rendered by <router-view>', - backgroundColor: PINK_500, - }); - } - // if multiple useLink are used - if (isArray(componentInstance.__vrl_devtools)) { - componentInstance.__devtoolsApi = api; - componentInstance.__vrl_devtools.forEach(devtoolsData => { - let backgroundColor = ORANGE_400; - let tooltip = ''; - if (devtoolsData.isExactActive) { - backgroundColor = LIME_500; - tooltip = 'This is exactly active'; - } - else if (devtoolsData.isActive) { - backgroundColor = BLUE_600; - tooltip = 'This link is active'; - } - node.tags.push({ - label: devtoolsData.route.path, - textColor: 0, - tooltip, - backgroundColor, - }); - }); - } - }); - watch(router.currentRoute, () => { - // refresh active state - refreshRoutesView(); - api.notifyComponentUpdate(); - api.sendInspectorTree(routerInspectorId); - api.sendInspectorState(routerInspectorId); - }); - const navigationsLayerId = 'router:navigations:' + id; - api.addTimelineLayer({ - id: navigationsLayerId, - label: `Router${id ? ' ' + id : ''} Navigations`, - color: 0x40a8c4, - }); - // const errorsLayerId = 'router:errors' - // api.addTimelineLayer({ - // id: errorsLayerId, - // label: 'Router Errors', - // color: 0xea5455, - // }) - router.onError((error, to) => { - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - title: 'Error during Navigation', - subtitle: to.fullPath, - logType: 'error', - time: api.now(), - data: { error }, - groupId: to.meta.__navigationId, - }, - }); - }); - // attached to `meta` and used to group events - let navigationId = 0; - router.beforeEach((to, from) => { - const data = { - guard: formatDisplay('beforeEach'), - from: formatRouteLocation(from, 'Current Location during this navigation'), - to: formatRouteLocation(to, 'Target location'), - }; - // Used to group navigations together, hide from devtools - Object.defineProperty(to.meta, '__navigationId', { - value: navigationId++, - }); - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - time: api.now(), - title: 'Start of navigation', - subtitle: to.fullPath, - data, - groupId: to.meta.__navigationId, - }, - }); - }); - router.afterEach((to, from, failure) => { - const data = { - guard: formatDisplay('afterEach'), - }; - if (failure) { - data.failure = { - _custom: { - type: Error, - readOnly: true, - display: failure ? failure.message : '', - tooltip: 'Navigation Failure', - value: failure, - }, - }; - data.status = formatDisplay('âŒ'); - } - else { - data.status = formatDisplay('✅'); - } - // we set here to have the right order - data.from = formatRouteLocation(from, 'Current Location during this navigation'); - data.to = formatRouteLocation(to, 'Target location'); - api.addTimelineEvent({ - layerId: navigationsLayerId, - event: { - title: 'End of navigation', - subtitle: to.fullPath, - time: api.now(), - data, - logType: failure ? 'warning' : 'default', - groupId: to.meta.__navigationId, - }, - }); - }); - /** - * Inspector of Existing routes - */ - const routerInspectorId = 'router-inspector:' + id; - api.addInspector({ - id: routerInspectorId, - label: 'Routes' + (id ? ' ' + id : ''), - icon: 'book', - treeFilterPlaceholder: 'Search routes', - }); - function refreshRoutesView() { - // the routes view isn't active - if (!activeRoutesPayload) - return; - const payload = activeRoutesPayload; - // children routes will appear as nested - let routes = matcher.getRoutes().filter(route => !route.parent || - // these routes have a parent with no component which will not appear in the view - // therefore we still need to include them - !route.parent.record.components); - // reset match state to false - routes.forEach(resetMatchStateOnRouteRecord); - // apply a match state if there is a payload - if (payload.filter) { - routes = routes.filter(route => - // save matches state based on the payload - isRouteMatching(route, payload.filter.toLowerCase())); - } - // mark active routes - routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value)); - payload.rootNodes = routes.map(formatRouteRecordForInspector); - } - let activeRoutesPayload; - api.on.getInspectorTree(payload => { - activeRoutesPayload = payload; - if (payload.app === app && payload.inspectorId === routerInspectorId) { - refreshRoutesView(); - } - }); - /** - * Display information about the currently selected route record - */ - api.on.getInspectorState(payload => { - if (payload.app === app && payload.inspectorId === routerInspectorId) { - const routes = matcher.getRoutes(); - const route = routes.find(route => route.record.__vd_id === payload.nodeId); - if (route) { - payload.state = { - options: formatRouteRecordMatcherForStateInspector(route), - }; - } - } - }); - api.sendInspectorTree(routerInspectorId); - api.sendInspectorState(routerInspectorId); - }); -} -function modifierForKey(key) { - if (key.optional) { - return key.repeatable ? '*' : '?'; - } - else { - return key.repeatable ? '+' : ''; - } -} -function formatRouteRecordMatcherForStateInspector(route) { - const { record } = route; - const fields = [ - { editable: false, key: 'path', value: record.path }, - ]; - if (record.name != null) { - fields.push({ - editable: false, - key: 'name', - value: record.name, - }); - } - fields.push({ editable: false, key: 'regexp', value: route.re }); - if (route.keys.length) { - fields.push({ - editable: false, - key: 'keys', - value: { - _custom: { - type: null, - readOnly: true, - display: route.keys - .map(key => `${key.name}${modifierForKey(key)}`) - .join(' '), - tooltip: 'Param keys', - value: route.keys, - }, - }, - }); - } - if (record.redirect != null) { - fields.push({ - editable: false, - key: 'redirect', - value: record.redirect, - }); - } - if (route.alias.length) { - fields.push({ - editable: false, - key: 'aliases', - value: route.alias.map(alias => alias.record.path), - }); - } - if (Object.keys(route.record.meta).length) { - fields.push({ - editable: false, - key: 'meta', - value: route.record.meta, - }); - } - fields.push({ - key: 'score', - editable: false, - value: { - _custom: { - type: null, - readOnly: true, - display: route.score.map(score => score.join(', ')).join(' | '), - tooltip: 'Score used to sort routes', - value: route.score, - }, - }, - }); - return fields; -} -/** - * Extracted from tailwind palette - */ -const PINK_500 = 0xec4899; -const BLUE_600 = 0x2563eb; -const LIME_500 = 0x84cc16; -const CYAN_400 = 0x22d3ee; -const ORANGE_400 = 0xfb923c; -// const GRAY_100 = 0xf4f4f5 -const DARK = 0x666666; -function formatRouteRecordForInspector(route) { - const tags = []; - const { record } = route; - if (record.name != null) { - tags.push({ - label: String(record.name), - textColor: 0, - backgroundColor: CYAN_400, - }); - } - if (record.aliasOf) { - tags.push({ - label: 'alias', - textColor: 0, - backgroundColor: ORANGE_400, - }); - } - if (route.__vd_match) { - tags.push({ - label: 'matches', - textColor: 0, - backgroundColor: PINK_500, - }); - } - if (route.__vd_exactActive) { - tags.push({ - label: 'exact', - textColor: 0, - backgroundColor: LIME_500, - }); - } - if (route.__vd_active) { - tags.push({ - label: 'active', - textColor: 0, - backgroundColor: BLUE_600, - }); - } - if (record.redirect) { - tags.push({ - label: typeof record.redirect === 'string' - ? `redirect: ${record.redirect}` - : 'redirects', - textColor: 0xffffff, - backgroundColor: DARK, - }); - } - // add an id to be able to select it. Using the `path` is not possible because - // empty path children would collide with their parents - let id = record.__vd_id; - if (id == null) { - id = String(routeRecordId++); - record.__vd_id = id; - } - return { - id, - label: record.path, - tags, - children: route.children.map(formatRouteRecordForInspector), - }; -} -// incremental id for route records and inspector state -let routeRecordId = 0; -const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/; -function markRouteRecordActive(route, currentRoute) { - // no route will be active if matched is empty - // reset the matching state - const isExactActive = currentRoute.matched.length && - isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record); - route.__vd_exactActive = route.__vd_active = isExactActive; - if (!isExactActive) { - route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record)); - } - route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute)); -} -function resetMatchStateOnRouteRecord(route) { - route.__vd_match = false; - route.children.forEach(resetMatchStateOnRouteRecord); -} -function isRouteMatching(route, filter) { - const found = String(route.re).match(EXTRACT_REGEXP_RE); - route.__vd_match = false; - if (!found || found.length < 3) { - return false; - } - // use a regexp without $ at the end to match nested routes better - const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]); - if (nonEndingRE.test(filter)) { - // mark children as matches - route.children.forEach(child => isRouteMatching(child, filter)); - // exception case: `/` - if (route.record.path !== '/' || filter === '/') { - route.__vd_match = route.re.test(filter); - return true; - } - // hide the / route - return false; - } - const path = route.record.path.toLowerCase(); - const decodedPath = decode(path); - // also allow partial matching on the path - if (!filter.startsWith('/') && - (decodedPath.includes(filter) || path.includes(filter))) - return true; - if (decodedPath.startsWith(filter) || path.startsWith(filter)) - return true; - if (route.record.name && String(route.record.name).includes(filter)) - return true; - return route.children.some(child => isRouteMatching(child, filter)); -} -function omit(obj, keys) { - const ret = {}; - for (const key in obj) { - if (!keys.includes(key)) { - // @ts-expect-error - ret[key] = obj[key]; - } - } - return ret; -} - -/** - * Creates a Router instance that can be used by a Vue app. - * - * @param options - {@link RouterOptions} - */ -function createRouter(options) { - const matcher = createRouterMatcher(options.routes, options); - const parseQuery$1 = options.parseQuery || parseQuery; - const stringifyQuery$1 = options.stringifyQuery || stringifyQuery; - const routerHistory = options.history; - if (!routerHistory) - throw new Error('Provide the "history" option when calling "createRouter()":' + - ' https://next.router.vuejs.org/api/#history.'); - const beforeGuards = useCallbacks(); - const beforeResolveGuards = useCallbacks(); - const afterGuards = useCallbacks(); - const currentRoute = shallowRef(START_LOCATION_NORMALIZED); - let pendingLocation = START_LOCATION_NORMALIZED; - // leave the scrollRestoration if no scrollBehavior is provided - if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) { - history.scrollRestoration = 'manual'; - } - const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue); - const encodeParams = applyToParams.bind(null, encodeParam); - const decodeParams = - // @ts-expect-error: intentionally avoid the type check - applyToParams.bind(null, decode); - function addRoute(parentOrRoute, route) { - let parent; - let record; - if (isRouteName(parentOrRoute)) { - parent = matcher.getRecordMatcher(parentOrRoute); - record = route; - } - else { - record = parentOrRoute; - } - return matcher.addRoute(record, parent); - } - function removeRoute(name) { - const recordMatcher = matcher.getRecordMatcher(name); - if (recordMatcher) { - matcher.removeRoute(recordMatcher); - } - else { - warn(`Cannot remove non-existent route "${String(name)}"`); - } - } - function getRoutes() { - return matcher.getRoutes().map(routeMatcher => routeMatcher.record); - } - function hasRoute(name) { - return !!matcher.getRecordMatcher(name); - } - function resolve(rawLocation, currentLocation) { - // const objectLocation = routerLocationAsObject(rawLocation) - // we create a copy to modify it later - currentLocation = assign({}, currentLocation || currentRoute.value); - if (typeof rawLocation === 'string') { - const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path); - const matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation); - const href = routerHistory.createHref(locationNormalized.fullPath); - { - if (href.startsWith('//')) - warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`); - else if (!matchedRoute.matched.length) { - warn(`No match found for location with path "${rawLocation}"`); - } - } - // locationNormalized is always a new object - return assign(locationNormalized, matchedRoute, { - params: decodeParams(matchedRoute.params), - hash: decode(locationNormalized.hash), - redirectedFrom: undefined, - href, - }); - } - let matcherLocation; - // path could be relative in object as well - if ('path' in rawLocation) { - if ('params' in rawLocation && - !('name' in rawLocation) && - // @ts-expect-error: the type is never - Object.keys(rawLocation.params).length) { - warn(`Path "${rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`); - } - matcherLocation = assign({}, rawLocation, { - path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path, - }); - } - else { - // remove any nullish param - const targetParams = assign({}, rawLocation.params); - for (const key in targetParams) { - if (targetParams[key] == null) { - delete targetParams[key]; - } - } - // pass encoded values to the matcher, so it can produce encoded path and fullPath - matcherLocation = assign({}, rawLocation, { - params: encodeParams(targetParams), - }); - // current location params are decoded, we need to encode them in case the - // matcher merges the params - currentLocation.params = encodeParams(currentLocation.params); - } - const matchedRoute = matcher.resolve(matcherLocation, currentLocation); - const hash = rawLocation.hash || ''; - if (hash && !hash.startsWith('#')) { - warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`); - } - // the matcher might have merged current location params, so - // we need to run the decoding again - matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params)); - const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, { - hash: encodeHash(hash), - path: matchedRoute.path, - })); - const href = routerHistory.createHref(fullPath); - { - if (href.startsWith('//')) { - warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`); - } - else if (!matchedRoute.matched.length) { - warn(`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`); - } - } - return assign({ - fullPath, - // keep the hash encoded so fullPath is effectively path + encodedQuery + - // hash - hash, - query: - // if the user is using a custom query lib like qs, we might have - // nested objects, so we keep the query as is, meaning it can contain - // numbers at `$route.query`, but at the point, the user will have to - // use their own type anyway. - // https://github.com/vuejs/router/issues/328#issuecomment-649481567 - stringifyQuery$1 === stringifyQuery - ? normalizeQuery(rawLocation.query) - : (rawLocation.query || {}), - }, matchedRoute, { - redirectedFrom: undefined, - href, - }); - } - function locationAsObject(to) { - return typeof to === 'string' - ? parseURL(parseQuery$1, to, currentRoute.value.path) - : assign({}, to); - } - function checkCanceledNavigation(to, from) { - if (pendingLocation !== to) { - return createRouterError(8 /* ErrorTypes.NAVIGATION_CANCELLED */, { - from, - to, - }); - } - } - function push(to) { - return pushWithRedirect(to); - } - function replace(to) { - return push(assign(locationAsObject(to), { replace: true })); - } - function handleRedirectRecord(to) { - const lastMatched = to.matched[to.matched.length - 1]; - if (lastMatched && lastMatched.redirect) { - const { redirect } = lastMatched; - let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect; - if (typeof newTargetLocation === 'string') { - newTargetLocation = - newTargetLocation.includes('?') || newTargetLocation.includes('#') - ? (newTargetLocation = locationAsObject(newTargetLocation)) - : // force empty params - { path: newTargetLocation }; - // @ts-expect-error: force empty params when a string is passed to let - // the router parse them again - newTargetLocation.params = {}; - } - if (!('path' in newTargetLocation) && - !('name' in newTargetLocation)) { - warn(`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`); - throw new Error('Invalid redirect'); - } - return assign({ - query: to.query, - hash: to.hash, - // avoid transferring params if the redirect has a path - params: 'path' in newTargetLocation ? {} : to.params, - }, newTargetLocation); - } - } - function pushWithRedirect(to, redirectedFrom) { - const targetLocation = (pendingLocation = resolve(to)); - const from = currentRoute.value; - const data = to.state; - const force = to.force; - // to could be a string where `replace` is a function - const replace = to.replace === true; - const shouldRedirect = handleRedirectRecord(targetLocation); - if (shouldRedirect) - return pushWithRedirect(assign(locationAsObject(shouldRedirect), { - state: typeof shouldRedirect === 'object' - ? assign({}, data, shouldRedirect.state) - : data, - force, - replace, - }), - // keep original redirectedFrom if it exists - redirectedFrom || targetLocation); - // if it was a redirect we already called `pushWithRedirect` above - const toLocation = targetLocation; - toLocation.redirectedFrom = redirectedFrom; - let failure; - if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) { - failure = createRouterError(16 /* ErrorTypes.NAVIGATION_DUPLICATED */, { to: toLocation, from }); - // trigger scroll to allow scrolling to the same anchor - handleScroll(from, from, - // this is a push, the only way for it to be triggered from a - // history.listen is with a redirect, which makes it become a push - true, - // This cannot be the first navigation because the initial location - // cannot be manually navigated to - false); - } - return (failure ? Promise.resolve(failure) : navigate(toLocation, from)) - .catch((error) => isNavigationFailure(error) - ? // navigation redirects still mark the router as ready - isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */) - ? error - : markAsReady(error) // also returns the error - : // reject any unknown error - triggerError(error, toLocation, from)) - .then((failure) => { - if (failure) { - if (isNavigationFailure(failure, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) { - if (// we are redirecting to the same location we were already at - isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) && - // and we have done it a couple of times - redirectedFrom && - // @ts-expect-error: added only in dev - (redirectedFrom._count = redirectedFrom._count - ? // @ts-expect-error - redirectedFrom._count + 1 - : 1) > 30) { - warn(`Detected a possibly infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow.\n Are you always returning a new location within a navigation guard? That would lead to this error. Only return when redirecting or aborting, that should fix this. This might break in production if not fixed.`); - return Promise.reject(new Error('Infinite redirect in navigation guard')); - } - return pushWithRedirect( - // keep options - assign({ - // preserve an existing replacement but allow the redirect to override it - replace, - }, locationAsObject(failure.to), { - state: typeof failure.to === 'object' - ? assign({}, data, failure.to.state) - : data, - force, - }), - // preserve the original redirectedFrom if any - redirectedFrom || toLocation); - } - } - else { - // if we fail we don't finalize the navigation - failure = finalizeNavigation(toLocation, from, true, replace, data); - } - triggerAfterEach(toLocation, from, failure); - return failure; - }); - } - /** - * Helper to reject and skip all navigation guards if a new navigation happened - * @param to - * @param from - */ - function checkCanceledNavigationAndReject(to, from) { - const error = checkCanceledNavigation(to, from); - return error ? Promise.reject(error) : Promise.resolve(); - } - function runWithContext(fn) { - const app = installedApps.values().next().value; - // support Vue < 3.3 - return app && typeof app.runWithContext === 'function' - ? app.runWithContext(fn) - : fn(); - } - // TODO: refactor the whole before guards by internally using router.beforeEach - function navigate(to, from) { - let guards; - const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from); - // all components here have been resolved once because we are leaving - guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from); - // leavingRecords is already reversed - for (const record of leavingRecords) { - record.leaveGuards.forEach(guard => { - guards.push(guardToPromiseFn(guard, to, from)); - }); - } - const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from); - guards.push(canceledNavigationCheck); - // run the queue of per route beforeRouteLeave guards - return (runGuardQueue(guards) - .then(() => { - // check global guards beforeEach - guards = []; - for (const guard of beforeGuards.list()) { - guards.push(guardToPromiseFn(guard, to, from)); - } - guards.push(canceledNavigationCheck); - return runGuardQueue(guards); - }) - .then(() => { - // check in components beforeRouteUpdate - guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from); - for (const record of updatingRecords) { - record.updateGuards.forEach(guard => { - guards.push(guardToPromiseFn(guard, to, from)); - }); - } - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // check the route beforeEnter - guards = []; - for (const record of enteringRecords) { - // do not trigger beforeEnter on reused views - if (record.beforeEnter) { - if (isArray(record.beforeEnter)) { - for (const beforeEnter of record.beforeEnter) - guards.push(guardToPromiseFn(beforeEnter, to, from)); - } - else { - guards.push(guardToPromiseFn(record.beforeEnter, to, from)); - } - } - } - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // NOTE: at this point to.matched is normalized and does not contain any () => Promise - // clear existing enterCallbacks, these are added by extractComponentsGuards - to.matched.forEach(record => (record.enterCallbacks = {})); - // check in-component beforeRouteEnter - guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from); - guards.push(canceledNavigationCheck); - // run the queue of per route beforeEnter guards - return runGuardQueue(guards); - }) - .then(() => { - // check global guards beforeResolve - guards = []; - for (const guard of beforeResolveGuards.list()) { - guards.push(guardToPromiseFn(guard, to, from)); - } - guards.push(canceledNavigationCheck); - return runGuardQueue(guards); - }) - // catch any navigation canceled - .catch(err => isNavigationFailure(err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */) - ? err - : Promise.reject(err))); - } - function triggerAfterEach(to, from, failure) { - // navigation is confirmed, call afterGuards - // TODO: wrap with error handlers - afterGuards - .list() - .forEach(guard => runWithContext(() => guard(to, from, failure))); - } - /** - * - Cleans up any navigation guards - * - Changes the url if necessary - * - Calls the scrollBehavior - */ - function finalizeNavigation(toLocation, from, isPush, replace, data) { - // a more recent navigation took place - const error = checkCanceledNavigation(toLocation, from); - if (error) - return error; - // only consider as push if it's not the first navigation - const isFirstNavigation = from === START_LOCATION_NORMALIZED; - const state = !isBrowser ? {} : history.state; - // change URL only if the user did a push/replace and if it's not the initial navigation because - // it's just reflecting the url - if (isPush) { - // on the initial navigation, we want to reuse the scroll position from - // history state if it exists - if (replace || isFirstNavigation) - routerHistory.replace(toLocation.fullPath, assign({ - scroll: isFirstNavigation && state && state.scroll, - }, data)); - else - routerHistory.push(toLocation.fullPath, data); - } - // accept current navigation - currentRoute.value = toLocation; - handleScroll(toLocation, from, isPush, isFirstNavigation); - markAsReady(); - } - let removeHistoryListener; - // attach listener to history to trigger navigations - function setupListeners() { - // avoid setting up listeners twice due to an invalid first navigation - if (removeHistoryListener) - return; - removeHistoryListener = routerHistory.listen((to, _from, info) => { - if (!router.listening) - return; - // cannot be a redirect route because it was in history - const toLocation = resolve(to); - // due to dynamic routing, and to hash history with manual navigation - // (manually changing the url or calling history.hash = '#/somewhere'), - // there could be a redirect record in history - const shouldRedirect = handleRedirectRecord(toLocation); - if (shouldRedirect) { - pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop); - return; - } - pendingLocation = toLocation; - const from = currentRoute.value; - // TODO: should be moved to web history? - if (isBrowser) { - saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition()); - } - navigate(toLocation, from) - .catch((error) => { - if (isNavigationFailure(error, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) { - return error; - } - if (isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) { - // Here we could call if (info.delta) routerHistory.go(-info.delta, - // false) but this is bug prone as we have no way to wait the - // navigation to be finished before calling pushWithRedirect. Using - // a setTimeout of 16ms seems to work but there is no guarantee for - // it to work on every browser. So instead we do not restore the - // history entry and trigger a new navigation as requested by the - // navigation guard. - // the error is already handled by router.push we just want to avoid - // logging the error - pushWithRedirect(error.to, toLocation - // avoid an uncaught rejection, let push call triggerError - ) - .then(failure => { - // manual change in hash history #916 ending up in the URL not - // changing, but it was changed by the manual url change, so we - // need to manually change it ourselves - if (isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | - 16 /* ErrorTypes.NAVIGATION_DUPLICATED */) && - !info.delta && - info.type === NavigationType.pop) { - routerHistory.go(-1, false); - } - }) - .catch(noop); - // avoid the then branch - return Promise.reject(); - } - // do not restore history on unknown direction - if (info.delta) { - routerHistory.go(-info.delta, false); - } - // unrecognized error, transfer to the global handler - return triggerError(error, toLocation, from); - }) - .then((failure) => { - failure = - failure || - finalizeNavigation( - // after navigation, all matched components are resolved - toLocation, from, false); - // revert the navigation - if (failure) { - if (info.delta && - // a new navigation has been triggered, so we do not want to revert, that will change the current history - // entry while a different route is displayed - !isNavigationFailure(failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) { - routerHistory.go(-info.delta, false); - } - else if (info.type === NavigationType.pop && - isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 16 /* ErrorTypes.NAVIGATION_DUPLICATED */)) { - // manual change in hash history #916 - // it's like a push but lacks the information of the direction - routerHistory.go(-1, false); - } - } - triggerAfterEach(toLocation, from, failure); - }) - // avoid warnings in the console about uncaught rejections, they are logged by triggerErrors - .catch(noop); - }); - } - // Initialization and Errors - let readyHandlers = useCallbacks(); - let errorListeners = useCallbacks(); - let ready; - /** - * Trigger errorListeners added via onError and throws the error as well - * - * @param error - error to throw - * @param to - location we were navigating to when the error happened - * @param from - location we were navigating from when the error happened - * @returns the error as a rejected promise - */ - function triggerError(error, to, from) { - markAsReady(error); - const list = errorListeners.list(); - if (list.length) { - list.forEach(handler => handler(error, to, from)); - } - else { - { - warn('uncaught error during route navigation:'); - } - console.error(error); - } - // reject the error no matter there were error listeners or not - return Promise.reject(error); - } - function isReady() { - if (ready && currentRoute.value !== START_LOCATION_NORMALIZED) - return Promise.resolve(); - return new Promise((resolve, reject) => { - readyHandlers.add([resolve, reject]); - }); - } - function markAsReady(err) { - if (!ready) { - // still not ready if an error happened - ready = !err; - setupListeners(); - readyHandlers - .list() - .forEach(([resolve, reject]) => (err ? reject(err) : resolve())); - readyHandlers.reset(); - } - return err; - } - // Scroll behavior - function handleScroll(to, from, isPush, isFirstNavigation) { - const { scrollBehavior } = options; - if (!isBrowser || !scrollBehavior) - return Promise.resolve(); - const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) || - ((isFirstNavigation || !isPush) && - history.state && - history.state.scroll) || - null; - return nextTick() - .then(() => scrollBehavior(to, from, scrollPosition)) - .then(position => position && scrollToPosition(position)) - .catch(err => triggerError(err, to, from)); - } - const go = (delta) => routerHistory.go(delta); - let started; - const installedApps = new Set(); - const router = { - currentRoute, - listening: true, - addRoute, - removeRoute, - hasRoute, - getRoutes, - resolve, - options, - push, - replace, - go, - back: () => go(-1), - forward: () => go(1), - beforeEach: beforeGuards.add, - beforeResolve: beforeResolveGuards.add, - afterEach: afterGuards.add, - onError: errorListeners.add, - isReady, - install(app) { - const router = this; - app.component('RouterLink', RouterLink); - app.component('RouterView', RouterView); - app.config.globalProperties.$router = router; - Object.defineProperty(app.config.globalProperties, '$route', { - enumerable: true, - get: () => unref(currentRoute), - }); - // this initial navigation is only necessary on client, on server it doesn't - // make sense because it will create an extra unnecessary navigation and could - // lead to problems - if (isBrowser && - // used for the initial navigation client side to avoid pushing - // multiple times when the router is used in multiple apps - !started && - currentRoute.value === START_LOCATION_NORMALIZED) { - // see above - started = true; - push(routerHistory.location).catch(err => { - warn('Unexpected error when starting the router:', err); - }); - } - const reactiveRoute = {}; - for (const key in START_LOCATION_NORMALIZED) { - Object.defineProperty(reactiveRoute, key, { - get: () => currentRoute.value[key], - enumerable: true, - }); - } - app.provide(routerKey, router); - app.provide(routeLocationKey, shallowReactive(reactiveRoute)); - app.provide(routerViewLocationKey, currentRoute); - const unmountApp = app.unmount; - installedApps.add(app); - app.unmount = function () { - installedApps.delete(app); - // the router is not attached to an app anymore - if (installedApps.size < 1) { - // invalidate the current navigation - pendingLocation = START_LOCATION_NORMALIZED; - removeHistoryListener && removeHistoryListener(); - removeHistoryListener = null; - currentRoute.value = START_LOCATION_NORMALIZED; - started = false; - ready = false; - } - unmountApp(); - }; - // TODO: this probably needs to be updated so it can be used by vue-termui - if (isBrowser) { - addDevtools(app, router, matcher); - } - }, - }; - // TODO: type this as NavigationGuardReturn or similar instead of any - function runGuardQueue(guards) { - return guards.reduce((promise, guard) => promise.then(() => runWithContext(guard)), Promise.resolve()); - } - return router; -} -function extractChangingRecords(to, from) { - const leavingRecords = []; - const updatingRecords = []; - const enteringRecords = []; - const len = Math.max(from.matched.length, to.matched.length); - for (let i = 0; i < len; i++) { - const recordFrom = from.matched[i]; - if (recordFrom) { - if (to.matched.find(record => isSameRouteRecord(record, recordFrom))) - updatingRecords.push(recordFrom); - else - leavingRecords.push(recordFrom); - } - const recordTo = to.matched[i]; - if (recordTo) { - // the type doesn't matter because we are comparing per reference - if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) { - enteringRecords.push(recordTo); - } - } - } - return [leavingRecords, updatingRecords, enteringRecords]; -} - -/** - * Returns the router instance. Equivalent to using `$router` inside - * templates. - */ -function useRouter() { - return inject(routerKey); -} -/** - * Returns the current route location. Equivalent to using `$route` inside - * templates. - */ -function useRoute() { - return inject(routeLocationKey); -} - -export { NavigationFailureType, RouterLink, RouterView, START_LOCATION_NORMALIZED as START_LOCATION, createMemoryHistory, createRouter, createRouterMatcher, createWebHashHistory, createWebHistory, isNavigationFailure, loadRouteLocation, matchedRouteKey, onBeforeRouteLeave, onBeforeRouteUpdate, parseQuery, routeLocationKey, routerKey, routerViewLocationKey, stringifyQuery, useLink, useRoute, useRouter, viewDepthKey }; \ No newline at end of file diff --git a/hal-core/resources/web/js/vue/vue.esm-browser.js b/hal-core/resources/web/js/vue/vue.esm-browser.js deleted file mode 100644 index 5f8d6664..00000000 --- a/hal-core/resources/web/js/vue/vue.esm-browser.js +++ /dev/null @@ -1,16633 +0,0 @@ -/** -* vue v3.4.15 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/ -function makeMap(str, expectsLowerCase) { - const set = new Set(str.split(",")); - return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); -} - -const EMPTY_OBJ = Object.freeze({}) ; -const EMPTY_ARR = Object.freeze([]) ; -const NOOP = () => { -}; -const NO = () => false; -const isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter -(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); -const isModelListener = (key) => key.startsWith("onUpdate:"); -const extend = Object.assign; -const remove = (arr, el) => { - const i = arr.indexOf(el); - if (i > -1) { - arr.splice(i, 1); - } -}; -const hasOwnProperty$1 = Object.prototype.hasOwnProperty; -const hasOwn = (val, key) => hasOwnProperty$1.call(val, key); -const isArray = Array.isArray; -const isMap = (val) => toTypeString(val) === "[object Map]"; -const isSet = (val) => toTypeString(val) === "[object Set]"; -const isDate = (val) => toTypeString(val) === "[object Date]"; -const isRegExp = (val) => toTypeString(val) === "[object RegExp]"; -const isFunction = (val) => typeof val === "function"; -const isString = (val) => typeof val === "string"; -const isSymbol = (val) => typeof val === "symbol"; -const isObject = (val) => val !== null && typeof val === "object"; -const isPromise = (val) => { - return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); -}; -const objectToString = Object.prototype.toString; -const toTypeString = (value) => objectToString.call(value); -const toRawType = (value) => { - return toTypeString(value).slice(8, -1); -}; -const isPlainObject = (val) => toTypeString(val) === "[object Object]"; -const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; -const isReservedProp = /* @__PURE__ */ makeMap( - // the leading comma is intentional so empty string "" is also included - ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" -); -const isBuiltInDirective = /* @__PURE__ */ makeMap( - "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" -); -const cacheStringFunction = (fn) => { - const cache = /* @__PURE__ */ Object.create(null); - return (str) => { - const hit = cache[str]; - return hit || (cache[str] = fn(str)); - }; -}; -const camelizeRE = /-(\w)/g; -const camelize = cacheStringFunction((str) => { - return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); -}); -const hyphenateRE = /\B([A-Z])/g; -const hyphenate = cacheStringFunction( - (str) => str.replace(hyphenateRE, "-$1").toLowerCase() -); -const capitalize = cacheStringFunction((str) => { - return str.charAt(0).toUpperCase() + str.slice(1); -}); -const toHandlerKey = cacheStringFunction((str) => { - const s = str ? `on${capitalize(str)}` : ``; - return s; -}); -const hasChanged = (value, oldValue) => !Object.is(value, oldValue); -const invokeArrayFns = (fns, arg) => { - for (let i = 0; i < fns.length; i++) { - fns[i](arg); - } -}; -const def = (obj, key, value) => { - Object.defineProperty(obj, key, { - configurable: true, - enumerable: false, - value - }); -}; -const looseToNumber = (val) => { - const n = parseFloat(val); - return isNaN(n) ? val : n; -}; -const toNumber = (val) => { - const n = isString(val) ? Number(val) : NaN; - return isNaN(n) ? val : n; -}; -let _globalThis; -const getGlobalThis = () => { - return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); -}; - -const PatchFlagNames = { - [1]: `TEXT`, - [2]: `CLASS`, - [4]: `STYLE`, - [8]: `PROPS`, - [16]: `FULL_PROPS`, - [32]: `NEED_HYDRATION`, - [64]: `STABLE_FRAGMENT`, - [128]: `KEYED_FRAGMENT`, - [256]: `UNKEYED_FRAGMENT`, - [512]: `NEED_PATCH`, - [1024]: `DYNAMIC_SLOTS`, - [2048]: `DEV_ROOT_FRAGMENT`, - [-1]: `HOISTED`, - [-2]: `BAIL` -}; - -const slotFlagsText = { - [1]: "STABLE", - [2]: "DYNAMIC", - [3]: "FORWARDED" -}; - -const GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; -const isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); - -const range = 2; -function generateCodeFrame(source, start = 0, end = source.length) { - let lines = source.split(/(\r?\n)/); - const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); - lines = lines.filter((_, idx) => idx % 2 === 0); - let count = 0; - const res = []; - for (let i = 0; i < lines.length; i++) { - count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); - if (count >= start) { - for (let j = i - range; j <= i + range || end > count; j++) { - if (j < 0 || j >= lines.length) - continue; - const line = j + 1; - res.push( - `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` - ); - const lineLength = lines[j].length; - const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; - if (j === i) { - const pad = start - (count - (lineLength + newLineSeqLength)); - const length = Math.max( - 1, - end > count ? lineLength - pad : end - start - ); - res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); - } else if (j > i) { - if (end > count) { - const length = Math.max(Math.min(end - count, lineLength), 1); - res.push(` | ` + "^".repeat(length)); - } - count += lineLength + newLineSeqLength; - } - } - break; - } - } - return res.join("\n"); -} - -function normalizeStyle(value) { - if (isArray(value)) { - const res = {}; - for (let i = 0; i < value.length; i++) { - const item = value[i]; - const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); - if (normalized) { - for (const key in normalized) { - res[key] = normalized[key]; - } - } - } - return res; - } else if (isString(value) || isObject(value)) { - return value; - } -} -const listDelimiterRE = /;(?![^(]*\))/g; -const propertyDelimiterRE = /:([^]+)/; -const styleCommentRE = /\/\*[^]*?\*\//g; -function parseStringStyle(cssText) { - const ret = {}; - cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { - if (item) { - const tmp = item.split(propertyDelimiterRE); - tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); - } - }); - return ret; -} -function stringifyStyle(styles) { - let ret = ""; - if (!styles || isString(styles)) { - return ret; - } - for (const key in styles) { - const value = styles[key]; - const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); - if (isString(value) || typeof value === "number") { - ret += `${normalizedKey}:${value};`; - } - } - return ret; -} -function normalizeClass(value) { - let res = ""; - if (isString(value)) { - res = value; - } else if (isArray(value)) { - for (let i = 0; i < value.length; i++) { - const normalized = normalizeClass(value[i]); - if (normalized) { - res += normalized + " "; - } - } - } else if (isObject(value)) { - for (const name in value) { - if (value[name]) { - res += name + " "; - } - } - } - return res.trim(); -} -function normalizeProps(props) { - if (!props) - return null; - let { class: klass, style } = props; - if (klass && !isString(klass)) { - props.class = normalizeClass(klass); - } - if (style) { - props.style = normalizeStyle(style); - } - return props; -} - -const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; -const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; -const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; -const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; -const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); -const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); -const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); -const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); - -const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; -const isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); -const isBooleanAttr = /* @__PURE__ */ makeMap( - specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` -); -function includeBooleanAttr(value) { - return !!value || value === ""; -} -const isKnownHtmlAttr = /* @__PURE__ */ makeMap( - `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` -); -const isKnownSvgAttr = /* @__PURE__ */ makeMap( - `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` -); -function isRenderableAttrValue(value) { - if (value == null) { - return false; - } - const type = typeof value; - return type === "string" || type === "number" || type === "boolean"; -} - -function looseCompareArrays(a, b) { - if (a.length !== b.length) - return false; - let equal = true; - for (let i = 0; equal && i < a.length; i++) { - equal = looseEqual(a[i], b[i]); - } - return equal; -} -function looseEqual(a, b) { - if (a === b) - return true; - let aValidType = isDate(a); - let bValidType = isDate(b); - if (aValidType || bValidType) { - return aValidType && bValidType ? a.getTime() === b.getTime() : false; - } - aValidType = isSymbol(a); - bValidType = isSymbol(b); - if (aValidType || bValidType) { - return a === b; - } - aValidType = isArray(a); - bValidType = isArray(b); - if (aValidType || bValidType) { - return aValidType && bValidType ? looseCompareArrays(a, b) : false; - } - aValidType = isObject(a); - bValidType = isObject(b); - if (aValidType || bValidType) { - if (!aValidType || !bValidType) { - return false; - } - const aKeysCount = Object.keys(a).length; - const bKeysCount = Object.keys(b).length; - if (aKeysCount !== bKeysCount) { - return false; - } - for (const key in a) { - const aHasKey = a.hasOwnProperty(key); - const bHasKey = b.hasOwnProperty(key); - if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) { - return false; - } - } - } - return String(a) === String(b); -} -function looseIndexOf(arr, val) { - return arr.findIndex((item) => looseEqual(item, val)); -} - -const toDisplayString = (val) => { - return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); -}; -const replacer = (_key, val) => { - if (val && val.__v_isRef) { - return replacer(_key, val.value); - } else if (isMap(val)) { - return { - [`Map(${val.size})`]: [...val.entries()].reduce( - (entries, [key, val2], i) => { - entries[stringifySymbol(key, i) + " =>"] = val2; - return entries; - }, - {} - ) - }; - } else if (isSet(val)) { - return { - [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) - }; - } else if (isSymbol(val)) { - return stringifySymbol(val); - } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { - return String(val); - } - return val; -}; -const stringifySymbol = (v, i = "") => { - var _a; - return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; -}; - -function warn$2(msg, ...args) { - console.warn(`[Vue warn] ${msg}`, ...args); -} - -let activeEffectScope; -class EffectScope { - constructor(detached = false) { - this.detached = detached; - /** - * @internal - */ - this._active = true; - /** - * @internal - */ - this.effects = []; - /** - * @internal - */ - this.cleanups = []; - this.parent = activeEffectScope; - if (!detached && activeEffectScope) { - this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( - this - ) - 1; - } - } - get active() { - return this._active; - } - run(fn) { - if (this._active) { - const currentEffectScope = activeEffectScope; - try { - activeEffectScope = this; - return fn(); - } finally { - activeEffectScope = currentEffectScope; - } - } else { - warn$2(`cannot run an inactive effect scope.`); - } - } - /** - * This should only be called on non-detached scopes - * @internal - */ - on() { - activeEffectScope = this; - } - /** - * This should only be called on non-detached scopes - * @internal - */ - off() { - activeEffectScope = this.parent; - } - stop(fromParent) { - if (this._active) { - let i, l; - for (i = 0, l = this.effects.length; i < l; i++) { - this.effects[i].stop(); - } - for (i = 0, l = this.cleanups.length; i < l; i++) { - this.cleanups[i](); - } - if (this.scopes) { - for (i = 0, l = this.scopes.length; i < l; i++) { - this.scopes[i].stop(true); - } - } - if (!this.detached && this.parent && !fromParent) { - const last = this.parent.scopes.pop(); - if (last && last !== this) { - this.parent.scopes[this.index] = last; - last.index = this.index; - } - } - this.parent = void 0; - this._active = false; - } - } -} -function effectScope(detached) { - return new EffectScope(detached); -} -function recordEffectScope(effect, scope = activeEffectScope) { - if (scope && scope.active) { - scope.effects.push(effect); - } -} -function getCurrentScope() { - return activeEffectScope; -} -function onScopeDispose(fn) { - if (activeEffectScope) { - activeEffectScope.cleanups.push(fn); - } else { - warn$2( - `onScopeDispose() is called when there is no active effect scope to be associated with.` - ); - } -} - -let activeEffect; -class ReactiveEffect { - constructor(fn, trigger, scheduler, scope) { - this.fn = fn; - this.trigger = trigger; - this.scheduler = scheduler; - this.active = true; - this.deps = []; - /** - * @internal - */ - this._dirtyLevel = 2; - /** - * @internal - */ - this._trackId = 0; - /** - * @internal - */ - this._runnings = 0; - /** - * @internal - */ - this._shouldSchedule = false; - /** - * @internal - */ - this._depsLength = 0; - recordEffectScope(this, scope); - } - get dirty() { - if (this._dirtyLevel === 1) { - pauseTracking(); - for (let i = 0; i < this._depsLength; i++) { - const dep = this.deps[i]; - if (dep.computed) { - triggerComputed(dep.computed); - if (this._dirtyLevel >= 2) { - break; - } - } - } - if (this._dirtyLevel < 2) { - this._dirtyLevel = 0; - } - resetTracking(); - } - return this._dirtyLevel >= 2; - } - set dirty(v) { - this._dirtyLevel = v ? 2 : 0; - } - run() { - this._dirtyLevel = 0; - if (!this.active) { - return this.fn(); - } - let lastShouldTrack = shouldTrack; - let lastEffect = activeEffect; - try { - shouldTrack = true; - activeEffect = this; - this._runnings++; - preCleanupEffect(this); - return this.fn(); - } finally { - postCleanupEffect(this); - this._runnings--; - activeEffect = lastEffect; - shouldTrack = lastShouldTrack; - } - } - stop() { - var _a; - if (this.active) { - preCleanupEffect(this); - postCleanupEffect(this); - (_a = this.onStop) == null ? void 0 : _a.call(this); - this.active = false; - } - } -} -function triggerComputed(computed) { - return computed.value; -} -function preCleanupEffect(effect2) { - effect2._trackId++; - effect2._depsLength = 0; -} -function postCleanupEffect(effect2) { - if (effect2.deps && effect2.deps.length > effect2._depsLength) { - for (let i = effect2._depsLength; i < effect2.deps.length; i++) { - cleanupDepEffect(effect2.deps[i], effect2); - } - effect2.deps.length = effect2._depsLength; - } -} -function cleanupDepEffect(dep, effect2) { - const trackId = dep.get(effect2); - if (trackId !== void 0 && effect2._trackId !== trackId) { - dep.delete(effect2); - if (dep.size === 0) { - dep.cleanup(); - } - } -} -function effect(fn, options) { - if (fn.effect instanceof ReactiveEffect) { - fn = fn.effect.fn; - } - const _effect = new ReactiveEffect(fn, NOOP, () => { - if (_effect.dirty) { - _effect.run(); - } - }); - if (options) { - extend(_effect, options); - if (options.scope) - recordEffectScope(_effect, options.scope); - } - if (!options || !options.lazy) { - _effect.run(); - } - const runner = _effect.run.bind(_effect); - runner.effect = _effect; - return runner; -} -function stop(runner) { - runner.effect.stop(); -} -let shouldTrack = true; -let pauseScheduleStack = 0; -const trackStack = []; -function pauseTracking() { - trackStack.push(shouldTrack); - shouldTrack = false; -} -function resetTracking() { - const last = trackStack.pop(); - shouldTrack = last === void 0 ? true : last; -} -function pauseScheduling() { - pauseScheduleStack++; -} -function resetScheduling() { - pauseScheduleStack--; - while (!pauseScheduleStack && queueEffectSchedulers.length) { - queueEffectSchedulers.shift()(); - } -} -function trackEffect(effect2, dep, debuggerEventExtraInfo) { - var _a; - if (dep.get(effect2) !== effect2._trackId) { - dep.set(effect2, effect2._trackId); - const oldDep = effect2.deps[effect2._depsLength]; - if (oldDep !== dep) { - if (oldDep) { - cleanupDepEffect(oldDep, effect2); - } - effect2.deps[effect2._depsLength++] = dep; - } else { - effect2._depsLength++; - } - { - (_a = effect2.onTrack) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); - } - } -} -const queueEffectSchedulers = []; -function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) { - var _a; - pauseScheduling(); - for (const effect2 of dep.keys()) { - if (effect2._dirtyLevel < dirtyLevel && dep.get(effect2) === effect2._trackId) { - const lastDirtyLevel = effect2._dirtyLevel; - effect2._dirtyLevel = dirtyLevel; - if (lastDirtyLevel === 0) { - effect2._shouldSchedule = true; - { - (_a = effect2.onTrigger) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); - } - effect2.trigger(); - } - } - } - scheduleEffects(dep); - resetScheduling(); -} -function scheduleEffects(dep) { - for (const effect2 of dep.keys()) { - if (effect2.scheduler && effect2._shouldSchedule && (!effect2._runnings || effect2.allowRecurse) && dep.get(effect2) === effect2._trackId) { - effect2._shouldSchedule = false; - queueEffectSchedulers.push(effect2.scheduler); - } - } -} - -const createDep = (cleanup, computed) => { - const dep = /* @__PURE__ */ new Map(); - dep.cleanup = cleanup; - dep.computed = computed; - return dep; -}; - -const targetMap = /* @__PURE__ */ new WeakMap(); -const ITERATE_KEY = Symbol("iterate" ); -const MAP_KEY_ITERATE_KEY = Symbol("Map key iterate" ); -function track(target, type, key) { - if (shouldTrack && activeEffect) { - let depsMap = targetMap.get(target); - if (!depsMap) { - targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); - } - let dep = depsMap.get(key); - if (!dep) { - depsMap.set(key, dep = createDep(() => depsMap.delete(key))); - } - trackEffect( - activeEffect, - dep, - { - target, - type, - key - } - ); - } -} -function trigger(target, type, key, newValue, oldValue, oldTarget) { - const depsMap = targetMap.get(target); - if (!depsMap) { - return; - } - let deps = []; - if (type === "clear") { - deps = [...depsMap.values()]; - } else if (key === "length" && isArray(target)) { - const newLength = Number(newValue); - depsMap.forEach((dep, key2) => { - if (key2 === "length" || !isSymbol(key2) && key2 >= newLength) { - deps.push(dep); - } - }); - } else { - if (key !== void 0) { - deps.push(depsMap.get(key)); - } - switch (type) { - case "add": - if (!isArray(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } else if (isIntegerKey(key)) { - deps.push(depsMap.get("length")); - } - break; - case "delete": - if (!isArray(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - if (isMap(target)) { - deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); - } - } - break; - case "set": - if (isMap(target)) { - deps.push(depsMap.get(ITERATE_KEY)); - } - break; - } - } - pauseScheduling(); - for (const dep of deps) { - if (dep) { - triggerEffects( - dep, - 2, - { - target, - type, - key, - newValue, - oldValue, - oldTarget - } - ); - } - } - resetScheduling(); -} -function getDepFromReactive(object, key) { - var _a; - return (_a = targetMap.get(object)) == null ? void 0 : _a.get(key); -} - -const isNonTrackableKeys = /* @__PURE__ */ makeMap(`__proto__,__v_isRef,__isVue`); -const builtInSymbols = new Set( - /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) -); -const arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations(); -function createArrayInstrumentations() { - const instrumentations = {}; - ["includes", "indexOf", "lastIndexOf"].forEach((key) => { - instrumentations[key] = function(...args) { - const arr = toRaw(this); - for (let i = 0, l = this.length; i < l; i++) { - track(arr, "get", i + ""); - } - const res = arr[key](...args); - if (res === -1 || res === false) { - return arr[key](...args.map(toRaw)); - } else { - return res; - } - }; - }); - ["push", "pop", "shift", "unshift", "splice"].forEach((key) => { - instrumentations[key] = function(...args) { - pauseTracking(); - pauseScheduling(); - const res = toRaw(this)[key].apply(this, args); - resetScheduling(); - resetTracking(); - return res; - }; - }); - return instrumentations; -} -function hasOwnProperty(key) { - const obj = toRaw(this); - track(obj, "has", key); - return obj.hasOwnProperty(key); -} -class BaseReactiveHandler { - constructor(_isReadonly = false, _shallow = false) { - this._isReadonly = _isReadonly; - this._shallow = _shallow; - } - get(target, key, receiver) { - const isReadonly2 = this._isReadonly, shallow = this._shallow; - if (key === "__v_isReactive") { - return !isReadonly2; - } else if (key === "__v_isReadonly") { - return isReadonly2; - } else if (key === "__v_isShallow") { - return shallow; - } else if (key === "__v_raw") { - if (receiver === (isReadonly2 ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype - // this means the reciever is a user proxy of the reactive proxy - Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) { - return target; - } - return; - } - const targetIsArray = isArray(target); - if (!isReadonly2) { - if (targetIsArray && hasOwn(arrayInstrumentations, key)) { - return Reflect.get(arrayInstrumentations, key, receiver); - } - if (key === "hasOwnProperty") { - return hasOwnProperty; - } - } - const res = Reflect.get(target, key, receiver); - if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { - return res; - } - if (!isReadonly2) { - track(target, "get", key); - } - if (shallow) { - return res; - } - if (isRef(res)) { - return targetIsArray && isIntegerKey(key) ? res : res.value; - } - if (isObject(res)) { - return isReadonly2 ? readonly(res) : reactive(res); - } - return res; - } -} -class MutableReactiveHandler extends BaseReactiveHandler { - constructor(shallow = false) { - super(false, shallow); - } - set(target, key, value, receiver) { - let oldValue = target[key]; - if (!this._shallow) { - const isOldValueReadonly = isReadonly(oldValue); - if (!isShallow(value) && !isReadonly(value)) { - oldValue = toRaw(oldValue); - value = toRaw(value); - } - if (!isArray(target) && isRef(oldValue) && !isRef(value)) { - if (isOldValueReadonly) { - return false; - } else { - oldValue.value = value; - return true; - } - } - } - const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); - const result = Reflect.set(target, key, value, receiver); - if (target === toRaw(receiver)) { - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - } - return result; - } - deleteProperty(target, key) { - const hadKey = hasOwn(target, key); - const oldValue = target[key]; - const result = Reflect.deleteProperty(target, key); - if (result && hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; - } - has(target, key) { - const result = Reflect.has(target, key); - if (!isSymbol(key) || !builtInSymbols.has(key)) { - track(target, "has", key); - } - return result; - } - ownKeys(target) { - track( - target, - "iterate", - isArray(target) ? "length" : ITERATE_KEY - ); - return Reflect.ownKeys(target); - } -} -class ReadonlyReactiveHandler extends BaseReactiveHandler { - constructor(shallow = false) { - super(true, shallow); - } - set(target, key) { - { - warn$2( - `Set operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } - deleteProperty(target, key) { - { - warn$2( - `Delete operation on key "${String(key)}" failed: target is readonly.`, - target - ); - } - return true; - } -} -const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler(); -const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(); -const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler( - true -); -const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true); - -const toShallow = (value) => value; -const getProto = (v) => Reflect.getPrototypeOf(v); -function get(target, key, isReadonly = false, isShallow = false) { - target = target["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!isReadonly) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "get", key); - } - track(rawTarget, "get", rawKey); - } - const { has: has2 } = getProto(rawTarget); - const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; - if (has2.call(rawTarget, key)) { - return wrap(target.get(key)); - } else if (has2.call(rawTarget, rawKey)) { - return wrap(target.get(rawKey)); - } else if (target !== rawTarget) { - target.get(key); - } -} -function has(key, isReadonly = false) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const rawKey = toRaw(key); - if (!isReadonly) { - if (hasChanged(key, rawKey)) { - track(rawTarget, "has", key); - } - track(rawTarget, "has", rawKey); - } - return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); -} -function size(target, isReadonly = false) { - target = target["__v_raw"]; - !isReadonly && track(toRaw(target), "iterate", ITERATE_KEY); - return Reflect.get(target, "size", target); -} -function add(value) { - value = toRaw(value); - const target = toRaw(this); - const proto = getProto(target); - const hadKey = proto.has.call(target, value); - if (!hadKey) { - target.add(value); - trigger(target, "add", value, value); - } - return this; -} -function set(key, value) { - value = toRaw(value); - const target = toRaw(this); - const { has: has2, get: get2 } = getProto(target); - let hadKey = has2.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has2.call(target, key); - } else { - checkIdentityKeys(target, has2, key); - } - const oldValue = get2.call(target, key); - target.set(key, value); - if (!hadKey) { - trigger(target, "add", key, value); - } else if (hasChanged(value, oldValue)) { - trigger(target, "set", key, value, oldValue); - } - return this; -} -function deleteEntry(key) { - const target = toRaw(this); - const { has: has2, get: get2 } = getProto(target); - let hadKey = has2.call(target, key); - if (!hadKey) { - key = toRaw(key); - hadKey = has2.call(target, key); - } else { - checkIdentityKeys(target, has2, key); - } - const oldValue = get2 ? get2.call(target, key) : void 0; - const result = target.delete(key); - if (hadKey) { - trigger(target, "delete", key, void 0, oldValue); - } - return result; -} -function clear() { - const target = toRaw(this); - const hadItems = target.size !== 0; - const oldTarget = isMap(target) ? new Map(target) : new Set(target) ; - const result = target.clear(); - if (hadItems) { - trigger(target, "clear", void 0, void 0, oldTarget); - } - return result; -} -function createForEach(isReadonly, isShallow) { - return function forEach(callback, thisArg) { - const observed = this; - const target = observed["__v_raw"]; - const rawTarget = toRaw(target); - const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; - !isReadonly && track(rawTarget, "iterate", ITERATE_KEY); - return target.forEach((value, key) => { - return callback.call(thisArg, wrap(value), wrap(key), observed); - }); - }; -} -function createIterableMethod(method, isReadonly, isShallow) { - return function(...args) { - const target = this["__v_raw"]; - const rawTarget = toRaw(target); - const targetIsMap = isMap(rawTarget); - const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; - const isKeyOnly = method === "keys" && targetIsMap; - const innerIterator = target[method](...args); - const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; - !isReadonly && track( - rawTarget, - "iterate", - isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY - ); - return { - // iterator protocol - next() { - const { value, done } = innerIterator.next(); - return done ? { value, done } : { - value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), - done - }; - }, - // iterable protocol - [Symbol.iterator]() { - return this; - } - }; - }; -} -function createReadonlyMethod(type) { - return function(...args) { - { - const key = args[0] ? `on key "${args[0]}" ` : ``; - console.warn( - `${capitalize(type)} operation ${key}failed: target is readonly.`, - toRaw(this) - ); - } - return type === "delete" ? false : type === "clear" ? void 0 : this; - }; -} -function createInstrumentations() { - const mutableInstrumentations2 = { - get(key) { - return get(this, key); - }, - get size() { - return size(this); - }, - has, - add, - set, - delete: deleteEntry, - clear, - forEach: createForEach(false, false) - }; - const shallowInstrumentations2 = { - get(key) { - return get(this, key, false, true); - }, - get size() { - return size(this); - }, - has, - add, - set, - delete: deleteEntry, - clear, - forEach: createForEach(false, true) - }; - const readonlyInstrumentations2 = { - get(key) { - return get(this, key, true); - }, - get size() { - return size(this, true); - }, - has(key) { - return has.call(this, key, true); - }, - add: createReadonlyMethod("add"), - set: createReadonlyMethod("set"), - delete: createReadonlyMethod("delete"), - clear: createReadonlyMethod("clear"), - forEach: createForEach(true, false) - }; - const shallowReadonlyInstrumentations2 = { - get(key) { - return get(this, key, true, true); - }, - get size() { - return size(this, true); - }, - has(key) { - return has.call(this, key, true); - }, - add: createReadonlyMethod("add"), - set: createReadonlyMethod("set"), - delete: createReadonlyMethod("delete"), - clear: createReadonlyMethod("clear"), - forEach: createForEach(true, true) - }; - const iteratorMethods = ["keys", "values", "entries", Symbol.iterator]; - iteratorMethods.forEach((method) => { - mutableInstrumentations2[method] = createIterableMethod( - method, - false, - false - ); - readonlyInstrumentations2[method] = createIterableMethod( - method, - true, - false - ); - shallowInstrumentations2[method] = createIterableMethod( - method, - false, - true - ); - shallowReadonlyInstrumentations2[method] = createIterableMethod( - method, - true, - true - ); - }); - return [ - mutableInstrumentations2, - readonlyInstrumentations2, - shallowInstrumentations2, - shallowReadonlyInstrumentations2 - ]; -} -const [ - mutableInstrumentations, - readonlyInstrumentations, - shallowInstrumentations, - shallowReadonlyInstrumentations -] = /* @__PURE__ */ createInstrumentations(); -function createInstrumentationGetter(isReadonly, shallow) { - const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations; - return (target, key, receiver) => { - if (key === "__v_isReactive") { - return !isReadonly; - } else if (key === "__v_isReadonly") { - return isReadonly; - } else if (key === "__v_raw") { - return target; - } - return Reflect.get( - hasOwn(instrumentations, key) && key in target ? instrumentations : target, - key, - receiver - ); - }; -} -const mutableCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(false, false) -}; -const shallowCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(false, true) -}; -const readonlyCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(true, false) -}; -const shallowReadonlyCollectionHandlers = { - get: /* @__PURE__ */ createInstrumentationGetter(true, true) -}; -function checkIdentityKeys(target, has2, key) { - const rawKey = toRaw(key); - if (rawKey !== key && has2.call(target, rawKey)) { - const type = toRawType(target); - console.warn( - `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` - ); - } -} - -const reactiveMap = /* @__PURE__ */ new WeakMap(); -const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); -const readonlyMap = /* @__PURE__ */ new WeakMap(); -const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); -function targetTypeMap(rawType) { - switch (rawType) { - case "Object": - case "Array": - return 1 /* COMMON */; - case "Map": - case "Set": - case "WeakMap": - case "WeakSet": - return 2 /* COLLECTION */; - default: - return 0 /* INVALID */; - } -} -function getTargetType(value) { - return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value)); -} -function reactive(target) { - if (isReadonly(target)) { - return target; - } - return createReactiveObject( - target, - false, - mutableHandlers, - mutableCollectionHandlers, - reactiveMap - ); -} -function shallowReactive(target) { - return createReactiveObject( - target, - false, - shallowReactiveHandlers, - shallowCollectionHandlers, - shallowReactiveMap - ); -} -function readonly(target) { - return createReactiveObject( - target, - true, - readonlyHandlers, - readonlyCollectionHandlers, - readonlyMap - ); -} -function shallowReadonly(target) { - return createReactiveObject( - target, - true, - shallowReadonlyHandlers, - shallowReadonlyCollectionHandlers, - shallowReadonlyMap - ); -} -function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { - if (!isObject(target)) { - { - console.warn(`value cannot be made reactive: ${String(target)}`); - } - return target; - } - if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { - return target; - } - const existingProxy = proxyMap.get(target); - if (existingProxy) { - return existingProxy; - } - const targetType = getTargetType(target); - if (targetType === 0 /* INVALID */) { - return target; - } - const proxy = new Proxy( - target, - targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers - ); - proxyMap.set(target, proxy); - return proxy; -} -function isReactive(value) { - if (isReadonly(value)) { - return isReactive(value["__v_raw"]); - } - return !!(value && value["__v_isReactive"]); -} -function isReadonly(value) { - return !!(value && value["__v_isReadonly"]); -} -function isShallow(value) { - return !!(value && value["__v_isShallow"]); -} -function isProxy(value) { - return isReactive(value) || isReadonly(value); -} -function toRaw(observed) { - const raw = observed && observed["__v_raw"]; - return raw ? toRaw(raw) : observed; -} -function markRaw(value) { - def(value, "__v_skip", true); - return value; -} -const toReactive = (value) => isObject(value) ? reactive(value) : value; -const toReadonly = (value) => isObject(value) ? readonly(value) : value; - -class ComputedRefImpl { - constructor(getter, _setter, isReadonly, isSSR) { - this._setter = _setter; - this.dep = void 0; - this.__v_isRef = true; - this["__v_isReadonly"] = false; - this.effect = new ReactiveEffect( - () => getter(this._value), - () => triggerRefValue(this, 1), - () => this.dep && scheduleEffects(this.dep) - ); - this.effect.computed = this; - this.effect.active = this._cacheable = !isSSR; - this["__v_isReadonly"] = isReadonly; - } - get value() { - const self = toRaw(this); - if (!self._cacheable || self.effect.dirty) { - if (hasChanged(self._value, self._value = self.effect.run())) { - triggerRefValue(self, 2); - } - } - trackRefValue(self); - if (self.effect._dirtyLevel >= 1) { - triggerRefValue(self, 1); - } - return self._value; - } - set value(newValue) { - this._setter(newValue); - } - // #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x - get _dirty() { - return this.effect.dirty; - } - set _dirty(v) { - this.effect.dirty = v; - } - // #endregion -} -function computed$1(getterOrOptions, debugOptions, isSSR = false) { - let getter; - let setter; - const onlyGetter = isFunction(getterOrOptions); - if (onlyGetter) { - getter = getterOrOptions; - setter = () => { - console.warn("Write operation failed: computed value is readonly"); - } ; - } else { - getter = getterOrOptions.get; - setter = getterOrOptions.set; - } - const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR); - if (debugOptions && !isSSR) { - cRef.effect.onTrack = debugOptions.onTrack; - cRef.effect.onTrigger = debugOptions.onTrigger; - } - return cRef; -} - -function trackRefValue(ref2) { - if (shouldTrack && activeEffect) { - ref2 = toRaw(ref2); - trackEffect( - activeEffect, - ref2.dep || (ref2.dep = createDep( - () => ref2.dep = void 0, - ref2 instanceof ComputedRefImpl ? ref2 : void 0 - )), - { - target: ref2, - type: "get", - key: "value" - } - ); - } -} -function triggerRefValue(ref2, dirtyLevel = 2, newVal) { - ref2 = toRaw(ref2); - const dep = ref2.dep; - if (dep) { - triggerEffects( - dep, - dirtyLevel, - { - target: ref2, - type: "set", - key: "value", - newValue: newVal - } - ); - } -} -function isRef(r) { - return !!(r && r.__v_isRef === true); -} -function ref(value) { - return createRef(value, false); -} -function shallowRef(value) { - return createRef(value, true); -} -function createRef(rawValue, shallow) { - if (isRef(rawValue)) { - return rawValue; - } - return new RefImpl(rawValue, shallow); -} -class RefImpl { - constructor(value, __v_isShallow) { - this.__v_isShallow = __v_isShallow; - this.dep = void 0; - this.__v_isRef = true; - this._rawValue = __v_isShallow ? value : toRaw(value); - this._value = __v_isShallow ? value : toReactive(value); - } - get value() { - trackRefValue(this); - return this._value; - } - set value(newVal) { - const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); - newVal = useDirectValue ? newVal : toRaw(newVal); - if (hasChanged(newVal, this._rawValue)) { - this._rawValue = newVal; - this._value = useDirectValue ? newVal : toReactive(newVal); - triggerRefValue(this, 2, newVal); - } - } -} -function triggerRef(ref2) { - triggerRefValue(ref2, 2, ref2.value ); -} -function unref(ref2) { - return isRef(ref2) ? ref2.value : ref2; -} -function toValue(source) { - return isFunction(source) ? source() : unref(source); -} -const shallowUnwrapHandlers = { - get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), - set: (target, key, value, receiver) => { - const oldValue = target[key]; - if (isRef(oldValue) && !isRef(value)) { - oldValue.value = value; - return true; - } else { - return Reflect.set(target, key, value, receiver); - } - } -}; -function proxyRefs(objectWithRefs) { - return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); -} -class CustomRefImpl { - constructor(factory) { - this.dep = void 0; - this.__v_isRef = true; - const { get, set } = factory( - () => trackRefValue(this), - () => triggerRefValue(this) - ); - this._get = get; - this._set = set; - } - get value() { - return this._get(); - } - set value(newVal) { - this._set(newVal); - } -} -function customRef(factory) { - return new CustomRefImpl(factory); -} -function toRefs(object) { - if (!isProxy(object)) { - console.warn(`toRefs() expects a reactive object but received a plain one.`); - } - const ret = isArray(object) ? new Array(object.length) : {}; - for (const key in object) { - ret[key] = propertyToRef(object, key); - } - return ret; -} -class ObjectRefImpl { - constructor(_object, _key, _defaultValue) { - this._object = _object; - this._key = _key; - this._defaultValue = _defaultValue; - this.__v_isRef = true; - } - get value() { - const val = this._object[this._key]; - return val === void 0 ? this._defaultValue : val; - } - set value(newVal) { - this._object[this._key] = newVal; - } - get dep() { - return getDepFromReactive(toRaw(this._object), this._key); - } -} -class GetterRefImpl { - constructor(_getter) { - this._getter = _getter; - this.__v_isRef = true; - this.__v_isReadonly = true; - } - get value() { - return this._getter(); - } -} -function toRef(source, key, defaultValue) { - if (isRef(source)) { - return source; - } else if (isFunction(source)) { - return new GetterRefImpl(source); - } else if (isObject(source) && arguments.length > 1) { - return propertyToRef(source, key, defaultValue); - } else { - return ref(source); - } -} -function propertyToRef(source, key, defaultValue) { - const val = source[key]; - return isRef(val) ? val : new ObjectRefImpl(source, key, defaultValue); -} - -const TrackOpTypes = { - "GET": "get", - "HAS": "has", - "ITERATE": "iterate" -}; -const TriggerOpTypes = { - "SET": "set", - "ADD": "add", - "DELETE": "delete", - "CLEAR": "clear" -}; - -const stack$1 = []; -function pushWarningContext(vnode) { - stack$1.push(vnode); -} -function popWarningContext() { - stack$1.pop(); -} -function warn$1(msg, ...args) { - pauseTracking(); - const instance = stack$1.length ? stack$1[stack$1.length - 1].component : null; - const appWarnHandler = instance && instance.appContext.config.warnHandler; - const trace = getComponentTrace(); - if (appWarnHandler) { - callWithErrorHandling( - appWarnHandler, - instance, - 11, - [ - msg + args.join(""), - instance && instance.proxy, - trace.map( - ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>` - ).join("\n"), - trace - ] - ); - } else { - const warnArgs = [`[Vue warn]: ${msg}`, ...args]; - if (trace.length && // avoid spamming console during tests - true) { - warnArgs.push(` -`, ...formatTrace(trace)); - } - console.warn(...warnArgs); - } - resetTracking(); -} -function getComponentTrace() { - let currentVNode = stack$1[stack$1.length - 1]; - if (!currentVNode) { - return []; - } - const normalizedStack = []; - while (currentVNode) { - const last = normalizedStack[0]; - if (last && last.vnode === currentVNode) { - last.recurseCount++; - } else { - normalizedStack.push({ - vnode: currentVNode, - recurseCount: 0 - }); - } - const parentInstance = currentVNode.component && currentVNode.component.parent; - currentVNode = parentInstance && parentInstance.vnode; - } - return normalizedStack; -} -function formatTrace(trace) { - const logs = []; - trace.forEach((entry, i) => { - logs.push(...i === 0 ? [] : [` -`], ...formatTraceEntry(entry)); - }); - return logs; -} -function formatTraceEntry({ vnode, recurseCount }) { - const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; - const isRoot = vnode.component ? vnode.component.parent == null : false; - const open = ` at <${formatComponentName( - vnode.component, - vnode.type, - isRoot - )}`; - const close = `>` + postfix; - return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; -} -function formatProps(props) { - const res = []; - const keys = Object.keys(props); - keys.slice(0, 3).forEach((key) => { - res.push(...formatProp(key, props[key])); - }); - if (keys.length > 3) { - res.push(` ...`); - } - return res; -} -function formatProp(key, value, raw) { - if (isString(value)) { - value = JSON.stringify(value); - return raw ? value : [`${key}=${value}`]; - } else if (typeof value === "number" || typeof value === "boolean" || value == null) { - return raw ? value : [`${key}=${value}`]; - } else if (isRef(value)) { - value = formatProp(key, toRaw(value.value), true); - return raw ? value : [`${key}=Ref<`, value, `>`]; - } else if (isFunction(value)) { - return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; - } else { - value = toRaw(value); - return raw ? value : [`${key}=`, value]; - } -} -function assertNumber(val, type) { - if (val === void 0) { - return; - } else if (typeof val !== "number") { - warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`); - } else if (isNaN(val)) { - warn$1(`${type} is NaN - the duration expression might be incorrect.`); - } -} - -const ErrorCodes = { - "SETUP_FUNCTION": 0, - "0": "SETUP_FUNCTION", - "RENDER_FUNCTION": 1, - "1": "RENDER_FUNCTION", - "WATCH_GETTER": 2, - "2": "WATCH_GETTER", - "WATCH_CALLBACK": 3, - "3": "WATCH_CALLBACK", - "WATCH_CLEANUP": 4, - "4": "WATCH_CLEANUP", - "NATIVE_EVENT_HANDLER": 5, - "5": "NATIVE_EVENT_HANDLER", - "COMPONENT_EVENT_HANDLER": 6, - "6": "COMPONENT_EVENT_HANDLER", - "VNODE_HOOK": 7, - "7": "VNODE_HOOK", - "DIRECTIVE_HOOK": 8, - "8": "DIRECTIVE_HOOK", - "TRANSITION_HOOK": 9, - "9": "TRANSITION_HOOK", - "APP_ERROR_HANDLER": 10, - "10": "APP_ERROR_HANDLER", - "APP_WARN_HANDLER": 11, - "11": "APP_WARN_HANDLER", - "FUNCTION_REF": 12, - "12": "FUNCTION_REF", - "ASYNC_COMPONENT_LOADER": 13, - "13": "ASYNC_COMPONENT_LOADER", - "SCHEDULER": 14, - "14": "SCHEDULER" -}; -const ErrorTypeStrings$1 = { - ["sp"]: "serverPrefetch hook", - ["bc"]: "beforeCreate hook", - ["c"]: "created hook", - ["bm"]: "beforeMount hook", - ["m"]: "mounted hook", - ["bu"]: "beforeUpdate hook", - ["u"]: "updated", - ["bum"]: "beforeUnmount hook", - ["um"]: "unmounted hook", - ["a"]: "activated hook", - ["da"]: "deactivated hook", - ["ec"]: "errorCaptured hook", - ["rtc"]: "renderTracked hook", - ["rtg"]: "renderTriggered hook", - [0]: "setup function", - [1]: "render function", - [2]: "watcher getter", - [3]: "watcher callback", - [4]: "watcher cleanup function", - [5]: "native event handler", - [6]: "component event handler", - [7]: "vnode hook", - [8]: "directive hook", - [9]: "transition hook", - [10]: "app errorHandler", - [11]: "app warnHandler", - [12]: "ref function", - [13]: "async component loader", - [14]: "scheduler flush. This is likely a Vue internals bug. Please open an issue at https://github.com/vuejs/core ." -}; -function callWithErrorHandling(fn, instance, type, args) { - let res; - try { - res = args ? fn(...args) : fn(); - } catch (err) { - handleError(err, instance, type); - } - return res; -} -function callWithAsyncErrorHandling(fn, instance, type, args) { - if (isFunction(fn)) { - const res = callWithErrorHandling(fn, instance, type, args); - if (res && isPromise(res)) { - res.catch((err) => { - handleError(err, instance, type); - }); - } - return res; - } - const values = []; - for (let i = 0; i < fn.length; i++) { - values.push(callWithAsyncErrorHandling(fn[i], instance, type, args)); - } - return values; -} -function handleError(err, instance, type, throwInDev = true) { - const contextVNode = instance ? instance.vnode : null; - if (instance) { - let cur = instance.parent; - const exposedInstance = instance.proxy; - const errorInfo = ErrorTypeStrings$1[type] ; - while (cur) { - const errorCapturedHooks = cur.ec; - if (errorCapturedHooks) { - for (let i = 0; i < errorCapturedHooks.length; i++) { - if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) { - return; - } - } - } - cur = cur.parent; - } - const appErrorHandler = instance.appContext.config.errorHandler; - if (appErrorHandler) { - callWithErrorHandling( - appErrorHandler, - null, - 10, - [err, exposedInstance, errorInfo] - ); - return; - } - } - logError(err, type, contextVNode, throwInDev); -} -function logError(err, type, contextVNode, throwInDev = true) { - { - const info = ErrorTypeStrings$1[type]; - if (contextVNode) { - pushWarningContext(contextVNode); - } - warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); - if (contextVNode) { - popWarningContext(); - } - if (throwInDev) { - throw err; - } else { - console.error(err); - } - } -} - -let isFlushing = false; -let isFlushPending = false; -const queue = []; -let flushIndex = 0; -const pendingPostFlushCbs = []; -let activePostFlushCbs = null; -let postFlushIndex = 0; -const resolvedPromise = /* @__PURE__ */ Promise.resolve(); -let currentFlushPromise = null; -const RECURSION_LIMIT = 100; -function nextTick(fn) { - const p = currentFlushPromise || resolvedPromise; - return fn ? p.then(this ? fn.bind(this) : fn) : p; -} -function findInsertionIndex(id) { - let start = flushIndex + 1; - let end = queue.length; - while (start < end) { - const middle = start + end >>> 1; - const middleJob = queue[middle]; - const middleJobId = getId(middleJob); - if (middleJobId < id || middleJobId === id && middleJob.pre) { - start = middle + 1; - } else { - end = middle; - } - } - return start; -} -function queueJob(job) { - if (!queue.length || !queue.includes( - job, - isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex - )) { - if (job.id == null) { - queue.push(job); - } else { - queue.splice(findInsertionIndex(job.id), 0, job); - } - queueFlush(); - } -} -function queueFlush() { - if (!isFlushing && !isFlushPending) { - isFlushPending = true; - currentFlushPromise = resolvedPromise.then(flushJobs); - } -} -function invalidateJob(job) { - const i = queue.indexOf(job); - if (i > flushIndex) { - queue.splice(i, 1); - } -} -function queuePostFlushCb(cb) { - if (!isArray(cb)) { - if (!activePostFlushCbs || !activePostFlushCbs.includes( - cb, - cb.allowRecurse ? postFlushIndex + 1 : postFlushIndex - )) { - pendingPostFlushCbs.push(cb); - } - } else { - pendingPostFlushCbs.push(...cb); - } - queueFlush(); -} -function flushPreFlushCbs(instance, seen, i = isFlushing ? flushIndex + 1 : 0) { - { - seen = seen || /* @__PURE__ */ new Map(); - } - for (; i < queue.length; i++) { - const cb = queue[i]; - if (cb && cb.pre) { - if (instance && cb.id !== instance.uid) { - continue; - } - if (checkRecursiveUpdates(seen, cb)) { - continue; - } - queue.splice(i, 1); - i--; - cb(); - } - } -} -function flushPostFlushCbs(seen) { - if (pendingPostFlushCbs.length) { - const deduped = [...new Set(pendingPostFlushCbs)].sort( - (a, b) => getId(a) - getId(b) - ); - pendingPostFlushCbs.length = 0; - if (activePostFlushCbs) { - activePostFlushCbs.push(...deduped); - return; - } - activePostFlushCbs = deduped; - { - seen = seen || /* @__PURE__ */ new Map(); - } - for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { - if (checkRecursiveUpdates(seen, activePostFlushCbs[postFlushIndex])) { - continue; - } - activePostFlushCbs[postFlushIndex](); - } - activePostFlushCbs = null; - postFlushIndex = 0; - } -} -const getId = (job) => job.id == null ? Infinity : job.id; -const comparator = (a, b) => { - const diff = getId(a) - getId(b); - if (diff === 0) { - if (a.pre && !b.pre) - return -1; - if (b.pre && !a.pre) - return 1; - } - return diff; -}; -function flushJobs(seen) { - isFlushPending = false; - isFlushing = true; - { - seen = seen || /* @__PURE__ */ new Map(); - } - queue.sort(comparator); - const check = (job) => checkRecursiveUpdates(seen, job) ; - try { - for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { - const job = queue[flushIndex]; - if (job && job.active !== false) { - if (check(job)) { - continue; - } - callWithErrorHandling(job, null, 14); - } - } - } finally { - flushIndex = 0; - queue.length = 0; - flushPostFlushCbs(seen); - isFlushing = false; - currentFlushPromise = null; - if (queue.length || pendingPostFlushCbs.length) { - flushJobs(seen); - } - } -} -function checkRecursiveUpdates(seen, fn) { - if (!seen.has(fn)) { - seen.set(fn, 1); - } else { - const count = seen.get(fn); - if (count > RECURSION_LIMIT) { - const instance = fn.ownerInstance; - const componentName = instance && getComponentName(instance.type); - handleError( - `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, - null, - 10 - ); - return true; - } else { - seen.set(fn, count + 1); - } - } -} - -let isHmrUpdating = false; -const hmrDirtyComponents = /* @__PURE__ */ new Set(); -{ - getGlobalThis().__VUE_HMR_RUNTIME__ = { - createRecord: tryWrap(createRecord), - rerender: tryWrap(rerender), - reload: tryWrap(reload) - }; -} -const map = /* @__PURE__ */ new Map(); -function registerHMR(instance) { - const id = instance.type.__hmrId; - let record = map.get(id); - if (!record) { - createRecord(id, instance.type); - record = map.get(id); - } - record.instances.add(instance); -} -function unregisterHMR(instance) { - map.get(instance.type.__hmrId).instances.delete(instance); -} -function createRecord(id, initialDef) { - if (map.has(id)) { - return false; - } - map.set(id, { - initialDef: normalizeClassComponent(initialDef), - instances: /* @__PURE__ */ new Set() - }); - return true; -} -function normalizeClassComponent(component) { - return isClassComponent(component) ? component.__vccOpts : component; -} -function rerender(id, newRender) { - const record = map.get(id); - if (!record) { - return; - } - record.initialDef.render = newRender; - [...record.instances].forEach((instance) => { - if (newRender) { - instance.render = newRender; - normalizeClassComponent(instance.type).render = newRender; - } - instance.renderCache = []; - isHmrUpdating = true; - instance.effect.dirty = true; - instance.update(); - isHmrUpdating = false; - }); -} -function reload(id, newComp) { - const record = map.get(id); - if (!record) - return; - newComp = normalizeClassComponent(newComp); - updateComponentDef(record.initialDef, newComp); - const instances = [...record.instances]; - for (const instance of instances) { - const oldComp = normalizeClassComponent(instance.type); - if (!hmrDirtyComponents.has(oldComp)) { - if (oldComp !== record.initialDef) { - updateComponentDef(oldComp, newComp); - } - hmrDirtyComponents.add(oldComp); - } - instance.appContext.propsCache.delete(instance.type); - instance.appContext.emitsCache.delete(instance.type); - instance.appContext.optionsCache.delete(instance.type); - if (instance.ceReload) { - hmrDirtyComponents.add(oldComp); - instance.ceReload(newComp.styles); - hmrDirtyComponents.delete(oldComp); - } else if (instance.parent) { - instance.parent.effect.dirty = true; - queueJob(instance.parent.update); - } else if (instance.appContext.reload) { - instance.appContext.reload(); - } else if (typeof window !== "undefined") { - window.location.reload(); - } else { - console.warn( - "[HMR] Root or manually mounted instance modified. Full reload required." - ); - } - } - queuePostFlushCb(() => { - for (const instance of instances) { - hmrDirtyComponents.delete( - normalizeClassComponent(instance.type) - ); - } - }); -} -function updateComponentDef(oldComp, newComp) { - extend(oldComp, newComp); - for (const key in oldComp) { - if (key !== "__file" && !(key in newComp)) { - delete oldComp[key]; - } - } -} -function tryWrap(fn) { - return (id, arg) => { - try { - return fn(id, arg); - } catch (e) { - console.error(e); - console.warn( - `[HMR] Something went wrong during Vue component hot-reload. Full reload required.` - ); - } - }; -} - -let devtools$1; -let buffer = []; -let devtoolsNotInstalled = false; -function emit$1(event, ...args) { - if (devtools$1) { - devtools$1.emit(event, ...args); - } else if (!devtoolsNotInstalled) { - buffer.push({ event, args }); - } -} -function setDevtoolsHook$1(hook, target) { - var _a, _b; - devtools$1 = hook; - if (devtools$1) { - devtools$1.enabled = true; - buffer.forEach(({ event, args }) => devtools$1.emit(event, ...args)); - buffer = []; - } else if ( - // handle late devtools injection - only do this if we are in an actual - // browser environment to avoid the timer handle stalling test runner exit - // (#4815) - typeof window !== "undefined" && // some envs mock window but not fully - window.HTMLElement && // also exclude jsdom - !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes("jsdom")) - ) { - const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []; - replay.push((newHook) => { - setDevtoolsHook$1(newHook, target); - }); - setTimeout(() => { - if (!devtools$1) { - target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null; - devtoolsNotInstalled = true; - buffer = []; - } - }, 3e3); - } else { - devtoolsNotInstalled = true; - buffer = []; - } -} -function devtoolsInitApp(app, version) { - emit$1("app:init" /* APP_INIT */, app, version, { - Fragment, - Text, - Comment, - Static - }); -} -function devtoolsUnmountApp(app) { - emit$1("app:unmount" /* APP_UNMOUNT */, app); -} -const devtoolsComponentAdded = /* @__PURE__ */ createDevtoolsComponentHook( - "component:added" /* COMPONENT_ADDED */ -); -const devtoolsComponentUpdated = /* @__PURE__ */ createDevtoolsComponentHook("component:updated" /* COMPONENT_UPDATED */); -const _devtoolsComponentRemoved = /* @__PURE__ */ createDevtoolsComponentHook( - "component:removed" /* COMPONENT_REMOVED */ -); -const devtoolsComponentRemoved = (component) => { - if (devtools$1 && typeof devtools$1.cleanupBuffer === "function" && // remove the component if it wasn't buffered - !devtools$1.cleanupBuffer(component)) { - _devtoolsComponentRemoved(component); - } -}; -function createDevtoolsComponentHook(hook) { - return (component) => { - emit$1( - hook, - component.appContext.app, - component.uid, - component.parent ? component.parent.uid : void 0, - component - ); - }; -} -const devtoolsPerfStart = /* @__PURE__ */ createDevtoolsPerformanceHook( - "perf:start" /* PERFORMANCE_START */ -); -const devtoolsPerfEnd = /* @__PURE__ */ createDevtoolsPerformanceHook( - "perf:end" /* PERFORMANCE_END */ -); -function createDevtoolsPerformanceHook(hook) { - return (component, type, time) => { - emit$1(hook, component.appContext.app, component.uid, component, type, time); - }; -} -function devtoolsComponentEmit(component, event, params) { - emit$1( - "component:emit" /* COMPONENT_EMIT */, - component.appContext.app, - component, - event, - params - ); -} - -function emit(instance, event, ...rawArgs) { - if (instance.isUnmounted) - return; - const props = instance.vnode.props || EMPTY_OBJ; - { - const { - emitsOptions, - propsOptions: [propsOptions] - } = instance; - if (emitsOptions) { - if (!(event in emitsOptions) && true) { - if (!propsOptions || !(toHandlerKey(event) in propsOptions)) { - warn$1( - `Component emitted event "${event}" but it is neither declared in the emits option nor as an "${toHandlerKey(event)}" prop.` - ); - } - } else { - const validator = emitsOptions[event]; - if (isFunction(validator)) { - const isValid = validator(...rawArgs); - if (!isValid) { - warn$1( - `Invalid event arguments: event validation failed for event "${event}".` - ); - } - } - } - } - } - let args = rawArgs; - const isModelListener = event.startsWith("update:"); - const modelArg = isModelListener && event.slice(7); - if (modelArg && modelArg in props) { - const modifiersKey = `${modelArg === "modelValue" ? "model" : modelArg}Modifiers`; - const { number, trim } = props[modifiersKey] || EMPTY_OBJ; - if (trim) { - args = rawArgs.map((a) => isString(a) ? a.trim() : a); - } - if (number) { - args = rawArgs.map(looseToNumber); - } - } - { - devtoolsComponentEmit(instance, event, args); - } - { - const lowerCaseEvent = event.toLowerCase(); - if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) { - warn$1( - `Event "${lowerCaseEvent}" is emitted in component ${formatComponentName( - instance, - instance.type - )} but the handler is registered for "${event}". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "${hyphenate( - event - )}" instead of "${event}".` - ); - } - } - let handlerName; - let handler = props[handlerName = toHandlerKey(event)] || // also try camelCase event handler (#2249) - props[handlerName = toHandlerKey(camelize(event))]; - if (!handler && isModelListener) { - handler = props[handlerName = toHandlerKey(hyphenate(event))]; - } - if (handler) { - callWithAsyncErrorHandling( - handler, - instance, - 6, - args - ); - } - const onceHandler = props[handlerName + `Once`]; - if (onceHandler) { - if (!instance.emitted) { - instance.emitted = {}; - } else if (instance.emitted[handlerName]) { - return; - } - instance.emitted[handlerName] = true; - callWithAsyncErrorHandling( - onceHandler, - instance, - 6, - args - ); - } -} -function normalizeEmitsOptions(comp, appContext, asMixin = false) { - const cache = appContext.emitsCache; - const cached = cache.get(comp); - if (cached !== void 0) { - return cached; - } - const raw = comp.emits; - let normalized = {}; - let hasExtends = false; - if (!isFunction(comp)) { - const extendEmits = (raw2) => { - const normalizedFromExtend = normalizeEmitsOptions(raw2, appContext, true); - if (normalizedFromExtend) { - hasExtends = true; - extend(normalized, normalizedFromExtend); - } - }; - if (!asMixin && appContext.mixins.length) { - appContext.mixins.forEach(extendEmits); - } - if (comp.extends) { - extendEmits(comp.extends); - } - if (comp.mixins) { - comp.mixins.forEach(extendEmits); - } - } - if (!raw && !hasExtends) { - if (isObject(comp)) { - cache.set(comp, null); - } - return null; - } - if (isArray(raw)) { - raw.forEach((key) => normalized[key] = null); - } else { - extend(normalized, raw); - } - if (isObject(comp)) { - cache.set(comp, normalized); - } - return normalized; -} -function isEmitListener(options, key) { - if (!options || !isOn(key)) { - return false; - } - key = key.slice(2).replace(/Once$/, ""); - return hasOwn(options, key[0].toLowerCase() + key.slice(1)) || hasOwn(options, hyphenate(key)) || hasOwn(options, key); -} - -let currentRenderingInstance = null; -let currentScopeId = null; -function setCurrentRenderingInstance(instance) { - const prev = currentRenderingInstance; - currentRenderingInstance = instance; - currentScopeId = instance && instance.type.__scopeId || null; - return prev; -} -function pushScopeId(id) { - currentScopeId = id; -} -function popScopeId() { - currentScopeId = null; -} -const withScopeId = (_id) => withCtx; -function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) { - if (!ctx) - return fn; - if (fn._n) { - return fn; - } - const renderFnWithContext = (...args) => { - if (renderFnWithContext._d) { - setBlockTracking(-1); - } - const prevInstance = setCurrentRenderingInstance(ctx); - let res; - try { - res = fn(...args); - } finally { - setCurrentRenderingInstance(prevInstance); - if (renderFnWithContext._d) { - setBlockTracking(1); - } - } - { - devtoolsComponentUpdated(ctx); - } - return res; - }; - renderFnWithContext._n = true; - renderFnWithContext._c = true; - renderFnWithContext._d = true; - return renderFnWithContext; -} - -let accessedAttrs = false; -function markAttrsAccessed() { - accessedAttrs = true; -} -function renderComponentRoot(instance) { - const { - type: Component, - vnode, - proxy, - withProxy, - props, - propsOptions: [propsOptions], - slots, - attrs, - emit, - render, - renderCache, - data, - setupState, - ctx, - inheritAttrs - } = instance; - let result; - let fallthroughAttrs; - const prev = setCurrentRenderingInstance(instance); - { - accessedAttrs = false; - } - try { - if (vnode.shapeFlag & 4) { - const proxyToUse = withProxy || proxy; - const thisProxy = setupState.__isScriptSetup ? new Proxy(proxyToUse, { - get(target, key, receiver) { - warn$1( - `Property '${String( - key - )}' was accessed via 'this'. Avoid using 'this' in templates.` - ); - return Reflect.get(target, key, receiver); - } - }) : proxyToUse; - result = normalizeVNode( - render.call( - thisProxy, - proxyToUse, - renderCache, - props, - setupState, - data, - ctx - ) - ); - fallthroughAttrs = attrs; - } else { - const render2 = Component; - if (attrs === props) { - markAttrsAccessed(); - } - result = normalizeVNode( - render2.length > 1 ? render2( - props, - true ? { - get attrs() { - markAttrsAccessed(); - return attrs; - }, - slots, - emit - } : { attrs, slots, emit } - ) : render2( - props, - null - /* we know it doesn't need it */ - ) - ); - fallthroughAttrs = Component.props ? attrs : getFunctionalFallthrough(attrs); - } - } catch (err) { - blockStack.length = 0; - handleError(err, instance, 1); - result = createVNode(Comment); - } - let root = result; - let setRoot = void 0; - if (result.patchFlag > 0 && result.patchFlag & 2048) { - [root, setRoot] = getChildRoot(result); - } - if (fallthroughAttrs && inheritAttrs !== false) { - const keys = Object.keys(fallthroughAttrs); - const { shapeFlag } = root; - if (keys.length) { - if (shapeFlag & (1 | 6)) { - if (propsOptions && keys.some(isModelListener)) { - fallthroughAttrs = filterModelListeners( - fallthroughAttrs, - propsOptions - ); - } - root = cloneVNode(root, fallthroughAttrs); - } else if (!accessedAttrs && root.type !== Comment) { - const allAttrs = Object.keys(attrs); - const eventAttrs = []; - const extraAttrs = []; - for (let i = 0, l = allAttrs.length; i < l; i++) { - const key = allAttrs[i]; - if (isOn(key)) { - if (!isModelListener(key)) { - eventAttrs.push(key[2].toLowerCase() + key.slice(3)); - } - } else { - extraAttrs.push(key); - } - } - if (extraAttrs.length) { - warn$1( - `Extraneous non-props attributes (${extraAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.` - ); - } - if (eventAttrs.length) { - warn$1( - `Extraneous non-emits event listeners (${eventAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.` - ); - } - } - } - } - if (vnode.dirs) { - if (!isElementRoot(root)) { - warn$1( - `Runtime directive used on component with non-element root node. The directives will not function as intended.` - ); - } - root = cloneVNode(root); - root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs; - } - if (vnode.transition) { - if (!isElementRoot(root)) { - warn$1( - `Component inside renders non-element root node that cannot be animated.` - ); - } - root.transition = vnode.transition; - } - if (setRoot) { - setRoot(root); - } else { - result = root; - } - setCurrentRenderingInstance(prev); - return result; -} -const getChildRoot = (vnode) => { - const rawChildren = vnode.children; - const dynamicChildren = vnode.dynamicChildren; - const childRoot = filterSingleRoot(rawChildren, false); - if (!childRoot) { - return [vnode, void 0]; - } else if (childRoot.patchFlag > 0 && childRoot.patchFlag & 2048) { - return getChildRoot(childRoot); - } - const index = rawChildren.indexOf(childRoot); - const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1; - const setRoot = (updatedRoot) => { - rawChildren[index] = updatedRoot; - if (dynamicChildren) { - if (dynamicIndex > -1) { - dynamicChildren[dynamicIndex] = updatedRoot; - } else if (updatedRoot.patchFlag > 0) { - vnode.dynamicChildren = [...dynamicChildren, updatedRoot]; - } - } - }; - return [normalizeVNode(childRoot), setRoot]; -}; -function filterSingleRoot(children, recurse = true) { - let singleRoot; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (isVNode(child)) { - if (child.type !== Comment || child.children === "v-if") { - if (singleRoot) { - return; - } else { - singleRoot = child; - if (recurse && singleRoot.patchFlag > 0 && singleRoot.patchFlag & 2048) { - return filterSingleRoot(singleRoot.children); - } - } - } - } else { - return; - } - } - return singleRoot; -} -const getFunctionalFallthrough = (attrs) => { - let res; - for (const key in attrs) { - if (key === "class" || key === "style" || isOn(key)) { - (res || (res = {}))[key] = attrs[key]; - } - } - return res; -}; -const filterModelListeners = (attrs, props) => { - const res = {}; - for (const key in attrs) { - if (!isModelListener(key) || !(key.slice(9) in props)) { - res[key] = attrs[key]; - } - } - return res; -}; -const isElementRoot = (vnode) => { - return vnode.shapeFlag & (6 | 1) || vnode.type === Comment; -}; -function shouldUpdateComponent(prevVNode, nextVNode, optimized) { - const { props: prevProps, children: prevChildren, component } = prevVNode; - const { props: nextProps, children: nextChildren, patchFlag } = nextVNode; - const emits = component.emitsOptions; - if ((prevChildren || nextChildren) && isHmrUpdating) { - return true; - } - if (nextVNode.dirs || nextVNode.transition) { - return true; - } - if (optimized && patchFlag >= 0) { - if (patchFlag & 1024) { - return true; - } - if (patchFlag & 16) { - if (!prevProps) { - return !!nextProps; - } - return hasPropsChanged(prevProps, nextProps, emits); - } else if (patchFlag & 8) { - const dynamicProps = nextVNode.dynamicProps; - for (let i = 0; i < dynamicProps.length; i++) { - const key = dynamicProps[i]; - if (nextProps[key] !== prevProps[key] && !isEmitListener(emits, key)) { - return true; - } - } - } - } else { - if (prevChildren || nextChildren) { - if (!nextChildren || !nextChildren.$stable) { - return true; - } - } - if (prevProps === nextProps) { - return false; - } - if (!prevProps) { - return !!nextProps; - } - if (!nextProps) { - return true; - } - return hasPropsChanged(prevProps, nextProps, emits); - } - return false; -} -function hasPropsChanged(prevProps, nextProps, emitsOptions) { - const nextKeys = Object.keys(nextProps); - if (nextKeys.length !== Object.keys(prevProps).length) { - return true; - } - for (let i = 0; i < nextKeys.length; i++) { - const key = nextKeys[i]; - if (nextProps[key] !== prevProps[key] && !isEmitListener(emitsOptions, key)) { - return true; - } - } - return false; -} -function updateHOCHostEl({ vnode, parent }, el) { - while (parent) { - const root = parent.subTree; - if (root.suspense && root.suspense.activeBranch === vnode) { - root.el = vnode.el; - } - if (root === vnode) { - (vnode = parent.vnode).el = el; - parent = parent.parent; - } else { - break; - } - } -} - -const COMPONENTS = "components"; -const DIRECTIVES = "directives"; -function resolveComponent(name, maybeSelfReference) { - return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; -} -const NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc"); -function resolveDynamicComponent(component) { - if (isString(component)) { - return resolveAsset(COMPONENTS, component, false) || component; - } else { - return component || NULL_DYNAMIC_COMPONENT; - } -} -function resolveDirective(name) { - return resolveAsset(DIRECTIVES, name); -} -function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { - const instance = currentRenderingInstance || currentInstance; - if (instance) { - const Component = instance.type; - if (type === COMPONENTS) { - const selfName = getComponentName( - Component, - false - ); - if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) { - return Component; - } - } - const res = ( - // local registration - // check instance[type] first which is resolved for options API - resolve(instance[type] || Component[type], name) || // global registration - resolve(instance.appContext[type], name) - ); - if (!res && maybeSelfReference) { - return Component; - } - if (warnMissing && !res) { - const extra = type === COMPONENTS ? ` -If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``; - warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`); - } - return res; - } else { - warn$1( - `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().` - ); - } -} -function resolve(registry, name) { - return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]); -} - -const isSuspense = (type) => type.__isSuspense; -let suspenseId = 0; -const SuspenseImpl = { - name: "Suspense", - // In order to make Suspense tree-shakable, we need to avoid importing it - // directly in the renderer. The renderer checks for the __isSuspense flag - // on a vnode's type and calls the `process` method, passing in renderer - // internals. - __isSuspense: true, - process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) { - if (n1 == null) { - mountSuspense( - n2, - container, - anchor, - parentComponent, - parentSuspense, - namespace, - slotScopeIds, - optimized, - rendererInternals - ); - } else { - if (parentSuspense && parentSuspense.deps > 0) { - n2.suspense = n1.suspense; - return; - } - patchSuspense( - n1, - n2, - container, - anchor, - parentComponent, - namespace, - slotScopeIds, - optimized, - rendererInternals - ); - } - }, - hydrate: hydrateSuspense, - create: createSuspenseBoundary, - normalize: normalizeSuspenseChildren -}; -const Suspense = SuspenseImpl ; -function triggerEvent(vnode, name) { - const eventListener = vnode.props && vnode.props[name]; - if (isFunction(eventListener)) { - eventListener(); - } -} -function mountSuspense(vnode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) { - const { - p: patch, - o: { createElement } - } = rendererInternals; - const hiddenContainer = createElement("div"); - const suspense = vnode.suspense = createSuspenseBoundary( - vnode, - parentSuspense, - parentComponent, - container, - hiddenContainer, - anchor, - namespace, - slotScopeIds, - optimized, - rendererInternals - ); - patch( - null, - suspense.pendingBranch = vnode.ssContent, - hiddenContainer, - null, - parentComponent, - suspense, - namespace, - slotScopeIds - ); - if (suspense.deps > 0) { - triggerEvent(vnode, "onPending"); - triggerEvent(vnode, "onFallback"); - patch( - null, - vnode.ssFallback, - container, - anchor, - parentComponent, - null, - // fallback tree will not have suspense context - namespace, - slotScopeIds - ); - setActiveBranch(suspense, vnode.ssFallback); - } else { - suspense.resolve(false, true); - } -} -function patchSuspense(n1, n2, container, anchor, parentComponent, namespace, slotScopeIds, optimized, { p: patch, um: unmount, o: { createElement } }) { - const suspense = n2.suspense = n1.suspense; - suspense.vnode = n2; - n2.el = n1.el; - const newBranch = n2.ssContent; - const newFallback = n2.ssFallback; - const { activeBranch, pendingBranch, isInFallback, isHydrating } = suspense; - if (pendingBranch) { - suspense.pendingBranch = newBranch; - if (isSameVNodeType(newBranch, pendingBranch)) { - patch( - pendingBranch, - newBranch, - suspense.hiddenContainer, - null, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - if (suspense.deps <= 0) { - suspense.resolve(); - } else if (isInFallback) { - if (!isHydrating) { - patch( - activeBranch, - newFallback, - container, - anchor, - parentComponent, - null, - // fallback tree will not have suspense context - namespace, - slotScopeIds, - optimized - ); - setActiveBranch(suspense, newFallback); - } - } - } else { - suspense.pendingId = suspenseId++; - if (isHydrating) { - suspense.isHydrating = false; - suspense.activeBranch = pendingBranch; - } else { - unmount(pendingBranch, parentComponent, suspense); - } - suspense.deps = 0; - suspense.effects.length = 0; - suspense.hiddenContainer = createElement("div"); - if (isInFallback) { - patch( - null, - newBranch, - suspense.hiddenContainer, - null, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - if (suspense.deps <= 0) { - suspense.resolve(); - } else { - patch( - activeBranch, - newFallback, - container, - anchor, - parentComponent, - null, - // fallback tree will not have suspense context - namespace, - slotScopeIds, - optimized - ); - setActiveBranch(suspense, newFallback); - } - } else if (activeBranch && isSameVNodeType(newBranch, activeBranch)) { - patch( - activeBranch, - newBranch, - container, - anchor, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - suspense.resolve(true); - } else { - patch( - null, - newBranch, - suspense.hiddenContainer, - null, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - if (suspense.deps <= 0) { - suspense.resolve(); - } - } - } - } else { - if (activeBranch && isSameVNodeType(newBranch, activeBranch)) { - patch( - activeBranch, - newBranch, - container, - anchor, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - setActiveBranch(suspense, newBranch); - } else { - triggerEvent(n2, "onPending"); - suspense.pendingBranch = newBranch; - if (newBranch.shapeFlag & 512) { - suspense.pendingId = newBranch.component.suspenseId; - } else { - suspense.pendingId = suspenseId++; - } - patch( - null, - newBranch, - suspense.hiddenContainer, - null, - parentComponent, - suspense, - namespace, - slotScopeIds, - optimized - ); - if (suspense.deps <= 0) { - suspense.resolve(); - } else { - const { timeout, pendingId } = suspense; - if (timeout > 0) { - setTimeout(() => { - if (suspense.pendingId === pendingId) { - suspense.fallback(newFallback); - } - }, timeout); - } else if (timeout === 0) { - suspense.fallback(newFallback); - } - } - } - } -} -let hasWarned = false; -function createSuspenseBoundary(vnode, parentSuspense, parentComponent, container, hiddenContainer, anchor, namespace, slotScopeIds, optimized, rendererInternals, isHydrating = false) { - if (!hasWarned) { - hasWarned = true; - console[console.info ? "info" : "log"]( - ` is an experimental feature and its API will likely change.` - ); - } - const { - p: patch, - m: move, - um: unmount, - n: next, - o: { parentNode, remove } - } = rendererInternals; - let parentSuspenseId; - const isSuspensible = isVNodeSuspensible(vnode); - if (isSuspensible) { - if (parentSuspense == null ? void 0 : parentSuspense.pendingBranch) { - parentSuspenseId = parentSuspense.pendingId; - parentSuspense.deps++; - } - } - const timeout = vnode.props ? toNumber(vnode.props.timeout) : void 0; - { - assertNumber(timeout, `Suspense timeout`); - } - const initialAnchor = anchor; - const suspense = { - vnode, - parent: parentSuspense, - parentComponent, - namespace, - container, - hiddenContainer, - deps: 0, - pendingId: suspenseId++, - timeout: typeof timeout === "number" ? timeout : -1, - activeBranch: null, - pendingBranch: null, - isInFallback: !isHydrating, - isHydrating, - isUnmounted: false, - effects: [], - resolve(resume = false, sync = false) { - { - if (!resume && !suspense.pendingBranch) { - throw new Error( - `suspense.resolve() is called without a pending branch.` - ); - } - if (suspense.isUnmounted) { - throw new Error( - `suspense.resolve() is called on an already unmounted suspense boundary.` - ); - } - } - const { - vnode: vnode2, - activeBranch, - pendingBranch, - pendingId, - effects, - parentComponent: parentComponent2, - container: container2 - } = suspense; - let delayEnter = false; - if (suspense.isHydrating) { - suspense.isHydrating = false; - } else if (!resume) { - delayEnter = activeBranch && pendingBranch.transition && pendingBranch.transition.mode === "out-in"; - if (delayEnter) { - activeBranch.transition.afterLeave = () => { - if (pendingId === suspense.pendingId) { - move( - pendingBranch, - container2, - anchor === initialAnchor ? next(activeBranch) : anchor, - 0 - ); - queuePostFlushCb(effects); - } - }; - } - if (activeBranch) { - if (parentNode(activeBranch.el) !== suspense.hiddenContainer) { - anchor = next(activeBranch); - } - unmount(activeBranch, parentComponent2, suspense, true); - } - if (!delayEnter) { - move(pendingBranch, container2, anchor, 0); - } - } - setActiveBranch(suspense, pendingBranch); - suspense.pendingBranch = null; - suspense.isInFallback = false; - let parent = suspense.parent; - let hasUnresolvedAncestor = false; - while (parent) { - if (parent.pendingBranch) { - parent.effects.push(...effects); - hasUnresolvedAncestor = true; - break; - } - parent = parent.parent; - } - if (!hasUnresolvedAncestor && !delayEnter) { - queuePostFlushCb(effects); - } - suspense.effects = []; - if (isSuspensible) { - if (parentSuspense && parentSuspense.pendingBranch && parentSuspenseId === parentSuspense.pendingId) { - parentSuspense.deps--; - if (parentSuspense.deps === 0 && !sync) { - parentSuspense.resolve(); - } - } - } - triggerEvent(vnode2, "onResolve"); - }, - fallback(fallbackVNode) { - if (!suspense.pendingBranch) { - return; - } - const { vnode: vnode2, activeBranch, parentComponent: parentComponent2, container: container2, namespace: namespace2 } = suspense; - triggerEvent(vnode2, "onFallback"); - const anchor2 = next(activeBranch); - const mountFallback = () => { - if (!suspense.isInFallback) { - return; - } - patch( - null, - fallbackVNode, - container2, - anchor2, - parentComponent2, - null, - // fallback tree will not have suspense context - namespace2, - slotScopeIds, - optimized - ); - setActiveBranch(suspense, fallbackVNode); - }; - const delayEnter = fallbackVNode.transition && fallbackVNode.transition.mode === "out-in"; - if (delayEnter) { - activeBranch.transition.afterLeave = mountFallback; - } - suspense.isInFallback = true; - unmount( - activeBranch, - parentComponent2, - null, - // no suspense so unmount hooks fire now - true - // shouldRemove - ); - if (!delayEnter) { - mountFallback(); - } - }, - move(container2, anchor2, type) { - suspense.activeBranch && move(suspense.activeBranch, container2, anchor2, type); - suspense.container = container2; - }, - next() { - return suspense.activeBranch && next(suspense.activeBranch); - }, - registerDep(instance, setupRenderEffect) { - const isInPendingSuspense = !!suspense.pendingBranch; - if (isInPendingSuspense) { - suspense.deps++; - } - const hydratedEl = instance.vnode.el; - instance.asyncDep.catch((err) => { - handleError(err, instance, 0); - }).then((asyncSetupResult) => { - if (instance.isUnmounted || suspense.isUnmounted || suspense.pendingId !== instance.suspenseId) { - return; - } - instance.asyncResolved = true; - const { vnode: vnode2 } = instance; - { - pushWarningContext(vnode2); - } - handleSetupResult(instance, asyncSetupResult, false); - if (hydratedEl) { - vnode2.el = hydratedEl; - } - const placeholder = !hydratedEl && instance.subTree.el; - setupRenderEffect( - instance, - vnode2, - // component may have been moved before resolve. - // if this is not a hydration, instance.subTree will be the comment - // placeholder. - parentNode(hydratedEl || instance.subTree.el), - // anchor will not be used if this is hydration, so only need to - // consider the comment placeholder case. - hydratedEl ? null : next(instance.subTree), - suspense, - namespace, - optimized - ); - if (placeholder) { - remove(placeholder); - } - updateHOCHostEl(instance, vnode2.el); - { - popWarningContext(); - } - if (isInPendingSuspense && --suspense.deps === 0) { - suspense.resolve(); - } - }); - }, - unmount(parentSuspense2, doRemove) { - suspense.isUnmounted = true; - if (suspense.activeBranch) { - unmount( - suspense.activeBranch, - parentComponent, - parentSuspense2, - doRemove - ); - } - if (suspense.pendingBranch) { - unmount( - suspense.pendingBranch, - parentComponent, - parentSuspense2, - doRemove - ); - } - } - }; - return suspense; -} -function hydrateSuspense(node, vnode, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals, hydrateNode) { - const suspense = vnode.suspense = createSuspenseBoundary( - vnode, - parentSuspense, - parentComponent, - node.parentNode, - // eslint-disable-next-line no-restricted-globals - document.createElement("div"), - null, - namespace, - slotScopeIds, - optimized, - rendererInternals, - true - ); - const result = hydrateNode( - node, - suspense.pendingBranch = vnode.ssContent, - parentComponent, - suspense, - slotScopeIds, - optimized - ); - if (suspense.deps === 0) { - suspense.resolve(false, true); - } - return result; -} -function normalizeSuspenseChildren(vnode) { - const { shapeFlag, children } = vnode; - const isSlotChildren = shapeFlag & 32; - vnode.ssContent = normalizeSuspenseSlot( - isSlotChildren ? children.default : children - ); - vnode.ssFallback = isSlotChildren ? normalizeSuspenseSlot(children.fallback) : createVNode(Comment); -} -function normalizeSuspenseSlot(s) { - let block; - if (isFunction(s)) { - const trackBlock = isBlockTreeEnabled && s._c; - if (trackBlock) { - s._d = false; - openBlock(); - } - s = s(); - if (trackBlock) { - s._d = true; - block = currentBlock; - closeBlock(); - } - } - if (isArray(s)) { - const singleChild = filterSingleRoot(s); - if (!singleChild && s.filter((child) => child !== NULL_DYNAMIC_COMPONENT).length > 0) { - warn$1(` slots expect a single root node.`); - } - s = singleChild; - } - s = normalizeVNode(s); - if (block && !s.dynamicChildren) { - s.dynamicChildren = block.filter((c) => c !== s); - } - return s; -} -function queueEffectWithSuspense(fn, suspense) { - if (suspense && suspense.pendingBranch) { - if (isArray(fn)) { - suspense.effects.push(...fn); - } else { - suspense.effects.push(fn); - } - } else { - queuePostFlushCb(fn); - } -} -function setActiveBranch(suspense, branch) { - suspense.activeBranch = branch; - const { vnode, parentComponent } = suspense; - let el = branch.el; - while (!el && branch.component) { - branch = branch.component.subTree; - el = branch.el; - } - vnode.el = el; - if (parentComponent && parentComponent.subTree === vnode) { - parentComponent.vnode.el = el; - updateHOCHostEl(parentComponent, el); - } -} -function isVNodeSuspensible(vnode) { - var _a; - return ((_a = vnode.props) == null ? void 0 : _a.suspensible) != null && vnode.props.suspensible !== false; -} - -const ssrContextKey = Symbol.for("v-scx"); -const useSSRContext = () => { - { - const ctx = inject(ssrContextKey); - if (!ctx) { - warn$1( - `Server rendering context not provided. Make sure to only call useSSRContext() conditionally in the server build.` - ); - } - return ctx; - } -}; - -function watchEffect(effect, options) { - return doWatch(effect, null, options); -} -function watchPostEffect(effect, options) { - return doWatch( - effect, - null, - extend({}, options, { flush: "post" }) - ); -} -function watchSyncEffect(effect, options) { - return doWatch( - effect, - null, - extend({}, options, { flush: "sync" }) - ); -} -const INITIAL_WATCHER_VALUE = {}; -function watch(source, cb, options) { - if (!isFunction(cb)) { - warn$1( - `\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.` - ); - } - return doWatch(source, cb, options); -} -function doWatch(source, cb, { - immediate, - deep, - flush, - once, - onTrack, - onTrigger -} = EMPTY_OBJ) { - if (cb && once) { - const _cb = cb; - cb = (...args) => { - _cb(...args); - unwatch(); - }; - } - if (deep !== void 0 && typeof deep === "number") { - warn$1( - `watch() "deep" option with number value will be used as watch depth in future versions. Please use a boolean instead to avoid potential breakage.` - ); - } - if (!cb) { - if (immediate !== void 0) { - warn$1( - `watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (deep !== void 0) { - warn$1( - `watch() "deep" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - if (once !== void 0) { - warn$1( - `watch() "once" option is only respected when using the watch(source, callback, options?) signature.` - ); - } - } - const warnInvalidSource = (s) => { - warn$1( - `Invalid watch source: `, - s, - `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.` - ); - }; - const instance = currentInstance; - const reactiveGetter = (source2) => deep === true ? source2 : ( - // for deep: false, only traverse root-level properties - traverse(source2, deep === false ? 1 : void 0) - ); - let getter; - let forceTrigger = false; - let isMultiSource = false; - if (isRef(source)) { - getter = () => source.value; - forceTrigger = isShallow(source); - } else if (isReactive(source)) { - getter = () => reactiveGetter(source); - forceTrigger = true; - } else if (isArray(source)) { - isMultiSource = true; - forceTrigger = source.some((s) => isReactive(s) || isShallow(s)); - getter = () => source.map((s) => { - if (isRef(s)) { - return s.value; - } else if (isReactive(s)) { - return reactiveGetter(s); - } else if (isFunction(s)) { - return callWithErrorHandling(s, instance, 2); - } else { - warnInvalidSource(s); - } - }); - } else if (isFunction(source)) { - if (cb) { - getter = () => callWithErrorHandling(source, instance, 2); - } else { - getter = () => { - if (cleanup) { - cleanup(); - } - return callWithAsyncErrorHandling( - source, - instance, - 3, - [onCleanup] - ); - }; - } - } else { - getter = NOOP; - warnInvalidSource(source); - } - if (cb && deep) { - const baseGetter = getter; - getter = () => traverse(baseGetter()); - } - let cleanup; - let onCleanup = (fn) => { - cleanup = effect.onStop = () => { - callWithErrorHandling(fn, instance, 4); - cleanup = effect.onStop = void 0; - }; - }; - let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE; - const job = () => { - if (!effect.active || !effect.dirty) { - return; - } - if (cb) { - const newValue = effect.run(); - if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue)) || false) { - if (cleanup) { - cleanup(); - } - callWithAsyncErrorHandling(cb, instance, 3, [ - newValue, - // pass undefined as the old value when it's changed for the first time - oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, - onCleanup - ]); - oldValue = newValue; - } - } else { - effect.run(); - } - }; - job.allowRecurse = !!cb; - let scheduler; - if (flush === "sync") { - scheduler = job; - } else if (flush === "post") { - scheduler = () => queuePostRenderEffect(job, instance && instance.suspense); - } else { - job.pre = true; - if (instance) - job.id = instance.uid; - scheduler = () => queueJob(job); - } - const effect = new ReactiveEffect(getter, NOOP, scheduler); - const scope = getCurrentScope(); - const unwatch = () => { - effect.stop(); - if (scope) { - remove(scope.effects, effect); - } - }; - { - effect.onTrack = onTrack; - effect.onTrigger = onTrigger; - } - if (cb) { - if (immediate) { - job(); - } else { - oldValue = effect.run(); - } - } else if (flush === "post") { - queuePostRenderEffect( - effect.run.bind(effect), - instance && instance.suspense - ); - } else { - effect.run(); - } - return unwatch; -} -function instanceWatch(source, value, options) { - const publicThis = this.proxy; - const getter = isString(source) ? source.includes(".") ? createPathGetter(publicThis, source) : () => publicThis[source] : source.bind(publicThis, publicThis); - let cb; - if (isFunction(value)) { - cb = value; - } else { - cb = value.handler; - options = value; - } - const reset = setCurrentInstance(this); - const res = doWatch(getter, cb.bind(publicThis), options); - reset(); - return res; -} -function createPathGetter(ctx, path) { - const segments = path.split("."); - return () => { - let cur = ctx; - for (let i = 0; i < segments.length && cur; i++) { - cur = cur[segments[i]]; - } - return cur; - }; -} -function traverse(value, depth, currentDepth = 0, seen) { - if (!isObject(value) || value["__v_skip"]) { - return value; - } - if (depth && depth > 0) { - if (currentDepth >= depth) { - return value; - } - currentDepth++; - } - seen = seen || /* @__PURE__ */ new Set(); - if (seen.has(value)) { - return value; - } - seen.add(value); - if (isRef(value)) { - traverse(value.value, depth, currentDepth, seen); - } else if (isArray(value)) { - for (let i = 0; i < value.length; i++) { - traverse(value[i], depth, currentDepth, seen); - } - } else if (isSet(value) || isMap(value)) { - value.forEach((v) => { - traverse(v, depth, currentDepth, seen); - }); - } else if (isPlainObject(value)) { - for (const key in value) { - traverse(value[key], depth, currentDepth, seen); - } - } - return value; -} - -function validateDirectiveName(name) { - if (isBuiltInDirective(name)) { - warn$1("Do not use built-in directive ids as custom directive id: " + name); - } -} -function withDirectives(vnode, directives) { - if (currentRenderingInstance === null) { - warn$1(`withDirectives can only be used inside render functions.`); - return vnode; - } - const instance = getExposeProxy(currentRenderingInstance) || currentRenderingInstance.proxy; - const bindings = vnode.dirs || (vnode.dirs = []); - for (let i = 0; i < directives.length; i++) { - let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]; - if (dir) { - if (isFunction(dir)) { - dir = { - mounted: dir, - updated: dir - }; - } - if (dir.deep) { - traverse(value); - } - bindings.push({ - dir, - instance, - value, - oldValue: void 0, - arg, - modifiers - }); - } - } - return vnode; -} -function invokeDirectiveHook(vnode, prevVNode, instance, name) { - const bindings = vnode.dirs; - const oldBindings = prevVNode && prevVNode.dirs; - for (let i = 0; i < bindings.length; i++) { - const binding = bindings[i]; - if (oldBindings) { - binding.oldValue = oldBindings[i].value; - } - let hook = binding.dir[name]; - if (hook) { - pauseTracking(); - callWithAsyncErrorHandling(hook, instance, 8, [ - vnode.el, - binding, - vnode, - prevVNode - ]); - resetTracking(); - } - } -} - -const leaveCbKey = Symbol("_leaveCb"); -const enterCbKey$1 = Symbol("_enterCb"); -function useTransitionState() { - const state = { - isMounted: false, - isLeaving: false, - isUnmounting: false, - leavingVNodes: /* @__PURE__ */ new Map() - }; - onMounted(() => { - state.isMounted = true; - }); - onBeforeUnmount(() => { - state.isUnmounting = true; - }); - return state; -} -const TransitionHookValidator = [Function, Array]; -const BaseTransitionPropsValidators = { - mode: String, - appear: Boolean, - persisted: Boolean, - // enter - onBeforeEnter: TransitionHookValidator, - onEnter: TransitionHookValidator, - onAfterEnter: TransitionHookValidator, - onEnterCancelled: TransitionHookValidator, - // leave - onBeforeLeave: TransitionHookValidator, - onLeave: TransitionHookValidator, - onAfterLeave: TransitionHookValidator, - onLeaveCancelled: TransitionHookValidator, - // appear - onBeforeAppear: TransitionHookValidator, - onAppear: TransitionHookValidator, - onAfterAppear: TransitionHookValidator, - onAppearCancelled: TransitionHookValidator -}; -const BaseTransitionImpl = { - name: `BaseTransition`, - props: BaseTransitionPropsValidators, - setup(props, { slots }) { - const instance = getCurrentInstance(); - const state = useTransitionState(); - let prevTransitionKey; - return () => { - const children = slots.default && getTransitionRawChildren(slots.default(), true); - if (!children || !children.length) { - return; - } - let child = children[0]; - if (children.length > 1) { - let hasFound = false; - for (const c of children) { - if (c.type !== Comment) { - if (hasFound) { - warn$1( - " can only be used on a single element or component. Use for lists." - ); - break; - } - child = c; - hasFound = true; - } - } - } - const rawProps = toRaw(props); - const { mode } = rawProps; - if (mode && mode !== "in-out" && mode !== "out-in" && mode !== "default") { - warn$1(`invalid mode: ${mode}`); - } - if (state.isLeaving) { - return emptyPlaceholder(child); - } - const innerChild = getKeepAliveChild(child); - if (!innerChild) { - return emptyPlaceholder(child); - } - const enterHooks = resolveTransitionHooks( - innerChild, - rawProps, - state, - instance - ); - setTransitionHooks(innerChild, enterHooks); - const oldChild = instance.subTree; - const oldInnerChild = oldChild && getKeepAliveChild(oldChild); - let transitionKeyChanged = false; - const { getTransitionKey } = innerChild.type; - if (getTransitionKey) { - const key = getTransitionKey(); - if (prevTransitionKey === void 0) { - prevTransitionKey = key; - } else if (key !== prevTransitionKey) { - prevTransitionKey = key; - transitionKeyChanged = true; - } - } - if (oldInnerChild && oldInnerChild.type !== Comment && (!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)) { - const leavingHooks = resolveTransitionHooks( - oldInnerChild, - rawProps, - state, - instance - ); - setTransitionHooks(oldInnerChild, leavingHooks); - if (mode === "out-in") { - state.isLeaving = true; - leavingHooks.afterLeave = () => { - state.isLeaving = false; - if (instance.update.active !== false) { - instance.effect.dirty = true; - instance.update(); - } - }; - return emptyPlaceholder(child); - } else if (mode === "in-out" && innerChild.type !== Comment) { - leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => { - const leavingVNodesCache = getLeavingNodesForType( - state, - oldInnerChild - ); - leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild; - el[leaveCbKey] = () => { - earlyRemove(); - el[leaveCbKey] = void 0; - delete enterHooks.delayedLeave; - }; - enterHooks.delayedLeave = delayedLeave; - }; - } - } - return child; - }; - } -}; -const BaseTransition = BaseTransitionImpl; -function getLeavingNodesForType(state, vnode) { - const { leavingVNodes } = state; - let leavingVNodesCache = leavingVNodes.get(vnode.type); - if (!leavingVNodesCache) { - leavingVNodesCache = /* @__PURE__ */ Object.create(null); - leavingVNodes.set(vnode.type, leavingVNodesCache); - } - return leavingVNodesCache; -} -function resolveTransitionHooks(vnode, props, state, instance) { - const { - appear, - mode, - persisted = false, - onBeforeEnter, - onEnter, - onAfterEnter, - onEnterCancelled, - onBeforeLeave, - onLeave, - onAfterLeave, - onLeaveCancelled, - onBeforeAppear, - onAppear, - onAfterAppear, - onAppearCancelled - } = props; - const key = String(vnode.key); - const leavingVNodesCache = getLeavingNodesForType(state, vnode); - const callHook = (hook, args) => { - hook && callWithAsyncErrorHandling( - hook, - instance, - 9, - args - ); - }; - const callAsyncHook = (hook, args) => { - const done = args[1]; - callHook(hook, args); - if (isArray(hook)) { - if (hook.every((hook2) => hook2.length <= 1)) - done(); - } else if (hook.length <= 1) { - done(); - } - }; - const hooks = { - mode, - persisted, - beforeEnter(el) { - let hook = onBeforeEnter; - if (!state.isMounted) { - if (appear) { - hook = onBeforeAppear || onBeforeEnter; - } else { - return; - } - } - if (el[leaveCbKey]) { - el[leaveCbKey]( - true - /* cancelled */ - ); - } - const leavingVNode = leavingVNodesCache[key]; - if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el[leaveCbKey]) { - leavingVNode.el[leaveCbKey](); - } - callHook(hook, [el]); - }, - enter(el) { - let hook = onEnter; - let afterHook = onAfterEnter; - let cancelHook = onEnterCancelled; - if (!state.isMounted) { - if (appear) { - hook = onAppear || onEnter; - afterHook = onAfterAppear || onAfterEnter; - cancelHook = onAppearCancelled || onEnterCancelled; - } else { - return; - } - } - let called = false; - const done = el[enterCbKey$1] = (cancelled) => { - if (called) - return; - called = true; - if (cancelled) { - callHook(cancelHook, [el]); - } else { - callHook(afterHook, [el]); - } - if (hooks.delayedLeave) { - hooks.delayedLeave(); - } - el[enterCbKey$1] = void 0; - }; - if (hook) { - callAsyncHook(hook, [el, done]); - } else { - done(); - } - }, - leave(el, remove) { - const key2 = String(vnode.key); - if (el[enterCbKey$1]) { - el[enterCbKey$1]( - true - /* cancelled */ - ); - } - if (state.isUnmounting) { - return remove(); - } - callHook(onBeforeLeave, [el]); - let called = false; - const done = el[leaveCbKey] = (cancelled) => { - if (called) - return; - called = true; - remove(); - if (cancelled) { - callHook(onLeaveCancelled, [el]); - } else { - callHook(onAfterLeave, [el]); - } - el[leaveCbKey] = void 0; - if (leavingVNodesCache[key2] === vnode) { - delete leavingVNodesCache[key2]; - } - }; - leavingVNodesCache[key2] = vnode; - if (onLeave) { - callAsyncHook(onLeave, [el, done]); - } else { - done(); - } - }, - clone(vnode2) { - return resolveTransitionHooks(vnode2, props, state, instance); - } - }; - return hooks; -} -function emptyPlaceholder(vnode) { - if (isKeepAlive(vnode)) { - vnode = cloneVNode(vnode); - vnode.children = null; - return vnode; - } -} -function getKeepAliveChild(vnode) { - return isKeepAlive(vnode) ? ( - // #7121 ensure get the child component subtree in case - // it's been replaced during HMR - vnode.component ? vnode.component.subTree : vnode.children ? vnode.children[0] : void 0 - ) : vnode; -} -function setTransitionHooks(vnode, hooks) { - if (vnode.shapeFlag & 6 && vnode.component) { - setTransitionHooks(vnode.component.subTree, hooks); - } else if (vnode.shapeFlag & 128) { - vnode.ssContent.transition = hooks.clone(vnode.ssContent); - vnode.ssFallback.transition = hooks.clone(vnode.ssFallback); - } else { - vnode.transition = hooks; - } -} -function getTransitionRawChildren(children, keepComment = false, parentKey) { - let ret = []; - let keyedFragmentCount = 0; - for (let i = 0; i < children.length; i++) { - let child = children[i]; - const key = parentKey == null ? child.key : String(parentKey) + String(child.key != null ? child.key : i); - if (child.type === Fragment) { - if (child.patchFlag & 128) - keyedFragmentCount++; - ret = ret.concat( - getTransitionRawChildren(child.children, keepComment, key) - ); - } else if (keepComment || child.type !== Comment) { - ret.push(key != null ? cloneVNode(child, { key }) : child); - } - } - if (keyedFragmentCount > 1) { - for (let i = 0; i < ret.length; i++) { - ret[i].patchFlag = -2; - } - } - return ret; -} - -/*! #__NO_SIDE_EFFECTS__ */ -// @__NO_SIDE_EFFECTS__ -function defineComponent(options, extraOptions) { - return isFunction(options) ? ( - // #8326: extend call and options.name access are considered side-effects - // by Rollup, so we have to wrap it in a pure-annotated IIFE. - /* @__PURE__ */ (() => extend({ name: options.name }, extraOptions, { setup: options }))() - ) : options; -} - -const isAsyncWrapper = (i) => !!i.type.__asyncLoader; -/*! #__NO_SIDE_EFFECTS__ */ -// @__NO_SIDE_EFFECTS__ -function defineAsyncComponent(source) { - if (isFunction(source)) { - source = { loader: source }; - } - const { - loader, - loadingComponent, - errorComponent, - delay = 200, - timeout, - // undefined = never times out - suspensible = true, - onError: userOnError - } = source; - let pendingRequest = null; - let resolvedComp; - let retries = 0; - const retry = () => { - retries++; - pendingRequest = null; - return load(); - }; - const load = () => { - let thisRequest; - return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => { - err = err instanceof Error ? err : new Error(String(err)); - if (userOnError) { - return new Promise((resolve, reject) => { - const userRetry = () => resolve(retry()); - const userFail = () => reject(err); - userOnError(err, userRetry, userFail, retries + 1); - }); - } else { - throw err; - } - }).then((comp) => { - if (thisRequest !== pendingRequest && pendingRequest) { - return pendingRequest; - } - if (!comp) { - warn$1( - `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.` - ); - } - if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) { - comp = comp.default; - } - if (comp && !isObject(comp) && !isFunction(comp)) { - throw new Error(`Invalid async component load result: ${comp}`); - } - resolvedComp = comp; - return comp; - })); - }; - return defineComponent({ - name: "AsyncComponentWrapper", - __asyncLoader: load, - get __asyncResolved() { - return resolvedComp; - }, - setup() { - const instance = currentInstance; - if (resolvedComp) { - return () => createInnerComp(resolvedComp, instance); - } - const onError = (err) => { - pendingRequest = null; - handleError( - err, - instance, - 13, - !errorComponent - ); - }; - if (suspensible && instance.suspense || false) { - return load().then((comp) => { - return () => createInnerComp(comp, instance); - }).catch((err) => { - onError(err); - return () => errorComponent ? createVNode(errorComponent, { - error: err - }) : null; - }); - } - const loaded = ref(false); - const error = ref(); - const delayed = ref(!!delay); - if (delay) { - setTimeout(() => { - delayed.value = false; - }, delay); - } - if (timeout != null) { - setTimeout(() => { - if (!loaded.value && !error.value) { - const err = new Error( - `Async component timed out after ${timeout}ms.` - ); - onError(err); - error.value = err; - } - }, timeout); - } - load().then(() => { - loaded.value = true; - if (instance.parent && isKeepAlive(instance.parent.vnode)) { - instance.parent.effect.dirty = true; - queueJob(instance.parent.update); - } - }).catch((err) => { - onError(err); - error.value = err; - }); - return () => { - if (loaded.value && resolvedComp) { - return createInnerComp(resolvedComp, instance); - } else if (error.value && errorComponent) { - return createVNode(errorComponent, { - error: error.value - }); - } else if (loadingComponent && !delayed.value) { - return createVNode(loadingComponent); - } - }; - } - }); -} -function createInnerComp(comp, parent) { - const { ref: ref2, props, children, ce } = parent.vnode; - const vnode = createVNode(comp, props, children); - vnode.ref = ref2; - vnode.ce = ce; - delete parent.vnode.ce; - return vnode; -} - -const isKeepAlive = (vnode) => vnode.type.__isKeepAlive; -const KeepAliveImpl = { - name: `KeepAlive`, - // Marker for special handling inside the renderer. We are not using a === - // check directly on KeepAlive in the renderer, because importing it directly - // would prevent it from being tree-shaken. - __isKeepAlive: true, - props: { - include: [String, RegExp, Array], - exclude: [String, RegExp, Array], - max: [String, Number] - }, - setup(props, { slots }) { - const instance = getCurrentInstance(); - const sharedContext = instance.ctx; - const cache = /* @__PURE__ */ new Map(); - const keys = /* @__PURE__ */ new Set(); - let current = null; - { - instance.__v_cache = cache; - } - const parentSuspense = instance.suspense; - const { - renderer: { - p: patch, - m: move, - um: _unmount, - o: { createElement } - } - } = sharedContext; - const storageContainer = createElement("div"); - sharedContext.activate = (vnode, container, anchor, namespace, optimized) => { - const instance2 = vnode.component; - move(vnode, container, anchor, 0, parentSuspense); - patch( - instance2.vnode, - vnode, - container, - anchor, - instance2, - parentSuspense, - namespace, - vnode.slotScopeIds, - optimized - ); - queuePostRenderEffect(() => { - instance2.isDeactivated = false; - if (instance2.a) { - invokeArrayFns(instance2.a); - } - const vnodeHook = vnode.props && vnode.props.onVnodeMounted; - if (vnodeHook) { - invokeVNodeHook(vnodeHook, instance2.parent, vnode); - } - }, parentSuspense); - { - devtoolsComponentAdded(instance2); - } - }; - sharedContext.deactivate = (vnode) => { - const instance2 = vnode.component; - move(vnode, storageContainer, null, 1, parentSuspense); - queuePostRenderEffect(() => { - if (instance2.da) { - invokeArrayFns(instance2.da); - } - const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted; - if (vnodeHook) { - invokeVNodeHook(vnodeHook, instance2.parent, vnode); - } - instance2.isDeactivated = true; - }, parentSuspense); - { - devtoolsComponentAdded(instance2); - } - }; - function unmount(vnode) { - resetShapeFlag(vnode); - _unmount(vnode, instance, parentSuspense, true); - } - function pruneCache(filter) { - cache.forEach((vnode, key) => { - const name = getComponentName(vnode.type); - if (name && (!filter || !filter(name))) { - pruneCacheEntry(key); - } - }); - } - function pruneCacheEntry(key) { - const cached = cache.get(key); - if (!current || !isSameVNodeType(cached, current)) { - unmount(cached); - } else if (current) { - resetShapeFlag(current); - } - cache.delete(key); - keys.delete(key); - } - watch( - () => [props.include, props.exclude], - ([include, exclude]) => { - include && pruneCache((name) => matches(include, name)); - exclude && pruneCache((name) => !matches(exclude, name)); - }, - // prune post-render after `current` has been updated - { flush: "post", deep: true } - ); - let pendingCacheKey = null; - const cacheSubtree = () => { - if (pendingCacheKey != null) { - cache.set(pendingCacheKey, getInnerChild(instance.subTree)); - } - }; - onMounted(cacheSubtree); - onUpdated(cacheSubtree); - onBeforeUnmount(() => { - cache.forEach((cached) => { - const { subTree, suspense } = instance; - const vnode = getInnerChild(subTree); - if (cached.type === vnode.type && cached.key === vnode.key) { - resetShapeFlag(vnode); - const da = vnode.component.da; - da && queuePostRenderEffect(da, suspense); - return; - } - unmount(cached); - }); - }); - return () => { - pendingCacheKey = null; - if (!slots.default) { - return null; - } - const children = slots.default(); - const rawVNode = children[0]; - if (children.length > 1) { - { - warn$1(`KeepAlive should contain exactly one component child.`); - } - current = null; - return children; - } else if (!isVNode(rawVNode) || !(rawVNode.shapeFlag & 4) && !(rawVNode.shapeFlag & 128)) { - current = null; - return rawVNode; - } - let vnode = getInnerChild(rawVNode); - const comp = vnode.type; - const name = getComponentName( - isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp - ); - const { include, exclude, max } = props; - if (include && (!name || !matches(include, name)) || exclude && name && matches(exclude, name)) { - current = vnode; - return rawVNode; - } - const key = vnode.key == null ? comp : vnode.key; - const cachedVNode = cache.get(key); - if (vnode.el) { - vnode = cloneVNode(vnode); - if (rawVNode.shapeFlag & 128) { - rawVNode.ssContent = vnode; - } - } - pendingCacheKey = key; - if (cachedVNode) { - vnode.el = cachedVNode.el; - vnode.component = cachedVNode.component; - if (vnode.transition) { - setTransitionHooks(vnode, vnode.transition); - } - vnode.shapeFlag |= 512; - keys.delete(key); - keys.add(key); - } else { - keys.add(key); - if (max && keys.size > parseInt(max, 10)) { - pruneCacheEntry(keys.values().next().value); - } - } - vnode.shapeFlag |= 256; - current = vnode; - return isSuspense(rawVNode.type) ? rawVNode : vnode; - }; - } -}; -const KeepAlive = KeepAliveImpl; -function matches(pattern, name) { - if (isArray(pattern)) { - return pattern.some((p) => matches(p, name)); - } else if (isString(pattern)) { - return pattern.split(",").includes(name); - } else if (isRegExp(pattern)) { - return pattern.test(name); - } - return false; -} -function onActivated(hook, target) { - registerKeepAliveHook(hook, "a", target); -} -function onDeactivated(hook, target) { - registerKeepAliveHook(hook, "da", target); -} -function registerKeepAliveHook(hook, type, target = currentInstance) { - const wrappedHook = hook.__wdc || (hook.__wdc = () => { - let current = target; - while (current) { - if (current.isDeactivated) { - return; - } - current = current.parent; - } - return hook(); - }); - injectHook(type, wrappedHook, target); - if (target) { - let current = target.parent; - while (current && current.parent) { - if (isKeepAlive(current.parent.vnode)) { - injectToKeepAliveRoot(wrappedHook, type, target, current); - } - current = current.parent; - } - } -} -function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) { - const injected = injectHook( - type, - hook, - keepAliveRoot, - true - /* prepend */ - ); - onUnmounted(() => { - remove(keepAliveRoot[type], injected); - }, target); -} -function resetShapeFlag(vnode) { - vnode.shapeFlag &= ~256; - vnode.shapeFlag &= ~512; -} -function getInnerChild(vnode) { - return vnode.shapeFlag & 128 ? vnode.ssContent : vnode; -} - -function injectHook(type, hook, target = currentInstance, prepend = false) { - if (target) { - const hooks = target[type] || (target[type] = []); - const wrappedHook = hook.__weh || (hook.__weh = (...args) => { - if (target.isUnmounted) { - return; - } - pauseTracking(); - const reset = setCurrentInstance(target); - const res = callWithAsyncErrorHandling(hook, target, type, args); - reset(); - resetTracking(); - return res; - }); - if (prepend) { - hooks.unshift(wrappedHook); - } else { - hooks.push(wrappedHook); - } - return wrappedHook; - } else { - const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, "")); - warn$1( - `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup().` + (` If you are using async setup(), make sure to register lifecycle hooks before the first await statement.` ) - ); - } -} -const createHook = (lifecycle) => (hook, target = currentInstance) => ( - // post-create lifecycle registrations are noops during SSR (except for serverPrefetch) - (!isInSSRComponentSetup || lifecycle === "sp") && injectHook(lifecycle, (...args) => hook(...args), target) -); -const onBeforeMount = createHook("bm"); -const onMounted = createHook("m"); -const onBeforeUpdate = createHook("bu"); -const onUpdated = createHook("u"); -const onBeforeUnmount = createHook("bum"); -const onUnmounted = createHook("um"); -const onServerPrefetch = createHook("sp"); -const onRenderTriggered = createHook( - "rtg" -); -const onRenderTracked = createHook( - "rtc" -); -function onErrorCaptured(hook, target = currentInstance) { - injectHook("ec", hook, target); -} - -function renderList(source, renderItem, cache, index) { - let ret; - const cached = cache && cache[index]; - if (isArray(source) || isString(source)) { - ret = new Array(source.length); - for (let i = 0, l = source.length; i < l; i++) { - ret[i] = renderItem(source[i], i, void 0, cached && cached[i]); - } - } else if (typeof source === "number") { - if (!Number.isInteger(source)) { - warn$1(`The v-for range expect an integer value but got ${source}.`); - } - ret = new Array(source); - for (let i = 0; i < source; i++) { - ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]); - } - } else if (isObject(source)) { - if (source[Symbol.iterator]) { - ret = Array.from( - source, - (item, i) => renderItem(item, i, void 0, cached && cached[i]) - ); - } else { - const keys = Object.keys(source); - ret = new Array(keys.length); - for (let i = 0, l = keys.length; i < l; i++) { - const key = keys[i]; - ret[i] = renderItem(source[key], key, i, cached && cached[i]); - } - } - } else { - ret = []; - } - if (cache) { - cache[index] = ret; - } - return ret; -} - -function createSlots(slots, dynamicSlots) { - for (let i = 0; i < dynamicSlots.length; i++) { - const slot = dynamicSlots[i]; - if (isArray(slot)) { - for (let j = 0; j < slot.length; j++) { - slots[slot[j].name] = slot[j].fn; - } - } else if (slot) { - slots[slot.name] = slot.key ? (...args) => { - const res = slot.fn(...args); - if (res) - res.key = slot.key; - return res; - } : slot.fn; - } - } - return slots; -} - -function renderSlot(slots, name, props = {}, fallback, noSlotted) { - if (currentRenderingInstance.isCE || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.isCE) { - if (name !== "default") - props.name = name; - return createVNode("slot", props, fallback && fallback()); - } - let slot = slots[name]; - if (slot && slot.length > 1) { - warn$1( - `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.` - ); - slot = () => []; - } - if (slot && slot._c) { - slot._d = false; - } - openBlock(); - const validSlotContent = slot && ensureValidVNode(slot(props)); - const rendered = createBlock( - Fragment, - { - key: props.key || // slot content array of a dynamic conditional slot may have a branch - // key attached in the `createSlots` helper, respect that - validSlotContent && validSlotContent.key || `_${name}` - }, - validSlotContent || (fallback ? fallback() : []), - validSlotContent && slots._ === 1 ? 64 : -2 - ); - if (!noSlotted && rendered.scopeId) { - rendered.slotScopeIds = [rendered.scopeId + "-s"]; - } - if (slot && slot._c) { - slot._d = true; - } - return rendered; -} -function ensureValidVNode(vnodes) { - return vnodes.some((child) => { - if (!isVNode(child)) - return true; - if (child.type === Comment) - return false; - if (child.type === Fragment && !ensureValidVNode(child.children)) - return false; - return true; - }) ? vnodes : null; -} - -function toHandlers(obj, preserveCaseIfNecessary) { - const ret = {}; - if (!isObject(obj)) { - warn$1(`v-on with no argument expects an object value.`); - return ret; - } - for (const key in obj) { - ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key]; - } - return ret; -} - -const getPublicInstance = (i) => { - if (!i) - return null; - if (isStatefulComponent(i)) - return getExposeProxy(i) || i.proxy; - return getPublicInstance(i.parent); -}; -const publicPropertiesMap = ( - // Move PURE marker to new line to workaround compiler discarding it - // due to type annotation - /* @__PURE__ */ extend(/* @__PURE__ */ Object.create(null), { - $: (i) => i, - $el: (i) => i.vnode.el, - $data: (i) => i.data, - $props: (i) => shallowReadonly(i.props) , - $attrs: (i) => shallowReadonly(i.attrs) , - $slots: (i) => shallowReadonly(i.slots) , - $refs: (i) => shallowReadonly(i.refs) , - $parent: (i) => getPublicInstance(i.parent), - $root: (i) => getPublicInstance(i.root), - $emit: (i) => i.emit, - $options: (i) => resolveMergedOptions(i) , - $forceUpdate: (i) => i.f || (i.f = () => { - i.effect.dirty = true; - queueJob(i.update); - }), - $nextTick: (i) => i.n || (i.n = nextTick.bind(i.proxy)), - $watch: (i) => instanceWatch.bind(i) - }) -); -const isReservedPrefix = (key) => key === "_" || key === "$"; -const hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key); -const PublicInstanceProxyHandlers = { - get({ _: instance }, key) { - const { ctx, setupState, data, props, accessCache, type, appContext } = instance; - if (key === "__isVue") { - return true; - } - let normalizedProps; - if (key[0] !== "$") { - const n = accessCache[key]; - if (n !== void 0) { - switch (n) { - case 1 /* SETUP */: - return setupState[key]; - case 2 /* DATA */: - return data[key]; - case 4 /* CONTEXT */: - return ctx[key]; - case 3 /* PROPS */: - return props[key]; - } - } else if (hasSetupBinding(setupState, key)) { - accessCache[key] = 1 /* SETUP */; - return setupState[key]; - } else if (data !== EMPTY_OBJ && hasOwn(data, key)) { - accessCache[key] = 2 /* DATA */; - return data[key]; - } else if ( - // only cache other properties when instance has declared (thus stable) - // props - (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key) - ) { - accessCache[key] = 3 /* PROPS */; - return props[key]; - } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { - accessCache[key] = 4 /* CONTEXT */; - return ctx[key]; - } else if (shouldCacheAccess) { - accessCache[key] = 0 /* OTHER */; - } - } - const publicGetter = publicPropertiesMap[key]; - let cssModule, globalProperties; - if (publicGetter) { - if (key === "$attrs") { - track(instance, "get", key); - markAttrsAccessed(); - } else if (key === "$slots") { - track(instance, "get", key); - } - return publicGetter(instance); - } else if ( - // css module (injected by vue-loader) - (cssModule = type.__cssModules) && (cssModule = cssModule[key]) - ) { - return cssModule; - } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { - accessCache[key] = 4 /* CONTEXT */; - return ctx[key]; - } else if ( - // global properties - globalProperties = appContext.config.globalProperties, hasOwn(globalProperties, key) - ) { - { - return globalProperties[key]; - } - } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading - // to infinite warning loop - key.indexOf("__v") !== 0)) { - if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) { - warn$1( - `Property ${JSON.stringify( - key - )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.` - ); - } else if (instance === currentRenderingInstance) { - warn$1( - `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.` - ); - } - } - }, - set({ _: instance }, key, value) { - const { data, setupState, ctx } = instance; - if (hasSetupBinding(setupState, key)) { - setupState[key] = value; - return true; - } else if (setupState.__isScriptSetup && hasOwn(setupState, key)) { - warn$1(`Cannot mutate - - - - - - - - - - - - - - - {{navigation}} - -
-
- {{#side_navigation}} - {{side_navigation}} - -
- {{/side_navigation}} - {{^side_navigation}}
{{/side_navigation}} -
- -
- - {{content}} -
- -
-
- - - - - - - diff --git a/hal-core/resources/web/main_nav.tmpl b/hal-core/resources/web/main_nav.tmpl deleted file mode 100644 index a62fb405..00000000 --- a/hal-core/resources/web/main_nav.tmpl +++ /dev/null @@ -1,65 +0,0 @@ - \ No newline at end of file diff --git a/hal-core/resources/web/main_nav_side.tmpl b/hal-core/resources/web/main_nav_side.tmpl deleted file mode 100644 index 346a658b..00000000 --- a/hal-core/resources/web/main_nav_side.tmpl +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/hal-core/resources/web/map.tmpl b/hal-core/resources/web/map.tmpl deleted file mode 100644 index c38ae6a4..00000000 --- a/hal-core/resources/web/map.tmpl +++ /dev/null @@ -1,82 +0,0 @@ - - -
- -
- -
- - - -
-
- -
- - -
-
-
- -
-
- Icons downloaded from icons8.com -
-
- - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hal-core/resources/web/plugin_config.tmpl b/hal-core/resources/web/plugin_config.tmpl deleted file mode 100644 index e2f5375a..00000000 --- a/hal-core/resources/web/plugin_config.tmpl +++ /dev/null @@ -1,111 +0,0 @@ -

Plugins

- -
-
-
Plugin Administration
-
- - - - - - - - - {{#plugins}} - - - - - - - {{/plugins}} -
NameVersionDescriptionEnable
{{.getName()}}{{.getVersion()}}{{#.getDescription()}}{{.getDescription()}}{{/.getDescription()}} -
- - - -
- -
-
-
-
-
-
- -
-
-
Controllers
-
- - - - - - - - - {{#controllers}} - - - - - - - {{/controllers}} -
NameStatusRegistered DevicesActions
{{.getClass().getName()}} - {{#.isAvailable()}}Running{{/.isAvailable()}} - {{^.isAvailable()}}Down{{/.isAvailable()}} - {{.size()}} - {{#.isScannable()}} -
-
- - - - {{#.isScanning()}} - - {{/.isScanning()}} - {{^.isScanning()}} - - {{/.isScanning()}} -
- {{/.isScannable()}} -
-
-
-
-
- -
-
-
Daemons
-
- - - - - - {{#daemons}} - - - - {{/daemons}} -
Name
{{.getClass().getName()}}
-
-
-
- - \ No newline at end of file diff --git a/hal-core/resources/web/property_config.tmpl b/hal-core/resources/web/property_config.tmpl deleted file mode 100644 index 74154ee2..00000000 --- a/hal-core/resources/web/property_config.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -

Hal Properties

- -
-
-
Registered properties
-
- - - - - - - - {{#properties}} - - - - - - {{/properties}} -
NameValueDescription
{{.getKey()}}:
-
-
-
\ No newline at end of file diff --git a/hal-core/resources/web/room_config.tmpl b/hal-core/resources/web/room_config.tmpl deleted file mode 100644 index 9afde729..00000000 --- a/hal-core/resources/web/room_config.tmpl +++ /dev/null @@ -1,86 +0,0 @@ -

Room Configuration

- -
-
-
Rooms
-
-

This is a list of all configured rooms.

- - - - - - - - {{#rooms}} - - - - - - {{/rooms}} -
#Name - -
{{.getId()}}{{.getName()}} -
- - -
- - - -
-
-
-
-
-
- - - - - - - diff --git a/hal-core/resources/web/sensor_detail.tmpl b/hal-core/resources/web/sensor_detail.tmpl deleted file mode 100644 index a7368ff8..00000000 --- a/hal-core/resources/web/sensor_detail.tmpl +++ /dev/null @@ -1,100 +0,0 @@ -

Details for {{sensor.getName()}}

- -
-
-
Last 24 hours
-
-
-
-
-
- - -
-
-
-
Configuration
-
- - - - - - - - - - - - - - - - - - - - - - - - {{#sensor.getDeviceConfigurator().getConfiguration()}} - - - - - {{/sensor.getDeviceConfigurator().getConfiguration()}} -
Sensor ID:{{sensor.getId()}}
Name:{{sensor.getName()}}
Type:{{sensor.getDeviceConfig().getClass().getSimpleName()}}
Public:{{sensor.isSynced()}}
Owner:{{sensor.getUser().getUsername()}}

{{.getNiceName()}}:{{.getString()}}
-
-
-
- -
-
-
History data
-
- - - - - - - {{#history}} - - - - - {{/history}} -
TimestampRaw Data
{{.timestamp}}{{.data}}
-
-
-
-
- -
-
-
-
7 days
-
-
-
-
-
- -
-
-
All History
-
-
-
-
-
-
- - \ No newline at end of file diff --git a/hal-core/resources/web/startup.tmpl b/hal-core/resources/web/startup.tmpl deleted file mode 100644 index 450ca1a2..00000000 --- a/hal-core/resources/web/startup.tmpl +++ /dev/null @@ -1,25 +0,0 @@ - - - - - HAL is initializing... - - - - - - - -
-
-
- - - HAL - - is initializing... -
-
-
- - \ No newline at end of file diff --git a/hal-core/resources/web/trigger.tmpl b/hal-core/resources/web/trigger.tmpl deleted file mode 100644 index b14aed79..00000000 --- a/hal-core/resources/web/trigger.tmpl +++ /dev/null @@ -1,317 +0,0 @@ -

- Triggers - -
- -
-

- - -{{#flows}} -
-
-
- {{.getName()}} -
- - -
- - - -
-
-
-
- - - - - - - - - - - - -
- Triggers - - - Actions - -
- - - {{#.getTriggers()}} - - {{/.getTriggers()}} -
-
-
- {{.getObjectClass()}} -
- - -
- - -
-
-
-
- - {{.}} -
-
-
- -
-
-
- -
- - - {{#.getActions()}} - - {{/.getActions()}} -
-
-
- {{.getObjectClass()}} -
- - -
- - -
-
-
-
- - {{.}} -
-
-
- -
-
-
-
-{{/flows}} - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hal-core/src/se/hal/EventControllerManager.java b/hal-core/src/se/hal/EventControllerManager.java deleted file mode 100644 index 64fde5ec..00000000 --- a/hal-core/src/se/hal/EventControllerManager.java +++ /dev/null @@ -1,195 +0,0 @@ -package se.hal; - -import se.hal.intf.*; -import se.hal.struct.Event; -import se.hal.util.HalDeviceUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.plugin.PluginManager; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This class manages all SensorController and EventController objects - */ -public class EventControllerManager extends HalAbstractControllerManager - implements HalDeviceReportListener { - - private static final Logger logger = LogUtil.getLogger(); - private static EventControllerManager instance; - - /** List of all registered events **/ - private List registeredEvents = Collections.synchronizedList(new ArrayList<>()); - /** List of auto-detected events **/ - private List detectedEvents = Collections.synchronizedList(new ArrayList<>()); - - - public void initialize(PluginManager pluginManager) { - super.initialize(pluginManager); - instance = this; - - // Read in existing devices - - try { - DBConnection db = HalContext.getDB(); - - logger.info("Reading in existing events."); - - for (Event event : Event.getLocalEvents(db)) { - register(event); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "Unable to read in existing events.", e); - } - } - - // ---------------------------------------------------- - // EVENTS - // ---------------------------------------------------- - - /** - * Register an Event instance on the manager. - * The manager will start to save reported data for the registered Event. - */ - @SuppressWarnings("unchecked") - @Override - public void register(Event event) { - if (event.getDeviceConfig() == null) { - logger.warning("Event config is null: " + event); - return; - } - if (!getAvailableDeviceConfigs().contains(event.getDeviceConfig().getClass())) { - logger.warning("Event data plugin not available: " + event.getDeviceConfig().getClass()); - return; - } - - logger.info("Registering new event(id: " + event.getId() + "): " + event.getDeviceConfig().getClass()); - Class controllerClass = (Class) event.getControllerClass(); - HalEventController controller = getControllerInstance(controllerClass); - - if (controller != null) - controller.register(event.getDeviceConfig()); - - registeredEvents.add(event); - detectedEvents.remove(HalDeviceUtil.findDevice(event.getDeviceConfig(), detectedEvents)); // Remove if this device was detected - } - - /** - * Deregister an Event from the manager. - * Data reported on the Event will no longer be saved but already saved data will not be modified. - * The Controller that owns the Event will be deallocated if it has no more registered devices. - */ - @SuppressWarnings("unchecked") - @Override - public void deregister(Event event){ - if (event.getDeviceConfig() == null) { - logger.warning("Event config is null: "+ event); - return; - } - - Class controllerClass = (Class) event.getControllerClass(); - HalEventController controller = (HalEventController) controllerMap.get(controllerClass); - if (controller != null) { - logger.info("Deregistering event(id: " + event.getId() + "): " + event.getDeviceConfig().getClass()); - controller.deregister(event.getDeviceConfig()); - registeredEvents.remove(event); - removeControllerIfEmpty(controller); - } else { - logger.warning("Controller not instantiated: "+ event.getControllerClass()); - } - } - - /** - * @return a List of Sensor instances that have been registered to this manager - */ - @Override - public List getRegisteredDevices(){ - return registeredEvents; - } - - /** - * @return a List of Event instances that have been reported but not registered on the manager - */ - @Override - public List getDetectedDevices(){ - return detectedEvents; - } - - /** - * Removes all auto detected events. - */ - @Override - public void clearDetectedDevices(){ - detectedEvents.clear(); - } - - /** - * Called by Controllers to report received Event data - */ - @Override - public void reportReceived(HalDeviceConfig eventConfig, HalDeviceData eventData) { - if (!(eventConfig instanceof HalEventConfig)) - return; - - try { - DBConnection db = HalContext.getDB(); - Event event = HalDeviceUtil.findDevice(eventConfig, registeredEvents); - - if (event != null) { - if (eventData != null) { - logger.finest("Received report from event(" + eventConfig.getClass().getSimpleName() + "): " + eventConfig); - PreparedStatement stmt = - db.getPreparedStatement("INSERT INTO event_data_raw (timestamp, event_id, data) VALUES(?, ?, ?)"); - stmt.setLong(1, eventData.getTimestamp()); - stmt.setLong(2, event.getId()); - stmt.setDouble(3, eventData.getData()); - DBConnection.exec(stmt); - } - } - else { // unknown sensor - logger.info("Received report from unregistered event" + - "(" + eventConfig.getClass().getSimpleName() + "): " + eventConfig); - event = HalDeviceUtil.findDevice(eventConfig, detectedEvents); - if (event == null) { - event = new Event(); - detectedEvents.add(event); - } - event.setDeviceConfig((HalEventConfig) eventConfig); - } - - event.setDeviceData((HalEventData) eventData); - - // call listeners - for (HalDeviceReportListener listener : event.getReportListeners()) - listener.reportReceived(event.getDeviceConfig(), eventData); - - } catch (SQLException e){ - logger.log(Level.WARNING, "Unable to store event report", e); - } - } - - @SuppressWarnings("unchecked") - public void send(Event event, HalEventData eventData){ - Class controllerClass = (Class) event.getControllerClass(); - HalEventController controller = getControllerInstance(controllerClass); - - if (controller != null) { - eventData.setTimestamp(System.currentTimeMillis()); // Set timestamp to now - controller.send(event.getDeviceConfig(), eventData); - reportReceived(event.getDeviceConfig(), eventData); // save action to db - } - else - logger.warning("No controller found for event id: "+ event.getId()); - } - - - public static EventControllerManager getInstance(){ - return instance; - } -} diff --git a/hal-core/src/se/hal/HalContext.java b/hal-core/src/se/hal/HalContext.java deleted file mode 100644 index c9f1184f..00000000 --- a/hal-core/src/se/hal/HalContext.java +++ /dev/null @@ -1,171 +0,0 @@ -package se.hal; - -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.db.handler.PropertiesSQLResult; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.ui.UserMessageManager; - -import java.io.File; -import java.io.FileReader; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - - -public class HalContext { - private static final Logger logger = LogUtil.getLogger(); - - // Constants - public static final String CONFIG_HTTP_PORT = "hal_core.http_port"; - public static final String CONFIG_HTTP_EXTERNAL_PORT = "hal_core.http_external_port"; - public static final String CONFIG_HTTP_EXTERNAL_DOMAIN = "hal_core.http_external_domain"; - public static final String CONFIG_HTTP_EXTERNAL_CERT = "hal_core.http_external_cert"; - public static final String CONFIG_DNS_LOCAL_DOMAIN = "hal_core.dns_local_domain"; - public static final String CONFIG_MAP_BACKGROUND_IMAGE = "hal_core.map_bgimage"; - - public static final String RESOURCE_ROOT; - static { - if (FileUtil.find("build/resources/") != null) // Development environment - RESOURCE_ROOT = "build/resources"; - else if (FileUtil.find("resources/") != null) // Release package environment - RESOURCE_ROOT = "resources"; - else - RESOURCE_ROOT = "."; - } - - public static final String RESOURCE_WEB_ROOT = HalContext.RESOURCE_ROOT + "/web"; - - private static final String CONF_FILE = "hal.conf"; - static final String DB_FILE = "hal.db"; - - // Variables - private static DBConnection db; // TODO: Should probably be a db pool as we have multiple threads accessing the DB - - private static HashMap registeredConf = new HashMap<>(); - private static Properties fileConf = new Properties(); - private static Properties dbConf = new Properties(); - - private static UserMessageManager messageManager = new UserMessageManager(); - - static { - // Set default values to get Hal up and running before DB is read in - fileConf.setProperty(CONFIG_HTTP_PORT, "" + 8080); - } - - - public static void initialize(){ - try { - // Read conf - if (FileUtil.find(CONF_FILE) != null) { - FileReader in = new FileReader(CONF_FILE); - fileConf.load(in); - in.close(); - } else { - logger.info("No hal.conf file found"); - } - - // Init DB - File dbFile = FileUtil.find(DB_FILE); - db = new DBConnection(DBConnection.DBMS.SQLite, DB_FILE); - - if (dbFile == null){ - logger.severe("Unable to find Hal DB: " + DB_FILE); - System.exit(1); - } else { - dbConf = db.exec("SELECT * FROM conf", new PropertiesSQLResult()); - } - } catch (Exception e){ - throw new RuntimeException(e); - } - } - - - - public static Map getProperties() { - HashMap map = new HashMap(); - map.putAll(registeredConf); - map.putAll(dbConf); - map.putAll(fileConf); - return map; - } - public static void registerProperty(String key) { - registeredConf.put(key, ""); - } - - public static boolean containsProperty(String key) { - return !ObjectUtil.isEmpty(getStringProperty(key)); - } - public static String getStringProperty(String key){ - return getStringProperty(key, null); - } - public static String getStringProperty(String key, String defaultValue){ - registerProperty(key); - - String value = null; - if (fileConf != null) - value = fileConf.getProperty(key); - if (value == null && dbConf != null) - value = dbConf.getProperty(key); - return value != null ? value : defaultValue; - } - - public static int getIntegerProperty(String key){ - return getIntegerProperty(key, 0); - } - public static int getIntegerProperty(String key, int defaultValue){ - String value = getStringProperty(key); - return value != null ? Integer.parseInt(getStringProperty(key)) : defaultValue; - } - - public static long getLongProperty(String key){ - return getLongProperty(key, 0); - } - public static long getLongProperty(String key, long defaultValue){ - String value = getStringProperty(key); - return value != null ? Long.parseLong(getStringProperty(key)) : defaultValue; - } - - public static boolean getBooleanProperty(String key) { - return getBooleanProperty(key, false); - } - public static boolean getBooleanProperty(String key, boolean defaultValue) { - String value = getStringProperty(key); - return value != null ? Boolean.parseBoolean(getStringProperty(key)) : defaultValue; - } - - public static void setProperty(String key, String value) { - try { - PreparedStatement stmt = db.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)"); - stmt.setObject(1, key); - stmt.setObject(2, value); - DBConnection.exec(stmt); - dbConf.setProperty(key, value); - } catch (SQLException e) { - logger.log(Level.SEVERE, "Was unable to save property: " + key + " = " + value, e); - } - } - - - - public static DBConnection getDB(){ - return db; - } - - /** - * For testing purposes. - */ - public static void setDB(DBConnection db){ - HalContext.db = db; - } - - - public static UserMessageManager getUserMessageManager() { - return messageManager; - } -} diff --git a/hal-core/src/se/hal/HalCoreDatabaseUpgrader.java b/hal-core/src/se/hal/HalCoreDatabaseUpgrader.java deleted file mode 100644 index 5e08bbbf..00000000 --- a/hal-core/src/se/hal/HalCoreDatabaseUpgrader.java +++ /dev/null @@ -1,44 +0,0 @@ -package se.hal; - -import se.hal.intf.HalDatabaseUpgrader; -import zutil.db.DBConnection; -import zutil.log.LogUtil; - -import java.sql.SQLException; -import java.util.logging.Logger; - -/** - * The DB upgrade class for Hal-Core - */ -public class HalCoreDatabaseUpgrader extends HalDatabaseUpgrader { - private static final Logger logger = LogUtil.getLogger(); - - private static final int REFERENCE_DB_VERSION = 17; - private static final String REFERENCE_DB_PATH = HalContext.RESOURCE_ROOT + "/hal-core-reference.db"; - - private static final int CLEAR_INTERNAL_AGGR_DATA_DB_VERSION = 11; - private static final int CLEAR_EXTERNAL_AGGR_DATA_DB_VERSION = 0; - - - public HalCoreDatabaseUpgrader() { - super(REFERENCE_DB_VERSION, REFERENCE_DB_PATH); - } - - - @Override - public void postDatabaseUpgrade(DBConnection db, int fromDBVersion, int toDBVersion) throws SQLException { - if (fromDBVersion <= CLEAR_EXTERNAL_AGGR_DATA_DB_VERSION){ - logger.fine("Clearing external aggregate data."); - db.exec("DELETE FROM sensor_data_aggr WHERE sensor_id = " - + "(SELECT sensor.id FROM user, sensor WHERE user.external == 1 AND sensor.user_id = user.id)"); - } - if (fromDBVersion <= CLEAR_INTERNAL_AGGR_DATA_DB_VERSION){ - logger.fine("Clearing local aggregate data."); - db.exec("DELETE FROM sensor_data_aggr WHERE sensor_id IN " - + "(SELECT sensor.id FROM user, sensor WHERE user.external == 0 AND sensor.user_id = user.id)"); - // Update all internal sensors aggregation version to indicate for peers that they need to re-sync all data - db.exec("UPDATE sensor SET aggr_version = (aggr_version+1) WHERE id = " - + "(SELECT sensor.id FROM user, sensor WHERE user.external == 0 AND sensor.user_id = user.id)"); - } - } -} diff --git a/hal-core/src/se/hal/HalDatabaseUpgradeManager.java b/hal-core/src/se/hal/HalDatabaseUpgradeManager.java deleted file mode 100644 index e54149f7..00000000 --- a/hal-core/src/se/hal/HalDatabaseUpgradeManager.java +++ /dev/null @@ -1,174 +0,0 @@ -package se.hal; - -import se.hal.intf.HalDatabaseUpgrader; -import se.hal.struct.User; -import zutil.db.DBConnection; -import zutil.db.DBUpgradeHandler; -import zutil.db.handler.PropertiesSQLResult; -import zutil.db.handler.SimpleSQLResult; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.plugin.PluginManager; - -import java.io.File; -import java.sql.PreparedStatement; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Properties; -import java.util.Queue; -import java.util.logging.Logger; - -/** - * A manager class handling all upgrades for the main Hal DB and any plugin changes. - */ -public class HalDatabaseUpgradeManager { - private static final Logger logger = LogUtil.getLogger(); - - private static HalDatabaseUpgrader halCoreUpgrade; - private static Queue upgradeQueue; - - - /** - * Method will read in all HalDatabaseUpgrader plugins and populate the upgrade queue. - * Node, method will only read in plugins on the first call, any subsequent calls will be ignored. - * - * @param pluginManager - */ - public static void initialize(PluginManager pluginManager) { - if (upgradeQueue != null) - return; - - upgradeQueue = new LinkedList<>(); - - for (Iterator it = pluginManager.getSingletonIterator(HalDatabaseUpgrader.class); it.hasNext(); ) { - HalDatabaseUpgrader dbUpgrade = it.next(); - - if (dbUpgrade instanceof HalCoreDatabaseUpgrader) - halCoreUpgrade = dbUpgrade; - else - upgradeQueue.add(dbUpgrade); - } - } - - /** - * Method will execute all queued database upgrades. - */ - public static void upgrade() { - if (upgradeQueue == null) - return; - - // Prioritize upgrade of HalCore - if (halCoreUpgrade != null) { - upgrade(halCoreUpgrade); - halCoreUpgrade = null; - } - - while (!upgradeQueue.isEmpty()) { - upgrade(upgradeQueue.poll()); - } - } - - private synchronized static void upgrade(HalDatabaseUpgrader dbUpgrade) { - DBConnection mainDB = null; - DBConnection referenceDB = null; - - String referenceDBPath = dbUpgrade.getReferenceDBPath(); - - try { - if (FileUtil.find(referenceDBPath) == null) - throw new IllegalArgumentException("Unable to find default DB: " + referenceDBPath); - - // Init DB - File dbFile = FileUtil.find(HalContext.DB_FILE); - mainDB = new DBConnection(DBConnection.DBMS.SQLite, HalContext.DB_FILE); - Properties dbConf = new Properties(); - - // Read in conf table from DB if it exists - - String confTableExists = mainDB.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='conf';", new SimpleSQLResult<>()); - if ("conf".equals(confTableExists)) - dbConf = mainDB.exec("SELECT * FROM conf", new PropertiesSQLResult()); - - if (dbFile == null) { - logger.info("No database file found, creating new DB..."); - } - - // Evaluate if DB upgrade is needed? - - referenceDB = new DBConnection(DBConnection.DBMS.SQLite, referenceDBPath); - String mainDBVersionProperty = "db_version." + dbUpgrade.getClass().getSimpleName(); - - // Check DB version - final int referenceDBVersion = dbUpgrade.getReferenceDBVersion(); - final int mainDBVersion = (dbConf.getProperty(mainDBVersionProperty) != null ? - Integer.parseInt(dbConf.getProperty(mainDBVersionProperty)) : - -1); - logger.info("DB version: " + mainDBVersion + "(" + dbUpgrade.getClass().getSimpleName() + ")"); - - if (referenceDBVersion > mainDBVersion) { - // ---------------------------------------- - // Prepare upgrade - // ---------------------------------------- - - logger.info("Starting DB upgrade from " + (mainDBVersion < 0 ? "scratch" : "v" + mainDBVersion) + " to v" + referenceDBVersion + "..."); - - final DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB); - handler.setForcedDBUpgrade(false); - handler.addIgnoredTable("sqlite_sequence"); // sqlite internal - handler.setTargetDB(mainDB); - - logger.fine("Performing pre-upgrade activities"); - - if (dbFile != null){ - File backupDB = FileUtil.getNextFile(dbFile); - logger.fine("Backing up DB to: "+ backupDB); - FileUtil.copy(dbFile, backupDB); - } - - dbUpgrade.preDatabaseUpgrade(mainDB, mainDBVersion, referenceDBVersion); - - // ---------------------------------------- - // Upgrade - // ---------------------------------------- - - handler.upgrade(); - - // ---------------------------------------- - // Post-upgrade - // ---------------------------------------- - - logger.fine("Performing post-upgrade activities."); - - // Check if there is a local user - - User localUser = User.getLocalUser(mainDB); - if (localUser == null){ - logger.fine("Creating local user."); - localUser = new User(); - localUser.setExternal(false); - localUser.save(mainDB); - } - - dbUpgrade.postDatabaseUpgrade(mainDB, mainDBVersion, referenceDBVersion); - - // Update DB version - - PreparedStatement stmt = mainDB.getPreparedStatement("REPLACE INTO conf (key, value) VALUES (?, ?)"); - stmt.setString(1, mainDBVersionProperty); - stmt.setInt(2, referenceDBVersion); - DBConnection.exec(stmt); - - logger.info("DB upgrade done."); - } else { - logger.info("No DB upgrade needed"); - } - } catch (Exception e){ - throw new RuntimeException(e); - } finally { - if (mainDB != null) - mainDB.close(); - if (referenceDB != null) - referenceDB.close(); - } - } -} diff --git a/hal-core/src/se/hal/HalServer.java b/hal-core/src/se/hal/HalServer.java deleted file mode 100644 index 50aeb924..00000000 --- a/hal-core/src/se/hal/HalServer.java +++ /dev/null @@ -1,237 +0,0 @@ -package se.hal; - -import se.hal.daemon.HalExternalWebDaemon; -import se.hal.intf.*; -import se.hal.intf.HalJavascriptModule.HalJsModule; -import se.hal.page.EmptyWebPage; -import se.hal.page.StartupWebPage; -import se.hal.struct.PluginConfig; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpServer; -import zutil.net.http.page.HttpFilePage; -import zutil.net.http.page.HttpRedirectPage; -import zutil.plugin.PluginData; -import zutil.plugin.PluginManager; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * Main class for Hal - */ -public class HalServer { - private static final Logger logger = LogUtil.getLogger(); - - private static List controllerManagers = new ArrayList<>(); - - private static ScheduledExecutorService daemonExecutor; - private static List daemons = new ArrayList<>(); - - private static HttpServer http; - private static HalExternalWebDaemon httpExternal; - private static PluginManager pluginManager; - - - public static void main(String[] args) { - try { - // ------------------------------------ - // Initialize Hal - // ------------------------------------ - - // Init basic dependencies - - LogUtil.readConfiguration("logging.properties"); - HalContext.initialize(); - DBConnection db = HalContext.getDB(); - - pluginManager = new PluginManager(); - daemonExecutor = Executors.newScheduledThreadPool(1); // We set only one thread for easier troubleshooting for now - - // Init core web server - - http = new HttpServer(HalContext.getIntegerProperty(HalContext.CONFIG_HTTP_PORT)); - http.setDefaultPage(new StartupWebPage()); - http.start(); - - // Upgrade database - - logger.info("Working directory: " + FileUtil.find(".").getAbsolutePath()); - HalDatabaseUpgradeManager.initialize(pluginManager); - HalDatabaseUpgradeManager.upgrade(); - - // Init external web server - - httpExternal = new HalExternalWebDaemon(); - registerDaemon(httpExternal); - - // ------------------------------------ - // Initialize Plugins - // ------------------------------------ - - logger.info("Looking for plugins."); - - // Disable plugins based on settings - for (PluginData plugin : (List) pluginManager.getAllPlugins()) { - PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, plugin.getName()); - - // If plugin is not found in DB then disable it and create an entry. - if (pluginConfig == null) { - pluginConfig = new PluginConfig(plugin.getName()); - pluginConfig.setEnabled(false); - pluginConfig.save(db); - } - - if (!pluginConfig.isEnabled() && !plugin.getName().equals("Hal-Core")) { - logger.info("Disabling plugin '" + plugin.getName() + "'."); - plugin.setEnabled(false); - } - } - - // ------------------------------------ - // Initialize Managers - // ------------------------------------ - - logger.info("Initializing managers."); - - TriggerManager.initialize(pluginManager); - - for (Iterator it = pluginManager.getSingletonIterator(HalAbstractControllerManager.class); it.hasNext(); ) { - HalAbstractControllerManager manager = it.next(); - manager.initialize(pluginManager); - controllerManagers.add(manager); - } - - // ------------------------------------ - // Init daemons - // ------------------------------------ - - logger.info("Initializing daemons."); - - for (Iterator it = pluginManager.getSingletonIterator(HalDaemon.class); it.hasNext(); ) { - HalDaemon daemon = it.next(); - registerDaemon(daemon); - } - - // ------------------------------------ - // Init http server - // ------------------------------------ - - logger.info("Initializing HTTP Server."); - - HalWebPage.getRootNav().createSubNav("Sensors"); - HalWebPage.getRootNav().createSubNav("Events").setWeight(100); - HalWebPage.getRootNav().createSubNav("Settings").setWeight(200); - - HttpFilePage filePage = new HttpFilePage(FileUtil.find(HalContext.RESOURCE_WEB_ROOT)); - filePage.disableCaching(".*\\.m3u8"); - - http.setDefaultPage(filePage); - http.setPage("/", new HttpRedirectPage("/map")); - - for (Iterator it = pluginManager.getSingletonIterator(HalJavascriptModule.class); it.hasNext(); ) { - HalJsModule[] jsModules = it.next().getJavascriptModules(); - - if (jsModules != null) { - for (HalJsModule module : jsModules) { - HalWebPage.addJavascriptModule(module); - - if (module instanceof HalJavascriptModule.HalJsModulePage) - registerPage(((HalJavascriptModule.HalJsModulePage) module).getPage(), new EmptyWebPage()); - } - } - } - for (Iterator it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); ) - registerPage(it.next()); - for (Iterator it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); ) - registerPage(it.next()); - - - } catch (Exception e) { - logger.log(Level.SEVERE, "Startup failed.", e); - System.exit(1); - } - } - - - public static void enablePlugin(String name, boolean enabled) throws SQLException { - if (name.equals("Hal-Core")) - throw new IllegalArgumentException("Hal-Core cannot be disabled as it is critical component of Hal."); - - DBConnection db = HalContext.getDB(); - PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, name); - - if (pluginConfig == null) - pluginConfig = new PluginConfig(name); - - logger.info("Plugin '" + name + "' has been " + (enabled ? "enabled" : "disabled") + ", change will take affect after restart."); - - pluginConfig.setEnabled(enabled); - pluginManager.getPlugin(name).setEnabled(pluginConfig.isEnabled()); - pluginConfig.save(db); - } - - - public static PluginManager getPluginManager() { - return pluginManager; - } - - public static List getControllerManagers() { - return controllerManagers; - } - - /** - * @param daemon registers the given daemon and starts execution of the Runnable. - */ - public static void registerDaemon(HalDaemon daemon){ - try { - logger.info("Registering daemon: " + daemon.getClass()); - daemons.add(daemon); - daemon.initiate(daemonExecutor); - } catch (Exception e) { - logger.log(Level.SEVERE, "Unable to initialize daemon: " + daemon.getClass(), e); - } - } - - public static List getAllDaemons() { - return daemons; - } - - - /** - * Registers the given page with the intranet Hal web server. - * - * @param url is the web path to the page. - * @param page is the page to register with the server. - */ - public static void registerPage(String url, HttpPage page){ - http.setPage(url, page); - } - - /** - * Registers the given page with the intranet Hal web server. - * - * @param page is the page to register with the server. - */ - public static void registerPage(HalWebPage page){ - registerPage(page.getId(), page); - } - - /** - * @return the HTTP server that handles external web pages. - */ - public static HalExternalWebDaemon getExternalWebDaemon(){ - return httpExternal; - } - - -} diff --git a/hal-core/src/se/hal/SensorControllerManager.java b/hal-core/src/se/hal/SensorControllerManager.java deleted file mode 100644 index 9203318f..00000000 --- a/hal-core/src/se/hal/SensorControllerManager.java +++ /dev/null @@ -1,181 +0,0 @@ -package se.hal; - -import se.hal.intf.*; -import se.hal.struct.Sensor; -import se.hal.util.HalDeviceUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.plugin.PluginManager; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This class manages all SensorController and EventController objects - */ -public class SensorControllerManager extends HalAbstractControllerManager - implements HalDeviceReportListener { - - private static final Logger logger = LogUtil.getLogger(); - private static SensorControllerManager instance; - - /** List of all registered sensors **/ - private List registeredSensors = Collections.synchronizedList(new ArrayList<>()); - /** List of auto-detected sensors **/ - private List detectedSensors = Collections.synchronizedList(new ArrayList<>()); - - - @Override - public void initialize(PluginManager pluginManager){ - super.initialize(pluginManager); - instance = this; - - // Read in existing devices - - try { - DBConnection db = HalContext.getDB(); - - logger.info("Reading in existing sensors."); - - for (Sensor sensor : Sensor.getLocalSensors(db)) { - register(sensor); - } - } catch (SQLException e) { - logger.log(Level.SEVERE, "Unable to read in existing sensors.", e); - } - } - - // ---------------------------------------------------- - // SENSORS - // ---------------------------------------------------- - - /** - * Register a Sensor instance on the manager. - * The manager will start to save reported data for the registered Sensor. - */ - @Override - public void register(Sensor sensor) { - if (sensor.getDeviceConfig() == null) { - logger.warning("Sensor config is null: " + sensor); - return; - } - if (!getAvailableDeviceConfigs().contains(sensor.getDeviceConfig().getClass())) { - logger.warning("Sensor data plugin not available: " + sensor.getDeviceConfig().getClass()); - return; - } - - logger.info("Registering new sensor(id: " + sensor.getId() + "): " + sensor.getDeviceConfig().getClass()); - Class c = sensor.getControllerClass(); - HalAbstractController controller = getControllerInstance(c); - - if (controller != null) - controller.register(sensor.getDeviceConfig()); - - registeredSensors.add(sensor); - detectedSensors.remove(HalDeviceUtil.findDevice(sensor.getDeviceConfig(), detectedSensors)); // Remove if this device was detected - } - - /** - * Deregisters a Sensor from the manager. - * Data reported on the Sensor will no longer be saved but already saved data will not be modified. - * The Controller that owns the Sensor will be deallocated if it has no more registered devices. - */ - @Override - public void deregister(Sensor sensor){ - if (sensor.getDeviceConfig() == null) { - logger.warning("Sensor config is null: " + sensor); - return; - } - - Class c = sensor.getControllerClass(); - HalAbstractController controller = controllerMap.get(c); - if (controller != null) { - logger.info("Deregistering sensor(id: " + sensor.getId() + "): " + sensor.getDeviceConfig().getClass()); - controller.deregister(sensor.getDeviceConfig()); - registeredSensors.remove(sensor); - removeControllerIfEmpty(controller); - } else { - logger.warning("Controller not instantiated: " + sensor.getControllerClass()); - } - } - - /** - * @return a List of Sensor instances that have been registered to this manager - */ - @Override - public List getRegisteredDevices(){ - return registeredSensors; - } - - - /** - * @return a List of Sensor instances that have been reported but not registered on the manager - */ - @Override - public List getDetectedDevices(){ - return detectedSensors; - } - - /** - * Removes all auto-detected sensors. - */ - @Override - public void clearDetectedDevices(){ - detectedSensors.clear(); - } - - /** - * Called by Controllers to report received Sensor data - */ - @Override - public void reportReceived(HalDeviceConfig sensorConfig, HalDeviceData sensorData) { - if (!(sensorConfig instanceof HalSensorConfig)) - return; - - try{ - DBConnection db = HalContext.getDB(); - Sensor sensor = HalDeviceUtil.findDevice(sensorConfig, registeredSensors); - - if (sensor != null) { - if (sensorData != null) { - logger.finest("Received report from sensor(" + sensorConfig.getClass().getSimpleName() + "): " + sensorConfig); - PreparedStatement stmt = - db.getPreparedStatement("INSERT INTO sensor_data_raw (timestamp, sensor_id, data) VALUES(?, ?, ?)"); - stmt.setLong(1, sensorData.getTimestamp()); - stmt.setLong(2, sensor.getId()); - stmt.setDouble(3, sensorData.getData()); - DBConnection.exec(stmt); - } - } - else { // unknown sensor - logger.finest("Received report from unregistered sensor" + - "(" + sensorConfig.getClass().getSimpleName() + "): " + sensorConfig); - sensor = HalDeviceUtil.findDevice(sensorConfig, detectedSensors); - if (sensor == null) { - sensor = new Sensor(); - detectedSensors.add(sensor); - } - sensor.setDeviceConfig((HalSensorConfig) sensorConfig); - } - - sensor.setDeviceData((HalSensorData) sensorData); - - // call listeners - for (HalDeviceReportListener listener : sensor.getReportListeners()) - listener.reportReceived(sensorConfig, sensorData); - - } catch (SQLException e){ - logger.log(Level.WARNING, "Unable to store sensor report", e); - } - } - - - public static SensorControllerManager getInstance(){ - return instance; - } -} diff --git a/hal-core/src/se/hal/TriggerManager.java b/hal-core/src/se/hal/TriggerManager.java deleted file mode 100644 index 2b30473c..00000000 --- a/hal-core/src/se/hal/TriggerManager.java +++ /dev/null @@ -1,127 +0,0 @@ -package se.hal; - -import se.hal.intf.HalAction; -import se.hal.intf.HalTrigger; -import se.hal.struct.TriggerFlow; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.plugin.PluginManager; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - - -/** - * Handles all triggers registered on Hal - */ -public class TriggerManager { - private static final Logger logger = LogUtil.getLogger(); - private static final long EVALUATION_INTERVAL = 5 * 1000; - private static TriggerManager instance; - - private ArrayList> availableTriggers = new ArrayList<>(); - private ArrayList> availableActions = new ArrayList<>(); - - private ArrayList triggerFlows = new ArrayList<>(); - private ScheduledExecutorService executor; - - - public static void initialize(PluginManager pluginManager) { - TriggerManager manager = new TriggerManager(); - - for (Iterator> it = pluginManager.getClassIterator(HalTrigger.class); - it.hasNext(); ) { - manager.addAvailableTrigger(it.next()); - } - - for (Iterator> it = pluginManager.getClassIterator(HalAction.class); - it.hasNext(); ) { - manager.addAvailableAction(it.next()); - } - - manager.setEvaluationInterval(EVALUATION_INTERVAL); - instance = manager; - - try { - // Import triggers - - DBConnection db = HalContext.getDB(); - - logger.info("Reading in existing sensors."); - - for (TriggerFlow flow : TriggerFlow.getTriggerFlows(db)) { - TriggerManager.getInstance().register(flow); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Unable to read in existing triggers.", e); - } - } - - - public void setEvaluationInterval(long interval) { - if (executor != null) - executor.shutdownNow(); - executor = Executors.newScheduledThreadPool(1); - executor.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - evaluateAndExecute(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Trigger Evaluation Thread has Crashed", e); - } - } - }, 0, interval, TimeUnit.MILLISECONDS); - } - - - public void addAvailableTrigger(Class clazz) { - if ( ! availableTriggers.contains(clazz)) - availableTriggers.add(clazz); - } - public List> getAvailableTriggers() { - return availableTriggers; - } - - public void addAvailableAction(Class clazz) { - if ( ! availableActions.contains(clazz)) - availableActions.add(clazz); - } - public List> getAvailableActions() { - return availableActions; - } - - public void register(TriggerFlow flow){ - if ( ! triggerFlows.contains(flow)) - triggerFlows.add(flow); - } - - - /** - * Main execution method. - * This method will go through all flows and evaluate them. If the - * evaluation of a trigger returns true then its execute method will be called. - */ - public synchronized void evaluateAndExecute() { - for (int i = 0; i < triggerFlows.size(); i++) { // avoid foreach as triggerFlows can change while we are running - TriggerFlow flow = triggerFlows.get(i); - if (flow.evaluate()) { - logger.fine("Flow "+ flow.getId() +" evaluated true, executing actions"); - flow.execute(); - flow.reset(); - } - } - } - - - public static TriggerManager getInstance(){ - return instance; - } -} diff --git a/hal-core/src/se/hal/action/AlertAction.java b/hal-core/src/se/hal/action/AlertAction.java deleted file mode 100644 index 4074846f..00000000 --- a/hal-core/src/se/hal/action/AlertAction.java +++ /dev/null @@ -1,38 +0,0 @@ -package se.hal.action; - -import se.hal.HalContext; -import se.hal.intf.HalAction; -import zutil.log.LogUtil; -import zutil.ui.UserMessageManager.MessageTTL; -import zutil.ui.conf.Configurator; - -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.*; - -/** - * Action that will alert users with a message - */ -public class AlertAction implements HalAction { - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable("Alert Severity") - private MessageLevel severity = MessageLevel.INFO; - @Configurator.Configurable("Alert Message") - private MessageTTL ttl = MessageTTL.ONE_VIEW; - @Configurator.Configurable("Alert Title") - private String title = ""; - @Configurator.Configurable("Alert Description") - private String description = ""; - - - @Override - public void execute() { - HalContext.getUserMessageManager().add(new UserMessage(severity, title, description, ttl)); - } - - - public String toString(){ - return "Generate Alert: " + severity + ": " + title; - } -} diff --git a/hal-core/src/se/hal/action/DismissRoomAlertAction.java b/hal-core/src/se/hal/action/DismissRoomAlertAction.java deleted file mode 100644 index fd52b061..00000000 --- a/hal-core/src/se/hal/action/DismissRoomAlertAction.java +++ /dev/null @@ -1,42 +0,0 @@ -package se.hal.action; - -import se.hal.HalContext; -import se.hal.intf.HalAction; -import se.hal.struct.Room; -import se.hal.util.RoomValueProvider; -import zutil.log.LogUtil; -import zutil.ui.UserMessageManager.MessageTTL; -import zutil.ui.conf.Configurator; - -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.MessageLevel; -import static zutil.ui.UserMessageManager.UserMessage; - -/** - * Action that will alert users with a message - */ -public class DismissRoomAlertAction implements HalAction { - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable(value = "Target Room", valueProvider = RoomValueProvider.class) - private Room room; - - - @Override - public void execute() { - if (room != null) { - room.clearRoomAlert(); - } else { - HalContext.getUserMessageManager().add(new UserMessage(MessageLevel.WARNING, "Room not defined for dismissing alert.", MessageTTL.ONE_VIEW)); - } - } - - - public String toString(){ - if (room != null) { - return "Dismiss alert for room: " + room.getName(); - } - return "No room defined."; - } -} diff --git a/hal-core/src/se/hal/action/RoomAlertAction.java b/hal-core/src/se/hal/action/RoomAlertAction.java deleted file mode 100644 index f47f3ba7..00000000 --- a/hal-core/src/se/hal/action/RoomAlertAction.java +++ /dev/null @@ -1,45 +0,0 @@ -package se.hal.action; - -import se.hal.HalContext; -import se.hal.intf.HalAction; -import se.hal.struct.Room; -import se.hal.util.ConfigSensorValueProvider; -import se.hal.util.RoomValueProvider; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.ui.UserMessageManager.MessageTTL; -import zutil.ui.conf.Configurator; - -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.MessageLevel; -import static zutil.ui.UserMessageManager.UserMessage; - -/** - * Action that will alert users with a message - */ -public class RoomAlertAction implements HalAction { - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable(value = "Target Room", valueProvider = RoomValueProvider.class) - private Room room; - @Configurator.Configurable("Alert Severity") - private MessageLevel severity = MessageLevel.INFO; - @Configurator.Configurable("Alert Title") - private String title = ""; - - - @Override - public void execute() { - if (room != null) { - room.setRoomAlert(new UserMessage(severity, title, MessageTTL.DISMISSED)); - } else { - HalContext.getUserMessageManager().add(new UserMessage(MessageLevel.WARNING, "Room not defined for room alert.", MessageTTL.ONE_VIEW)); - } - } - - - public String toString(){ - return "Generate Room Alert: " + severity + ": " + title; - } -} diff --git a/hal-core/src/se/hal/action/SendEventAction.java b/hal-core/src/se/hal/action/SendEventAction.java deleted file mode 100644 index 428d1bb9..00000000 --- a/hal-core/src/se/hal/action/SendEventAction.java +++ /dev/null @@ -1,49 +0,0 @@ -package se.hal.action; - -import se.hal.EventControllerManager; -import se.hal.HalContext; -import se.hal.intf.HalAction; -import se.hal.intf.HalEventData; -import se.hal.struct.Event; -import se.hal.util.ConfigEventValueProvider; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - */ -public class SendEventAction implements HalAction { - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable(value = "Event Device", valueProvider = ConfigEventValueProvider.class) - private Event event; - @Configurator.Configurable("Data to Send") - private double data; - - - @Override - public void execute() { - try { - if (event != null) { - HalEventData dataObj = (HalEventData) event.getDeviceConfig().getDeviceDataClass().getDeclaredConstructor().newInstance(); - dataObj.setData(data); - // Send - EventControllerManager.getInstance().send(event, dataObj); - } else - logger.warning("Unable to find event for EventAction: " + this); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - - public String toString(){ - return "Send event: " + (event != null ? event.getId() : null) + - " (" + (event != null ? event.getName() : null) + ")" + - " with data: " + data; - } -} diff --git a/hal-core/src/se/hal/action/TelegramMessageAction.java b/hal-core/src/se/hal/action/TelegramMessageAction.java deleted file mode 100644 index 12d8fa9c..00000000 --- a/hal-core/src/se/hal/action/TelegramMessageAction.java +++ /dev/null @@ -1,29 +0,0 @@ -package se.hal.action; - -import se.hal.intf.HalAction; -import zutil.net.ws.app.TelegramBot; -import zutil.ui.conf.Configurator; - -/** - * Action that will send a telegram message to a user. - */ -public class TelegramMessageAction implements HalAction { - @Configurator.Configurable("Bot Token") - private String token = ""; - @Configurator.Configurable("Chat ID") - private long chatId = 0; - @Configurator.Configurable("Message") - private String message = ""; - - - @Override - public void execute() { - TelegramBot bot = new TelegramBot(token); - bot.sendMessage(chatId, message); - } - - - public String toString(){ - return "tgram://" + token + "/" + chatId; - } -} diff --git a/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java b/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java deleted file mode 100644 index e5308278..00000000 --- a/hal-core/src/se/hal/daemon/HalExternalWebDaemon.java +++ /dev/null @@ -1,187 +0,0 @@ -package se.hal.daemon; - -import org.shredzone.acme4j.exception.AcmeException; -import se.hal.HalContext; -import se.hal.intf.HalDaemon; -import se.hal.intf.HalWebPage; -import se.hal.util.HalAcmeDataStore; -import se.hal.util.HalOAuth2RegistryStore; -import zutil.log.LogUtil; -import zutil.net.acme.AcmeClient; -import zutil.net.acme.AcmeHttpChallengeFactory; -import zutil.net.acme.AcmeManualDnsChallengeFactory; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpServer; -import zutil.net.http.HttpURL; -import zutil.net.http.page.oauth.OAuth2AuthorizationPage; -import zutil.net.http.page.oauth.OAuth2Registry; -import zutil.net.http.page.oauth.OAuth2TokenPage; -import zutil.ui.UserMessageManager; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.concurrent.ScheduledExecutorService; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static se.hal.HalContext.CONFIG_HTTP_EXTERNAL_DOMAIN; -import static se.hal.HalContext.CONFIG_HTTP_EXTERNAL_PORT; - - -public class HalExternalWebDaemon implements HalDaemon { - private static final Logger logger = LogUtil.getLogger(); - - public static final String ENDPOINT_AUTH = "api/auth/authorize"; - public static final String ENDPOINT_TOKEN = "api/auth/token"; - - - // Certificate fields - private HalAcmeDataStore acmeDataStore = new HalAcmeDataStore(); - private String externalServerUrl; - private X509Certificate certificate; - - // Oauth fields - private OAuth2Registry oAuth2Registry; - - // Web server fields - private HttpServer httpExternal; - private HashMap pageMap = new HashMap<>(); - - - @Override - public void initiate(ScheduledExecutorService executor) { - // ------------------------------------ - // Initialize Oauth2 - // ------------------------------------ - - oAuth2Registry = new OAuth2Registry(new HalOAuth2RegistryStore()); - registerPage(ENDPOINT_AUTH, new OAuth2AuthorizationPage(oAuth2Registry)); - registerPage(ENDPOINT_TOKEN, new OAuth2TokenPage(oAuth2Registry)); - - // ------------------------------------ - // Initialize External HttpServer - // ------------------------------------ - - try { - if (HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_DOMAIN) && HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_PORT)) { - HttpURL urlGenerator = new HttpURL(); - urlGenerator.setProtocol("https"); - urlGenerator.setHost(HalContext.getStringProperty(HalContext.CONFIG_HTTP_EXTERNAL_DOMAIN)); - urlGenerator.setPort(HalContext.getIntegerProperty(HalContext.CONFIG_HTTP_EXTERNAL_PORT)); - externalServerUrl = urlGenerator.getURL(); - - certificate = acmeDataStore.getCertificate(); - - renewCertificate(); - startHttpServer(); - } else { - logger.warning("Missing '" + CONFIG_HTTP_EXTERNAL_PORT + "' and '" + CONFIG_HTTP_EXTERNAL_DOMAIN + "' configuration, will not setup external web-server."); - HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( - UserMessageManager.MessageLevel.WARNING, "Missing '" + CONFIG_HTTP_EXTERNAL_PORT + "' and '" + CONFIG_HTTP_EXTERNAL_DOMAIN + "' configuration, will not setup external web-server.", UserMessageManager.MessageTTL.DISMISSED)); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Was unable to initiate external web-server.", e); - HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( - UserMessageManager.MessageLevel.ERROR, "Was unable to initiate external web-server: " + e.getMessage(), UserMessageManager.MessageTTL.DISMISSED)); - } - } - - private void renewCertificate() throws AcmeException, IOException { - if (!AcmeClient.isCertificateValid(certificate)) { - // Prepare ACME Client - AcmeClient acme; - HttpServer tmpHttpServer = null; - String acmeType = HalContext.getStringProperty(HalContext.CONFIG_HTTP_EXTERNAL_CERT, "acme_http"); - - try { - if ("acme_http".equals(acmeType)) { - tmpHttpServer = new HttpServer(80); - tmpHttpServer.start(); - - acme = new AcmeClient(acmeDataStore, new AcmeHttpChallengeFactory(tmpHttpServer)); - } else if ("none".equals(acmeType)) { - acme = null; - } else if ("acme_dns".equals(acmeType)) { - acme = new AcmeClient(acmeDataStore, new AcmeManualDnsChallengeFactory()); - } else { - throw new IllegalArgumentException("Unknown config value for " + externalServerUrl); - } - - // Request certificate and start the external webserver - - if (acme != null) { - acme.addDomain(HalContext.getStringProperty(CONFIG_HTTP_EXTERNAL_DOMAIN)); - acme.prepareRequest(); - certificate = acme.requestCertificate(); - acmeDataStore.storeCertificate(certificate); - - logger.info("SSL certificate successfully generated."); - HalContext.getUserMessageManager().add(new UserMessageManager.UserMessage( - UserMessageManager.MessageLevel.INFO, "SSL certificate successfully generated for external web-server.", UserMessageManager.MessageTTL.DISMISSED)); - } else { - logger.warning("No SSL certificate is configured for external web-server, will run server in unsecure mode (not recommended)."); - certificate = null; - } - } finally { - // Cleanup - if (tmpHttpServer != null) { - tmpHttpServer.close(); - } - } - } - } - - private void startHttpServer() throws GeneralSecurityException, IOException { - // Shutdown old server - if (httpExternal != null) - httpExternal.close(); - - // Start new Server - - if (certificate != null) - httpExternal = new HttpServer(HalContext.getIntegerProperty(CONFIG_HTTP_EXTERNAL_PORT), acmeDataStore.getDomainKeyPair().getPrivate(), certificate); - else - httpExternal = new HttpServer(HalContext.getIntegerProperty(CONFIG_HTTP_EXTERNAL_PORT)); - - for (String url : pageMap.keySet()) { - httpExternal.setPage(url, pageMap.get(url)); - } - httpExternal.start(); - - logger.info("External https server up and running at: " + externalServerUrl); - } - - - /** - * @return the OAuth2Registry used by the general OAuth2 authorization pages. - */ - public OAuth2Registry getOAuth2Registry() { - return oAuth2Registry; - } - - /** - * Registers the given page with the external Hal web server. - * Note: as this page will most likely be accessible through the internet it needs to be robust and secure. - * - * @param url is the web path to the page. - * @param page is the page to register with the server. - */ - public void registerPage(String url, HttpPage page) { - pageMap.put(url, page); - - if (httpExternal != null) - httpExternal.setPage(url, page); - } - - /** - * Registers the given page with the external Hal web server. - * Note: as this page will most likely be accessible through the internet it needs to be robust and secure. - * - * @param page is the page to register with the server. - */ - public void registerPage(HalWebPage page){ - registerPage(page.getId(), page); - } -} diff --git a/hal-core/src/se/hal/daemon/HalMulticastDnsDaemon.java b/hal-core/src/se/hal/daemon/HalMulticastDnsDaemon.java deleted file mode 100644 index 36f7b4ee..00000000 --- a/hal-core/src/se/hal/daemon/HalMulticastDnsDaemon.java +++ /dev/null @@ -1,53 +0,0 @@ -package se.hal.daemon; - -import se.hal.HalContext; -import se.hal.intf.HalDaemon; -import zutil.log.LogUtil; -import zutil.net.dns.MulticastDnsServer; - -import java.io.IOException; -import java.net.InetAddress; -import java.util.concurrent.ScheduledExecutorService; -import java.util.logging.Level; -import java.util.logging.Logger; - - -public class HalMulticastDnsDaemon implements HalDaemon { - private static final Logger logger = LogUtil.getLogger(); - private static HalMulticastDnsDaemon instance; - - private MulticastDnsServer server; - - @Override - public synchronized void initiate(ScheduledExecutorService executor) { - if (instance != null) - return; - - String localDomain = HalContext.getStringProperty(HalContext.CONFIG_DNS_LOCAL_DOMAIN, "hal.local"); - - if (!localDomain.isEmpty()) { - try { - logger.info("Initializing local mDNS server for domain: " + localDomain); - - server = new MulticastDnsServer(); - server.addEntry(localDomain, InetAddress.getLocalHost()); - server.start(); - - instance = this; - } catch (IOException e) { - logger.log(Level.SEVERE, "Was unable to start mDNS Server.", e); - } - } else { - logger.info("Disabling local mDNS server."); - } - } - - public void addDnsEntry(String name, InetAddress ip) { - server.addEntry(name, ip); - } - - - public static HalMulticastDnsDaemon getInstance() { - return instance; - } -} diff --git a/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java b/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java deleted file mode 100644 index 95986d94..00000000 --- a/hal-core/src/se/hal/daemon/SensorDataAggregatorDaemon.java +++ /dev/null @@ -1,292 +0,0 @@ -package se.hal.daemon; - -import se.hal.HalContext; -import se.hal.intf.HalDaemon; -import se.hal.intf.HalSensorConfig.AggregationMethod; -import se.hal.struct.Sensor; -import se.hal.util.UTCTimePeriod; -import se.hal.util.UTCTimeUtility; -import zutil.db.DBConnection; -import zutil.db.SQLResultHandler; -import zutil.db.handler.SimpleSQLResult; -import zutil.log.LogUtil; -import zutil.ui.UserMessageManager; -import zutil.ui.UserMessageManager.MessageTTL; -import zutil.ui.UserMessageManager.UserMessage; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class SensorDataAggregatorDaemon implements HalDaemon, Runnable { - private static final Logger logger = LogUtil.getLogger(); - - public enum AggregationPeriodLength{ - SECOND, - MINUTE, - FIVE_MINUTES, - FIFTEEN_MINUTES, - HOUR, - DAY, - WEEK, - MONTH, - YEAR - } - - private HashMap alertMap = new HashMap<>(); - - - public void initiate(ScheduledExecutorService executor){ - executor.scheduleAtFixedRate(this, 0, UTCTimeUtility.FIVE_MINUTES_IN_MS, TimeUnit.MILLISECONDS); - } - - @Override - public void run(){ - try { - List sensorList = Sensor.getLocalSensors(HalContext.getDB()); - for (Sensor sensor : sensorList){ - logger.fine("Aggregating sensor_id: " + sensor.getId()); - aggregateSensor(sensor); - } - logger.fine("Aggregation Done"); - } catch (Exception e) { - logger.log(Level.SEVERE, "Thread has crashed", e); - } - } - - public void aggregateSensor(Sensor sensor) { - if (sensor.getDeviceConfig() == null){ - logger.fine("The sensor config is not available - ignoring it"); - return; - } - logger.fine("The sensor is of type: " + sensor.getDeviceConfig().getClass().getName()); - - long aggregationStartTime = System.currentTimeMillis(); - - logger.fine("Aggregating raw data up to a day old into five minute periods"); - aggregateRawData(sensor, AggregationPeriodLength.FIVE_MINUTES, UTCTimeUtility.DAY_IN_MS, 5, aggregationStartTime); - - logger.fine("Aggregating raw data up to a week old into one hour periods"); - aggregateRawData(sensor, AggregationPeriodLength.HOUR, UTCTimeUtility.WEEK_IN_MS, 60, aggregationStartTime); - - logger.fine("Aggregating raw data into one day periods"); - aggregateRawData(sensor, AggregationPeriodLength.DAY, UTCTimeUtility.INFINITY, 60*24, aggregationStartTime); - - logger.fine("Aggregating raw data into one week periods"); - aggregateRawData(sensor, AggregationPeriodLength.WEEK, UTCTimeUtility.INFINITY, 60*24*7, aggregationStartTime); - } - - /** - * Aggregate data from the raw DB table to the aggregated table - * @param sensor The sensor for to aggregate data - * @param ageLimitInMs Only aggregate up to this age - */ - private void aggregateRawData(Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs, int expectedSampleCount, long aggregationStartTime){ - long sensorId = sensor.getId(); - AggregationMethod aggrMethod = sensor.getDeviceConfig().getAggregationMethod(); - DBConnection db = HalContext.getDB(); - PreparedStatement stmt; - try { - // DB timestamps - long dbMaxRawTimestamp = getLatestRawTimestamp(db, sensor); - long dbMaxAggrEndTimestamp = getLatestAggrEndTimestamp(db, sensor, aggrPeriodLength); - // Periods - long periodLatestEndTimestamp = new UTCTimePeriod(aggregationStartTime, aggrPeriodLength).getPreviosPeriod().getEndTimestamp(); - long periodOldestStartTimestamp = new UTCTimePeriod(aggregationStartTime-ageLimitInMs, aggrPeriodLength).getStartTimestamp(); - - // Check if the sensor has stopped responding for 15 min or 3 times the data interval if so alert the user - if (aggrPeriodLength == AggregationPeriodLength.FIVE_MINUTES) { - long dataInterval = sensor.getDeviceConfig().getDataInterval(); - if (dataInterval < UTCTimeUtility.FIVE_MINUTES_IN_MS) - dataInterval = UTCTimeUtility.FIVE_MINUTES_IN_MS; - if (dbMaxRawTimestamp > 0 && - dbMaxRawTimestamp + (dataInterval * 3) < System.currentTimeMillis()) { - logger.fine("Sensor \"" + sensorId + "\" stopped sending data at: "+ dbMaxRawTimestamp); - - if (alertMap.containsKey(sensor.getId())) - alertMap.get(sensor.getId()).dismiss(); - - UserMessage alert = new UserMessage(UserMessageManager.MessageLevel.WARNING, - "Sensor \"" + sensor.getName() + "\" stopped responding", - "at "+dbMaxRawTimestamp+"", - MessageTTL.DISMISSED); - alertMap.put(sensor.getId(), alert); - HalContext.getUserMessageManager().add(alert); - } - else { - // Sensor has responded remove alert - if (alertMap.containsKey(sensor.getId())) - alertMap.get(sensor.getId()).dismiss(); - } - } - - // Is there any new data to evaluate? - if (dbMaxRawTimestamp < dbMaxAggrEndTimestamp || dbMaxRawTimestamp < periodOldestStartTimestamp){ - logger.fine("No new data to evaluate - aggregation is up to date"); - return; - } - - // Start processing - logger.fine("evaluating period: "+ - (dbMaxAggrEndTimestamp+1) + "=>" + periodLatestEndTimestamp + - " (" + UTCTimeUtility.getDateString(dbMaxAggrEndTimestamp+1) + "=>" + - UTCTimeUtility.getDateString(periodLatestEndTimestamp) + ") " + - "with expected sample count: " + expectedSampleCount); - stmt = db.getPreparedStatement("SELECT *, 1 AS confidence FROM sensor_data_raw" - +" WHERE sensor_id == ?" - + " AND timestamp > ?" - + " AND timestamp <= ? " - + " AND timestamp >= ? " - +" ORDER BY timestamp ASC"); - stmt.setLong(1, sensorId); - stmt.setLong(2, dbMaxAggrEndTimestamp); - stmt.setLong(3, periodLatestEndTimestamp); - stmt.setLong(4, periodOldestStartTimestamp); - DBConnection.exec(stmt, new DataAggregator(sensorId, aggrPeriodLength, expectedSampleCount, aggrMethod, aggregationStartTime)); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - - private long getLatestAggrEndTimestamp(DBConnection db, Sensor sensor, AggregationPeriodLength aggrPeriodLength) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement("SELECT MAX(timestamp_end) FROM sensor_data_aggr" - + " WHERE sensor_id == ?" - + " AND timestamp_end-timestamp_start == ?"); - stmt.setLong(1, sensor.getId()); - switch(aggrPeriodLength){ - case SECOND: stmt.setLong(2, UTCTimeUtility.SECOND_IN_MS-1); break; - case MINUTE: stmt.setLong(2, UTCTimeUtility.MINUTE_IN_MS-1); break; - case FIVE_MINUTES: stmt.setLong(2, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break; - case FIFTEEN_MINUTES: stmt.setLong(2, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break; - case HOUR: stmt.setLong(2, UTCTimeUtility.HOUR_IN_MS-1); break; - case DAY: stmt.setLong(2, UTCTimeUtility.DAY_IN_MS-1); break; - case WEEK: stmt.setLong(2, UTCTimeUtility.WEEK_IN_MS-1); break; - default: - throw new IllegalArgumentException("aggregation period length is not supported."); - } - Long timestamp = DBConnection.exec(stmt, new SimpleSQLResult()); - return (timestamp == null ? 0l : timestamp); - } - private long getLatestRawTimestamp(DBConnection db, Sensor sensor) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement( - "SELECT MAX(timestamp) FROM sensor_data_raw WHERE sensor_id == ?"); - stmt.setLong(1, sensor.getId()); - Long timestamp = DBConnection.exec(stmt, new SimpleSQLResult()); - return (timestamp == null ? 0l : timestamp); - } - - - /** - * Internal class for aggregating data to the aggregated DB table - */ - private class DataAggregator implements SQLResultHandler{ - private final long sensorId; - private final AggregationPeriodLength aggrPeriodLength; - private final int expectedSampleCount; - private final AggregationMethod aggrMethod; - private final long aggregationStartTime; - - public DataAggregator(long sensorId, AggregationPeriodLength aggrPeriodLength, int expectedSampleCount, AggregationMethod aggrMethod, long aggregationStartTime) { - this.sensorId = sensorId; - this.aggrPeriodLength = aggrPeriodLength; - this.expectedSampleCount = expectedSampleCount; - this.aggrMethod = aggrMethod; - this.aggregationStartTime = aggregationStartTime; - } - - @Override - public Object handleQueryResult(Statement stmt, ResultSet result) throws SQLException { - try{ - HalContext.getDB().getConnection().setAutoCommit(false); - - UTCTimePeriod currentPeriod = null; - float sum = 0; - float confidenceSum = 0; - int samples = 0; - long highestSequenceId = Sensor.getHighestSequenceId(sensorId); - PreparedStatement preparedInsertStmt = HalContext.getDB().getPreparedStatement( - "INSERT INTO sensor_data_aggr" + - "(sensor_id, sequence_id, timestamp_start, timestamp_end, data, confidence) " + - "VALUES(?, ?, ?, ?, ?, ?)"); - while (result.next()){ - if (sensorId != result.getInt("sensor_id")){ - throw new IllegalArgumentException("found entry for aggregation for the wrong sensorId " + - "(expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")"); - } - - long timestamp = result.getLong("timestamp"); - UTCTimePeriod dataPeriod = new UTCTimePeriod(timestamp, this.aggrPeriodLength); - - if (currentPeriod == null) - currentPeriod = dataPeriod; - - if (!dataPeriod.equals(currentPeriod)){ - saveData(preparedInsertStmt, confidenceSum, sum, samples, currentPeriod, ++highestSequenceId); - - // Reset variables - currentPeriod = dataPeriod; - confidenceSum = 0; - sum = 0; - samples = 0; - } - sum += result.getFloat("data"); - confidenceSum += result.getFloat("confidence"); - ++samples; - } - - //check if the last period is complete and also should be aggregated - if (currentPeriod != null && - currentPeriod.getEndTimestamp() <= new UTCTimePeriod(aggregationStartTime, aggrPeriodLength).getPreviosPeriod().getEndTimestamp()){ - saveData(preparedInsertStmt, confidenceSum, sum, samples, currentPeriod, ++highestSequenceId); - } - - DBConnection.execBatch(preparedInsertStmt); - HalContext.getDB().getConnection().commit(); - - }catch(Exception e){ - HalContext.getDB().getConnection().rollback(); - throw e; - }finally{ - HalContext.getDB().getConnection().setAutoCommit(true); - } - return null; - } - - private void saveData(PreparedStatement preparedInsertStmt, float confidenceSum, float sum, int samples, UTCTimePeriod currentPeriod, long sequenceId) throws SQLException{ - float aggrConfidence = -1; - float data = -1; - switch(aggrMethod){ - case SUM: - data = sum; - aggrConfidence = confidenceSum / (float)this.expectedSampleCount; - break; - case AVERAGE: - data = sum/samples; - aggrConfidence = 1; // ignore confidence for average - break; - } - logger.finer("Saved period: " + currentPeriod + - ", data: " + data + - ", confidence: " + aggrConfidence + - ", samples: " + samples + - ", aggrMethod: " + aggrMethod); - - preparedInsertStmt.setLong(1, sensorId); - preparedInsertStmt.setLong(2, sequenceId); - preparedInsertStmt.setLong(3, currentPeriod.getStartTimestamp()); - preparedInsertStmt.setLong(4, currentPeriod.getEndTimestamp()); - preparedInsertStmt.setFloat(5, data); - preparedInsertStmt.setFloat(6, aggrConfidence); - preparedInsertStmt.addBatch(); - } - - } - -} diff --git a/hal-core/src/se/hal/daemon/SensorDataCleanupDaemon.java b/hal-core/src/se/hal/daemon/SensorDataCleanupDaemon.java deleted file mode 100644 index 5309ad0b..00000000 --- a/hal-core/src/se/hal/daemon/SensorDataCleanupDaemon.java +++ /dev/null @@ -1,124 +0,0 @@ -package se.hal.daemon; - -import se.hal.HalContext; -import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength; -import se.hal.intf.HalDaemon; -import se.hal.struct.Sensor; -import se.hal.util.UTCTimeUtility; -import zutil.db.DBConnection; -import zutil.db.SQLResultHandler; -import zutil.log.LogUtil; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class SensorDataCleanupDaemon implements HalDaemon, Runnable { - private static final Logger logger = LogUtil.getLogger(); - - public void initiate(ScheduledExecutorService executor){ - executor.scheduleAtFixedRate(this, 5000, UTCTimeUtility.FIVE_MINUTES_IN_MS, TimeUnit.MILLISECONDS); - } - - @Override - public void run(){ - try { - List sensorList = Sensor.getSensors(HalContext.getDB()); - for (Sensor sensor : sensorList){ - logger.fine("Deleting old aggregated data for sensor id: " + sensor.getId()); - cleanupSensor(sensor); - } - logger.fine("Data cleanup done"); - } catch (Exception e) { - logger.log(Level.SEVERE, "Thread has crashed", e); - } - } - - public void cleanupSensor(Sensor sensor) { - if (sensor.getUser() != null) { - cleanupSensorData(sensor.getId(), AggregationPeriodLength.FIVE_MINUTES, UTCTimeUtility.DAY_IN_MS); //clear 5-minute data older than a day - cleanupSensorData(sensor.getId(), AggregationPeriodLength.HOUR, UTCTimeUtility.WEEK_IN_MS); //clear 1-hour data older than a week - //cleanupSensorData(sensor.getId(), AggregationPeriodLength.day, TimeUtility.INFINITY); //clear 1-day data older than infinity - //cleanupSensorData(sensor.getId(), AggregationPeriodLength.week, TimeUtility.INFINITY); //clear 1-week data older than infinity - } - } - - - /** - * Will clear periods if they are too old. - * - * @param sensorId - * @Param cleanupPeriodLength Will clear periods with this length - * @param olderThan Data must be older than this many ms to be cleared from the DB - */ - private void cleanupSensorData(long sensorId, AggregationPeriodLength cleanupPeriodLength, long olderThan){ - DBConnection db = HalContext.getDB(); - PreparedStatement stmt = null; - try { - - stmt = db.getPreparedStatement("SELECT * FROM sensor_data_aggr" - +" WHERE sensor_id == ? " - + "AND timestamp_end-timestamp_start == ?" - + "AND timestamp_end < ?"); - stmt.setLong(1, sensorId); - switch(cleanupPeriodLength){ - case SECOND: stmt.setLong(2, UTCTimeUtility.SECOND_IN_MS-1); break; - case MINUTE: stmt.setLong(2, UTCTimeUtility.MINUTE_IN_MS-1); break; - case FIVE_MINUTES: stmt.setLong(2, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break; - case FIFTEEN_MINUTES: stmt.setLong(2, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break; - case HOUR: stmt.setLong(2, UTCTimeUtility.HOUR_IN_MS-1); break; - case DAY: stmt.setLong(2, UTCTimeUtility.DAY_IN_MS-1); break; - case WEEK: stmt.setLong(2, UTCTimeUtility.WEEK_IN_MS-1); break; - default: logger.warning("cleanup period length is not supported."); return; - } - stmt.setLong(3, System.currentTimeMillis()-olderThan); - DBConnection.exec(stmt, new AggregateDataDeleter(sensorId)); - } catch (SQLException e) { - logger.log(Level.SEVERE, null, e); - } - } - - private class AggregateDataDeleter implements SQLResultHandler{ - private long sensorId = -1; - - public AggregateDataDeleter(long sensorId){ - this.sensorId = sensorId; - } - - @Override - public Long handleQueryResult(Statement stmt, ResultSet result) throws SQLException { - long count = 0; - try{ - HalContext.getDB().getConnection().setAutoCommit(false); - PreparedStatement preparedDeleteStmt = HalContext.getDB().getPreparedStatement("DELETE FROM sensor_data_aggr WHERE sensor_id == ? AND sequence_id == ?"); - while (result.next()){ - if (sensorId != result.getInt("sensor_id")){ - throw new IllegalArgumentException("Found entry for aggregation for the wrong sensorId (expecting: "+sensorId+", but was: "+result.getInt("sensor_id")+")"); - } - logger.finer("Deleting sensor(id: "+ sensorId +") aggregate entry timestamp: "+ result.getLong("timestamp_start") +" - "+ result.getLong("timestamp_end") + " (" + UTCTimeUtility.timeInMsToString(1+result.getLong("timestamp_end")-result.getLong("timestamp_start")) + ")"); - preparedDeleteStmt.setInt(1, result.getInt("sensor_id")); - preparedDeleteStmt.setLong(2, result.getLong("sequence_id")); - preparedDeleteStmt.addBatch(); - count++; - } - - DBConnection.execBatch(preparedDeleteStmt); - HalContext.getDB().getConnection().commit(); - }catch(Exception e){ - HalContext.getDB().getConnection().rollback(); - throw e; - }finally{ - HalContext.getDB().getConnection().setAutoCommit(true); - } - return count; - } - - } - -} diff --git a/hal-core/src/se/hal/intf/HalAbstractController.java b/hal-core/src/se/hal/intf/HalAbstractController.java deleted file mode 100644 index dc8e99c5..00000000 --- a/hal-core/src/se/hal/intf/HalAbstractController.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.intf; - - -public interface HalAbstractController { - - /** - * Indicates if the controller has all the configuration - * data and resources needed to be able to initialize correctly. - */ - default boolean isAvailable() { - return true; - } - - /** - * The framework might create dummy objects so any type of - * resource initialization should be handled in this method - * and not in the constructor. - */ - void initialize() throws Exception; - - /** - * Will register an device type to be handled by this controller. - */ - void register(HalDeviceConfig deviceConfig); - - /** - * Deregisters an device from this controller, the controller - * will no longer handle that type of event. - */ - void deregister(HalDeviceConfig deviceConfig); - - /** - * @return the number of registered devices. - */ - int size(); - - /** - * Add a listener to the controller that will receive all device reports from registered devices. - * Multiple calls with the same listener should only add register the listener once any subsequent calls should be ignored. - */ - void addListener(HalDeviceReportListener listener); - - /** - * Close any resources associated with this controller. - * This method could be called multiple times, first time - * should be handled as normal any subsequent calls should be ignored. - */ - void close(); -} diff --git a/hal-core/src/se/hal/intf/HalAbstractControllerManager.java b/hal-core/src/se/hal/intf/HalAbstractControllerManager.java deleted file mode 100644 index 34076a54..00000000 --- a/hal-core/src/se/hal/intf/HalAbstractControllerManager.java +++ /dev/null @@ -1,198 +0,0 @@ -package se.hal.intf; - -import zutil.ClassUtil; -import zutil.log.LogUtil; -import zutil.plugin.PluginManager; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @param is the device controller class - * @param is the device class - * @param is the device configuration class - */ -public abstract class HalAbstractControllerManager { - private static final Logger logger = LogUtil.getLogger(); - - /** A map of all instantiated controllers **/ - protected static Map controllerMap = new ConcurrentHashMap<>(); - /** Internal variable indicating if autostart controllers have been processed **/ - private Boolean autostartProcessed = false; - /** All available sensor plugins **/ - protected List> availableDeviceConfigs = new ArrayList<>(); - - /** - * Will instantiate a generic ControllerManager. - * - * @param pluginManager a PluginManager instance that will be used to find Controller plugins. - */ - public void initialize(PluginManager pluginManager) { - Class[] genericClasses = ClassUtil.getGenericClasses(this.getClass()); - - if (genericClasses.length >= 3 && genericClasses[2] != null) { - for (Iterator> it = pluginManager.getClassIterator(genericClasses[2]); it.hasNext(); ) { - addAvailableDeviceConfig(it.next()); - } - } else { - logger.severe("Unable to retrieve Controller class from generics for class: " + this.getClass()); - } - - // Instantiate autostart controllers, but only the first time - - synchronized (autostartProcessed) { - if (!autostartProcessed) { - for (Iterator> it = pluginManager.getClassIterator(HalAutostartController.class); it.hasNext(); ) { - Class controller = it.next(); - logger.fine("Autostarting controller: " + controller.getName()); - getControllerInstance(controller); // Instantiate controller - } - autostartProcessed = true; - } - } - } - - // ---------------------------------------------------- - // Abstract methods - // ---------------------------------------------------- - - /** - * Register a device instance on the manager. - * The manager will start to track and save reported data for the registered device. - */ - public abstract void register(V device); - - /** - * Deregisters a device from the manager. - * Data reported on the device will no longer be saved but already saved data will not be modified. - * The Controller that owns the device will be deallocated if it has no more registered devices. - */ - public abstract void deregister(V device); - - /** - * @return a List of device instances that have been registered on this manager - */ - public abstract List getRegisteredDevices(); - - /** - * @return a List of device instances that have reported data but have not yet been registered on the manager - */ - public abstract List getDetectedDevices(); - - /** - * Removes all auto detected devices. - */ - public abstract void clearDetectedDevices(); - - // ---------------------------------------------------- - // Common Device Logic - // ---------------------------------------------------- - - /** - * Registers a device configuration class type as usable by this manager - */ - public void addAvailableDeviceConfig(Class deviceConfigClass) { - if (!availableDeviceConfigs.contains(deviceConfigClass)) - availableDeviceConfigs.add(deviceConfigClass); - } - - /** - * @return a List of all available device configurations that can be registered with this manager - */ - public List> getAvailableDeviceConfigs() { - return availableDeviceConfigs; - } - - // ---------------------------------------------------- - // Common Controller Logic - // ---------------------------------------------------- - - /** - * @return all active instantiated controllers. - */ - public static List getControllers() { - return new ArrayList<>(controllerMap.values()); - } - - /** - * @param controllerClass the class of the wanted controller - * @return the first controller matching the given class, null if no controller was found. - */ - public static T getController(Class controllerClass) { - for (HalAbstractController controller : HalAbstractControllerManager.getControllers()) { - if (controllerClass.isAssignableFrom(controller.getClass())) { - return (T) controller; - } - } - - return null; - } - - - - /** - * Will return a singleton controller instance of the given class. - * If a instance does not exist yet then a new instance will be allocated - * depending on if the controller is ready thorough the {@link HalAbstractController#isAvailable()} method. - * - * @param clazz is the class of the wanted object instance wanted - * @return A singleton instance of the input clazz or null if the class is unavailable or not ready to be instantiated. - */ - protected T getControllerInstance(Class clazz){ - T controller; - - if (controllerMap.containsKey(clazz)) { - //noinspection unchecked - controller = (T) controllerMap.get(clazz); - } else { - try { - // Instantiate controller - controller = clazz.getDeclaredConstructor().newInstance(); - - if (!controller.isAvailable()) { - logger.warning("Controller is not ready: " + clazz.getName()); - return null; - } - - logger.info("Instantiating new controller: " + clazz.getName()); - controller.initialize(); - - controllerMap.put(clazz, controller); - } catch (Exception e){ - logger.log(Level.SEVERE, "Unable to instantiate controller: " + clazz.getName(), e); - return null; - } - } - - // Assign the manager as a listener, This needs to be done every time as the controllerMap is a - // shared static for all HalControllerManagers and as we do not know if the current manager is - // added as listener or not we need to add it and let the implementation of addListener method - // handle duplicate calls. - if (this instanceof HalDeviceReportListener) - controller.addListener((HalDeviceReportListener) this); - - return controller; - } - - /** - * Will check if a controller no longer has any managed devices, - * in that case the controller will be deallocated. - * - * @param controller is the controller instance. - */ - protected void removeControllerIfEmpty(HalAbstractController controller){ - if (controller instanceof HalAutostartController) - return; // Don't do anything if controller is scannable - - if (controller.size() < 0){ - // Remove controller as it has no more registered sensors - logger.info("Closing controller as it has no more registered devices: " + controller.getClass().getName()); - controllerMap.remove(controller.getClass()); - - controller.close(); - } - } -} diff --git a/hal-core/src/se/hal/intf/HalAbstractDevice.java b/hal-core/src/se/hal/intf/HalAbstractDevice.java deleted file mode 100644 index c1d9d7fa..00000000 --- a/hal-core/src/se/hal/intf/HalAbstractDevice.java +++ /dev/null @@ -1,277 +0,0 @@ -package se.hal.intf; - -import se.hal.HalContext; -import se.hal.struct.Room; -import se.hal.struct.User; -import se.hal.util.HalDeviceChangeListener; -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.log.LogUtil; -import zutil.parser.DataNode; -import zutil.parser.json.JSONParser; -import zutil.parser.json.JSONWriter; -import zutil.ui.conf.Configurator; - -import java.sql.SQLException; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Contains logic and data common to devices (Events and Sensors) - * - * @param is the device type - * @param is the device configuration class - * @param is the device data class - */ -public abstract class HalAbstractDevice extends DBBean { - private static final Logger logger = LogUtil.getLogger(); - - // Sensor specific data - private String name; - private String type; - private String config; // only used to store the deviceConfig configuration in DB - - /** Sensor specific configuration **/ - private transient C deviceConfig; - /** latest device data received **/ - private transient D deviceData; - - // User configuration - @DBColumn("user_id") - private User user; - - @DBColumn("room_id") - private Room room; - - // UI variables - @DBColumn("map_x") - private double mapX = 0; - @DBColumn("map_y") - private double mapY = 0; - - protected transient List deviceListeners = new LinkedList<>(); - - - // ---------------------------------------------------- - // Device config methods - // ---------------------------------------------------- - - public Configurator getDeviceConfigurator() { - C obj = getDeviceConfig(); - if (obj != null) { - HalDeviceChangeListener listener = new HalDeviceChangeListener<>(); - - Configurator configurator = new Configurator<>(obj); - configurator.setPreConfigurationListener(listener); - configurator.setPostConfigurationListener(listener); - return configurator; - } - return null; - } - @SuppressWarnings("unchecked") - public C getDeviceConfig() { - if (deviceConfig == null || !deviceConfig.getClass().getName().equals(type)) { - try { - Class clazz = Class.forName(type); - deviceConfig = (C) clazz.getDeclaredConstructor().newInstance(); - - applyConfig(); - deviceData = getLatestDeviceData(HalContext.getDB()); - } catch (Exception e) { - logger.log(Level.SEVERE, "Unable instantiate HalDeviceConfig: " + type, e); - } - } - return deviceConfig; - } - - /** - * Will replace the current device configuration. - * The device configuration will be reset if the input is set as null. - */ - public void setDeviceConfig(C data) { - if (data != null) { - type = data.getClass().getName(); - deviceConfig = data; - deviceData = getLatestDeviceData(HalContext.getDB()); - } else { - deviceConfig = null; - deviceData = null; - type = null; - config = null; - } - } - - @Override - public void save(DBConnection db) throws SQLException { - if (deviceConfig != null) - updateConfigString(); - else - this.config = null; - super.save(db); - } - - /** - * Will update the config String that will be stored in DB. - */ - private void updateConfigString() { - Configurator configurator = getDeviceConfigurator(); - this.config = JSONWriter.toString(configurator.getValuesAsNode()); - } - /** - * This method will configure the current DeviceData with the - * configuration from the config String. - */ - private void applyConfig() { - if (config != null && !config.isEmpty()) { - Configurator configurator = getDeviceConfigurator(); - configurator.setValues(JSONParser.read(config)); - configurator.applyConfiguration(); - } - } - - /** - * @return the class of the Controller responsible for this device type. - */ - public Class getControllerClass() { - return getDeviceConfig().getDeviceControllerClass(); - } - - // ---------------------------------------------------- - // Device data methods - // ---------------------------------------------------- - - /** - * @return the latest known data from the device - */ - public D getDeviceData() { - if (deviceData == null) - deviceData = getLatestDeviceData(HalContext.getDB()); - return deviceData; - } - - public void setDeviceData(D deviceData) { - this.deviceData = deviceData; - } - - /** - * Reads latest device data from DB - */ - protected abstract D getLatestDeviceData(DBConnection db); - - // ---------------------------------------------------- - // Other methods - // ---------------------------------------------------- - - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - - /** - * @return a String containing the class name of the DeviceData - */ - public String getType() { - return type; - } - - /** - * Will set the DeviceData class type. This method will - * reset set the current DeviceData if the input type is - * null or a different type from the current DeviceData class. - */ - public void setType(String type) { - if (this.type == null || !this.type.equals(type)) { - setDeviceConfig(null); // reset - this.type = type; - } - } - - /** - * @return the owner of this device. - */ - public User getUser() { - return user; - } - public void setUser(User user) { - this.user = user; - } - - /** - * @return the room that this device is located in. - */ - public Room getRoom() { - return room; - } - public void setRoom(Room room) { - this.room = room; - } - - /** - * @return the X coordinate of this device on the floor map - */ - public double getMapX() { - return mapX; - } - /** - * @return the Y coordinate of this device on the floor map - */ - public double getMapY() { - return mapY; - } - public void setMapCoordinates(double x, double y) { - this.mapX = Math.max(0.0, x); - this.mapY = Math.max(0.0, y); - } - - public void addReportListener(HalDeviceReportListener listener) { - deviceListeners.add(listener); - } - public void removeReportListener(HalDeviceReportListener listener) { - deviceListeners.remove(listener); - } - public List getReportListeners() { - return deviceListeners; - } - - /** - * @return a DataNode object containing general Device information that can be written in JSON format. - */ - public DataNode getDataNode() { - DataNode deviceNode = new DataNode(DataNode.DataType.Map); - deviceNode.set("id", getId()); - deviceNode.set("name", getName()); - deviceNode.set("owner", getUser().getUsername()); - - DataNode mapNode = deviceNode.set("map", DataNode.DataType.Map); - mapNode.set("x", getMapX()); - mapNode.set("y", getMapY()); - - if (getDeviceConfig() != null) { - deviceNode.set("configType", getDeviceConfig().getClass().getSimpleName()); - - DataNode configNode = deviceNode.set("config", DataNode.DataType.Map); - for (Configurator.ConfigurationParam param : getDeviceConfigurator().getConfiguration()) { - configNode.set(param.getName(), param.getString()); - } - } else { - deviceNode.set("configType", (String)null); - } - - if (getDeviceData() != null) { - deviceNode.set("dataType", getDeviceConfig().getDeviceDataClass().getSimpleName()); - - DataNode dataNode = deviceNode.set("data", DataNode.DataType.Map); - dataNode.set("value", getDeviceData().getData()); - dataNode.set("valueStr", getDeviceData().toString()); - dataNode.set("timestamp", getDeviceData().getTimestamp()); - } else { - deviceNode.set("dataType", (String)null); - } - - return deviceNode; - } -} diff --git a/hal-core/src/se/hal/intf/HalAction.java b/hal-core/src/se/hal/intf/HalAction.java deleted file mode 100644 index 119789ce..00000000 --- a/hal-core/src/se/hal/intf/HalAction.java +++ /dev/null @@ -1,14 +0,0 @@ -package se.hal.intf; - - - -/** - * Defines a action that will be executed - */ -public interface HalAction{ - - /** - * Executes this specific action - */ - void execute(); -} diff --git a/hal-core/src/se/hal/intf/HalApiEndpoint.java b/hal-core/src/se/hal/intf/HalApiEndpoint.java deleted file mode 100644 index 4d7567e0..00000000 --- a/hal-core/src/se/hal/intf/HalApiEndpoint.java +++ /dev/null @@ -1,64 +0,0 @@ -package se.hal.intf; - -import zutil.log.LogUtil; -import zutil.net.http.HttpHeader; -import zutil.net.http.HttpPrintStream; -import zutil.parser.DataNode; -import zutil.parser.Templator; -import zutil.parser.json.JSONWriter; - -import java.io.IOException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A interface defining a Hal json endpoint - */ -public abstract class HalApiEndpoint extends HalWebPage { - private static final Logger logger = LogUtil.getLogger(); - - public HalApiEndpoint(String id) { - super(id); - } - - - @Override - public void respond(HttpPrintStream out, - HttpHeader headers, - Map session, - Map cookie, - Map request) throws IOException { - - out.setHeader("Content-Type", "application/json"); - out.setHeader("Access-Control-Allow-Origin", "*"); - out.setHeader("Pragma", "no-cache"); - - JSONWriter writer = new JSONWriter(out); - try{ - writer.write( - jsonRespond(session, cookie, request)); - } catch (Exception e){ - logger.log(Level.SEVERE, null, e); - DataNode root = new DataNode(DataNode.DataType.Map); - root.set("error", e.getMessage()); - writer.write(root); - } - writer.close(); - } - - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) throws Exception { - return null; - } - - - protected abstract DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception; -} \ No newline at end of file diff --git a/hal-core/src/se/hal/intf/HalAutostartController.java b/hal-core/src/se/hal/intf/HalAutostartController.java deleted file mode 100644 index 36b1987f..00000000 --- a/hal-core/src/se/hal/intf/HalAutostartController.java +++ /dev/null @@ -1,9 +0,0 @@ -package se.hal.intf; - -/** - * A interface that indicates that the implementing - * controller can be auto started when HalServer starts up. - */ -public interface HalAutostartController { - -} diff --git a/hal-core/src/se/hal/intf/HalDaemon.java b/hal-core/src/se/hal/intf/HalDaemon.java deleted file mode 100644 index 45fca083..00000000 --- a/hal-core/src/se/hal/intf/HalDaemon.java +++ /dev/null @@ -1,17 +0,0 @@ -package se.hal.intf; - -import java.util.concurrent.ScheduledExecutorService; - -/** - * Defines a standalone process that will run parallel to the main application - */ -public interface HalDaemon { - - /** - * Setup the execution of the daemon with the provided executor. - * - * @param executor the scheduler provided for the daemon to setup its execution. - */ - void initiate(ScheduledExecutorService executor); - -} diff --git a/hal-core/src/se/hal/intf/HalDatabaseUpgrader.java b/hal-core/src/se/hal/intf/HalDatabaseUpgrader.java deleted file mode 100644 index 77eeab01..00000000 --- a/hal-core/src/se/hal/intf/HalDatabaseUpgrader.java +++ /dev/null @@ -1,53 +0,0 @@ -package se.hal.intf; - -import zutil.db.DBConnection; - -import java.sql.SQLException; - -/** - * A plugin interface for custom plugin DB changes. - */ -public abstract class HalDatabaseUpgrader { - - private int referenceDBVersion; - private String referenceDBPath; - - - public HalDatabaseUpgrader(int referenceDBVersion, String referenceDBPath) { - this.referenceDBVersion = referenceDBVersion; - this.referenceDBPath = referenceDBPath; - } - - - /** - * @return the reference DB version which will be used as a to-state during the upgrade process. - */ - public int getReferenceDBVersion() { - return referenceDBVersion; - } - - /** - * @return the path to the reference DB file that represents the target structure of the DB. - */ - public String getReferenceDBPath() { - return referenceDBPath; - } - - /** - * Method will be called just before the DB upgrade is performed. - * - * @param db Connection to the DB that will be upgraded. - * @param fromDBVersion The current version of the to be upgraded DB. - * @param toDBVersion The target version that the DB will be upgraded to. - */ - public void preDatabaseUpgrade(DBConnection db, int fromDBVersion, int toDBVersion) throws SQLException {} - - /** - * Method will be called after the DB has been upgraded. - * - * @param db Connection to the upgraded DB. - * @param fromDBVersion The before upgrade version of the DB. - * @param toDBVersion The target version of the DB. - */ - public void postDatabaseUpgrade(DBConnection db, int fromDBVersion, int toDBVersion) throws SQLException {} -} diff --git a/hal-core/src/se/hal/intf/HalDeviceConfig.java b/hal-core/src/se/hal/intf/HalDeviceConfig.java deleted file mode 100644 index 0c22057b..00000000 --- a/hal-core/src/se/hal/intf/HalDeviceConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package se.hal.intf; - -/** - * Interface representing a generic device configuration data. - */ -public interface HalDeviceConfig { - - /** - * @return the controller class that is responsible to track this configuration - */ - Class getDeviceControllerClass(); - - /** - * @return the class that should be instantiated and used for data received from this event - */ - Class getDeviceDataClass(); - - /** - * This method is required to be implemented. - * This method compares two configuration objects static or unique configuration. it should not compare data and timestamp type dynamic values. - * - * @param obj is the target object to compare to. - * @return true if the configuration of the two objects are same, false if the objects are not of same type or configuration does not match. - */ - boolean equals(Object obj); -} diff --git a/hal-core/src/se/hal/intf/HalDeviceReportListener.java b/hal-core/src/se/hal/intf/HalDeviceReportListener.java deleted file mode 100644 index ce7e91e2..00000000 --- a/hal-core/src/se/hal/intf/HalDeviceReportListener.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.intf; - -/** - * A listener interface that will be called when the - * Event or Sensor that it is registered to receives a report - */ -public interface HalDeviceReportListener { - - void reportReceived(HalDeviceConfig deviceConfig, HalDeviceData deviceData); -} diff --git a/hal-core/src/se/hal/intf/HalEventConfig.java b/hal-core/src/se/hal/intf/HalEventConfig.java deleted file mode 100644 index d19b1c80..00000000 --- a/hal-core/src/se/hal/intf/HalEventConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package se.hal.intf; - -/** - * Interface representing event type specific configuration data. - */ -public interface HalEventConfig extends HalDeviceConfig { - - /** - * @return true if this event device is only a reporting data and do not accept writing/changing the data from Hal. - */ - default boolean isReadOnly() { - return false; - } -} diff --git a/hal-core/src/se/hal/intf/HalEventController.java b/hal-core/src/se/hal/intf/HalEventController.java deleted file mode 100644 index 51c3fc6b..00000000 --- a/hal-core/src/se/hal/intf/HalEventController.java +++ /dev/null @@ -1,13 +0,0 @@ -package se.hal.intf; - -/** - * Controller interface for handling event based devices. - */ -public interface HalEventController extends HalAbstractController { - - /** - * @param eventConfig the event configuration to target when sending - * @param eventData the data to send - */ - void send(HalEventConfig eventConfig, HalEventData eventData); -} \ No newline at end of file diff --git a/hal-core/src/se/hal/intf/HalEventData.java b/hal-core/src/se/hal/intf/HalEventData.java deleted file mode 100644 index 3b7962dd..00000000 --- a/hal-core/src/se/hal/intf/HalEventData.java +++ /dev/null @@ -1,8 +0,0 @@ -package se.hal.intf; - -/** - * Interface representing one report from an event - */ -public abstract class HalEventData extends HalDeviceData { - -} diff --git a/hal-core/src/se/hal/intf/HalJavascriptModule.java b/hal-core/src/se/hal/intf/HalJavascriptModule.java deleted file mode 100644 index a398ae61..00000000 --- a/hal-core/src/se/hal/intf/HalJavascriptModule.java +++ /dev/null @@ -1,50 +0,0 @@ -package se.hal.intf; - -/** - * A plugin interface for configuring Javascript modules - */ -public interface HalJavascriptModule { - - /** - * @return a List of Objects defining the Javascript configuration - */ - HalJsModule[] getJavascriptModules(); - - - /** - * Defines configuration for a Javascript module to be loaded during webpage generation. - */ - class HalJsModule { - private String moduleName; - private String scriptPath; - - public HalJsModule(String moduleName, String scriptPath) { - this.moduleName = moduleName; - this.scriptPath = scriptPath; - } - - public String getModuleName() { - return moduleName; - } - - public String getScriptPath() { - return scriptPath; - } - } - - /** - * Declares a Javascript module to be assigned to a specific web address. - */ - class HalJsModulePage extends HalJsModule { - private String page; - - public HalJsModulePage(String moduleName, String scriptPath, String page) { - super(moduleName, scriptPath); - this.page = page; - } - - public String getPage() { - return page; - } - } -} diff --git a/hal-core/src/se/hal/intf/HalScannableController.java b/hal-core/src/se/hal/intf/HalScannableController.java deleted file mode 100644 index 793c7297..00000000 --- a/hal-core/src/se/hal/intf/HalScannableController.java +++ /dev/null @@ -1,27 +0,0 @@ -package se.hal.intf; - -/** - * Controllers that implement this interface support manual scanning of devices. - */ -public interface HalScannableController { - - /** - * Calling this method will start the scanning or pairing - * mode of the controller to find new devices. - */ - void startScan(); - - /** - * @return a boolean indication if a scan is still ongoing. - */ - boolean isScanning(); - - /** - * Function that can be used by template to identify a scannable controller. - * - * @return always true. - */ - default boolean isScannable() { - return true; - } -} diff --git a/hal-core/src/se/hal/intf/HalSensorConfig.java b/hal-core/src/se/hal/intf/HalSensorConfig.java deleted file mode 100644 index 0ba9f8c4..00000000 --- a/hal-core/src/se/hal/intf/HalSensorConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package se.hal.intf; - -/** - * Interface representing sensor type specific configuration data. - */ -public interface HalSensorConfig extends HalDeviceConfig { - enum AggregationMethod{ - SUM, - AVERAGE - } - - - /** - * @return the intended data reporting interval in milliseconds. - */ - default long getDataInterval() { - return 60 * 60 * 1000; // 1 hour - } - - /** - * @return which aggregation method that should be used to aggregate the reported data. - */ - AggregationMethod getAggregationMethod(); -} diff --git a/hal-core/src/se/hal/intf/HalSensorController.java b/hal-core/src/se/hal/intf/HalSensorController.java deleted file mode 100644 index eb31344b..00000000 --- a/hal-core/src/se/hal/intf/HalSensorController.java +++ /dev/null @@ -1,8 +0,0 @@ -package se.hal.intf; - -/** - * Controller interface for handling Sensor devices. - */ -public interface HalSensorController extends HalAbstractController { - -} diff --git a/hal-core/src/se/hal/intf/HalTrigger.java b/hal-core/src/se/hal/intf/HalTrigger.java deleted file mode 100644 index b05f1c7f..00000000 --- a/hal-core/src/se/hal/intf/HalTrigger.java +++ /dev/null @@ -1,22 +0,0 @@ -package se.hal.intf; - - -/** - * A interface that declares a trigger/condition that - * needs to be validated before an action can be run - */ -public interface HalTrigger{ - - /** - * Evaluates if this trigger has passed. If the trigger is - * true then this method will return true until the {@link #reset()} - * method is called. - */ - boolean evaluate(); - - /** - * Reset the evaluation to false. - */ - void reset(); - -} diff --git a/hal-core/src/se/hal/intf/HalWebPage.java b/hal-core/src/se/hal/intf/HalWebPage.java deleted file mode 100644 index 09d66903..00000000 --- a/hal-core/src/se/hal/intf/HalWebPage.java +++ /dev/null @@ -1,125 +0,0 @@ -package se.hal.intf; - -import se.hal.HalContext; -import se.hal.intf.HalJavascriptModule.HalJsModule; -import se.hal.intf.HalJavascriptModule.HalJsModulePage; -import se.hal.struct.User; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.net.http.HttpHeader; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.parser.Templator; -import zutil.ui.Navigation; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - - -public abstract class HalWebPage implements HttpPage { - private static final String TEMPLATE_MAIN = HalContext.RESOURCE_WEB_ROOT + "/main_index.tmpl"; - private static final String TEMPLATE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav.tmpl"; - private static final String TEMPLATE_SIDE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav_side.tmpl"; - - private static Navigation rootNav = Navigation.createRootNav(); - private static Navigation userNav = Navigation.createRootNav(); - private static List jsModules = new ArrayList<>(); - private static List jsPages = new ArrayList<>(); - - private String pageId; - private boolean showSubNav; - - - public HalWebPage(String id){ - this.pageId = id; - this.showSubNav = true; - } - - - public String getId(){ - return pageId; - } - - - @Override - public void respond(HttpPrintStream out, HttpHeader header, - Map session, Map cookie, - Map request) throws IOException { - - try { - DBConnection db = HalContext.getDB(); - - // Prepare common template data - - Map data = new HashMap<>(); - data.put("page", Navigation.getPagedNavigation(header)); - data.put("user", User.getLocalUser(db)); - data.put("rootNav", rootNav.createPagedNavInstance(header).getSubNavs()); - data.put("userNav", userNav.createPagedNavInstance(header).getSubNavs()); - - List breadcrumb = Navigation.getBreadcrumb(Navigation.getPagedNavigation(header)); - if (!breadcrumb.isEmpty()) { - data.put("breadcrumb", breadcrumb); - data.put("subNav", breadcrumb.get(1).createPagedNavInstance(header).getSubNavs()); - } - - // Create templates - - Templator navigationTemplate = new Templator(FileUtil.find(TEMPLATE_NAVIGATION)); - navigationTemplate.setAll(data); - - Templator subNavigationTemplate = null; - if (showSubNav) { - subNavigationTemplate = new Templator(FileUtil.find(TEMPLATE_SIDE_NAVIGATION)); - subNavigationTemplate.setAll(data); - } - - Templator main = new Templator(FileUtil.find(TEMPLATE_MAIN)); - main.setAll(data); - main.set("navigation", navigationTemplate); - main.set("side_navigation", subNavigationTemplate); - main.set("javascriptModules", jsModules); - main.set("javascriptPages", jsPages); - - Templator body = httpRespond(session, cookie, request); - if (body == null) - main.set("content", ""); - else - main.set("content", body); - - out.print(main.compile()); - } catch (Exception e) { - throw new IOException(e); - } - } - - /** - * Defines if the sub-navigation should be shown on the page - */ - protected void showSubNav(boolean show) { - this.showSubNav = show; - } - - public static Navigation getRootNav(){ - return rootNav; - } - public static Navigation getUserNav(){ - return userNav; - } - public static void addJavascriptModule(HalJsModule module) { - jsModules.add(module); - - if (module instanceof HalJsModulePage) - jsPages.add((HalJsModulePage) module); - } - - public abstract Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception; - -} \ No newline at end of file diff --git a/hal-core/src/se/hal/page/EmptyWebPage.java b/hal-core/src/se/hal/page/EmptyWebPage.java deleted file mode 100644 index 383f4540..00000000 --- a/hal-core/src/se/hal/page/EmptyWebPage.java +++ /dev/null @@ -1,44 +0,0 @@ -package se.hal.page; - -import se.hal.EventControllerManager; -import se.hal.HalContext; -import se.hal.intf.HalEventData; -import se.hal.intf.HalWebPage; -import se.hal.struct.Event; -import se.hal.struct.devicedata.ColorEventData; -import se.hal.struct.devicedata.LevelEventData; -import se.hal.struct.devicedata.OnOffEventData; -import se.hal.util.DeviceNameComparator; -import se.hal.util.HistoryDataListSqlResult; -import se.hal.util.HistoryDataListSqlResult.HistoryData; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.parser.Templator; - -import java.sql.PreparedStatement; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * A empty page with an empty body content generated from server side. - */ -public class EmptyWebPage extends HalWebPage { - - public EmptyWebPage() { - super("empty"); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception{ - - return null; - } -} diff --git a/hal-core/src/se/hal/page/EventConfigWebPage.java b/hal-core/src/se/hal/page/EventConfigWebPage.java deleted file mode 100644 index f022c3d2..00000000 --- a/hal-core/src/se/hal/page/EventConfigWebPage.java +++ /dev/null @@ -1,153 +0,0 @@ -package se.hal.page; - -import se.hal.EventControllerManager; -import se.hal.HalContext; -import se.hal.intf.HalAbstractController; -import se.hal.intf.HalAbstractControllerManager; -import se.hal.intf.HalScannableController; -import se.hal.intf.HalWebPage; -import se.hal.struct.Room; -import se.hal.util.ClassConfigurationFacade; -import se.hal.struct.Event; -import se.hal.struct.User; -import se.hal.util.RoomValueProvider; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.parser.Templator; -import zutil.ui.UserMessageManager.MessageLevel; -import zutil.ui.UserMessageManager.MessageTTL; -import zutil.ui.UserMessageManager.UserMessage; - -import java.util.ArrayList; -import java.util.Map; -import java.util.logging.Logger; - - -public class EventConfigWebPage extends HalWebPage { - private static final Logger logger = LogUtil.getLogger(); - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/event_config.tmpl"; - - private ArrayList eventConfigurations = new ArrayList<>(); - - - public EventConfigWebPage() { - super("event_config"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Event Settings").setWeight(200); - - for (Class c : EventControllerManager.getInstance().getAvailableDeviceConfigs()) - eventConfigurations.add(new ClassConfigurationFacade(c)); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception { - - DBConnection db = HalContext.getDB(); - User localUser = User.getLocalUser(db); - - // Save new input - if (request.containsKey("action")) { - int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id"))); - int roomId = (ObjectUtil.isEmpty(request.get("room-id")) ? -1 : Integer.parseInt(request.get("room-id"))); - - Event event = null; - Room room = (roomId >= 0 ? Room.getRoom(db, roomId) : null); - - if (id >= 0) { - // Read in requested id - event = Event.getEvent(db, id); - - if (event == null) { - logger.warning("Unknown event id: " + id); - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Unknown event id: " + id, MessageTTL.ONE_VIEW)); - } - } - - switch(request.get("action")) { - case "create_local_event": - logger.info("Creating new event: " + request.get("name")); - event = new Event(); - event.setRoom(room); - event.setName(request.get("name")); - event.setType(request.get("type")); - event.setUser(localUser); - event.getDeviceConfigurator().setValues(request).applyConfiguration(); - event.save(db); - EventControllerManager.getInstance().register(event); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully created new event: " + event.getName(), MessageTTL.ONE_VIEW)); - break; - - case "modify_local_event": - if (event != null) { - logger.info("Modifying event(id: " + event.getId() + "): " + event.getName()); - event.setRoom(room); - event.setName(request.get("name")); - event.setType(request.get("type")); - event.setUser(localUser); - event.getDeviceConfigurator().setValues(request).applyConfiguration(); - event.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully saved event: "+event.getName(), MessageTTL.ONE_VIEW)); - } - break; - - case "remove_local_event": - if (event != null) { - logger.info("Removing event(id: " + event.getId() + "): " + event.getName()); - EventControllerManager.getInstance().deregister(event); - event.delete(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully removed event: "+event.getName(), MessageTTL.ONE_VIEW)); - } - break; - - case "remove_all_detected_events": - EventControllerManager.getInstance().clearDetectedDevices(); - break; - - case "start_scan": - for (HalAbstractController controller : HalAbstractControllerManager.getControllers()) { - if (controller instanceof HalScannableController) { - ((HalScannableController) controller).startScan(); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Initiated scanning on controller: " + controller.getClass().getName(), MessageTTL.ONE_VIEW)); - } - } - break; - } - } - - // Is any scan active? - boolean scanning = false; - - for (HalAbstractController controller : HalAbstractControllerManager.getControllers()) { - if (controller instanceof HalScannableController) { - scanning |= ((HalScannableController) controller).isScanning(); - } - } - - // Output - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("user", localUser); - tmpl.set("rooms", Room.getRooms(db)); - tmpl.set("scanning", scanning); - tmpl.set("localEvents", Event.getLocalEvents(db)); - tmpl.set("detectedEvents", EventControllerManager.getInstance().getDetectedDevices()); - tmpl.set("availableEventConfigClasses", EventControllerManager.getInstance().getAvailableDeviceConfigs()); - tmpl.set("availableEventObjectConfig", eventConfigurations); - - return tmpl; - } - -} diff --git a/hal-core/src/se/hal/page/JavascriptModules.java b/hal-core/src/se/hal/page/JavascriptModules.java deleted file mode 100644 index cd69c9ca..00000000 --- a/hal-core/src/se/hal/page/JavascriptModules.java +++ /dev/null @@ -1,27 +0,0 @@ -package se.hal.page; - -import se.hal.intf.HalJavascriptModule; -import se.hal.intf.HalWebPage; - -public class JavascriptModules implements HalJavascriptModule { - - @Override - public HalJsModule[] getJavascriptModules() { - HalWebPage.getRootNav().createSubNav("Events").createSubNav("event_overview", "Overview"); - - return new HalJsModule[] { - new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"), - new HalJsModule("EventStore", "./js/vue/stores/EventStore.js"), - new HalJsModule("SensorStore", "./js/vue/stores/SensorStore.js"), - - new HalJsModule("AlertComponent", "./js/vue/components/AlertComponent.js"), - new HalJsModule("AlertListComponent", "./js/vue/components/AlertListComponent.js"), - - new HalJsModule("EventActionComponent", "./js/vue/components/EventActionComponent.js"), - new HalJsModulePage("EventDetailPageComponent", "./js/vue/components/EventDetailPageComponent.js", "/event/:id"), - new HalJsModulePage("EventOverviewPageComponent", "./js/vue/components/EventOverviewPageComponent.js", "/event_overview"), - new HalJsModule("EventTableComponent", "./js/vue/components/EventTableComponent.js"), - new HalJsModule("EventTableRowComponent", "./js/vue/components/EventTableRowComponent.js"), - }; - } -} diff --git a/hal-core/src/se/hal/page/PluginConfigWebPage.java b/hal-core/src/se/hal/page/PluginConfigWebPage.java deleted file mode 100644 index 3d96e0ca..00000000 --- a/hal-core/src/se/hal/page/PluginConfigWebPage.java +++ /dev/null @@ -1,85 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import se.hal.HalServer; -import se.hal.intf.HalAbstractController; -import se.hal.intf.HalAbstractControllerManager; -import se.hal.intf.HalScannableController; -import se.hal.intf.HalWebPage; -import zutil.io.file.FileUtil; -import zutil.parser.Templator; - -import java.util.Map; - -import static zutil.ui.UserMessageManager.*; - - -public class PluginConfigWebPage extends HalWebPage { - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/plugin_config.tmpl"; - - - public PluginConfigWebPage() { - super("plugins"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Plugins").setWeight(500); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception{ - - if (request.containsKey("action")) { - switch (request.get("action")) { - case "plugin_enable": - String name = request.get("plugin_name"); - - if (!name.equals("Hal-Core")) { - HalServer.enablePlugin(name, - (request.containsKey("enabled") && "on".equals(request.get("enabled")))); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully updated plugin " + name + ", change will take affect after restart.", MessageTTL.ONE_VIEW)); - } else { - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Hal-Core cannot be disabled as it is critical component of Hal.", MessageTTL.ONE_VIEW)); - } - break; - - case "controller_scan": - String controllerName = request.get("controller"); - - HalAbstractController controller = null; - for (HalAbstractController c : HalAbstractControllerManager.getControllers()) { - if (c.getClass().getName().equals(controllerName)) { - controller = c; - break; - } - } - - if (controller != null) { - if (controller instanceof HalScannableController) { - ((HalScannableController) controller).startScan(); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Initiated scanning on controller: " + controllerName, MessageTTL.ONE_VIEW)); - } else { - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Controller " + controllerName + " does not support scanning.", MessageTTL.ONE_VIEW)); - } - } else { - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Unable to find controller: " + controllerName, MessageTTL.ONE_VIEW)); - } - break; - } - } - - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("plugins", HalServer.getPluginManager().getAllPlugins()); - tmpl.set("controllers", HalAbstractControllerManager.getControllers()); - tmpl.set("daemons", HalServer.getAllDaemons()); - return tmpl; - } -} diff --git a/hal-core/src/se/hal/page/PropertyConfigWebPage.java b/hal-core/src/se/hal/page/PropertyConfigWebPage.java deleted file mode 100644 index 715c1ba2..00000000 --- a/hal-core/src/se/hal/page/PropertyConfigWebPage.java +++ /dev/null @@ -1,33 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import se.hal.intf.HalWebPage; -import zutil.io.file.FileUtil; -import zutil.parser.Templator; - -import java.util.Map; - -public class PropertyConfigWebPage extends HalWebPage { - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/property_config.tmpl"; - - - public PropertyConfigWebPage() { - super("properties"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Properties"); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception{ - - Map properties = HalContext.getProperties(); - - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("properties", properties.entrySet()); - return tmpl; - - } -} diff --git a/hal-core/src/se/hal/page/RoomConfigWebPage.java b/hal-core/src/se/hal/page/RoomConfigWebPage.java deleted file mode 100644 index fe912c6c..00000000 --- a/hal-core/src/se/hal/page/RoomConfigWebPage.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.page; - -import se.hal.HalContext; -import se.hal.intf.HalWebPage; -import se.hal.struct.Room; -import se.hal.struct.User; -import se.hal.util.ClassConfigurationFacade; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.parser.Templator; - -import java.util.Map; -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.*; - -public class RoomConfigWebPage extends HalWebPage { - private static final Logger logger = LogUtil.getLogger(); - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/room_config.tmpl"; - - private ClassConfigurationFacade roomConfiguration = new ClassConfigurationFacade(Room.class); - - - public RoomConfigWebPage() { - super("room_config"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Room Settings").setWeight(200); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception { - - DBConnection db = HalContext.getDB(); - - // Save new input - if (request.containsKey("action")){ - int id = (ObjectUtil.isEmpty(request.get("id")) ? -1 : Integer.parseInt(request.get("id"))); - - Room room = null; - - if (id >= 0) { - // Read in requested id - room = Room.getRoom(db, id); - - if (room == null) { - logger.warning("Unknown room id: " + id); - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Unknown room id: " + id, MessageTTL.ONE_VIEW)); - } - } - - switch(request.get("action")) { - case "create_room": - logger.info("Creating new room: " + request.get("name")); - room = new Room(); - /* FALLTHROUGH */ - - case "modify_room": - if (room != null) { - logger.info("Modifying room(id: " + room.getId() + "): " + room.getName()); - room.setName(request.get("name")); - room.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully updated room: " + room.getName(), MessageTTL.ONE_VIEW)); - } - break; - - case "remove_room": - if (room != null) { - logger.info("Removing room(id: " + room.getId() + "): " + room.getName()); - room.delete(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully removed room: " + room.getName(), MessageTTL.ONE_VIEW)); - } - break; - } - } - - // Output - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("rooms", Room.getRooms(db)); - tmpl.set("roomConfiguration", roomConfiguration); - - return tmpl; - - } -} diff --git a/hal-core/src/se/hal/page/SensorConfigWebPage.java b/hal-core/src/se/hal/page/SensorConfigWebPage.java deleted file mode 100644 index 38e0cfe4..00000000 --- a/hal-core/src/se/hal/page/SensorConfigWebPage.java +++ /dev/null @@ -1,226 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import se.hal.SensorControllerManager; -import se.hal.intf.HalAbstractController; -import se.hal.intf.HalAbstractControllerManager; -import se.hal.intf.HalScannableController; -import se.hal.intf.HalWebPage; -import se.hal.struct.Room; -import se.hal.util.ClassConfigurationFacade; -import se.hal.struct.Sensor; -import se.hal.struct.User; -import se.hal.util.RoomValueProvider; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.parser.Templator; - -import java.util.ArrayList; -import java.util.Map; -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.*; - - -public class SensorConfigWebPage extends HalWebPage { - private static final Logger logger = LogUtil.getLogger(); - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/sensor_config.tmpl"; - - private ArrayList sensorConfigurations; - - - public SensorConfigWebPage() { - super("sensor_config"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "Sensor Settings").setWeight(100); - - sensorConfigurations = new ArrayList<>(); - for (Class c : SensorControllerManager.getInstance().getAvailableDeviceConfigs()) - sensorConfigurations.add(new ClassConfigurationFacade(c)); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception { - - DBConnection db = HalContext.getDB(); - User localUser = User.getLocalUser(db); - - // Save new input - if (request.containsKey("action")) { - int sensorId = (ObjectUtil.isEmpty(request.get("sensor-id")) ? -1 : Integer.parseInt(request.get("sensor-id"))); - int userId = (ObjectUtil.isEmpty(request.get("user-id")) ? -1 : Integer.parseInt(request.get("user-id"))); - int roomId = (ObjectUtil.isEmpty(request.get("room-id")) ? -1 : Integer.parseInt(request.get("room-id"))); - - Sensor sensor = null; - User user = null; - Room room = (roomId >= 0 ? Room.getRoom(db, roomId) : null); - - if (sensorId >= 0) { - // Read in requested id - sensor = Sensor.getSensor(db, sensorId); - - if (sensor == null) { - logger.warning("Unknown sensor id: " + sensorId); - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Unknown sensor id: " + sensorId, MessageTTL.ONE_VIEW)); - } - } - - if (userId >= 0) { - // Read in requested id - user = User.getUser(db, userId); - - if (user == null) { - logger.warning("Unknown user id: " + userId); - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Unknown user id: " + userId, MessageTTL.ONE_VIEW)); - } - } - - switch(request.get("action")) { - // ---------------------------------------- - // Local Sensors - // ---------------------------------------- - - case "create_local_sensor": - logger.info("Creating sensor: " + request.get("name")); - sensor = new Sensor(); - sensor.setRoom(room); - sensor.setName(request.get("name")); - sensor.setType(request.get("type")); - sensor.setSynced(Boolean.parseBoolean(request.get("sync"))); - sensor.setUser(localUser); - sensor.getDeviceConfigurator().setValues(request).applyConfiguration(); - sensor.save(db); - SensorControllerManager.getInstance().register(sensor); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully created new sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); - break; - - case "modify_local_sensor": - if (sensor != null) { - logger.info("Modifying sensor(id: " + sensor.getId() + "): " + sensor.getName()); - sensor.setRoom(room); - sensor.setName(request.get("name")); - sensor.setType(request.get("type")); - sensor.setSynced(Boolean.parseBoolean(request.get("sync"))); - sensor.getDeviceConfigurator().setValues(request).applyConfiguration(); - sensor.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully saved sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); - } - break; - - case "remove_local_sensor": - if (sensor != null) { - logger.warning("Removing sensor(id: " + sensor.getId() + "): " + sensor.getName()); - SensorControllerManager.getInstance().deregister(sensor); - sensor.delete(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully removed sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); - } - break; - - case "remove_all_detected_sensors": - SensorControllerManager.getInstance().clearDetectedDevices(); - break; - - case "start_scan": - for (HalAbstractController controller : HalAbstractControllerManager.getControllers()) { - if (controller instanceof HalScannableController) { - ((HalScannableController) controller).startScan(); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Initiated scanning on controller: " + controller.getClass().getName(), MessageTTL.ONE_VIEW)); - } - } - break; - - // ---------------------------------------- - // External Users - // ---------------------------------------- - - case "create_external_user": - logger.info("Creating external user: " + request.get("hostname")); - user = new User(); - user.setHostname(request.get("hostname")); - user.setPort(Integer.parseInt(request.get("port"))); - user.setExternal(true); - user.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully created new external user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); - break; - - case "modify_external_user": - if (user != null) { - logger.info("Modifying external user: " + user.getHostname()); - user.setHostname(request.get("hostname")); - user.setPort(Integer.parseInt(request.get("port"))); - user.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully saved external user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); - } - break; - case "remove_external_user": - if (user != null) { - logger.info("Removing external user: " + user.getHostname()); - user.delete(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully removed user with host: "+user.getHostname(), MessageTTL.ONE_VIEW)); - } - break; - - // ---------------------------------------- - // External Sensors - // ---------------------------------------- - - case "modify_external_sensor": - if (sensor != null) { - logger.warning("Modifying external sensor: " + sensor.getName()); - sensor.setSynced(Boolean.parseBoolean(request.get("sync"))); - sensor.save(db); - - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.SUCCESS, "Successfully saved external sensor: " + sensor.getName(), MessageTTL.ONE_VIEW)); - } - break; - } - } - - // Is any scan active? - boolean scanning = false; - - for (HalAbstractController controller : HalAbstractControllerManager.getControllers()) { - if (controller instanceof HalScannableController) { - scanning |= ((HalScannableController) controller).isScanning(); - } - } - - // Output - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("user", localUser); - tmpl.set("rooms", Room.getRooms(db)); - tmpl.set("scanning", scanning); - tmpl.set("localSensors", Sensor.getLocalSensors(db)); - tmpl.set("detectedSensors", SensorControllerManager.getInstance().getDetectedDevices()); - tmpl.set("extUsers", User.getExternalUsers(db)); - tmpl.set("extSensor", Sensor.getExternalSensors(db)); - tmpl.set("availableSensorConfigClasses", SensorControllerManager.getInstance().getAvailableDeviceConfigs()); - tmpl.set("availableSensorObjectConfig", sensorConfigurations); - - return tmpl; - - } - -} diff --git a/hal-core/src/se/hal/page/StartupWebPage.java b/hal-core/src/se/hal/page/StartupWebPage.java deleted file mode 100644 index 4fd561ec..00000000 --- a/hal-core/src/se/hal/page/StartupWebPage.java +++ /dev/null @@ -1,42 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import zutil.MimeTypeUtil; -import zutil.io.file.FileUtil; -import zutil.net.http.HttpHeader; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; -import zutil.parser.Templator; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - - -public class StartupWebPage implements HttpPage { - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/startup.tmpl"; - private static final List ACCEPTED_FILE_LIST = Arrays.asList( - "/css/lib/bootstrap.min.css", - "/css/hal.css", - "/fonts/glyphicons-halflings-regular.ttf", - "/fonts/glyphicons-halflings-regular.woff", - "/fonts/glyphicons-halflings-regular.woff2" - ); - - @Override - public void respond(HttpPrintStream out, HttpHeader headers, Map session, Map cookie, Map request) throws IOException { - if (ACCEPTED_FILE_LIST.contains(headers.getRequestURL())) { - printFileContents(headers.getRequestURL(), out); - } else { - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - out.print(tmpl.compile()); - } - } - - private void printFileContents(String path, HttpPrintStream out) throws IOException { - out.setHeader(HttpHeader.HEADER_CONTENT_TYPE, MimeTypeUtil.getMimeByExtension(FileUtil.getFileExtension(path)).toString()); - out.write(FileUtil.getByteContent(FileUtil.find(HalContext.RESOURCE_WEB_ROOT + path))); - } -} diff --git a/hal-core/src/se/hal/page/TriggerWebPage.java b/hal-core/src/se/hal/page/TriggerWebPage.java deleted file mode 100644 index 0c8f9b87..00000000 --- a/hal-core/src/se/hal/page/TriggerWebPage.java +++ /dev/null @@ -1,150 +0,0 @@ -package se.hal.page; - -import se.hal.HalContext; -import se.hal.TriggerManager; -import se.hal.intf.HalWebPage; -import se.hal.struct.Action; -import se.hal.util.ClassConfigurationFacade; -import se.hal.struct.Trigger; -import se.hal.struct.TriggerFlow; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.io.file.FileUtil; -import zutil.log.LogUtil; -import zutil.parser.Templator; - -import java.util.ArrayList; -import java.util.Map; -import java.util.logging.Logger; - -import static zutil.ui.UserMessageManager.*; - -public class TriggerWebPage extends HalWebPage { - private static final Logger logger = LogUtil.getLogger(); - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/trigger.tmpl"; - - private ArrayList triggerConfigurators; - private ArrayList actionConfigurators; - - - public TriggerWebPage() { - super("trigger"); - super.getRootNav().createSubNav("Events").createSubNav(this.getId(), "Triggers"); - - triggerConfigurators = new ArrayList<>(); - for (Class c : TriggerManager.getInstance().getAvailableTriggers()) - triggerConfigurators.add(new ClassConfigurationFacade(c)); - actionConfigurators = new ArrayList<>(); - for (Class c : TriggerManager.getInstance().getAvailableActions()) - actionConfigurators.add(new ClassConfigurationFacade(c)); - } - - @Override - public Templator httpRespond ( - Map session, - Map cookie, - Map request) - throws Exception { - DBConnection db = HalContext.getDB(); - - if (request.containsKey("action")) { - TriggerFlow flow = (ObjectUtil.isEmpty(request.get("flow-id")) ? null : - TriggerFlow.getTriggerFlow(db, Integer.parseInt(request.get("flow-id")))); - Trigger trigger = (ObjectUtil.isEmpty(request.get("trigger-id")) ? null : - Trigger.getTrigger(db, Integer.parseInt(request.get("trigger-id")))); - Action action = (ObjectUtil.isEmpty(request.get("action-id")) ? null : - Action.getAction(db, Integer.parseInt(request.get("action-id")))); - - switch(request.get("action")) { - // Flows - case "create_flow": - logger.info("Creating new flow."); - flow = new TriggerFlow(); - flow.save(db); - break; - - case "modify_flow": - logger.info("Modifying flow(id: " + flow.getId() + "): " + flow.getName()); - flow.setEnabled("on".equals(request.get("enabled"))); - flow.setName(request.get("name")); - flow.save(db); - break; - - case "remove_flow": - logger.info("Removing flow(id: " + flow.getId() + "): " + flow.getName()); - flow.delete(db); - break; - - case "execute_flow": - flow.execute(); - break; - - // Triggers - case "create_trigger": - if (flow == null) { - logger.warning("Invalid flow id: " + request.get("flow-id")); - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Invalid flow id: " + request.get("flow-id"), MessageTTL.ONE_VIEW)); - break; - } - - logger.info("Creating trigger associated to flow: " + flow.getName()); - trigger = new Trigger(); - flow.addTrigger(trigger); - flow.save(db); - /* FALLTHROUGH */ - case "modify_trigger": - logger.info("Modifying trigger: " + trigger.getId()); - trigger.setObjectClass(request.get("type")); - trigger.getObjectConfigurator().setValues(request).applyConfiguration(); - trigger.save(db); // will save all sub beans also - break; - - case "remove_trigger": - if (flow == null) - flow = TriggerFlow.getTriggerFlow(db, trigger); - logger.info("Removing trigger: " + trigger.getId()); - flow.removeTrigger(trigger); - trigger.delete(db); - break; - - // Triggers - case "create_action": - if (flow == null) { - HalContext.getUserMessageManager().add(new UserMessage( - MessageLevel.ERROR, "Invalid flow id", MessageTTL.ONE_VIEW)); - break; - } - - logger.info("Creating action associated with flow: " + flow.getName()); - action = new Action(); - flow.addAction(action); - flow.save(db); - /* FALLTHROUGH */ - case "modify_action": - logger.info("Modifying action: " + action.getId()); - action.setObjectClass(request.get("type")); - action.getObjectConfigurator().setValues(request).applyConfiguration(); - action.save(db); // will save all sub beans also - break; - - case "remove_action": - if (flow == null) - flow = TriggerFlow.getTriggerFlow(db, action); - logger.info("Removing action: " + action.getId()); - flow.removeAction(action); - action.delete(db); - break; - } - } - - - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("triggerConf", triggerConfigurators); - tmpl.set("actionConf", actionConfigurators); - tmpl.set("availableTriggers", TriggerManager.getInstance().getAvailableTriggers()); - tmpl.set("availableActions", TriggerManager.getInstance().getAvailableActions()); - tmpl.set("flows", TriggerFlow.getTriggerFlows(db)); - return tmpl; - } -} diff --git a/hal-core/src/se/hal/page/api/AlertApiEndpoint.java b/hal-core/src/se/hal/page/api/AlertApiEndpoint.java deleted file mode 100644 index d75cb426..00000000 --- a/hal-core/src/se/hal/page/api/AlertApiEndpoint.java +++ /dev/null @@ -1,114 +0,0 @@ -package se.hal.page.api; - -import se.hal.HalContext; -import se.hal.intf.HalApiEndpoint; -import se.hal.struct.Event; -import zutil.ArrayUtil; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.parser.DataNode; -import zutil.ui.UserMessageManager; -import zutil.ui.UserMessageManager.UserMessage; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * RESTish API for accessing and managing user alert. - * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html - */ -public class AlertApiEndpoint extends HalApiEndpoint { - private static final Logger logger = LogUtil.getLogger(); - - - public AlertApiEndpoint() { - super("api/alert"); - } - - @Override - public DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception{ - - // -------------------------------------- - // Filter alerts - // -------------------------------------- - - String[] req_ids = new String[0]; - if (request.get("id") != null) - req_ids = request.get("id").split(","); - - // Filter devices - - List messages = new ArrayList<>(); - for (UserMessage message : HalContext.getUserMessageManager().getMessages()) { - boolean filter_match = true; - - // id filtering - if (!ObjectUtil.isEmpty((Object) req_ids) && !ArrayUtil.contains(req_ids, "" + message.getId())) { - filter_match = false; - } - - // Check the filter - if (filter_match) { - messages.add(message); - } - } - - if (ObjectUtil.isEmpty(request.get("action"))) { - messages.clear(); // Reset the message list - } else { - switch (request.get("action")) { - case "poll": - for (UserMessage message : messages) { - message.decreaseTTL(); - } - case "peek": - break; - - case "dismiss": - for (UserMessage message : messages) { - message.dismiss(); - } - break; - - default: - messages.clear(); - break; - } - } - - // -------------------------------------- - // Generate DataNode - // -------------------------------------- - - DataNode root = new DataNode(DataNode.DataType.List); - - for (UserMessage message : messages) { - root.add(getUserMessageDataNode(message)); - } - - return root; - } - - /** - * @param message the source alert to generate data node for. - * @return a DataNode containing relevant information from the given alert. Will return null if the message is null. - */ - public static DataNode getUserMessageDataNode(UserMessage message) { - if (message == null) - return null; - - DataNode node = new DataNode(DataNode.DataType.Map); - node.set("id", message.getId()); - node.set("level", message.getLevel().toString()); - node.set("ttl", message.getTitle()); - node.set("title", message.getTitle()); - node.set("description", message.getDescription()); - return node; - } -} diff --git a/hal-core/src/se/hal/page/api/EventApiEndpoint.java b/hal-core/src/se/hal/page/api/EventApiEndpoint.java deleted file mode 100644 index 1e60197c..00000000 --- a/hal-core/src/se/hal/page/api/EventApiEndpoint.java +++ /dev/null @@ -1,132 +0,0 @@ -package se.hal.page.api; - -import se.hal.EventControllerManager; -import se.hal.HalContext; -import se.hal.intf.HalApiEndpoint; -import se.hal.intf.HalEventData; -import se.hal.struct.Event; -import se.hal.struct.devicedata.ColorEventData; -import se.hal.struct.devicedata.LevelEventData; -import se.hal.struct.devicedata.OnOffEventData; -import zutil.ArrayUtil; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.parser.DataNode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * RESTish API for accessing and managing Events. - * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html - */ -public class EventApiEndpoint extends HalApiEndpoint { - private static final Logger logger = LogUtil.getLogger(); - - - public EventApiEndpoint() { - super("api/event"); - } - - @Override - public DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception{ - - DBConnection db = HalContext.getDB(); - DataNode root = new DataNode(DataNode.DataType.List); - - // -------------------------------------- - // Get Action - // -------------------------------------- - - String[] reqIds = new String[0]; - if (request.get("id") != null) - reqIds = request.get("id").split(","); - - if ("modify".equals(request.get("action"))) { - for (String idStr : reqIds) { - int id = Integer.parseInt(idStr); - Event event = Event.getEvent(db, id); - - if (event == null) { - logger.warning("Could not find given Event id: " + id); - continue; - } - - // change event data - HalEventData eventData; - - switch (event.getDeviceConfig().getDeviceDataClass().getSimpleName()) { - case "LevelEventData": - eventData = new LevelEventData(Integer.parseInt(request.get("data")) / 100.0, 0); - break; - - case "ColorEventData": - eventData = new ColorEventData(request.get("data"), 0); - break; - - case "OnOffEventData": - boolean booleanData = "on".equalsIgnoreCase(request.get("data")) || "true".equalsIgnoreCase(request.get("data")); - eventData = new OnOffEventData(booleanData, 0); - break; - - default: - logger.warning("Unable to process event change request for event " + id + ", data type not supported: " + event.getDeviceConfig().getDeviceDataClass()); - continue; - } - - logger.info("Modifying Event(" + id + ") state: " + eventData); - EventControllerManager.getInstance().send(event, eventData); - } - } else { - String reqConfigType = request.get("configType"); - String reqDataType = request.get("dataType"); - - // Filter devices - - List events = new ArrayList<>(); - for (Event event : Event.getLocalEvents(db)) { - boolean filter_match = true; - - // id filtering - if (!ObjectUtil.isEmpty((Object) reqIds) && !ArrayUtil.contains(reqIds, "" + event.getId())) { - filter_match = false; - } - - // device type filtering - if (!ObjectUtil.isEmpty(reqConfigType) && - !event.getDeviceConfig().getClass().getSimpleName().equals(reqConfigType)) { - filter_match = false; - } - - // data type filtering - if (!ObjectUtil.isEmpty(reqDataType) && - !event.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(reqDataType)) { - filter_match = false; - } - - // Check the filter - if (filter_match) { - events.add(event); - } - } - - // -------------------------------------- - // Generate DataNode - // -------------------------------------- - - for (Event event : events) { - DataNode deviceNode = event.getDataNode(); - deviceNode.set("type", "Event"); - root.add(deviceNode); - } - } - - return root; - } -} diff --git a/hal-core/src/se/hal/page/api/MapApiEndpoint.java b/hal-core/src/se/hal/page/api/MapApiEndpoint.java deleted file mode 100644 index d94ecee9..00000000 --- a/hal-core/src/se/hal/page/api/MapApiEndpoint.java +++ /dev/null @@ -1,73 +0,0 @@ -package se.hal.page.api; - -import se.hal.HalContext; -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalApiEndpoint; -import se.hal.struct.Event; -import se.hal.struct.Room; -import se.hal.struct.Sensor; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.parser.DataNode; - -import java.sql.SQLException; -import java.util.Map; -import java.util.logging.Logger; - -/** - * TODO: This json endpoint might not be needed as we have SensorJsonPage? - */ -public class MapApiEndpoint extends HalApiEndpoint { - private static final Logger logger = LogUtil.getLogger(); - - public MapApiEndpoint() { - super("api/map"); - } - - - @Override - public DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception { - - DBConnection db = HalContext.getDB(); - DataNode root = new DataNode(DataNode.DataType.Map); - - if ("save".equals(request.get("action"))) { - int id = Integer.parseInt(request.get("id")); - - logger.info("Saving map coordinates."); - - switch (request.get("type")) { - case "room": - Room room = Room.getRoom(db, id); - room.setMapCoordinates( - Float.parseFloat(request.get("x")), - Float.parseFloat(request.get("y")), - Float.parseFloat(request.get("width")), - Float.parseFloat(request.get("height"))); - room.save(db); - break; - - case "sensor": - Sensor sensor = Sensor.getSensor(db, id); - sensor.setMapCoordinates( - Float.parseFloat(request.get("x")), - Float.parseFloat(request.get("y"))); - sensor.save(db); - break; - - case "event": - Event event = Event.getEvent(db, id); - event.setMapCoordinates( - Float.parseFloat(request.get("x")), - Float.parseFloat(request.get("y"))); - event.save(db); - break; - } - } - - return root; - } -} diff --git a/hal-core/src/se/hal/page/api/RoomApiEndpoint.java b/hal-core/src/se/hal/page/api/RoomApiEndpoint.java deleted file mode 100644 index eeb90f95..00000000 --- a/hal-core/src/se/hal/page/api/RoomApiEndpoint.java +++ /dev/null @@ -1,74 +0,0 @@ -package se.hal.page.api; - -import se.hal.HalContext; -import se.hal.intf.HalApiEndpoint; -import se.hal.struct.Room; -import zutil.ArrayUtil; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.parser.DataNode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * REST service to fetch event device information. - * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html - */ -public class RoomApiEndpoint extends HalApiEndpoint { - private static final Logger logger = LogUtil.getLogger(); - - - public RoomApiEndpoint() { - super("api/room"); - } - - @Override - public DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception{ - - DBConnection db = HalContext.getDB(); - DataNode root = new DataNode(DataNode.DataType.List); - - // -------------------------------------- - // Get Action - // -------------------------------------- - - String[] req_ids = new String[0]; - if (request.get("id") != null) - req_ids = request.get("id").split(","); - - // Filter devices - - List rooms = new ArrayList<>(); - for (Room room : Room.getRooms(db)) { - boolean filter_match = true; - - // id filtering - if (!ObjectUtil.isEmpty((Object) req_ids) && !ArrayUtil.contains(req_ids, "" + room.getId())) { - filter_match = false; - } - - // Check the filter - if (filter_match) { - rooms.add(room); - } - } - - // -------------------------------------- - // Generate DataNode - // -------------------------------------- - - for (Room room : rooms) { - DataNode deviceNode = room.getDataNode(); - root.add(deviceNode); - } - - return root; - } -} diff --git a/hal-core/src/se/hal/page/api/SensorApiEndpoint.java b/hal-core/src/se/hal/page/api/SensorApiEndpoint.java deleted file mode 100644 index f9992105..00000000 --- a/hal-core/src/se/hal/page/api/SensorApiEndpoint.java +++ /dev/null @@ -1,158 +0,0 @@ -package se.hal.page.api; - -import se.hal.HalContext; -import se.hal.daemon.SensorDataAggregatorDaemon; -import se.hal.intf.HalApiEndpoint; -import se.hal.struct.Sensor; -import se.hal.util.AggregateDataListSqlResult; -import se.hal.util.UTCTimeUtility; -import zutil.ArrayUtil; -import zutil.ObjectUtil; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.parser.DataNode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.logging.Logger; - -/** - * RESTish API for accessing and managing Sensors. - * For web interface definition see the OpenApi definition hal-core/resources/web/api/doc.html - */ -public class SensorApiEndpoint extends HalApiEndpoint { - private static final Logger logger = LogUtil.getLogger(); - - - public SensorApiEndpoint() { - super("api/sensor"); - } - - @Override - public DataNode jsonRespond( - Map session, - Map cookie, - Map request) throws Exception{ - - DBConnection db = HalContext.getDB(); - DataNode root = new DataNode(DataNode.DataType.List); - - // -------------------------------------- - // Get Action - // -------------------------------------- - - String[] reqIds = new String[0]; - if (request.get("id") != null) - reqIds = request.get("id").split(","); - - String reqConfigType = request.get("configType"); - String reqDataType = request.get("dataType"); - - - List sensors = new ArrayList<>(); - for (Sensor sensor : Sensor.getSensors(db)) { - boolean filter_match = true; - - // id filtering - if (!ObjectUtil.isEmpty((Object) reqIds) && !ArrayUtil.contains(reqIds, "" + sensor.getId())) { - filter_match = false; - } - - // device type filtering - if (!ObjectUtil.isEmpty(reqConfigType) && - !sensor.getDeviceConfig().getClass().getSimpleName().equals(reqConfigType)) { - filter_match = false; - } - - // data type filtering - if (!ObjectUtil.isEmpty(reqDataType) && - !sensor.getDeviceConfig().getDeviceDataClass().getSimpleName().equals(reqDataType)) { - filter_match = false; - } - - // Check the filter - if (filter_match) { - sensors.add(sensor); - } - } - - // -------------------------------------- - // Was aggregated data requested - // -------------------------------------- - - SensorDataAggregatorDaemon.AggregationPeriodLength aggrType = null; - long aggregationLength = -1; - - if (request.get("aggregation") != null) { - switch (request.get("aggregation")) { - case "minute": - aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.FIVE_MINUTES; - aggregationLength = UTCTimeUtility.DAY_IN_MS; - break; - case "hour": - aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.HOUR; - aggregationLength = UTCTimeUtility.WEEK_IN_MS; - break; - case "day": - aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.DAY; - aggregationLength = UTCTimeUtility.INFINITY; - break; - case "week": - aggrType = SensorDataAggregatorDaemon.AggregationPeriodLength.WEEK; - aggregationLength = UTCTimeUtility.INFINITY; - break; - } - } - - // -------------------------------------- - // Generate DataNode - // -------------------------------------- - - for (Sensor sensor : sensors) { - DataNode deviceNode = sensor.getDataNode(); - - if (aggregationLength > 0) { - DataNode aggregateNode = getAggregateDataNode(aggregationLength, - AggregateDataListSqlResult.getAggregateDataForPeriod(db, sensor, aggrType, aggregationLength)); - deviceNode.set("aggregate", aggregateNode); - } - - deviceNode.set("type", "Sensor"); - root.add(deviceNode); - } - - return root; - } - - private DataNode getAggregateDataNode(long endTime, List dataList) { - DataNode aggregateNode = new DataNode(DataNode.DataType.Map); - DataNode timestampNode = new DataNode(DataNode.DataType.List); - DataNode dataNode = new DataNode(DataNode.DataType.List); - - // end timestamp - if (endTime != UTCTimeUtility.INFINITY) { - timestampNode.add(System.currentTimeMillis() - endTime); - dataNode.add((String)null); - } - - // actual data - for (AggregateDataListSqlResult.AggregateData data : dataList) { - timestampNode.add(data.timestamp); - if (data.data == null || Float.isNaN(data.data)) - dataNode.add((String)null); - else - dataNode.add(data.data); - } - - // start timestamp - timestampNode.add(System.currentTimeMillis()); - dataNode.add((String)null); - - aggregateNode.set("timestamps", timestampNode); - aggregateNode.set("data", dataNode); - - return aggregateNode; - } - -} diff --git a/hal-core/src/se/hal/plugin.json b/hal-core/src/se/hal/plugin.json deleted file mode 100644 index c3ce564f..00000000 --- a/hal-core/src/se/hal/plugin.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "version": 1.0, - "name": "Hal-Core", - "description": "Plugin contains core logic for running Hal.", - "interfaces": [ - {"se.hal.intf.HalDatabaseUpgrader": "se.hal.HalCoreDatabaseUpgrader"}, - - {"se.hal.intf.HalAbstractControllerManager": "se.hal.EventControllerManager"}, - {"se.hal.intf.HalAbstractControllerManager": "se.hal.SensorControllerManager"}, - - {"se.hal.intf.HalDaemon": "se.hal.daemon.HalMulticastDnsDaemon"}, - {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataAggregatorDaemon"}, - {"se.hal.intf.HalDaemon": "se.hal.daemon.SensorDataCleanupDaemon"}, - - {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.AlertApiEndpoint"}, - {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.EventApiEndpoint"}, - {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.MapApiEndpoint"}, - {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.RoomApiEndpoint"}, - {"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"}, - - {"se.hal.intf.HalJavascriptModule": "se.hal.page.JavascriptModules"}, - {"se.hal.intf.HalWebPage": "se.hal.page.EventConfigWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.PropertyConfigWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.PluginConfigWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.MapWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.RoomConfigWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.SensorOverviewWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.SensorConfigWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.TriggerWebPage"}, - {"se.hal.intf.HalWebPage": "se.hal.page.UserConfigWebPage"}, - - - {"se.hal.intf.HalTrigger": "se.hal.trigger.DateTimeTrigger"}, - {"se.hal.intf.HalTrigger": "se.hal.trigger.EventTrigger"}, - {"se.hal.intf.HalTrigger": "se.hal.trigger.SensorTrigger"}, - {"se.hal.intf.HalTrigger": "se.hal.trigger.TimerTrigger"}, - - {"se.hal.intf.HalAction": "se.hal.action.AlertAction"}, - {"se.hal.intf.HalAction": "se.hal.action.DismissRoomAlertAction"}, - {"se.hal.intf.HalAction": "se.hal.action.RoomAlertAction"}, - {"se.hal.intf.HalAction": "se.hal.action.SendEventAction"}, - {"se.hal.intf.HalAction": "se.hal.action.TelegramMessageAction"} - ] -} diff --git a/hal-core/src/se/hal/struct/Action.java b/hal-core/src/se/hal/struct/Action.java deleted file mode 100644 index f4224c8a..00000000 --- a/hal-core/src/se/hal/struct/Action.java +++ /dev/null @@ -1,37 +0,0 @@ -package se.hal.struct; - -import se.hal.intf.HalAction; -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanObjectDSO; - -import java.sql.SQLException; - -/** - * Defines a action that will be executed - */ -@DBBean.DBTable(value = "action", superBean = true) -public class Action extends DBBeanObjectDSO{ - - - public static Action getAction(DBConnection db, long id) throws SQLException { - return DBBean.load(db, Action.class, id); - } - - - public Action() { } - public Action(HalAction action) { - this.setObject(action); - } - - - - /** - * Executes this specific action - */ - public void execute(){ - if (getObject() != null) - getObject().execute(); - } - -} diff --git a/hal-core/src/se/hal/struct/PluginConfig.java b/hal-core/src/se/hal/struct/PluginConfig.java deleted file mode 100644 index 1be7a297..00000000 --- a/hal-core/src/se/hal/struct/PluginConfig.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct; - -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanSQLResultHandler; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - - -@DBBean.DBTable(value="plugin") -public class PluginConfig extends DBBean { - private String name; - private boolean enabled; - - - /** - * @return a PluginConfig bean for the specific plugin name. - */ - public static PluginConfig getPluginConfig(DBConnection db, String name) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement( "SELECT plugin.* FROM plugin WHERE name == ?" ); - stmt.setString(1, name); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(PluginConfig.class, db)); - } - - - public PluginConfig() {} - public PluginConfig(String name) { - this.name = name; - } - - - public String getName() { - return name; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } -} diff --git a/hal-core/src/se/hal/struct/Room.java b/hal-core/src/se/hal/struct/Room.java deleted file mode 100644 index 4789014b..00000000 --- a/hal-core/src/se/hal/struct/Room.java +++ /dev/null @@ -1,97 +0,0 @@ -package se.hal.struct; - -import se.hal.page.api.AlertApiEndpoint; -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanSQLResultHandler; -import zutil.parser.DataNode; -import zutil.ui.UserMessageManager.UserMessage; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.List; - -/** - * Class represents a physical room. - */ -public class Room extends DBBean { - - private transient UserMessage roomAlert; - - private String name = ""; - - @DBColumn("map_x") - private double mapX = 0; - @DBColumn("map_y") - private double mapY = 0; - @DBColumn("map_width") - private double mapWidth = 10.0; - @DBColumn("map_height") - private double mapHeight = 10.0; - - public static List getRooms(DBConnection db) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM room" ); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(Room.class, db) ); - } - - public static Room getRoom(DBConnection db, int id) throws SQLException { - return DBBean.load(db, Room.class, id); - } - - - public UserMessage getRoomAlert() { - return roomAlert; - } - public void clearRoomAlert() { - setRoomAlert(null); - } - public void setRoomAlert(UserMessage roomAlert) { - this.roomAlert = roomAlert; - } - - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - - public double getMapX() { - return mapX; - } - public double getMapY() { - return mapY; - } - public double getMapWidth() { - return mapWidth; - } - public double getMapHeight() { - return mapHeight; - } - - public void setMapCoordinates(double x, double y, double width, double height) { - this.mapX = x; - this.mapY = y; - this.mapWidth = width; - this.mapHeight = height; - } - - public DataNode getDataNode() { - DataNode rootNode = new DataNode(DataNode.DataType.Map); - rootNode.set("id", getId()); - rootNode.set("name", getName()); - - DataNode mapNode = rootNode.set("map", DataNode.DataType.Map); - mapNode.set("x", getMapX()); - mapNode.set("y", getMapY()); - mapNode.set("width", getMapWidth()); - mapNode.set("height", getMapHeight()); - - if (roomAlert != null) { - DataNode alertNode = AlertApiEndpoint.getUserMessageDataNode(roomAlert); - rootNode.set("alert", alertNode); - } - - return rootNode; - } -} diff --git a/hal-core/src/se/hal/struct/Trigger.java b/hal-core/src/se/hal/struct/Trigger.java deleted file mode 100644 index 34bbdc41..00000000 --- a/hal-core/src/se/hal/struct/Trigger.java +++ /dev/null @@ -1,50 +0,0 @@ -package se.hal.struct; - -import se.hal.intf.HalTrigger; -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanObjectDSO; - -import java.sql.SQLException; - -/** - * A class that declares a trigger/condition that - * needs to be validated before an action can be run - */ -@DBBean.DBTable(value = "trigger", superBean = true) -public class Trigger extends DBBeanObjectDSO{ - - - - public static Trigger getTrigger(DBConnection db, long id) throws SQLException { - return DBBean.load(db, Trigger.class, id); - } - - - - public Trigger() { } - public Trigger(HalTrigger trigger) { - this.setObject(trigger); - } - - - /** - * Evaluates if this trigger has passed. If the trigger is - * true then this method will return true until the {@link #reset()} - * method is called. - */ - public boolean evaluate(){ - if (getObject() != null) - return getObject().evaluate(); - return false; - } - - /** - * Reset the evaluation to false. - */ - public void reset(){ - if (getObject() != null) - getObject().reset(); - } - -} diff --git a/hal-core/src/se/hal/struct/TriggerFlow.java b/hal-core/src/se/hal/struct/TriggerFlow.java deleted file mode 100644 index 2526c76d..00000000 --- a/hal-core/src/se/hal/struct/TriggerFlow.java +++ /dev/null @@ -1,133 +0,0 @@ -package se.hal.struct; - -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanSQLResultHandler; -import zutil.db.handler.SimpleSQLResult; -import zutil.log.LogUtil; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -/** - * A class that encapsulates triggerList and their actionList. - * TODO: Bad class name, should be renamed when we come up with a better one - */ -@DBBean.DBTable("trigger_flow") -public class TriggerFlow extends DBBean { - private static final Logger logger = LogUtil.getLogger(); - - private boolean enabled = true; - private String name = ""; - - @DBLinkTable(beanClass=Trigger.class, table="trigger", idColumn = "flow_id") - private List triggerList = new ArrayList<>(); - @DBLinkTable(beanClass=Action.class, table="action", idColumn = "flow_id") - private List actionList = new ArrayList<>(); - - - public static List getTriggerFlows(DBConnection db) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement("SELECT * FROM trigger_flow"); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(TriggerFlow.class, db)); - } - public static TriggerFlow getTriggerFlow(DBConnection db, int id) throws SQLException { - return DBBean.load(db, TriggerFlow.class, id); - } - /** - * Looks up the parent TriggerFlow for the specified Trigger - */ - public static TriggerFlow getTriggerFlow(DBConnection db, Trigger trigger) throws SQLException { - return getParentFlow(db, "trigger", trigger); - } - /** - * Looks up the parent TriggerFlow for the specified Action - */ - public static TriggerFlow getTriggerFlow(DBConnection db, Action action) throws SQLException { - return getParentFlow(db, "action", action); - } - private static TriggerFlow getParentFlow(DBConnection db, String table, DBBean subObj) throws SQLException { - if (subObj.getId() == null) - return null; - Integer flowId = db.exec("SELECT flow_id FROM "+table+" WHERE id=="+subObj.getId(), - new SimpleSQLResult()); - if (flowId == null) - return null; - return TriggerFlow.getTriggerFlow(db, flowId); - } - - - public boolean isEnabled() { - return enabled; - } - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - - - public void addTrigger(Trigger trigger) { - triggerList.add(trigger); - } - public List getTriggers() { - return triggerList; - } - public void removeTrigger(Trigger trigger) { - triggerList.remove(trigger); - } - - public void addAction(Action action) { - actionList.add(action); - } - public List getActions() { - return actionList; - } - public void removeAction(Action action) { - actionList.remove(action); - } - - /** - * @return true if any one of the triggerList evaluate to true, - * false if there are no triggerList added. - * Note: this method will not execute any actionList - */ - public boolean evaluate(){ - if (triggerList.isEmpty() || !enabled) - return false; - for (Trigger trigger : triggerList){ - if (!trigger.evaluate()) - return false; - } - return true; - } - - /** - * Executes the associated actionList in this flow - */ - public void execute(){ - if (!enabled) - return; - - logger.info("Executing flow(id: " + getId() + "): " + getName()); - - for (Action action : actionList){ - action.execute(); - } - } - - /** - * Resets all trigger evaluations - */ - public void reset() { - for (Trigger trigger : triggerList){ - trigger.reset(); - } - } -} diff --git a/hal-core/src/se/hal/struct/User.java b/hal-core/src/se/hal/struct/User.java deleted file mode 100644 index d6cd7bd9..00000000 --- a/hal-core/src/se/hal/struct/User.java +++ /dev/null @@ -1,101 +0,0 @@ -package se.hal.struct; - -import zutil.api.Gravatar; -import zutil.db.DBConnection; -import zutil.db.bean.DBBean; -import zutil.db.bean.DBBeanSQLResultHandler; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.List; - -@DBBean.DBTable("user") -public class User extends DBBean{ - - private String username; - private String email; - private String address; - private int external; - - private String hostname; - private int port; - - - - public static List getExternalUsers(DBConnection db) throws SQLException{ - PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user WHERE user.external == 1" ); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(User.class, db) ); - } - - public static User getLocalUser(DBConnection db) throws SQLException{ - PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user WHERE user.external == 0" ); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(User.class, db) ); - } - - public static List getUsers(DBConnection db) throws SQLException{ - PreparedStatement stmt = db.getPreparedStatement( "SELECT * FROM user" ); - return DBConnection.exec(stmt, DBBeanSQLResultHandler.createList(User.class, db) ); - } - - public static User getUser(DBConnection db, int id) throws SQLException { - return DBBean.load(db, User.class, id); - } - - - /** - * Will delete this user and all its Sensors - */ - @Override - public void delete(DBConnection db) throws SQLException { - List sensorList = Sensor.getSensors(db, this); - for (Sensor sensor : sensorList){ - sensor.delete(db); - } - super.delete(db); - } - - - public String getUsername() { - return username; - } - public void setUsername(String name) { - this.username = name; - } - public String getEmail() { - return email; - } - public void setEmail(String email) { - this.email = email; - } - public String getAvatarUrl(){ - return Gravatar.getImageUrl(email, 130); - } - public String getLargeAvatarUrl(){ - return Gravatar.getImageUrl(email, 250); - } - public String getAddress() { - return address; - } - public void setAddress(String address) { - this.address = address; - } - public boolean isExternal() { - return external > 0; - } - public void setExternal(boolean external) { - this.external = (external? 1:0 ); - } - public String getHostname() { - return hostname; - } - public void setHostname(String hostname) { - this.hostname = hostname; - } - public int getPort() { - return port; - } - public void setPort(int port) { - this.port = port; - } - -} diff --git a/hal-core/src/se/hal/struct/devicedata/AvailabilityEventData.java b/hal-core/src/se/hal/struct/devicedata/AvailabilityEventData.java deleted file mode 100644 index 3a8b70d2..00000000 --- a/hal-core/src/se/hal/struct/devicedata/AvailabilityEventData.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2015 Ziver - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalEventData; - - -public class AvailabilityEventData extends HalEventData { - - private boolean available; - - - public AvailabilityEventData() { } - public AvailabilityEventData(boolean available, long timestamp) { - this.available = available; - this.setTimestamp(timestamp); - } - - public void setAvailable(){ - available = true; - } - public void setUnavailable(){ - available = false; - } - public void toggle(){ - available = !available; - } - - public boolean isAvailable(){ - return available; - } - - @Override - public String toString(){ - return available ? "Available" : "Unavailable"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - return (available ? 1.0 : 0.0); - } - - @Override - public void setData(double enabled) { - this.available = enabled > 0; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/ColorEventData.java b/hal-core/src/se/hal/struct/devicedata/ColorEventData.java deleted file mode 100644 index 9a072658..00000000 --- a/hal-core/src/se/hal/struct/devicedata/ColorEventData.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2015 Ziver - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalEventData; -import zutil.ColorUtil; - -import java.awt.color.ColorSpace; - -/** - * Represents a color based event. - */ -public class ColorEventData extends HalEventData { - private static final ColorSpace cieXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); - - private int red; - private int green; - private int blue; - - - public ColorEventData() { } - - /** - * Create object based on sRGB. - */ - public ColorEventData(String hex, long timestamp) { - int[] rgb = ColorUtil.getRgbFromHexString(hex); - this.red = rgb[0]; - this.green = rgb[1]; - this.blue = rgb[2]; - this.setTimestamp(timestamp); - } - /** - * Create object based on sRGB. - */ - public ColorEventData(int red, int green, int blue, long timestamp) { - this.red = red; - this.green = green; - this.blue = blue; - this.setTimestamp(timestamp); - } - /** - * Create object based on CIE XYZ. - */ - public static ColorEventData createFromCieXYZ(float x, float y, float z, long timestamp) { - float[] rgb = cieXYZ.toRGB(new float[]{x, y, z}); - return new ColorEventData((int) rgb[0], (int) rgb[0], (int) rgb[0], timestamp); - } - /** - * Create object based on HLS. - */ - public static ColorEventData createFromHLS(float h, float l, float s, long timestamp) { - return null; - } - - - public int getRed() { - return red; - } - public int getGreen() { - return green; - } - public int getBlue() { - return blue; - } - - public double getHue() { - return ColorUtil.getHue(red, green, blue); - } - public double getSaturation() { - return ColorUtil.getSaturation(red, green, blue); - } - public double getLightness() { - return ColorUtil.getLightness(red, green, blue); - } - - public String getHex() { - return ColorUtil.getHexString(red, green, blue); - } - - @Override - public String toString(){ - return getHex(); //return "Red: " + red + ", Green: " + green + ", Blue: " + blue; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - int rgb = 0; - rgb |= 0xFF0000 & (red << 16); - rgb |= 0x00FF00 & (green << 8); - rgb |= 0x0000FF & (blue); - return rgb; - } - - @Override - public void setData(double data) { - int rgb = (int) data; - red = 0xFF & (rgb >> 16); - green = 0xFF & (rgb >> 8); - blue = 0xFF & (rgb); - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/CostSensorData.java b/hal-core/src/se/hal/struct/devicedata/CostSensorData.java deleted file mode 100644 index 219a1806..00000000 --- a/hal-core/src/se/hal/struct/devicedata/CostSensorData.java +++ /dev/null @@ -1,36 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class CostSensorData extends HalSensorData { - - private double cost; - - - public CostSensorData(){} - public CostSensorData(double cost, long timestamp){ - this.cost = cost; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return cost + ""; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - return cost; - } - - @Override - public void setData(double cost) { - this.cost = cost; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/CurrentSensorData.java b/hal-core/src/se/hal/struct/devicedata/CurrentSensorData.java deleted file mode 100644 index ae67a0f2..00000000 --- a/hal-core/src/se/hal/struct/devicedata/CurrentSensorData.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class CurrentSensorData extends HalSensorData { - - private double current; - - - public CurrentSensorData() { } - public CurrentSensorData(double current, long timestamp) { - this.current = current; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - if (current < 0.1) - return current * 100 + " mA"; - return current + " A"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return number representing Volts measured - */ - @Override - public double getData() { - return current; - } - - @Override - public void setData(double current){ - this.current = current; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/IlluminanceSensorData.java b/hal-core/src/se/hal/struct/devicedata/IlluminanceSensorData.java deleted file mode 100644 index 71055bd3..00000000 --- a/hal-core/src/se/hal/struct/devicedata/IlluminanceSensorData.java +++ /dev/null @@ -1,42 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class IlluminanceSensorData extends HalSensorData { - - private double lux; - - - public IlluminanceSensorData(){} - public IlluminanceSensorData(double lux, long timestamp){ - this.lux = lux; - this.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return lux + " lux"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return the light intensity in lux - */ - @Override - public double getData() { - return lux; - } - - /** - * @param data set the light intensity in lux - */ - @Override - public void setData(double data) { - this.lux = data; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/LevelEventData.java b/hal-core/src/se/hal/struct/devicedata/LevelEventData.java deleted file mode 100644 index 4bf35638..00000000 --- a/hal-core/src/se/hal/struct/devicedata/LevelEventData.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2015 Ziver - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalEventData; - -/** - * Represents a percentage based level event. - */ -public class LevelEventData extends HalEventData { - - private double percent; - - - public LevelEventData() { } - /** - * @param percent a percentage value (from 0.0 to 1.0). - * @param timestamp a timestamp value when this event was triggered. - */ - public LevelEventData(double percent, long timestamp) { - setData(percent); - this.setTimestamp(timestamp); - } - - /** - * @return percentage in integer format (from 0 to 100) - */ - public int getPercent() { - return (int) Math.floor(percent * 100); - } - - @Override - public String toString(){ - return getPercent() + "%"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return a percentage value (from 0.0 to 1.0) - */ - @Override - public double getData() { - return percent; - } - - @Override - public void setData(double data) { - this.percent = data; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/OccupancyEventData.java b/hal-core/src/se/hal/struct/devicedata/OccupancyEventData.java deleted file mode 100644 index 4dd968d3..00000000 --- a/hal-core/src/se/hal/struct/devicedata/OccupancyEventData.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2015 Ziver - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalEventData; - - -public class OccupancyEventData extends HalEventData { - - private boolean isOccupied; - - - public OccupancyEventData() { } - public OccupancyEventData(boolean isOccupied, long timestamp) { - this.isOccupied = isOccupied; - this.setTimestamp(timestamp); - } - - public void setOccupied(){ - this.isOccupied = true; - } - public void setOccupied(boolean isOccupied){ - this.isOccupied = isOccupied; - } - public void setUnoccupied(){ - this.isOccupied = false; - } - public void toggle(){ - isOccupied = !isOccupied; - } - - public boolean isOccupied(){ - return isOccupied; - } - - @Override - public String toString(){ - return isOccupied ? "Occupied" : "Unoccupied"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - return (isOccupied ? 1.0 : 0.0); - } - - @Override - public void setData(double data) { - this.isOccupied = data > 0; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/OpenClosedEventData.java b/hal-core/src/se/hal/struct/devicedata/OpenClosedEventData.java deleted file mode 100644 index 4324f745..00000000 --- a/hal-core/src/se/hal/struct/devicedata/OpenClosedEventData.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2015 Ziver - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalEventData; - - -/** - * Represents an 'Opened' or 'Closed' state based event. - */ -public class OpenClosedEventData extends HalEventData { - - private boolean isOpen; - - - public OpenClosedEventData() { } - public OpenClosedEventData(boolean isOpen, long timestamp) { - this.isOpen = isOpen; - this.setTimestamp(timestamp); - } - - public void setOpen(){ - isOpen = true; - } - public void setClose(){ - isOpen = false; - } - public void toggle(){ - isOpen = !isOpen; - } - - public boolean isOpen(){ - return isOpen; - } - - @Override - public String toString(){ - return isOpen ? "Open" : "Closed"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - return (isOpen ? 1.0 : 0.0); - } - - @Override - public void setData(double data) { - this.isOpen = data > 0; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/ParticulateMatterSensorData.java b/hal-core/src/se/hal/struct/devicedata/ParticulateMatterSensorData.java deleted file mode 100644 index c0660e0e..00000000 --- a/hal-core/src/se/hal/struct/devicedata/ParticulateMatterSensorData.java +++ /dev/null @@ -1,42 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class ParticulateMatterSensorData extends HalSensorData { - - private double particulateMatter; - - - public ParticulateMatterSensorData(){} - public ParticulateMatterSensorData(double particulateMatter, long timestamp){ - this.particulateMatter = particulateMatter; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return particulateMatter + " µg/m3"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return the particulate matter - */ - @Override - public double getData() { - return particulateMatter; - } - - /** - * @param particulateMatter the particulate matter to set - */ - @Override - public void setData(double particulateMatter) { - this.particulateMatter = particulateMatter; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java b/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java deleted file mode 100644 index 29fed6ce..00000000 --- a/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java +++ /dev/null @@ -1,42 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class PressureSensorData extends HalSensorData { - - private double pressure; - - - public PressureSensorData(){} - public PressureSensorData(double pressure, long timestamp){ - super.setTimestamp(timestamp); - this.pressure = pressure; - } - - - @Override - public String toString(){ - return pressure + " hPa"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return pressure in degrees hPa - */ - @Override - public double getData() { - return pressure; - } - - /** - * @param pressure the temperature to set in degrees hPa - */ - @Override - public void setData(double pressure) { - this.pressure = pressure; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/PriceSensorData.java b/hal-core/src/se/hal/struct/devicedata/PriceSensorData.java deleted file mode 100644 index 07145686..00000000 --- a/hal-core/src/se/hal/struct/devicedata/PriceSensorData.java +++ /dev/null @@ -1,36 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class PriceSensorData extends HalSensorData { - - private double price; - - - public PriceSensorData(){} - public PriceSensorData(double price, long timestamp){ - this.price = price; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return price + ""; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - @Override - public double getData() { - return price; - } - - @Override - public void setData(double price) { - this.price = price; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/RadonSensorData.java b/hal-core/src/se/hal/struct/devicedata/RadonSensorData.java deleted file mode 100644 index a44b41c6..00000000 --- a/hal-core/src/se/hal/struct/devicedata/RadonSensorData.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2025 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - -public class RadonSensorData extends HalSensorData { - - private double radon; - - - public RadonSensorData(){} - public RadonSensorData(double radon, long timestamp){ - this.radon = radon; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return radon + " Bq/m³"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return radon measurement in Bq/m3 - */ - @Override - public double getData() { - return radon; - } - - /** - * @param radon the radon measurement in Bq/m3 - */ - @Override - public void setData(double radon) { - this.radon = radon; - } -} diff --git a/hal-core/src/se/hal/struct/devicedata/VoltageSensorData.java b/hal-core/src/se/hal/struct/devicedata/VoltageSensorData.java deleted file mode 100644 index 74207515..00000000 --- a/hal-core/src/se/hal/struct/devicedata/VoltageSensorData.java +++ /dev/null @@ -1,39 +0,0 @@ -package se.hal.struct.devicedata; - -import se.hal.intf.HalSensorData; - - -public class VoltageSensorData extends HalSensorData { - - private double voltage; - - - public VoltageSensorData() { } - public VoltageSensorData(double voltage, long timestamp) { - this.voltage = voltage; - super.setTimestamp(timestamp); - } - - - @Override - public String toString(){ - return voltage+" V"; - } - - // ---------------------------------------- - // Storage methods - // ---------------------------------------- - - /** - * @return number representing Volts measured - */ - @Override - public double getData() { - return voltage; - } - - @Override - public void setData(double voltage){ - this.voltage = voltage; - } -} diff --git a/hal-core/src/se/hal/trigger/DateTimeTrigger.java b/hal-core/src/se/hal/trigger/DateTimeTrigger.java deleted file mode 100644 index 128084b6..00000000 --- a/hal-core/src/se/hal/trigger/DateTimeTrigger.java +++ /dev/null @@ -1,65 +0,0 @@ -package se.hal.trigger; - -import se.hal.intf.HalTrigger; -import zutil.CronTimer; -import zutil.ObjectUtil; -import zutil.ui.conf.Configurator; - -import java.text.SimpleDateFormat; - - -public class DateTimeTrigger implements HalTrigger,Configurator.PostConfigurationActionListener { - - @Configurator.Configurable("Minute (Cron format)") - private String minute = "00"; - @Configurator.Configurable("Hour (Cron format)") - private String hour = "12"; - @Configurator.Configurable("Day of the Month (Cron format)") - private String dayOfMonth = "*"; - @Configurator.Configurable("Month (1-12 or Cron format)") - private String month = "*"; - @Configurator.Configurable("Day of the Week (1-7 or Cron format)") - private String dayOfWeek = "*"; - @Configurator.Configurable("Year (Cron format)") - private String year = "*"; - - private transient CronTimer cronTimer; - private transient long timeOut = -1; - - - @Override - public void postConfigurationAction(Configurator configurator, Object obj) { - if (ObjectUtil.isEmpty(minute)) minute = "*"; - if (ObjectUtil.isEmpty(hour)) hour = "*"; - if (ObjectUtil.isEmpty(dayOfMonth)) dayOfMonth = "*"; - if (ObjectUtil.isEmpty(month)) month = "*"; - if (ObjectUtil.isEmpty(dayOfWeek)) dayOfWeek = "*"; - if (ObjectUtil.isEmpty(year)) year = "*"; - - cronTimer = new CronTimer(minute, hour, dayOfMonth, month, dayOfWeek, year); - reset(); - } - - @Override - public boolean evaluate() { - if (cronTimer == null) - return false; - // have we passed the majority of the minute? then get next timeout - if (System.currentTimeMillis() - timeOut > 50*1000) - reset(); - return timeOut <= System.currentTimeMillis(); - } - - @Override - public void reset() { - if (cronTimer != null) - timeOut = cronTimer.next(); - } - - public String toString(){ - return //"Cron: \""+minute+" "+hour+" "+dayOfMonth+" "+month+" "+dayOfWeek+" "+year+"\" "+ - "Next timeout: "+ - (timeOut>0 ? new SimpleDateFormat("yyyy-MM-dd HH:mm").format(timeOut) : timeOut); - } - -} diff --git a/hal-core/src/se/hal/trigger/DeviceTrigger.java b/hal-core/src/se/hal/trigger/DeviceTrigger.java deleted file mode 100644 index a1029405..00000000 --- a/hal-core/src/se/hal/trigger/DeviceTrigger.java +++ /dev/null @@ -1,65 +0,0 @@ -package se.hal.trigger; - -import se.hal.TriggerManager; -import se.hal.intf.*; -import zutil.ui.conf.Configurator; -import zutil.ui.conf.Configurator.PostConfigurationActionListener; -import zutil.ui.conf.Configurator.PreConfigurationActionListener; - -/** - * An abstract class that implements generic device data logic - */ -public abstract class DeviceTrigger implements HalTrigger, - PreConfigurationActionListener, - PostConfigurationActionListener, - HalDeviceReportListener { - - @Configurator.Configurable("Trigger only on change") - protected boolean triggerOnChange = true; - @Configurator.Configurable("Trigger when data equals") - protected double expectedData; - - private transient HalDeviceData receivedData; - - - - @Override - public void preConfigurationAction(Configurator configurator, Object obj) { - HalAbstractDevice device = getDevice(); - if (device != null) - device.removeReportListener(this); - reset(); - } - @Override - public void postConfigurationAction(Configurator configurator, Object obj) { - HalAbstractDevice device = getDevice(); - if (device != null) - device.addReportListener(this); - } - - @Override - public void reportReceived(HalDeviceConfig deviceConfig, HalDeviceData deviceData) { - receivedData = deviceData; - // Instant trigger evaluation - if (triggerOnChange) - TriggerManager.getInstance().evaluateAndExecute(); - } - - - @Override - public boolean evaluate() { - if (receivedData != null) - return expectedData == receivedData.getData(); - return false; - } - - @Override - public void reset() { - if (triggerOnChange) // only reset if we want to trigger on change - receivedData = null; - } - - - - protected abstract HalAbstractDevice getDevice(); -} diff --git a/hal-core/src/se/hal/trigger/EventTrigger.java b/hal-core/src/se/hal/trigger/EventTrigger.java deleted file mode 100644 index cbb72afb..00000000 --- a/hal-core/src/se/hal/trigger/EventTrigger.java +++ /dev/null @@ -1,30 +0,0 @@ -package se.hal.trigger; - -import se.hal.struct.Event; -import se.hal.util.ConfigEventValueProvider; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.util.logging.Logger; - -public class EventTrigger extends DeviceTrigger{ - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable(value = "Event", valueProvider = ConfigEventValueProvider.class) - protected Event device; - - - @Override - protected Event getDevice() { - return device; - } - - @Override - public String toString() { - Event event = getDevice(); - return "Trigger " + (triggerOnChange ? "on" : "when") + - " event: " + (device != null ? device.getId() : null) + " (" + (event != null ? event.getName() : null) + ")" + - " == " + expectedData; - } - -} diff --git a/hal-core/src/se/hal/trigger/SensorTrigger.java b/hal-core/src/se/hal/trigger/SensorTrigger.java deleted file mode 100644 index b5ecfbd4..00000000 --- a/hal-core/src/se/hal/trigger/SensorTrigger.java +++ /dev/null @@ -1,30 +0,0 @@ -package se.hal.trigger; - -import se.hal.struct.Sensor; -import se.hal.util.ConfigSensorValueProvider; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.util.logging.Logger; - -public class SensorTrigger extends DeviceTrigger { - private static final Logger logger = LogUtil.getLogger(); - - @Configurator.Configurable(value = "Sensor", valueProvider = ConfigSensorValueProvider.class) - protected Sensor device; - - - @Override - protected Sensor getDevice() { - return device; - } - - @Override - public String toString(){ - Sensor sensor = getDevice(); - return "Trigger " + (triggerOnChange ? "on" : "when") + - " sensor: " + sensor.getId() +" (" + (sensor != null ? sensor.getName() : null) + ")" + - " == " + expectedData; - } - -} diff --git a/hal-core/src/se/hal/trigger/TimerTrigger.java b/hal-core/src/se/hal/trigger/TimerTrigger.java deleted file mode 100644 index e33f7743..00000000 --- a/hal-core/src/se/hal/trigger/TimerTrigger.java +++ /dev/null @@ -1,27 +0,0 @@ -package se.hal.trigger; - -import se.hal.intf.HalTrigger; -import zutil.Timer; -import zutil.ui.conf.Configurator; - -public class TimerTrigger implements HalTrigger { - - @Configurator.Configurable("Countdown time (in seconds)") - private int timerTime = 10; // default 10s - private Timer timer; - - - @Override - public boolean evaluate() { - return timer == null || timer.hasTimedOut(); - } - - @Override - public void reset() { - timer = new Timer(timerTime * 1000).start(); - } - - public String toString(){ - return "Timer: "+ timerTime +"s"; - } -} diff --git a/hal-core/src/se/hal/util/AggregateDataListSqlResult.java b/hal-core/src/se/hal/util/AggregateDataListSqlResult.java deleted file mode 100644 index aa0a5536..00000000 --- a/hal-core/src/se/hal/util/AggregateDataListSqlResult.java +++ /dev/null @@ -1,106 +0,0 @@ -package se.hal.util; - -import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength; -import se.hal.struct.Sensor; -import se.hal.struct.devicedata.PowerConsumptionSensorData; -import zutil.db.DBConnection; -import zutil.db.SQLResultHandler; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; - -public class AggregateDataListSqlResult implements SQLResultHandler> { - - public static class AggregateData { - public int id; - public long timestamp; - public Float data; - public String username; - - public AggregateData(int id, long time, Float data, String uname) { - this.id = id; - this.timestamp = time; - this.data = data; - this.username = uname; - } - } - - public static List getAggregateDataForPeriod(DBConnection db, Sensor sensor, AggregationPeriodLength aggrPeriodLength, long ageLimitInMs) throws SQLException { - PreparedStatement stmt = db.getPreparedStatement( - "SELECT user.username as username," - + " sensor.*," - + " sensor_data_aggr.*" - + " FROM sensor_data_aggr, user, sensor" - + " WHERE sensor.id = sensor_data_aggr.sensor_id" - + " AND sensor.id = ?" - + " AND user.id = sensor.user_id" - + " AND user.id = ?" - + " AND timestamp_end-timestamp_start == ?" - + " AND timestamp_start > ?" - + " ORDER BY timestamp_start ASC"); - stmt.setLong(1, sensor.getId()); - stmt.setLong(2, sensor.getUser().getId()); - switch(aggrPeriodLength) { - case SECOND: stmt.setLong(3, UTCTimeUtility.SECOND_IN_MS-1); break; - case MINUTE: stmt.setLong(3, UTCTimeUtility.MINUTE_IN_MS-1); break; - case FIVE_MINUTES: stmt.setLong(3, UTCTimeUtility.FIVE_MINUTES_IN_MS-1); break; - case FIFTEEN_MINUTES: stmt.setLong(3, UTCTimeUtility.FIFTEEN_MINUTES_IN_MS-1); break; - case HOUR: stmt.setLong(3, UTCTimeUtility.HOUR_IN_MS-1); break; - case DAY: stmt.setLong(3, UTCTimeUtility.DAY_IN_MS-1); break; - case WEEK: stmt.setLong(3, UTCTimeUtility.WEEK_IN_MS-1); break; - default: throw new IllegalArgumentException("selected aggrPeriodLength is not supported"); - } - stmt.setLong(4, (System.currentTimeMillis() - ageLimitInMs) ); - return DBConnection.exec(stmt , new AggregateDataListSqlResult(sensor)); - } - - - private Sensor sensor; - - private AggregateDataListSqlResult(Sensor sensor) { - this.sensor = sensor; - } - - - - @Override - public ArrayList handleQueryResult(Statement stmt, ResultSet result) throws SQLException { - ArrayList list = new ArrayList<>(); - long previousTimestampEnd = -1; - while (result.next()) { - - int id = result.getInt("id"); - long timestampStart = result.getLong("timestamp_start"); - long timestampEnd = result.getLong("timestamp_end"); - String username = result.getString("username"); - float confidence = result.getFloat("confidence"); - - // Calculate the data point - float data = result.getFloat("data"); //the "raw" recorded data - float estimatedData = data/confidence; //estimate the "real" value of the data by looking at the confidence value - - // Add null data point to list if one or more periods of data is missing before this - if (previousTimestampEnd != -1 && sensor.getDeviceConfig() != null) { - boolean shortInterval = timestampEnd-timestampStart < sensor.getDeviceConfig().getDataInterval(); - long distance = timestampStart - (previousTimestampEnd + 1); - if (// Only add nulls if the report interval is smaller than the aggregated interval - !shortInterval && distance > 0 || - // Only add nulls if space between aggr is larger than sensor report interval - shortInterval && distance > sensor.getDeviceConfig().getDataInterval()) - list.add(new AggregateData(id, previousTimestampEnd + 1, null /*Float.NaN*/, username)); - } - - if (sensor.getDeviceConfig().getDeviceDataClass() == PowerConsumptionSensorData.class) - estimatedData = (estimatedData/1000f); - list.add(new AggregateData(id, timestampEnd, estimatedData, username)); //add this data point to list - - // Update previous end timestamp - previousTimestampEnd = timestampEnd; - } - return list; - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/ClassConfigurationFacade.java b/hal-core/src/se/hal/util/ClassConfigurationFacade.java deleted file mode 100644 index 0a26ca88..00000000 --- a/hal-core/src/se/hal/util/ClassConfigurationFacade.java +++ /dev/null @@ -1,18 +0,0 @@ -package se.hal.util; - -import zutil.ui.conf.Configurator; -import zutil.ui.conf.Configurator.ConfigurationParam; - -/** - * A Data class used by the dynamic class configuration pages - */ -public class ClassConfigurationFacade { - public Class clazz; - public ConfigurationParam[] params; - - - public ClassConfigurationFacade(Class clazz) { - this.clazz = clazz; - this.params = Configurator.getConfiguration(clazz); - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/ConfigEventValueProvider.java b/hal-core/src/se/hal/util/ConfigEventValueProvider.java deleted file mode 100644 index 2f0d2d28..00000000 --- a/hal-core/src/se/hal/util/ConfigEventValueProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -package se.hal.util; - -import se.hal.HalContext; -import se.hal.struct.Event; -import zutil.Timer; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A value provider that will give all Enum values - */ -public class ConfigEventValueProvider implements Configurator.ConfigValueProvider { - private static final Logger logger = LogUtil.getLogger(); - - protected static int DEVICE_REFRESH_TIME_IN_SECONDS = 3; - - private Map events = new HashMap<>(); - private Timer updateTimer = new Timer(DEVICE_REFRESH_TIME_IN_SECONDS * 1000); - - - public ConfigEventValueProvider() { - refreshEventMap(); - } - - - private void refreshEventMap() { - if (!updateTimer.hasTimedOut()) - return; - - try { - DBConnection db = HalContext.getDB(); - - for (Event event : Event.getLocalEvents(db)) { - events.put(getValue(event), event); - } - - updateTimer.start(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "Unable to retrieve local events.", e); - } - } - - - @Override - public String getValue(Event event) { - return (event != null ? - event.getName() + " (id: " + event.getId() + ")" : - null); - } - - @Override - public List getPossibleValues() { - refreshEventMap(); - return new ArrayList<>(events.keySet()); - } - - @Override - public Event getObject(String value) { - return events.get(value); - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/ConfigSensorValueProvider.java b/hal-core/src/se/hal/util/ConfigSensorValueProvider.java deleted file mode 100644 index 0ab898c9..00000000 --- a/hal-core/src/se/hal/util/ConfigSensorValueProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -package se.hal.util; - -import se.hal.HalContext; -import se.hal.struct.Event; -import se.hal.struct.Sensor; -import zutil.Timer; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static se.hal.util.ConfigEventValueProvider.DEVICE_REFRESH_TIME_IN_SECONDS; - -/** - * A value provider that will give all Enum values - */ -public class ConfigSensorValueProvider implements Configurator.ConfigValueProvider { - private static final Logger logger = LogUtil.getLogger(); - - private Timer updateTimer = new Timer(DEVICE_REFRESH_TIME_IN_SECONDS * 1000); - private Map sensors = new HashMap<>(); - - - public ConfigSensorValueProvider() { - refreshEventMap(); - } - - - private synchronized void refreshEventMap() { - if (!updateTimer.hasTimedOut()) - return; - - try { - DBConnection db = HalContext.getDB(); - - sensors.clear(); - for (Sensor sensor : Sensor.getLocalSensors(db)) { - sensors.put(getValue(sensor), sensor); - } - - updateTimer.start(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "Unable to retrieve Sensor devices.", e); - } - } - - - @Override - public String getValue(Sensor sensor) { - return sensor.getName() + " (id: " + sensor.getId() + ")"; - } - - @Override - public List getPossibleValues() { - refreshEventMap(); - return new ArrayList<>(sensors.keySet()); - } - - @Override - public Sensor getObject(String value) { - return sensors.get(value); - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/DeviceNameComparator.java b/hal-core/src/se/hal/util/DeviceNameComparator.java deleted file mode 100644 index 2162806e..00000000 --- a/hal-core/src/se/hal/util/DeviceNameComparator.java +++ /dev/null @@ -1,23 +0,0 @@ -package se.hal.util; - -import se.hal.intf.HalAbstractDevice; - -import java.util.Comparator; - -/** - * A comparator that compares on the device name. - */ -public class DeviceNameComparator implements Comparator { - private static DeviceNameComparator instance; - - @Override - public int compare(HalAbstractDevice device1, HalAbstractDevice device2) { - return device1.getName().compareTo(device2.getName()); - } - - public static DeviceNameComparator getInstance() { - if (instance == null) - instance = new DeviceNameComparator(); - return instance; - } -} diff --git a/hal-core/src/se/hal/util/HalAcmeDataStore.java b/hal-core/src/se/hal/util/HalAcmeDataStore.java deleted file mode 100644 index f5c23654..00000000 --- a/hal-core/src/se/hal/util/HalAcmeDataStore.java +++ /dev/null @@ -1,116 +0,0 @@ -package se.hal.util; - -import org.shredzone.acme4j.toolbox.AcmeUtils; -import org.shredzone.acme4j.util.KeyPairUtils; -import se.hal.HalContext; -import zutil.io.StringInputStream; -import zutil.log.LogUtil; -import zutil.net.acme.AcmeDataStore; -import zutil.parser.Base64Decoder; -import zutil.parser.Base64Encoder; - -import java.io.*; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.KeyPair; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class HalAcmeDataStore implements AcmeDataStore { - private static final Logger logger = LogUtil.getLogger(); - - private static final String CONFIG_HTTP_EXTERNAL_ACCOUNT_LOCATION = "hal_core.http_external_account_location"; - private static final String CONFIG_HTTP_EXTERNAL_ACCOUNT_KEY = "hal_core.http_external_account_key"; - private static final String CONFIG_HTTP_EXTERNAL_DOMAIN_KEY = "hal_core.http_external_domain_key"; - private static final String CONFIG_HTTP_EXTERNAL_CERTIFICATE = "hal_core.http_external_certificate"; - - - @Override - public URL getAccountLocation() { - try { - if (HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_ACCOUNT_LOCATION)) - return new URL(HalContext.getStringProperty(CONFIG_HTTP_EXTERNAL_ACCOUNT_LOCATION)); - } catch (MalformedURLException e) { - logger.log(Level.SEVERE, "Unable to create account URL.", e); - } - - return null; - } - - @Override - public KeyPair getAccountKeyPair() { - return loadKeyPair(CONFIG_HTTP_EXTERNAL_ACCOUNT_KEY); - } - - @Override - public KeyPair getDomainKeyPair() { - return loadKeyPair(CONFIG_HTTP_EXTERNAL_DOMAIN_KEY); - } - - private KeyPair loadKeyPair(String configName) { - if (HalContext.containsProperty(configName)) { - try { - byte[] data = Base64Decoder.decodeToByte( - HalContext.getStringProperty(configName)); - return KeyPairUtils.readKeyPair(new InputStreamReader(new ByteArrayInputStream(data))); - } catch (IOException e) { - logger.log(Level.SEVERE, "Was unable to load KeyPair from DB.", e); - } - } - return null; - } - - - @Override - public void storeAccountKeyPair(URL accountLocation, KeyPair accountKeyPair) { - HalContext.setProperty(CONFIG_HTTP_EXTERNAL_ACCOUNT_LOCATION, accountLocation.toString()); - storeKeyPair(accountKeyPair, CONFIG_HTTP_EXTERNAL_ACCOUNT_KEY); - } - - @Override - public void storeDomainKeyPair(KeyPair keyPair) { - storeKeyPair(keyPair, CONFIG_HTTP_EXTERNAL_DOMAIN_KEY); - } - - private void storeKeyPair(KeyPair keyPair, String configName) { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutputStreamWriter writer = new OutputStreamWriter(out); - KeyPairUtils.writeKeyPair(keyPair, writer); - - HalContext.setProperty(configName, Base64Encoder.encode(out.toByteArray())); - } catch (IOException e) { - logger.log(Level.SEVERE, "Was unable to store KeyPair to DB.", e); - } - } - - - @Override - public X509Certificate getCertificate() { - if (HalContext.containsProperty(CONFIG_HTTP_EXTERNAL_CERTIFICATE)) { - try { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - X509Certificate certificate = (X509Certificate) factory.generateCertificate( - new StringInputStream(HalContext.getStringProperty(CONFIG_HTTP_EXTERNAL_CERTIFICATE))); - return certificate; - } catch (CertificateException e) { - logger.log(Level.SEVERE, "Was unable to read certificate from DB.", e); - } - } - return null; - } - - @Override - public void storeCertificate(X509Certificate certificate) { - try (StringWriter out = new StringWriter()) { - AcmeUtils.writeToPem(certificate.getEncoded(), AcmeUtils.PemLabel.CERTIFICATE, out); - HalContext.setProperty(CONFIG_HTTP_EXTERNAL_CERTIFICATE, out.toString()); - } catch (IOException | CertificateEncodingException e) { - logger.log(Level.SEVERE, "Was unable to store certificate to DB.", e); - } - } -} diff --git a/hal-core/src/se/hal/util/HalDeviceChangeListener.java b/hal-core/src/se/hal/util/HalDeviceChangeListener.java deleted file mode 100644 index 269aedf1..00000000 --- a/hal-core/src/se/hal/util/HalDeviceChangeListener.java +++ /dev/null @@ -1,52 +0,0 @@ -package se.hal.util; - -import se.hal.HalServer; -import se.hal.intf.HalAbstractControllerManager; -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalDeviceConfig; -import zutil.ui.conf.Configurator; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A listener implementation that will deregister a device and then re-register it on all interested managers. - */ -@SuppressWarnings({"rawtypes", "unchecked"}) -public class HalDeviceChangeListener implements Configurator.PreConfigurationActionListener, Configurator.PostConfigurationActionListener { - /** List of sensors that are currently being reconfigured **/ - private Map> limboDevices = new HashMap<>(); - - - @Override - public void preConfigurationAction(Configurator configurator, HalDeviceConfig deviceConfig) { - List managers = HalServer.getControllerManagers(); - - for (HalAbstractControllerManager manager : managers) { - HalAbstractDevice device = HalDeviceUtil.findDevice(deviceConfig, manager.getRegisteredDevices()); - if (device != null) { - manager.deregister(device); - - if (!limboDevices.containsKey(device)) - limboDevices.put(device, new ArrayList<>(2)); - limboDevices.get(device).add(manager); - } - } - } - - @Override - public void postConfigurationAction(Configurator configurator, HalDeviceConfig deviceConfig) { - HalAbstractDevice device = HalDeviceUtil.findDevice(deviceConfig, new ArrayList<>(limboDevices.keySet())); - List managers = limboDevices.get(device); - - if (managers != null) { - for (HalAbstractControllerManager manager : managers) { - manager.register(device); - } - - limboDevices.remove(device); - } - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/HalDeviceUtil.java b/hal-core/src/se/hal/util/HalDeviceUtil.java deleted file mode 100644 index a56991d1..00000000 --- a/hal-core/src/se/hal/util/HalDeviceUtil.java +++ /dev/null @@ -1,29 +0,0 @@ -package se.hal.util; - -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalDeviceConfig; - -import java.util.List; - -/** - * A class containing utility methods for HalDevice objects. - */ -public class HalDeviceUtil { - - /** - * Method will search a list for a device matching the given configuration object. - * - * @param deviceConfig the configuration object to identify the device by. - * @param list the list to search through. - * @return a HalAbstractDevice object matching the configuration object, null if no device was found. - */ - public static D findDevice(HalDeviceConfig deviceConfig, List list){ - for (int i=0; i registerList = new ArrayList<>(); - - - @Override - public synchronized List getClientRegistries() { - String json = HalContext.getStringProperty(CONFIG_HTTP_EXTERNAL_OAUTH2_REGISTRY); - - if (!ObjectUtil.isEmpty(json)) { - registerList = JSONObjectInputStream.parse(json); - return registerList; - } - return Collections.emptyList(); - } - - @Override - public synchronized void storeClientRegister(OAuth2ClientRegister register) { - registerList.remove(register); - registerList.add(register); - - HalContext.setProperty(CONFIG_HTTP_EXTERNAL_OAUTH2_REGISTRY, - JSONObjectOutputStream.toString(registerList)); - } -} diff --git a/hal-core/src/se/hal/util/ListenerUtil.java b/hal-core/src/se/hal/util/ListenerUtil.java deleted file mode 100644 index 5c5f61bf..00000000 --- a/hal-core/src/se/hal/util/ListenerUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.util; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalDeviceReportListener; -import zutil.log.LogUtil; - -import java.util.List; -import java.util.logging.Logger; -import java.util.logging.Level; - -/** - * Utility class that contains general code for interacting with listeners. - */ -public class ListenerUtil { - private static final Logger logger = LogUtil.getLogger(); - - /** - * Helper function to call {@link HalDeviceReportListener#reportReceived(HalDeviceConfig, HalDeviceData)} on all provided - * listeners with the same input parameters. Exceptions will be handled individually per listener and will not prevent - * other listeners to be called. - * - * @param deviceListeners a list of listeners to be called. - * @param deviceConfig the device config that will be provided on each call. - * @param deviceData the data config that will be provided on each call. - */ - public static void callReportReceived(List deviceListeners, HalDeviceConfig deviceConfig, HalDeviceData deviceData) { - for (HalDeviceReportListener deviceListener : deviceListeners) { - try { - deviceListener.reportReceived(deviceConfig, deviceData); - } catch (Exception e) { - logger.log(Level.WARNING, "Device listener threw exception during new device report, exception is ignored.", e); - } - } - } -} diff --git a/hal-core/src/se/hal/util/RoomValueProvider.java b/hal-core/src/se/hal/util/RoomValueProvider.java deleted file mode 100644 index 8655c0ff..00000000 --- a/hal-core/src/se/hal/util/RoomValueProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -package se.hal.util; - -import se.hal.HalContext; -import se.hal.struct.Room; -import zutil.Timer; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.ui.conf.Configurator; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A value provider that will give all Rooms as selection option - */ -public class RoomValueProvider implements Configurator.ConfigValueProvider { - private static final Logger logger = LogUtil.getLogger(); - - private static int REFRESH_TIME_IN_SECONDS = 3; - - private Map rooms = new HashMap<>(); - private Timer updateTimer = new Timer(REFRESH_TIME_IN_SECONDS * 1000); - - - public RoomValueProvider() { - refreshEventMap(); - } - - - private void refreshEventMap() { - if (!updateTimer.hasTimedOut()) - return; - - try { - DBConnection db = HalContext.getDB(); - - for (Room room : Room.getRooms(db)) { - rooms.put(getValue(room), room); - } - - updateTimer.start(); - } catch (SQLException e) { - logger.log(Level.SEVERE, "Unable to retrieve rooms.", e); - } - } - - - @Override - public String getValue(Room room) { - return (room != null ? - room.getName() + " (id: " + room.getId() + ")" : - null); - } - - @Override - public List getPossibleValues() { - refreshEventMap(); - return new ArrayList<>(rooms.keySet()); - } - - @Override - public Room getObject(String value) { - return rooms.get(value); - } -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/UTCTimePeriod.java b/hal-core/src/se/hal/util/UTCTimePeriod.java deleted file mode 100644 index 8ef0d24f..00000000 --- a/hal-core/src/se/hal/util/UTCTimePeriod.java +++ /dev/null @@ -1,53 +0,0 @@ -package se.hal.util; - -import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength; - -public class UTCTimePeriod{ - private final long start; - private final long end; - private final AggregationPeriodLength periodLength; - - public UTCTimePeriod(long timestamp, AggregationPeriodLength periodLength) { - start = UTCTimeUtility.getTimestampPeriodStart(periodLength, timestamp); - end = UTCTimeUtility.getTimestampPeriodEnd(periodLength, timestamp); - this.periodLength = periodLength; - } - - public long getStartTimestamp() { - return start; - } - - public long getEndTimestamp() { - return end; - } - - public UTCTimePeriod getNextPeriod() { - return new UTCTimePeriod(end+1, periodLength); - } - - public UTCTimePeriod getPreviosPeriod() { - return new UTCTimePeriod(start-1, periodLength); - } - - public boolean containsTimestamp(long timestamp) { - return start <= timestamp && timestamp <= end; - } - - public boolean equals(Object other) { - if (other == null) - return false; - - if (other instanceof UTCTimePeriod) { - UTCTimePeriod o = (UTCTimePeriod)other; - return start == o.start - && end == o.end - && periodLength == o.periodLength; - } - return false; - } - - public String toString() { - return start + "=>" + end + " (" + UTCTimeUtility.getDateString(start) + "=>" + UTCTimeUtility.getDateString(end) + ")"; - } - -} \ No newline at end of file diff --git a/hal-core/src/se/hal/util/UTCTimeUtility.java b/hal-core/src/se/hal/util/UTCTimeUtility.java deleted file mode 100644 index b4a375f7..00000000 --- a/hal-core/src/se/hal/util/UTCTimeUtility.java +++ /dev/null @@ -1,250 +0,0 @@ -package se.hal.util; - -import se.hal.daemon.SensorDataAggregatorDaemon.AggregationPeriodLength; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Locale; -import java.util.TimeZone; - -public class UTCTimeUtility { - public static final TimeZone TIMEZONE = TimeZone.getTimeZone("UTC"); - public static final Locale LOCALE = new Locale("sv","SE"); - - public static final long SECOND_IN_MS = 1000; - public static final long MINUTE_IN_MS = SECOND_IN_MS * 60; - public static final long FIVE_MINUTES_IN_MS = MINUTE_IN_MS * 5; - public static final long FIFTEEN_MINUTES_IN_MS = MINUTE_IN_MS * 15; - public static final long HOUR_IN_MS = MINUTE_IN_MS * 60; - public static final long DAY_IN_MS = HOUR_IN_MS * 24; - public static final long WEEK_IN_MS = DAY_IN_MS * 7; - public static final long INFINITY = Long.MAX_VALUE; //sort of true - - public static long getTimestampPeriodStart(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{ - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(timestamp); - cal.setFirstDayOfWeek(Calendar.MONDAY); - switch(aggrPeriodLength) { - case YEAR: - cal.set(Calendar.DAY_OF_YEAR, 1); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case MONTH: - cal.set(Calendar.DAY_OF_MONTH, 1); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case WEEK: - cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case DAY: - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case HOUR: - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case FIVE_MINUTES: - cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/5)*5); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case FIFTEEN_MINUTES: - cal.set(Calendar.MINUTE, (cal.get(Calendar.MINUTE)/15)*15); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case MINUTE: - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - case SECOND: - cal.set(Calendar.MILLISECOND, 0); - break; - } - return cal.getTimeInMillis(); - } - - public static long getTimestampPeriodEnd(AggregationPeriodLength aggrPeriodLength, long timestamp) throws NumberFormatException{ - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(timestamp); - cal.setFirstDayOfWeek(Calendar.MONDAY); - switch(aggrPeriodLength) { - case YEAR: - cal.set(Calendar.DAY_OF_YEAR, cal.getActualMaximum(Calendar.DAY_OF_YEAR)); - cal.set(Calendar.HOUR_OF_DAY, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case MONTH: - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); - cal.set(Calendar.HOUR_OF_DAY, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case WEEK: - cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); - cal.set(Calendar.HOUR_OF_DAY, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case DAY: - cal.set(Calendar.HOUR_OF_DAY, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case HOUR: - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case FIVE_MINUTES: - cal.set(Calendar.MINUTE, 4+(cal.get(Calendar.MINUTE)/5)*5); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case FIFTEEN_MINUTES: - cal.set(Calendar.MINUTE, 14+(cal.get(Calendar.MINUTE)/15)*15); - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case MINUTE: - cal.set(Calendar.SECOND, 59); - cal.set(Calendar.MILLISECOND, 1000); - break; - case SECOND: - cal.set(Calendar.MILLISECOND, 1000); - break; - } - return cal.getTimeInMillis()-1; //subtract one - } - - public static int getMillisecondInSecondFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.MILLISECOND); - } - - public static int getSecondOfMinuteFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.SECOND); - } - - public static int getMinuteOfHourFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.MINUTE); - } - - public static int getHourOfDayFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.HOUR_OF_DAY); - } - - public static int getDayOfWeekFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.DAY_OF_WEEK); - } - - public static int getDayOfMonthFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.DAY_OF_MONTH); - } - - public static int getDayOfYearFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.DAY_OF_YEAR); - } - - public static int getWeekOfYearFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.WEEK_OF_YEAR); - } - - public static int getMonthOfYearFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.MONTH); - } - - public static int getYearFromTimestamp(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(ms); - return cal.get(Calendar.YEAR); - } - - public static String timeInMsToString(long ms) throws NumberFormatException{ - if (ms < 0) - throw new NumberFormatException("argument must be positive"); - String retval = ""; - int weeks = (int) (ms / WEEK_IN_MS); - if (weeks > 0) { - retval += weeks + "w+"; - } - int days = ((int) (ms / DAY_IN_MS)) % 7; - if (days > 0) { - retval += days + "d+"; - } - int hours = (int) ((ms % DAY_IN_MS) / HOUR_IN_MS); - retval += (hours<10?"0"+hours:hours); - int minutes = (int) ((ms % HOUR_IN_MS) / MINUTE_IN_MS); - retval += ":" + (minutes<10?"0"+minutes:minutes); - int seconds = (int) ((ms % MINUTE_IN_MS) / SECOND_IN_MS); - retval += ":" + (seconds<10?"0"+seconds:seconds); - int milliseconds = (int) (ms % SECOND_IN_MS); - retval += "." + (milliseconds<100?"0"+(milliseconds<10?"0"+milliseconds:milliseconds):milliseconds); - return retval; - } - - public static String getDateString(long timestamp) { - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - format.setTimeZone(TIMEZONE); - Calendar cal = Calendar.getInstance(TIMEZONE, LOCALE); - cal.setTimeInMillis(timestamp); - return format.format(cal.getTime()); - } - -} diff --git a/hal-core/test/se/hal/EventControllerManagerTest.java b/hal-core/test/se/hal/EventControllerManagerTest.java deleted file mode 100644 index e8ecf78a..00000000 --- a/hal-core/test/se/hal/EventControllerManagerTest.java +++ /dev/null @@ -1,152 +0,0 @@ -package se.hal; - -import org.junit.Test; -import se.hal.intf.*; -import se.hal.struct.Event; -import se.hal.struct.devicedata.OnOffEventData; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - - -public class EventControllerManagerTest { - - private EventControllerManager manager = new EventControllerManager(); - - - @Test - public void addAvailableEventDevice(){ - assertEquals(0, manager.getAvailableDeviceConfigs().size()); - - manager.addAvailableDeviceConfig(TestEvent1.class); - assertEquals(1, manager.getAvailableDeviceConfigs().size()); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestEvent1.class)); - - manager.addAvailableDeviceConfig(TestEvent2.class); - assertEquals(2, manager.getAvailableDeviceConfigs().size()); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestEvent1.class)); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestEvent2.class)); - - // Add duplicate Event - manager.addAvailableDeviceConfig(TestEvent1.class); - assertEquals("No duplicate check",2, manager.getAvailableDeviceConfigs().size()); - } - - - @Test - public void registerUnavailableEvent(){ - assertEquals(Collections.EMPTY_LIST, manager.getAvailableDeviceConfigs()); - - Event Event = new Event(); - Event.setDeviceConfig(new TestEvent1()); - manager.register(Event); - assertEquals("No Event registered", Collections.EMPTY_LIST, manager.getRegisteredDevices()); - } - - - @Test - public void registerOneEvent() { - Event Event1 = registerEvent(new TestEvent1()); - assertEquals(1, manager.getRegisteredDevices().size()); - assertTrue(manager.getRegisteredDevices().contains(Event1)); - } - public void registerTwoEvents(){ - Event Event1 = registerEvent(new TestEvent1()); - Event Event2 = registerEvent(new TestEvent2()); - assertEquals(2, manager.getRegisteredDevices().size()); - assertTrue(manager.getRegisteredDevices().contains(Event1)); - assertTrue(manager.getRegisteredDevices().contains(Event2)); - } - - - @Test - public void deregisterEvent(){ - Event Event1 = registerEvent(new TestEvent1()); - manager.deregister(Event1); - assertEquals(Collections.EMPTY_LIST, manager.getRegisteredDevices()); - } - - - // TODO: TC for reportReceived - - - ////////////////////////////////////////////////////////// - private Event registerEvent(HalEventConfig config){ - Event Event = new Event(); - Event.setDeviceConfig(config); - manager.addAvailableDeviceConfig(config.getClass()); - manager.register(Event); - return Event; - } - - public static class TestEvent1 implements HalEventConfig { - - @Override - public Class getDeviceControllerClass() { - return TestController.class; - } - - @Override - public Class getDeviceDataClass() { - return OnOffEventData.class; - } - - @Override - public boolean equals(Object obj) { - return this.equals(obj); - } - } - - public static class TestEvent2 implements HalEventConfig { - - @Override - public Class getDeviceControllerClass() { - return TestController.class; - } - - @Override - public Class getDeviceDataClass() { - return OnOffEventData.class; - } - - @Override - public boolean equals(Object obj) { - return this.equals(obj); - } - } - - public static class TestController implements HalEventController { - int size; - - @Override - public void initialize() { } - - @Override - public void register(HalDeviceConfig event) { - size++; - } - - @Override - public void deregister(HalDeviceConfig event) { - size--; - } - - @Override - public void send(HalEventConfig eventConfig, HalEventData eventData) { - - } - - @Override - public int size() { - return size; - } - - @Override - public void addListener(HalDeviceReportListener listener) { } - - @Override - public void close() { } - } -} \ No newline at end of file diff --git a/hal-core/test/se/hal/SensorControllerManagerTest.java b/hal-core/test/se/hal/SensorControllerManagerTest.java deleted file mode 100644 index 48d3ac9d..00000000 --- a/hal-core/test/se/hal/SensorControllerManagerTest.java +++ /dev/null @@ -1,168 +0,0 @@ -package se.hal; - -import org.junit.Test; -import se.hal.intf.*; -import se.hal.struct.Sensor; -import se.hal.struct.devicedata.HumiditySensorData; -import se.hal.struct.devicedata.TemperatureSensorData; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - - -public class SensorControllerManagerTest { - - private SensorControllerManager manager = new SensorControllerManager(); - - - @Test - public void addAvailableDevice(){ - assertEquals(Collections.EMPTY_LIST, manager.getAvailableDeviceConfigs()); - - manager.addAvailableDeviceConfig(TestSensor1.class); - assertEquals(1, manager.getAvailableDeviceConfigs().size()); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestSensor1.class)); - - manager.addAvailableDeviceConfig(TestSensor2.class); - assertEquals(2, manager.getAvailableDeviceConfigs().size()); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestSensor1.class)); - assertTrue(manager.getAvailableDeviceConfigs().contains(TestSensor2.class)); - - // Add duplicate sensor - manager.addAvailableDeviceConfig(TestSensor1.class); - assertEquals("No duplicate check",2, manager.getAvailableDeviceConfigs().size()); - } - - - @Test - public void registerUnavailableSensor(){ - assertEquals(Collections.EMPTY_LIST, manager.getAvailableDeviceConfigs()); - - Sensor sensor = new Sensor(); - sensor.setDeviceConfig(new TestSensor1()); - manager.register(sensor); - assertEquals("No Sensor registered", Collections.EMPTY_LIST, manager.getRegisteredDevices()); - } - - - @Test - public void registerOneSensor() { - Sensor sensor1 = registerSensor(new TestSensor1()); - assertEquals(1, manager.getRegisteredDevices().size()); - assertTrue(manager.getRegisteredDevices().contains(sensor1)); - } - @Test - public void registerTwoSensors(){ - Sensor sensor1 = registerSensor(new TestSensor1()); - Sensor sensor2 = registerSensor(new TestSensor2()); - assertEquals(2, manager.getRegisteredDevices().size()); - assertTrue(manager.getRegisteredDevices().contains(sensor1)); - assertTrue(manager.getRegisteredDevices().contains(sensor2)); - } - - - @Test - public void deregisterSensor(){ - Sensor sensor1 = registerSensor(new TestSensor1()); - manager.deregister(sensor1); - assertEquals(Collections.EMPTY_LIST, manager.getRegisteredDevices()); - } - - - // TODO: TC for reportReceived - - - ////////////////////////////////////////////////////////// - private Sensor registerSensor(HalSensorConfig config){ - Sensor sensor = new Sensor(); - sensor.setDeviceConfig(config); - manager.addAvailableDeviceConfig(config.getClass()); - manager.register(sensor); - return sensor; - } - - - public static class TestSensor1 implements HalSensorConfig { - - @Override - public long getDataInterval() { - return 0; - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } - - @Override - public Class getDeviceControllerClass() { - return TestController.class; - } - - @Override - public Class getDeviceDataClass() { - return TemperatureSensorData.class; - } - - @Override - public boolean equals(Object obj) { - return this.equals(obj); - } - } - - public static class TestSensor2 implements HalSensorConfig { - - @Override - public long getDataInterval() { - return 0; - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.SUM; - } - - @Override - public Class getDeviceControllerClass() { - return TestController.class; - } - - @Override - public Class getDeviceDataClass() { - return HumiditySensorData.class; - } - - @Override - public boolean equals(Object obj) { - return this.equals(obj); - } - } - - public static class TestController implements HalSensorController { - int size; - - @Override - public void initialize() { } - - @Override - public void register(HalDeviceConfig sensor) { - size++; - } - @Override - public void deregister(HalDeviceConfig sensor) { - size--; - } - @Override - public int size() { - return size; - } - - @Override - public void addListener(HalDeviceReportListener listener) { } - - @Override - public void close() { } - } -} \ No newline at end of file diff --git a/hal-core/test/se/hal/TriggerManagerTest.java b/hal-core/test/se/hal/TriggerManagerTest.java deleted file mode 100644 index aea4ca13..00000000 --- a/hal-core/test/se/hal/TriggerManagerTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package se.hal; - -import org.junit.Test; -import se.hal.intf.HalAction; -import se.hal.intf.HalTrigger; -import se.hal.struct.Action; -import se.hal.struct.Trigger; -import se.hal.struct.TriggerFlow; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * - */ -public class TriggerManagerTest { - - private TriggerManager manager = new TriggerManager(); - - - @Test - public void registerAvailableTrigger(){ - assertEquals(Collections.EMPTY_LIST, manager.getAvailableTriggers()); - - manager.addAvailableTrigger(TestTrigger.class); - manager.addAvailableTrigger(TestTrigger.class); - assertEquals(1, manager.getAvailableTriggers().size()); - assertTrue(manager.getAvailableTriggers().contains(TestTrigger.class)); - } - - @Test - public void registerAvailableAction(){ - assertEquals(Collections.EMPTY_LIST, manager.getAvailableActions()); - - manager.addAvailableAction(TestAction.class); - manager.addAvailableAction(TestAction.class); - assertEquals(1, manager.getAvailableActions().size()); - assertTrue(manager.getAvailableActions().contains(TestAction.class)); - } - - - @Test - public void register(){ - registerAvailableTrigger(); - - TriggerFlow flow = new TriggerFlow(); - flow.addTrigger(new Trigger(new TestTrigger(true))); - TestAction action = new TestAction(); - flow.addAction(new Action(action)); - manager.register(flow); - manager.evaluateAndExecute(); - assertEquals(1, action.nrOfExecutions); - } - - - @Test - public void evaluateAndExecute(){ - registerAvailableTrigger(); - - TriggerFlow flow = new TriggerFlow(); - TestTrigger trigger = new TestTrigger(true); - flow.addTrigger(new Trigger(trigger)); - TestAction action = new TestAction(); - flow.addAction(new Action(action)); - manager.register(flow); - - manager.evaluateAndExecute(); - assertEquals("Action executed nr of times", - 1, action.nrOfExecutions); - - manager.evaluateAndExecute(); - assertEquals("Action executed nr of times", - 1, action.nrOfExecutions); - - } - - ///////////////////////////////////////////////////////////////////////////// - - private static class TestTrigger implements HalTrigger { - boolean evaluation; - TestTrigger(boolean b){ evaluation = b; } - @Override - public boolean evaluate() { return evaluation; } - - @Override - public void reset() { evaluation = false; } - } - - - private class TestAction implements HalAction { - int nrOfExecutions; - @Override - public void execute() { nrOfExecutions++; } - } - -} \ No newline at end of file diff --git a/hal-core/test/se/hal/daemon/SensorDataAggregationDaemonTest.java b/hal-core/test/se/hal/daemon/SensorDataAggregationDaemonTest.java deleted file mode 100644 index b42cfd12..00000000 --- a/hal-core/test/se/hal/daemon/SensorDataAggregationDaemonTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package se.hal.daemon; - -import org.junit.Before; -import org.junit.Test; -import se.hal.HalContext; -import se.hal.util.UTCTimeUtility; -import zutil.db.DBConnection; -import zutil.db.DBUpgradeHandler; -import zutil.log.LogUtil; - -import java.sql.PreparedStatement; - -public class SensorDataAggregationDaemonTest { - private static final String DEFAULT_DB_FILE = HalContext.RESOURCE_ROOT + "/hal-core-reference.db"; - - private static DBConnection db; - - @Before - public void setupDatabase() throws Exception { - System.out.println("-----------------------------------------------"); - - // Setup logging - System.out.println("Setting up logging"); - LogUtil.readConfiguration("logging.properties"); - - //create in memory database - System.out.println("Creating a in-memory database for test"); - db = new DBConnection(DBConnection.DBMS.SQLite, ":memory:"); - HalContext.setDB(db); - - //upgrade the database to the latest version - System.out.println("Upgrading in-memory database to latest version"); - DBConnection referenceDB = new DBConnection(DBConnection.DBMS.SQLite, DEFAULT_DB_FILE); - final DBUpgradeHandler handler = new DBUpgradeHandler(referenceDB); - handler.addIgnoredTable("sqlite_sequence"); //sqlite internal - handler.setTargetDB(db); - handler.upgrade(); - - //populate the database with data - System.out.println("Adding user to database"); - db.exec("INSERT INTO user(id, external, username) VALUES(222, 0, 'test')"); //adding user - System.out.println("Adding sensor to database"); - db.exec("INSERT INTO sensor(id, user_id, external_id, type) VALUES(111, 222, 333, 'se.hal.plugin.netscan.NetworkDevice')"); //adding sensor - System.out.println("Generating raw data and saving it to the database..."); - PreparedStatement stmt = db.getPreparedStatement("INSERT INTO sensor_data_raw (timestamp, sensor_id, data) VALUES(?, ?, ?)"); - try { - db.getConnection().setAutoCommit(false); - - long startTime = System.currentTimeMillis(); - for (int i = 0; i < 100000; ++i) { - stmt.setLong(1, startTime - (UTCTimeUtility.MINUTE_IN_MS * i)); - stmt.setLong(2, 111); - stmt.setFloat(3, 7.323f); - stmt.addBatch(); - } - - DBConnection.execBatch(stmt); - db.getConnection().commit(); - } catch (Exception e) { - db.getConnection().rollback(); - throw e; - } finally { - db.getConnection().setAutoCommit(true); - } - - System.out.println("Ready for test!"); - } - - @Test - public void testAggregation() { - System.out.println("Start testing raw data aggregation"); - SensorDataAggregatorDaemon aggrDeamon = new SensorDataAggregatorDaemon(); - aggrDeamon.run(); - - //TODO: verify the aggregation - - System.out.println("Finished testing raw data aggregation"); - } - - -} diff --git a/hal-core/test/se/hal/struct/devicedata/ColorEventDataTest.java b/hal-core/test/se/hal/struct/devicedata/ColorEventDataTest.java deleted file mode 100644 index a42afd56..00000000 --- a/hal-core/test/se/hal/struct/devicedata/ColorEventDataTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package se.hal.struct.devicedata; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class ColorEventDataTest { - - @Test - public void getData() { - assertEquals(0.0, new ColorEventData(0, 0, 0, 0).getData(), 0); - assertEquals(6553600.0, new ColorEventData(100, 0, 0, 0).getData(), 0); - assertEquals(25600.0, new ColorEventData(0, 100, 0, 0).getData(), 0); - assertEquals(100.0, new ColorEventData(0, 0, 100, 0).getData(), 0); - assertEquals(16777215.0, new ColorEventData(255, 255, 255, 0).getData(), 0); - } - - @Test - public void setData() { - ColorEventData colorData = new ColorEventData(); - colorData.setData(0); - assertEquals(0, colorData.getRed()); - assertEquals(0, colorData.getGreen()); - assertEquals(0, colorData.getBlue()); - - colorData.setData(6553600.0); - assertEquals(100, colorData.getRed()); - assertEquals(0, colorData.getGreen()); - assertEquals(0, colorData.getBlue()); - - colorData.setData(25600.0); - assertEquals(0, colorData.getRed()); - assertEquals(100, colorData.getGreen()); - assertEquals(0, colorData.getBlue()); - - colorData.setData(100.0); - assertEquals(0, colorData.getRed()); - assertEquals(0, colorData.getGreen()); - assertEquals(100, colorData.getBlue()); - - colorData.setData(16777215.0); - assertEquals(255, colorData.getRed()); - assertEquals(255, colorData.getGreen()); - assertEquals(255, colorData.getBlue()); - } -} \ No newline at end of file diff --git a/hal-core/test/se/hal/test/HalAssert.java b/hal-core/test/se/hal/test/HalAssert.java deleted file mode 100644 index bdddba44..00000000 --- a/hal-core/test/se/hal/test/HalAssert.java +++ /dev/null @@ -1,22 +0,0 @@ -package se.hal.test; - -import se.hal.intf.HalDeviceData; - -import static org.junit.Assert.assertEquals; - -/** - * Class contains assertions that can be useful - */ -public class HalAssert { - - public static void assertEqualsIgnoreTimestamp(HalDeviceData expected, HalDeviceData actual) { - if (expected != null) { - expected.setTimestamp(0); - } - if (actual != null) { - actual.setTimestamp(0); - } - - assertEquals(expected, actual); - } -} diff --git a/hal-core/test/se/hal/test/MockHalDeviceReportListener.java b/hal-core/test/se/hal/test/MockHalDeviceReportListener.java deleted file mode 100644 index 48c25592..00000000 --- a/hal-core/test/se/hal/test/MockHalDeviceReportListener.java +++ /dev/null @@ -1,45 +0,0 @@ -package se.hal.test; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalDeviceReportListener; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class will store all reports to be used later in a test to verify the behaviours - */ -public class MockHalDeviceReportListener implements HalDeviceReportListener { - - private List reports = new ArrayList<>(); - - - public int getNumberOfReports() { - return reports.size(); - } - - public DeviceTestReport getReport(int index) { - return reports.get(index); - } - - public void reset() { - reports = new ArrayList<>(); - } - - @Override - public void reportReceived(HalDeviceConfig deviceConfig, HalDeviceData deviceData) { - reports.add(new DeviceTestReport(deviceConfig, deviceData)); - } - - - public static class DeviceTestReport { - public HalDeviceConfig config; - public HalDeviceData data; - - private DeviceTestReport(HalDeviceConfig config, HalDeviceData data) { - this.config = config; - this.data = data; - } - } -} diff --git a/hal-core/test/se/hal/test/TestDeviceConfig.java b/hal-core/test/se/hal/test/TestDeviceConfig.java deleted file mode 100644 index 4e16d890..00000000 --- a/hal-core/test/se/hal/test/TestDeviceConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -package se.hal.test; - -import se.hal.intf.HalEventConfig; -import se.hal.intf.HalSensorConfig; -import se.hal.intf.HalSensorController; -import se.hal.intf.HalSensorData; -import se.hal.struct.devicedata.TemperatureSensorData; - -/** - * A dymmy device config class - */ -public class TestDeviceConfig implements HalSensorConfig, HalEventConfig { - - private String id; - - - public TestDeviceConfig(String id) { - this.id = id; - } - - - @Override - public long getDataInterval() { - return 60 * 1000; // 1 min - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } - - @Override - public Class getDeviceControllerClass() { - return null; - } - - @Override - public Class getDeviceDataClass() { - return TemperatureSensorData.class; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TestDeviceConfig) - return id.equals(((TestDeviceConfig) obj).id); - return false; - } -} diff --git a/hal-core/test/se/hal/util/HalDeviceUtilTest.java b/hal-core/test/se/hal/util/HalDeviceUtilTest.java deleted file mode 100644 index 9f40b97f..00000000 --- a/hal-core/test/se/hal/util/HalDeviceUtilTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package se.hal.util; - -import junit.framework.TestCase; -import org.junit.Test; -import se.hal.struct.Event; -import se.hal.test.TestDeviceConfig; - -import java.util.ArrayList; -import java.util.List; - -public class HalDeviceUtilTest extends TestCase { - - @Test - public void testFindDevice() { - List eventList = new ArrayList<>(); - - Event device = new Event(); - device.setDeviceConfig(new TestDeviceConfig("not test")); - eventList.add(device); - - Event device2 = new Event(); - device2.setDeviceConfig(new TestDeviceConfig("not test")); - eventList.add(device2); - - Event expectedDevice = new Event(); - expectedDevice.setDeviceConfig(new TestDeviceConfig("test")); - eventList.add(expectedDevice); - - assertEquals(expectedDevice, HalDeviceUtil.findDevice(new TestDeviceConfig("test"), eventList)); - } -} \ No newline at end of file diff --git a/hal-default.db b/hal-default.db new file mode 100755 index 00000000..926937a5 Binary files /dev/null and b/hal-default.db differ diff --git a/hal.conf.example b/hal.conf.example old mode 100644 new mode 100755 index 977961ba..76e4ee75 --- a/hal.conf.example +++ b/hal.conf.example @@ -1,51 +1,9 @@ -# ------------------------------------ -# Core settings -# ------------------------------------ +http_port=8080 +sync_port=6666 -hal_core.http_port=8080 - -# External HTTP server requires SSL certificate which is provided by letsencrypt, for the authentication -# to letsencrypt to work port 80 needs to be opened temporarily during the certificate generation process -# make sure the firewall forwarding rules are setup for this process to work. -# In the future DNS authentication will also be available so no port forwarding will be necessary. -#hal_core.http_external_cert=acme_http|acme_dns|none -#hal_core.http_external_port=8081 -#hal_core.http_external_domain=example.com - -# Set the local domain that hal will announce to the local network. Default value is hal.local leave empty to disable. -#hal_core.dns_local_domain=hal.local| - -# ------------------------------------------------------------------------ # Plugin configurations -# ------------------------------------------------------------------------ - -# ------------------------------------ -# Sensors and Events -# ------------------------------------ - -## NetUPS plugin -#hal_nutups.host= -#hal_nutups.port= - -## Power Challenge Plugin -#hal_powerchallenge.sync_port=6666 - -## Tellstick plugin -#hal_tellstick.com_port=COM5|/dev/serial/by-id/usb-Telldus_TellStick_Duo_A6XNNE6Z-if00-port0 - -## Tibber Plugin -#hal_tibber.token= - -## Zigbee plugin -#hal_zigbee.com_port=COM4 -#hal_zigbee.dongle=CC2531|CONBEE|XBEE - -## ZWave plugin -#hal_zwave.com_port=COM4 - -# ------------------------------------ -# Assistants -# ------------------------------------ - -## Google Assistant -#hal_assistant.google.client_id=https://oauth-redirect.googleusercontent.com/r/optimal-comfort-XXXXX +#tellstick.com_port=COM5 +#nutups.host= +#nutups.port= +# Network scanning should probably be disabled in some networks (default on) +#netscan.ipscan=false \ No newline at end of file diff --git a/hal.service b/hal.service deleted file mode 100644 index 6decfaad..00000000 --- a/hal.service +++ /dev/null @@ -1,16 +0,0 @@ -# This is a SystemD configuration for the Hal Server. -# -# To activate the service run: -# - sudo cp hal.service /lib/systemd/system/ -# - sudo systemctl enable hal.service -# - -[Unit] -Description=Hal Server - -[Service] -Type=idle -ExecStart=/home/pi/repo/hal/run.sh --foreground - -[Install] -WantedBy=multi-user.target diff --git a/lib/Ab.jar b/lib/Ab.jar new file mode 100755 index 00000000..34453494 Binary files /dev/null and b/lib/Ab.jar differ diff --git a/lib/commons-math3-3.5.jar b/lib/commons-math3-3.5.jar new file mode 100755 index 00000000..db99f8c2 Binary files /dev/null and b/lib/commons-math3-3.5.jar differ diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar new file mode 100755 index 00000000..9d5fe16e Binary files /dev/null and b/lib/hamcrest-core-1.3.jar differ diff --git a/lib/jSerialComm-1.3.10.jar b/lib/jSerialComm-1.3.10.jar new file mode 100755 index 00000000..75156f34 Binary files /dev/null and b/lib/jSerialComm-1.3.10.jar differ diff --git a/lib/java-speech-api-master.jar b/lib/java-speech-api-master.jar new file mode 100755 index 00000000..46a7c5a4 Binary files /dev/null and b/lib/java-speech-api-master.jar differ diff --git a/lib/junit-4.12.jar b/lib/junit-4.12.jar new file mode 100644 index 00000000..3a7fc266 Binary files /dev/null and b/lib/junit-4.12.jar differ diff --git a/lib/marytts-client-5.1.2-jar-with-dependencies.jar b/lib/marytts-client-5.1.2-jar-with-dependencies.jar new file mode 100755 index 00000000..6c5cbb3b Binary files /dev/null and b/lib/marytts-client-5.1.2-jar-with-dependencies.jar differ diff --git a/lib/marytts-runtime-5.1.2-jar-with-dependencies.jar b/lib/marytts-runtime-5.1.2-jar-with-dependencies.jar new file mode 100755 index 00000000..47c8c0d8 Binary files /dev/null and b/lib/marytts-runtime-5.1.2-jar-with-dependencies.jar differ diff --git a/lib/pi4j-core-1.1-SNAPSHOT.jar b/lib/pi4j-core-1.1-SNAPSHOT.jar new file mode 100644 index 00000000..700c601d Binary files /dev/null and b/lib/pi4j-core-1.1-SNAPSHOT.jar differ diff --git a/lib/pi4j-device-1.1-SNAPSHOT.jar b/lib/pi4j-device-1.1-SNAPSHOT.jar new file mode 100644 index 00000000..f7532e79 Binary files /dev/null and b/lib/pi4j-device-1.1-SNAPSHOT.jar differ diff --git a/lib/pi4j-gpio-extension-1.1-SNAPSHOT.jar b/lib/pi4j-gpio-extension-1.1-SNAPSHOT.jar new file mode 100644 index 00000000..e4a53e47 Binary files /dev/null and b/lib/pi4j-gpio-extension-1.1-SNAPSHOT.jar differ diff --git a/lib/pi4j-service-1.1-SNAPSHOT.jar b/lib/pi4j-service-1.1-SNAPSHOT.jar new file mode 100644 index 00000000..73027cfd Binary files /dev/null and b/lib/pi4j-service-1.1-SNAPSHOT.jar differ diff --git a/lib/sphinx4-core.jar b/lib/sphinx4-core.jar new file mode 100755 index 00000000..b93e32f5 Binary files /dev/null and b/lib/sphinx4-core.jar differ diff --git a/lib/sqlite-jdbc-3.8.11.2_HACKED_FOR_RPI.jar b/lib/sqlite-jdbc-3.8.11.2_HACKED_FOR_RPI.jar new file mode 100644 index 00000000..780cf067 Binary files /dev/null and b/lib/sqlite-jdbc-3.8.11.2_HACKED_FOR_RPI.jar differ diff --git a/logging.properties b/logging.properties old mode 100644 new mode 100755 index 2383112d..8a356b01 --- a/logging.properties +++ b/logging.properties @@ -1,33 +1,15 @@ # logging.properties # LogUtil.readConfiguration("logging.properties"); -# ------------------------------------- -# HAL -# ------------------------------------- - handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler java.util.logging.ConsoleHandler.level = ALL java.util.logging.ConsoleHandler.formatter = zutil.log.CompactLogFormatter java.util.logging.FileHandler.level = ALL java.util.logging.FileHandler.formatter = zutil.log.CompactLogFormatter -# Libs - -zutil.level = ALL -zutil.db.bean.level = INFO -zutil.net.http.HttpServer.level = FINE -zutil.net.http.page.HttpFilePage.level = INFO -zutil.net.dns.MulticastDnsServer.level = FINE - -# Hal Core se.hal.level = ALL -# ------------------------------------- -# Plugins -# ------------------------------------- - -com.zsmartsystems.zigbee.level = INFO -profiling.com.zsmartsystems.zigbee.level = INFO -#com.zsmartsystems.zigbee.dongle.cc2531.zigbee.util.ByteUtils.level = INFO -#com.zsmartsystems.zigbee.dongle.cc2531.network.packet.ZToolPacketStream.level = INFO \ No newline at end of file +zutil.level = ALL +zutil.db.bean.level = INFO +zutil.net.http.page.level = INFO \ No newline at end of file diff --git a/plugins/hal-assistant-google/READNME.md b/plugins/hal-assistant-google/READNME.md deleted file mode 100644 index 6d784cf3..00000000 --- a/plugins/hal-assistant-google/READNME.md +++ /dev/null @@ -1,70 +0,0 @@ -# Plugin Configuration - -|Config Parameter |Value |Description | -|---------------------------|----------------|------------| -|hal_assistant.google.client_id |String client ID|A value matching client ID configured in Google Actions Console| - -# Initial Setup - -This lazy is a modified version of https://www.home-assistant.io/integrations/google_assistant/ - -To use Google Assistant, your server has to be **externally accessible with a hostname and SSL certificate**. If you haven't already configured that, you should do so before continuing. If you make DNS changes to accomplish this, please ensure you have allowed up to the full 48 hours for DNS changes to propagate, otherwise Google may not be able to reach your server. - -You will need to create a service account Create Service account key which allows you to update devices without unlinking and relinking an account (see below). If you don't provide the service account, the google_assistant.request_sync service is not exposed. It is recommended to set up this configuration key as it also allows the usage of the following command, “Ok Google, sync my devicesâ€. Once you have set up this component, you will need to call this service (or command) each time you add a new device. - -## Configure Google Assistant -* Go to https://console.actions.google.com/ -* Create a new project in the Actions on Google console. -* Click New Project and give your project a name. -* Click on the Smart Home card, then click the Start Building button. -* Click Build your Action, then click Add Action(s). -* Add your Home Assistant URL: https://{Hal External URL}:{External Port}/api/assistant/google/smarthome in the Fulfillment URL box. -* Click Save. -* Click on the Overview tab, which will lead you back to the app details screen. - -Account linking is required for your app to interact with the server. -* Clicking on Setup account linking under the Quick Setup section of the Overview page. - * If asked, leave options as they default No, I only want to allow account creation on my website and select Next. - * Then if asked, for the Linking type select OAuth and Authorization Code. Click "Next". -* Enter the following: - * Client ID: https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID. (Find your "YOUR_PROJECT_ID" by clicking on the three little dots (more) icon in the upper right corner of the console, selecting Project settings, your Project ID will be listed on the GENERAL tab of the Settings page.) - * Client Secret: Anything you like, Hal doesn't need this field. - * Authorization URL (replace with your actual URL): https://{Hal External URL}:{External Port}/api/assistant/google/auth/authorize. - * Token URL (replace with your actual URL): https://{Hal External URL}:{External Port}/api/assistant/google/auth/token. - * Click Next, then Next again. -* In the "Configure" your client Scopes textbox: - * type "email" and click Add scope, - * then type "name" and click Add scope again. - * Do NOT check Google to transmit clientID and secret via HTTP basic auth header. - * Click Next, then click Save - - -### On Phone -* Open the Google Home app and go to Settings. -* Click Add... --> Set up or add --> Set up device --> and click Have something already setup?. You should have [test] your app name listed under ‘Add new'. Selecting that should lead you to a browser to login your Home Assistant instance, then redirect back to a screen where you can set rooms and nicknames for your devices if you wish. - -## Active Reporting -If you want to support actively reporting of state to Google's server (configuration option report_state) and support google_assistant.request_sync, you need to generate a service account. -* In the GCP Console, go to the "Create Service account" key page. -* From the Service account list, select "New service account". -* In the "Service account name" field, enter a name. -* In the "Service account ID" field, enter an ID. -* From the Role list, select "Service Accounts > Service Account Token Creator". -* For the Key type, select the JSON option. -* Click "Create". A JSON file that contains your key downloads to your computer. -* Use the information in this file, or the file directly to add to the service_account key in the configuration. -* Go to the "Google API Console". -* Select your project and click "Enable HomeGraph API". - -## Multiuser -If you want to allow other household users to control the devices: -* Open the project you created in the Actions on Google console. -* Click Test on the top of the page, then click Simulator located to the page left, then click the three little dots (more) icon in the upper right corner of the console. -* Click Manage user access. This redirects you to the Google Cloud Platform IAM permissions page. -* Click ADD at the top of the page. - * Enter the email address of the user you want to add. - * Click Select a role and choose Project < Viewer. - * Click SAVE - * Copy and share the link with the new user. - * When the new user opens the link with their own Google account, it will enable your draft test app under their account. -* Have the new user go to their Google Assistant app to add [test] your app name to their account. diff --git a/plugins/hal-assistant-google/build.gradle b/plugins/hal-assistant-google/build.gradle deleted file mode 100644 index 31f0f1e1..00000000 --- a/plugins/hal-assistant-google/build.gradle +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - implementation project(':hal-core') - implementation 'com.google.actions:actions-on-google:1.8.0' -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java deleted file mode 100644 index 4c286737..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeDaemon.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google; - -import se.hal.HalContext; -import se.hal.HalServer; -import se.hal.intf.HalDaemon; -import zutil.log.LogUtil; -import zutil.net.http.page.oauth.OAuth2AuthorizationPage; -import zutil.net.http.page.oauth.OAuth2Registry; -import zutil.net.http.page.oauth.OAuth2TokenPage; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.logging.Logger; - - -public class SmartHomeDaemon implements HalDaemon { - private static final Logger logger = LogUtil.getLogger(); - - private static final String ENDPOINT_SMARTHOME = "api/assistant/google/smarthome"; - protected static final String CONFIG_CLIENT_ID = "hal_assistant.google.client_id"; - - private SmartHomeImpl smartHome; - - - @Override - public void initiate(ScheduledExecutorService executor) { - if (smartHome == null) { - if (!HalContext.containsProperty(CONFIG_CLIENT_ID)) { - logger.severe("Missing configuration, aborting initializations."); - return; - } - - smartHome = new SmartHomeImpl(); - - HalServer.getExternalWebDaemon().getOAuth2Registry().addWhitelist(HalContext.getStringProperty(CONFIG_CLIENT_ID)); - HalServer.getExternalWebDaemon().getOAuth2Registry().addTokenListener(smartHome); - HalServer.getExternalWebDaemon().registerPage(ENDPOINT_SMARTHOME, new SmartHomePage(smartHome)); - } - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeImpl.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeImpl.java deleted file mode 100644 index 12bf00f2..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomeImpl.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2019 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package se.hal.plugin.assistant.google; - -import com.google.actions.api.smarthome.*; -import com.google.auth.oauth2.AccessToken; -import com.google.auth.oauth2.GoogleCredentials; -import com.google.home.graph.v1.DeviceProto; -import com.google.home.graph.v1.HomeGraphApiServiceProto; -import com.google.protobuf.Struct; -import com.google.protobuf.Value; -import org.json.JSONObject; -import se.hal.HalContext; -import se.hal.HalServer; -import se.hal.intf.HalAbstractDevice; -import se.hal.plugin.assistant.google.trait.DeviceTrait; -import se.hal.plugin.assistant.google.trait.DeviceTraitFactory; -import se.hal.plugin.assistant.google.trait.OnOffTrait; -import se.hal.plugin.assistant.google.type.DeviceType; -import se.hal.struct.Event; -import se.hal.struct.Sensor; -import zutil.db.DBConnection; -import zutil.log.LogUtil; -import zutil.net.http.page.oauth.OAuth2Registry.OAuth2TokenRegistrationListener; - -import java.sql.SQLException; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - - -public class SmartHomeImpl extends SmartHomeApp implements OAuth2TokenRegistrationListener { - private static final Logger logger = LogUtil.getLogger(); - - public static final String CONFIG_USER_AGENT = "hal_assistant_google.user_agent"; - - - private final String userAgent; - private final String clientId; - - - public SmartHomeImpl() { - if (!HalContext.containsProperty(CONFIG_USER_AGENT)) - HalContext.setProperty(CONFIG_USER_AGENT, "Hal-" + (int) (Math.random() * 10000)); - userAgent = HalContext.getStringProperty(CONFIG_USER_AGENT); - - clientId = HalContext.getStringProperty(SmartHomeDaemon.CONFIG_CLIENT_ID); - for (String token : HalServer.getExternalWebDaemon().getOAuth2Registry().getAccessTokens(clientId)) { - if (HalServer.getExternalWebDaemon().getOAuth2Registry().isAccessTokenValid(token)) { - onTokenRegistration(clientId, - token, - HalServer.getExternalWebDaemon().getOAuth2Registry().getAccessTokenTimeout(token)); - } - } - } - - - @Override - public void onTokenRegistration(String clientId, String token, long timeoutMillis) { - if (this.clientId == null || !this.clientId.equals(clientId)) - return; // Only assign the token for the Google client ID - - try { - GoogleCredentials credentials = GoogleCredentials.create(new AccessToken( - token, - new Date(timeoutMillis) - )); - this.setCredentials(credentials); - logger.fine("New OAuth2 token registered."); - } catch (Exception e) { - logger.log(Level.SEVERE, "Could not load google credentials", e); - } - } - - /** - * https://developers.google.com/assistant/smarthome/reference/intent/sync - */ - @SuppressWarnings("unchecked") - @Override - public SyncResponse onSync(SyncRequest syncRequest, Map headers) { - logger.fine("Received sync request."); - - SyncResponse res = new SyncResponse(); - res.setRequestId(syncRequest.requestId); - res.setPayload(new SyncResponse.Payload()); - - List deviceList = new LinkedList<>(); - - try { - DBConnection db = HalContext.getDB(); - deviceList.addAll(Sensor.getLocalSensors(db)); - deviceList.addAll(Event.getLocalEvents(db)); - } catch (SQLException e) { - logger.log(Level.WARNING, "Unable to retrieve devices.", e); - } - - ArrayList responseDeviceList = new ArrayList<>(deviceList.size()); - for (HalAbstractDevice device : deviceList) { - DeviceType type = DeviceType.getType(device); - DeviceTrait[] traits = DeviceTraitFactory.getTraits(device); - - if (type == null) { - logger.warning("DeviceType returned null for Hal device(" + device.getDeviceConfig() + "): " + device); - continue; - } - - // Generate payload - - SyncResponse.Payload.Device.Builder deviceBuilder = - new SyncResponse.Payload.Device.Builder() - .setId(device.getClass().getSimpleName() + "-" + device.getId()) - .setType("" + type) - .setTraits(DeviceTraitFactory.getTraitIds(traits)) - .setName( - DeviceProto.DeviceNames.newBuilder() - .setName(device.getName()) - .build()) - .setWillReportState(true) - .setRoomHint((device.getRoom() != null ? device.getRoom().getName() : "")) - .setDeviceInfo( - DeviceProto.DeviceInfo.newBuilder() - //.setManufacturer((String) device.get("manufacturer")) - //.setModel((String) device.get("model")) - //.setHwVersion((String) device.get("hwVersion")) - //.setSwVersion((String) device.get("swVersion")) - .build()); - - JSONObject attribJson = new JSONObject(); - for (DeviceTrait trait : traits) { - for (Map.Entry entry : trait.generateSyncResponse(device.getDeviceConfig()).entrySet()) { - attribJson.put(entry.getKey(), entry.getValue()); - } - } - deviceBuilder.setAttributes(attribJson); - - // Set custom data - - JSONObject customDataJson = new JSONObject(); - customDataJson.put("type", device.getClass().getSimpleName()); - customDataJson.put("id", device.getId()); - deviceBuilder.setCustomData(customDataJson); - - responseDeviceList.add(deviceBuilder.build()); - } - - res.payload.agentUserId = userAgent; - res.payload.devices = responseDeviceList.toArray( - new SyncResponse.Payload.Device[responseDeviceList.size()]); - - return res; - } - - /** - * Creates a and sends a request for a sync from Google - * - * @param userId The agent user ID - * @param deviceId The device ID - * @param states A Map of state keys and their values for the provided device ID - */ - public void syncRequest(String userId, String deviceId, Map states) { - Struct.Builder statesStruct = Struct.newBuilder(); - - HomeGraphApiServiceProto.ReportStateAndNotificationDevice.Builder deviceBuilder = - HomeGraphApiServiceProto.ReportStateAndNotificationDevice.newBuilder().setStates( - Struct.newBuilder().putFields(deviceId, - Value.newBuilder().setStructValue(statesStruct).build() - )); - - HomeGraphApiServiceProto.ReportStateAndNotificationRequest request = - HomeGraphApiServiceProto.ReportStateAndNotificationRequest.newBuilder() - .setRequestId(String.valueOf(Math.random())) - .setAgentUserId(userId) // our single user's id - .setPayload(HomeGraphApiServiceProto.StateAndNotificationPayload.newBuilder().setDevices(deviceBuilder)) - .build(); - - this.reportState(request); - } - - /** - * https://developers.google.com/assistant/smarthome/reference/intent/query - */ - @Override - public QueryResponse onQuery(QueryRequest queryRequest, Map headers) { - DBConnection db = HalContext.getDB(); - - QueryResponse res = new QueryResponse(); - res.setRequestId(queryRequest.requestId); - res.setPayload(new QueryResponse.Payload()); - Map> deviceStates = new HashMap<>(); - - for (SmartHomeRequest.RequestInputs input : queryRequest.getInputs()) { - if (!"action.devices.QUERY".equals(input.intent)) - continue; - - for (QueryRequest.Inputs.Payload.Device deviceRequest : ((QueryRequest.Inputs) input).payload.devices) { - try { - logger.fine("Received query request for: type=" + deviceRequest.getId()); - - if (deviceRequest.getCustomData() == null || !deviceRequest.getCustomData().containsKey("type") || !deviceRequest.getCustomData().containsKey("id")) - throw new IllegalArgumentException("Device Type and ID was no supplied in customData: " + deviceRequest.getId()); - - String deviceTypeStr = (String) deviceRequest.getCustomData().get("type"); - int deviceId = (Integer) deviceRequest.getCustomData().get("id"); - - HalAbstractDevice device; - switch (deviceTypeStr) { - case "Sensor": device = Sensor.getSensor(db, deviceId); break; - case "Event": device = Event.getEvent(db, deviceId); break; - default: throw new IllegalArgumentException("Unknown device type: " + deviceTypeStr); - } - - logger.fine("Generating response for sensor: " + device.getName() + " (Id: " + device.getId() + ")"); - - DeviceTrait[] traits = DeviceTraitFactory.getTraits(device); - Map deviceState = new HashMap<>(); - - if (traits.length > 0) { - for (DeviceTrait trait : traits) { - deviceState.putAll(trait.generateQueryResponse(device.getDeviceData())); - } - - deviceState.put("status", "SUCCESS"); - } else { - deviceState.put("status", "UNKNOWN"); - } - deviceStates.put(deviceRequest.getId(), deviceState); - } catch (Exception e) { - logger.log(Level.SEVERE, "Query request failed for sensor: " + deviceRequest.getId(), e); - Map failedDevice = new HashMap<>(); - failedDevice.put("status", "ERROR"); - failedDevice.put("errorCode", "deviceOffline"); - deviceStates.put(deviceRequest.id, failedDevice); - } - } - } - - res.payload.setDevices(deviceStates); - return res; - } - - /** - * TODO: - */ - @Override - public ExecuteResponse onExecute(ExecuteRequest executeRequest, Map headers) { - DBConnection db = HalContext.getDB(); - - ExecuteResponse res = new ExecuteResponse(); - List commandsResponse = new ArrayList<>(); - - for (ExecuteRequest.Inputs.Payload.Commands command : ((ExecuteRequest.Inputs) executeRequest.inputs[0]).payload.commands) { - for (ExecuteRequest.Inputs.Payload.Commands.Devices deviceRequest : command.devices) { - try { - if (deviceRequest.getCustomData() == null || !deviceRequest.getCustomData().containsKey("type") || !deviceRequest.getCustomData().containsKey("id")) - throw new IllegalArgumentException("Device Type and ID was no supplied in customData: " + deviceRequest.getId()); - - String deviceTypeStr = (String) deviceRequest.getCustomData().get("type"); - int deviceId = (Integer) deviceRequest.getCustomData().get("id"); - - HalAbstractDevice device; - switch (deviceTypeStr) { - case "Sensor": device = Sensor.getSensor(db, deviceId); break; - case "Event": device = Event.getEvent(db, deviceId); break; - default: throw new IllegalArgumentException("Unknown device type: " + deviceTypeStr); - } - - for (ExecuteRequest.Inputs.Payload.Commands.Execution execution : command.execution) { - if ("action.devices.commands.OnOff".equals(execution.command)) { // TODO: This looks ugly! - new OnOffTrait().execute(device, execution); - } else - throw new UnsupportedOperationException("Unsupported command requested: " + execution.command); - } - - ExecuteResponse.Payload.Commands successfulCommands = new ExecuteResponse.Payload.Commands(); - successfulCommands.status = "SUCCESS"; - successfulCommands.ids = new String[]{deviceRequest.id}; - commandsResponse.add(successfulCommands); - } catch (Exception e) { - ExecuteResponse.Payload.Commands failedDevice = new ExecuteResponse.Payload.Commands(); - failedDevice.ids = new String[]{deviceRequest.id}; - failedDevice.status = "ERROR"; - failedDevice.setErrorCode(e.getMessage()); - commandsResponse.add(failedDevice); - } - } - } - - res.requestId = executeRequest.requestId; - ExecuteResponse.Payload payload = new ExecuteResponse.Payload( - commandsResponse.toArray(new ExecuteResponse.Payload.Commands[]{})); - res.setPayload(payload); - - return res; - } - - @Override - public void onDisconnect(DisconnectRequest disconnectRequest, Map headers) { - logger.fine("Received disconnect request."); - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomePage.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomePage.java deleted file mode 100644 index 8772bef7..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/SmartHomePage.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google; - -import zutil.io.IOUtil; -import zutil.log.LogUtil; -import zutil.net.http.HttpHeader; -import zutil.net.http.HttpPage; -import zutil.net.http.HttpPrintStream; - -import java.io.IOException; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Handles Google SMartHome request received via HTTP POST. - */ -public class SmartHomePage implements HttpPage { - private static final Logger logger = LogUtil.getLogger(); - - private SmartHomeImpl smartHome; - - - public SmartHomePage(SmartHomeImpl smartHome) { - this.smartHome = smartHome; - } - - - @Override - public void respond( - HttpPrintStream out, - HttpHeader headers, - Map session, - Map cookie, - Map request) throws IOException { - - int contentLength = 0; - if (headers.containsHeader(HttpHeader.HEADER_CONTENT_LENGTH)) - contentLength = Integer.parseInt(headers.getHeader(HttpHeader.HEADER_CONTENT_LENGTH)); - - String body = IOUtil.readContentAsString(headers.getInputStream(), contentLength); - logger.finest("Request body = " + body); - - try { - String response = smartHome.handleRequest(body, request).get(); - logger.finest("Response body = " + response); - - out.setHeader("Content-Type", "application/json"); - out.setHeader("Access-Control-Allow-Origin", "*"); - out.setHeader("Pragma", "no-cache"); - out.println(response); - } catch (Exception e) { - logger.log(Level.SEVERE, "Was unable to handle SmartHome request.", e); - } - } -} \ No newline at end of file diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/plugin.json b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/plugin.json deleted file mode 100644 index 9f1e9e68..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/plugin.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 0.1, - "name": "Hal-Assistant-Google", - "interfaces": [ - {"se.hal.intf.HalDaemon": "se.hal.plugin.assistant.google.SmartHomeDaemon"} - ] -} \ No newline at end of file diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/BrightnessSetting.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/BrightnessSetting.java deleted file mode 100644 index 639408ed..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/BrightnessSetting.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import com.google.actions.api.smarthome.ExecuteRequest; -import se.hal.EventControllerManager; -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.Event; -import se.hal.struct.devicedata.ColorEventData; -import se.hal.struct.devicedata.LevelEventData; -import zutil.converter.Converter; - -import java.util.HashMap; - - -/** - * https://developers.home.google.com/cloud-to-cloud/traits/brightness - */ -public class BrightnessSetting extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.Brightness"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - response.put("commandOnlyBrightness", false); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap response = new HashMap<>(); - - if (data instanceof LevelEventData) { - response.put("brightness", ((LevelEventData) data).getData() * 100); - } - - return response; - } - - @Override - public void execute(HalAbstractDevice device, ExecuteRequest.Inputs.Payload.Commands.Execution execution) { - if ("action.devices.commands.BrightnessAbsolute".equals(execution.command)) { - LevelEventData eventData = new LevelEventData((int) execution.getParams().get("brightness"), System.currentTimeMillis()); - - EventControllerManager.getInstance().send((Event) device, eventData); - } else if ("action.devices.commands.BrightnessRelative".equals(execution.command) && device.getDeviceData() != null) { - LevelEventData currentLevel = (LevelEventData) device.getDeviceData(); - int newPercentage = currentLevel.getPercent(); - - if (execution.getParams().containsKey("brightnessRelativePercent")) { - newPercentage += (int) execution.getParams().get("brightnessRelativePercent"); - } else if (execution.getParams().containsKey("brightnessRelativeWeight")) { - newPercentage += 10 * (int) execution.getParams().get("brightnessRelativeWeight"); - } - - LevelEventData eventData = new LevelEventData(newPercentage, System.currentTimeMillis()); - EventControllerManager.getInstance().send((Event) device, eventData); - } - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/ColorSetting.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/ColorSetting.java deleted file mode 100644 index ff9be4cb..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/ColorSetting.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import com.google.actions.api.smarthome.ExecuteRequest; -import se.hal.EventControllerManager; -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.Event; -import se.hal.struct.devicedata.ColorEventData; -import se.hal.struct.devicedata.OnOffEventData; -import zutil.converter.Converter; - -import java.util.HashMap; - - -/** - * https://developers.home.google.com/cloud-to-cloud/traits/colorsetting - */ -public class ColorSetting extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.ColorSetting"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - response.put("commandOnlyColorSetting", false); - response.put("colorModel", "rgb"); // rgb or hsv - //response.put("colorTemperatureRange", new HashMap() {{ - // put("temperatureMinK", 2000); - // put("temperatureMaxK", 9000); - //}}); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap colorState = new HashMap<>(); - - //colorState.put("temperatureK", data.getData()); - - if (data instanceof ColorEventData) { - colorState.put("spectrumRgb", ((ColorEventData) data).getData()); - } - - //colorState.put("spectrumHsv", new HashMap() {{ - // put("hue", ((ColorEventData) data).getHex()); - // put("saturation", ((ColorEventData) data).getSaturation()); - // put("value", ((ColorEventData) data).getLightness()); - //}}); - - HashMap response = new HashMap<>(); - response.put("color", colorState); - return response; - } - - @Override - public void execute(HalAbstractDevice device, ExecuteRequest.Inputs.Payload.Commands.Execution execution) { - if ("action.devices.commands.ColorAbsolute".equals(execution.command)) { - byte[] rgb = Converter.toBytes((int) execution.getParams().get("spectrumRGB")); - ColorEventData eventData = new ColorEventData(rgb[2], rgb[1], rgb[0], System.currentTimeMillis()); - - EventControllerManager.getInstance().send((Event) device, eventData); - } - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/DeviceTraitFactory.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/DeviceTraitFactory.java deleted file mode 100644 index ddd6df13..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/DeviceTraitFactory.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - -import se.hal.intf.HalAbstractDevice; -import se.hal.struct.Sensor; -import zutil.log.LogUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -/** - * A factory class that will associate traits to Hal devices - */ -public class DeviceTraitFactory { - private static final Logger logger = LogUtil.getLogger(); - - private DeviceTraitFactory() {} - - - /** - * @param device the device to get supported DeviceTraits for. - * @return an array of DeviceTrait objects that are able to handle the given device type or empty array if there is no supported DeviceTrait available. - */ - public static DeviceTrait[] getTraits(HalAbstractDevice device) { - if (device == null || device.getDeviceConfig() == null) - return new DeviceTrait[0]; - - switch (device.getDeviceConfig().getDeviceDataClass().getName()) { - case "se.hal.struct.devicedata.LevelEventData": - return new DeviceTrait[]{new BrightnessSetting()}; - - case "se.hal.struct.devicedata.ColorEventData": - return new DeviceTrait[]{new ColorSetting()}; - - case "se.hal.struct.devicedata.HumiditySensorData": - return new DeviceTrait[]{new SensorStateTrait(), new HumiditySettingTrait()}; - - case "se.hal.struct.devicedata.DimmerEventData": - case "se.hal.struct.devicedata.OnOffEventData": - return new DeviceTrait[]{new OnOffTrait()}; - - case "se.hal.struct.devicedata.OpenClosedEventData": - return new DeviceTrait[]{new OpenCloseTrait()}; - - case "se.hal.struct.devicedata.ParticulateMatterSensorData": - case "se.hal.struct.devicedata.PowerConsumptionSensorData": - case "se.hal.struct.devicedata.LightSensorData": - return new DeviceTrait[]{new SensorStateTrait()}; - - case "se.hal.struct.devicedata.TemperatureSensorData": - return new DeviceTrait[]{new SensorStateTrait(), new TemperatureControlTrait()}; - - default: - logger.warning("Unknown device data class (" + device.getDeviceConfig().getDeviceDataClass() + ") provided by device config: " + device.getDeviceConfig().getClass()); - return new DeviceTrait[0]; - } - } - - /** - * @param traits - * @return a list integers of trait IDs matching the traits given. - */ - public static List getTraitIds(DeviceTrait[] traits) { - List list = new ArrayList<>(traits.length); - - for (DeviceTrait trait : traits) { - list.add(trait.getId()); - } - - return list; - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/HumiditySettingTrait.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/HumiditySettingTrait.java deleted file mode 100644 index 5fecdb84..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/HumiditySettingTrait.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.HumiditySensorData; - -import java.util.HashMap; - - -/** - * https://developers.google.com/assistant/smarthome/traits/humiditysetting - */ -public class HumiditySettingTrait extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.HumiditySetting"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - //response.put("humiditySetpointRange", new HashMap() {{ - // put("minPercent", 0); - // put("maxPercent", 100); - //}}); - //response.put("commandOnlyHumiditySetting", false); - response.put("queryOnlyHumiditySetting", true); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap response = new HashMap<>(); - - if (data instanceof HumiditySensorData) { - //response.put("humiditySetpointPercent", (int) data.getData()); - response.put("humidityAmbientPercent", (int) data.getData()); - } - - return response; - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OnOffTrait.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OnOffTrait.java deleted file mode 100644 index 6db5517a..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OnOffTrait.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import com.google.actions.api.smarthome.ExecuteRequest; -import se.hal.EventControllerManager; -import se.hal.intf.HalAbstractDevice; -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.Event; -import se.hal.struct.devicedata.OnOffEventData; - -import java.util.HashMap; - - -/** - * https://developers.google.com/assistant/smarthome/traits/onoff - */ -public class OnOffTrait extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.OnOff"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - response.put("commandOnlyOnOff", false); - response.put("queryOnlyOnOff", false); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap response = new HashMap<>(); - - if (data instanceof OnOffEventData) { - response.put("on", ((OnOffEventData) data).isOn()); - } - - return response; - } - - @Override - public void execute(HalAbstractDevice device, ExecuteRequest.Inputs.Payload.Commands.Execution execution) { - if ("action.devices.commands.OnOff".equals(execution.command)) { - OnOffEventData eventData = new OnOffEventData(); - if ((boolean) execution.getParams().get("on")) - eventData.setOn(); - else - eventData.setOff(); - - EventControllerManager.getInstance().send((Event) device, eventData); - } - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OpenCloseTrait.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OpenCloseTrait.java deleted file mode 100644 index 7d85277a..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/OpenCloseTrait.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.OnOffEventData; -import se.hal.struct.devicedata.OpenClosedEventData; - -import java.util.HashMap; - - -/** - * https://developers.google.com/assistant/smarthome/traits/openclose - */ -public class OpenCloseTrait extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.OpenClose"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - response.put("queryOnlyOnOff", true); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap response = new HashMap<>(); - - if (data instanceof OpenClosedEventData) { - response.put("openPercent", ((OpenClosedEventData) data).isOpen() ? 100 : 0); - } - - return response; - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/SensorStateTrait.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/SensorStateTrait.java deleted file mode 100644 index 58ea6be5..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/SensorStateTrait.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.ParticulateMatterSensorData; - -import java.util.ArrayList; -import java.util.HashMap; - - -/** - * https://developers.home.google.com/cloud-to-cloud/traits/sensorstate - */ -public class SensorStateTrait extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.SensorState"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - ArrayList sensorStatesSupported = new ArrayList<>(); - - sensorStatesSupported.add(new HashMap() {{ - if (config.getDeviceDataClass() == ParticulateMatterSensorData.class) { - put("name", "PM2.5"); - put("numericCapabilities", new HashMap() {{ - put("rawValueUnit", "MICROGRAMS_PER_CUBIC_METER"); - }}); - } - }}); - - HashMap response = new HashMap<>(); - response.put("sensorStatesSupported", sensorStatesSupported); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - ArrayList currentSensorStateData = new ArrayList<>(); - - currentSensorStateData.add(new HashMap() {{ - if (data instanceof ParticulateMatterSensorData) { - put("name", "PM2.5"); - //put("currentSensorState", xxx); - put("rawValue", data.getData()); - } - }}); - - HashMap response = new HashMap<>(); - response.put("currentSensorStateData", currentSensorStateData); - return response; - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/TemperatureControlTrait.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/TemperatureControlTrait.java deleted file mode 100644 index c6130300..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/trait/TemperatureControlTrait.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.trait; - - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.TemperatureSensorData; - -import java.util.HashMap; - - -/** - * https://developers.google.com/assistant/smarthome/traits/temperaturecontrol - */ -public class TemperatureControlTrait extends DeviceTrait { - - @Override - String getId() { - return "action.devices.traits.TemperatureControl"; - } - - @Override - public HashMap generateSyncResponse(HalDeviceConfig config) { - HashMap response = new HashMap<>(); - response.put("temperatureRange", new HashMap() {{ - put("minThresholdCelsius", -20); - put("maxThresholdCelsius", 60); - }}); - //response.put("temperatureStepCelsius", 0.5); - response.put("temperatureUnitForUX", "C"); - //response.put("commandOnlyTemperatureControl", false); - response.put("queryOnlyTemperatureControl", true); - return response; - } - - @Override - public HashMap generateQueryResponse(HalDeviceData data) { - HashMap response = new HashMap<>(); - - if (data instanceof TemperatureSensorData) { - //response.put("temperatureSetpointCelsius", data.getData()); - response.put("temperatureAmbientCelsius", data.getData()); - } - - return response; - } -} diff --git a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/type/DeviceType.java b/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/type/DeviceType.java deleted file mode 100644 index 0a55febb..00000000 --- a/plugins/hal-assistant-google/src/se/hal/plugin/assistant/google/type/DeviceType.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.assistant.google.type; - -import se.hal.intf.HalAbstractDevice; -import se.hal.struct.Sensor; -import zutil.log.LogUtil; - -import java.util.logging.Logger; - -/** - * Enum for https://developers.google.com/assistant/smarthome/types - */ -public enum DeviceType { - AC_UNIT("action.devices.types.AC_UNIT"), - AIRCOOLER("action.devices.types.AIRCOOLER"), - AIRFRESHENER("action.devices.types.AIRFRESHENER"), - AIRPURIFIER("action.devices.types.AIRPURIFIER"), - AUDIO_VIDEO_RECEIVER("action.devices.types.AUDIO_VIDEO_RECEIVER"), - AWNING("action.devices.types.AWNING"), - BATHTUB("action.devices.types.BATHTUB"), - BED("action.devices.types.BED"), - BLENDER("action.devices.types.BLENDER"), - BLINDS("action.devices.types.BLINDS"), - BOILER("action.devices.types.BOILER"), - CAMERA("action.devices.types.CAMERA"), - CHARGER("action.devices.types.CHARGER"), - CLOSET("action.devices.types.CLOSET"), - COFFEE_MAKER("action.devices.types.COFFEE_MAKER"), - COOKTOP("action.devices.types.COOKTOP"), - CURTAIN("action.devices.types.CURTAIN"), - DEHUMIDIFIER("action.devices.types.DEHUMIDIFIER"), - DEHYDRATOR("action.devices.types.DEHYDRATOR"), - DISHWASHER("action.devices.types.DISHWASHER"), - DOOR("action.devices.types.DOOR"), - DRAWER("action.devices.types.DRAWER"), - DRYER("action.devices.types.DRYER"), - FAN("action.devices.types.FAN"), - FAUCET("action.devices.types.FAUCET"), - FIREPLACE("action.devices.types.FIREPLACE"), - FREEZER("action.devices.types.FREEZER"), - FRYER("action.devices.types.FRYER"), - GARAGE("action.devices.types.GARAGE"), - GATE("action.devices.types.GATE"), - GRILL("action.devices.types.GRILL"), - HEATER("action.devices.types.HEATER"), - HOOD("action.devices.types.HOOD"), - HUMIDIFIER("action.devices.types.HUMIDIFIER"), - KETTLE("action.devices.types.KETTLE"), - LIGHT("action.devices.types.LIGHT"), - LOCK("action.devices.types.LOCK"), - REMOTECONTROL("action.devices.types.REMOTECONTROL"), - MOP("action.devices.types.MOP"), - MOWER("action.devices.types.MOWER"), - MICROWAVE("action.devices.types.MICROWAVE"), - MULTICOOKER("action.devices.types.MULTICOOKER"), - NETWORK("action.devices.types.NETWORK"), - OUTLET("action.devices.types.OUTLET"), - OVEN("action.devices.types.OVEN"), - PERGOLA("action.devices.types.PERGOLA"), - PETFEEDER("action.devices.types.PETFEEDER"), - PRESSURECOOKER("action.devices.types.PRESSURECOOKER"), - RADIATOR("action.devices.types.RADIATOR"), - REFRIGERATOR("action.devices.types.REFRIGERATOR"), - ROUTER("action.devices.types.ROUTER"), - SCENE("action.devices.types.SCENE"), - SENSOR("action.devices.types.SENSOR"), - SECURITYSYSTEM("action.devices.types.SECURITYSYSTEM"), - SETTOP("action.devices.types.SETTOP"), - SHUTTER("action.devices.types.SHUTTER"), - SHOWER("action.devices.types.SHOWER"), - SMOKE_DETECTOR("action.devices.types.SMOKE_DETECTOR"), - SOUSVIDE("action.devices.types.SOUSVIDE"), - SPEAKER("action.devices.types.SPEAKER"), - STREAMING_BOX("action.devices.types.STREAMING_BOX"), - STREAMING_STICK("action.devices.types.STREAMING_STICK"), - STREAMING_SOUNDBAR("action.devices.types.STREAMING_SOUNDBAR"), - SOUNDBAR("action.devices.types.SOUNDBAR"), - SPRINKLER("action.devices.types.SPRINKLER"), - STANDMIXER("action.devices.types.STANDMIXER"), - SWITCH("action.devices.types.SWITCH"), - TV("action.devices.types.TV"), - THERMOSTAT("action.devices.types.THERMOSTAT"), - VACUUM("action.devices.types.VACUUM"), - VALVE("action.devices.types.VALVE"), - WASHER("action.devices.types.WASHER"), - WATERHEATER("action.devices.types.WATERHEATER"), - WATERPURIFIER("action.devices.types.WATERPURIFIER"), - WATERSOFTENER("action.devices.types.WATERSOFTENER"), - WINDOW("action.devices.types.WINDOW"), - YOGURTMAKER("action.devices.types.YOGURTMAKER"); - - - private static final Logger logger = LogUtil.getLogger(); - - private final String typeId; - - - private DeviceType(String typeId) { - this.typeId = typeId; - } - - - public String toString() { - return typeId; - } - - - /** - * @param device - * @return a DeviceType ENUM matching the given device or null if given device is not supported. - */ - public static DeviceType getType(HalAbstractDevice device) { - if (device == null || device.getDeviceConfig() == null) - return null; - - switch (device.getDeviceConfig().getDeviceDataClass().getName()) { - case "se.hal.struct.devicedata.ColorEventData": - case "se.hal.struct.devicedata.LevelEventData": - case "se.hal.struct.devicedata.OnOffEventData": - return LIGHT; - - case "se.hal.struct.devicedata.HumiditySensorData": - case "se.hal.struct.devicedata.IlluminanceSensorData": - case "se.hal.struct.devicedata.PowerConsumptionSensorData": - case "se.hal.struct.devicedata.TemperatureSensorData": - return SENSOR; - - default: - logger.warning("Unknown device data class (" + device.getDeviceConfig().getDeviceDataClass() + ") provided by device config: " + device.getDeviceConfig().getClass()); - return null; - } - } -} diff --git a/plugins/hal-dummy/build.gradle b/plugins/hal-dummy/build.gradle deleted file mode 100644 index 81fb360f..00000000 --- a/plugins/hal-dummy/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation project(':hal-core') -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyController.java b/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyController.java deleted file mode 100644 index 9d37cbf8..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyController.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.dummy; - -import se.hal.HalServer; -import se.hal.intf.*; -import se.hal.util.ListenerUtil; -import zutil.log.LogUtil; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - - -public class DummyController implements HalSensorController, HalEventController, Runnable, HalDaemon { - private static final Logger logger = LogUtil.getLogger(); - - private List registeredDevices = new ArrayList<>(); - private List deviceListeners = new CopyOnWriteArrayList<>(); - - - public DummyController() {} - - - @Override - public void initialize() { - HalServer.registerDaemon(this); - } - - @Override - public void initiate(ScheduledExecutorService executor) { - executor.scheduleAtFixedRate(this, 0, 60, TimeUnit.SECONDS); - } - - @Override - public synchronized void run() { - try { - for (DummyDevice device : registeredDevices) { - HalDeviceData data = device.generateData(); - - ListenerUtil.callReportReceived(deviceListeners, device, data); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "Dummy device data generation has crashed.", e); - } - } - - @Override - public synchronized void register(HalDeviceConfig deviceConfig) { - if (deviceConfig instanceof DummyDevice) - registeredDevices.add((DummyDevice) deviceConfig); - } - - @SuppressWarnings("SuspiciousMethodCalls") - @Override - public synchronized void deregister(HalDeviceConfig deviceConfig) { - registeredDevices.remove(deviceConfig); - } - - @Override - public synchronized void send(HalEventConfig eventConfig, HalEventData eventData) { - // Nothing to do as this is a dummy controller - } - - @Override - public int size() { - return registeredDevices.size(); - } - - @Override - public void addListener(HalDeviceReportListener listener) { - if (!deviceListeners.contains(listener)) - deviceListeners.add(listener); - } - - @Override - public synchronized void close() { - registeredDevices = new ArrayList<>(); - } -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyDevice.java b/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyDevice.java deleted file mode 100644 index cb09e504..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/DummyDevice.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.dummy; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; - -public interface DummyDevice extends HalDeviceConfig { - - HalDeviceData generateData(); -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyHumiditySensor.java b/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyHumiditySensor.java deleted file mode 100644 index 9757584e..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyHumiditySensor.java +++ /dev/null @@ -1,48 +0,0 @@ -package se.hal.plugin.dummy.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalSensorConfig; -import se.hal.intf.HalSensorController; -import se.hal.intf.HalSensorData; -import se.hal.plugin.dummy.DummyController; -import se.hal.plugin.dummy.DummyDevice; -import se.hal.struct.devicedata.HumiditySensorData; - - -public class DummyHumiditySensor implements DummyDevice, HalSensorConfig { - - - @Override - public HalDeviceData generateData() { - return new HumiditySensorData( - (int) (Math.random() * 100), - System.currentTimeMillis() - ); - } - - - @Override - public long getDataInterval() { - return 60*1000; // 1 min - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } - - @Override - public Class getDeviceControllerClass() { - return DummyController.class; - } - - @Override - public Class getDeviceDataClass() { - return HumiditySensorData.class; - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummySwitchEvent.java b/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummySwitchEvent.java deleted file mode 100644 index a926a9cd..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummySwitchEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -package se.hal.plugin.dummy.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventConfig; -import se.hal.intf.HalEventController; -import se.hal.intf.HalEventData; -import se.hal.plugin.dummy.DummyController; -import se.hal.plugin.dummy.DummyDevice; -import se.hal.struct.devicedata.OnOffEventData; - -import java.util.Objects; - -public class DummySwitchEvent implements DummyDevice, HalEventConfig { - - - @Override - public HalDeviceData generateData() { - return new OnOffEventData( - (int) (Math.random() * 10) < 5, - System.currentTimeMillis() - ); - } - - - @Override - public Class getDeviceControllerClass() { - return DummyController.class; - } - - @Override - public Class getDeviceDataClass() { - return OnOffEventData.class; - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyTemperatureSensor.java b/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyTemperatureSensor.java deleted file mode 100644 index cf5d8262..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/device/DummyTemperatureSensor.java +++ /dev/null @@ -1,48 +0,0 @@ -package se.hal.plugin.dummy.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalSensorConfig; -import se.hal.intf.HalSensorController; -import se.hal.intf.HalSensorData; -import se.hal.plugin.dummy.DummyController; -import se.hal.plugin.dummy.DummyDevice; -import se.hal.struct.devicedata.TemperatureSensorData; - - -public class DummyTemperatureSensor implements DummyDevice, HalSensorConfig { - - - @Override - public HalDeviceData generateData() { - return new TemperatureSensorData( - (int) (Math.random() * 30), - System.currentTimeMillis() - ); - } - - - @Override - public long getDataInterval() { - return 60 * 1000; // 1 min - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } - - @Override - public Class getDeviceControllerClass() { - return DummyController.class; - } - - @Override - public Class getDeviceDataClass() { - return TemperatureSensorData.class; - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } -} diff --git a/plugins/hal-dummy/src/se/hal/plugin/dummy/plugin.json b/plugins/hal-dummy/src/se/hal/plugin/dummy/plugin.json deleted file mode 100644 index b8cbf758..00000000 --- a/plugins/hal-dummy/src/se/hal/plugin/dummy/plugin.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 0.1, - "name": "Hal-Dummy Devices", - "description": "Dummy devices with simulated data, can be used for demo or testing purposes.", - "interfaces": [ - {"se.hal.intf.HalEventConfig": "se.hal.plugin.dummy.device.DummySwitchEvent"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.dummy.device.DummyHumiditySensor"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.dummy.device.DummyTemperatureSensor"} - ] -} \ No newline at end of file diff --git a/plugins/hal-mqtt/build.gradle b/plugins/hal-mqtt/build.gradle deleted file mode 100644 index c388f552..00000000 --- a/plugins/hal-mqtt/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -dependencies { - implementation project(':hal-core') - - testImplementation project(path: ':hal-core', configuration: 'testClasses') -} diff --git a/plugins/hal-mqtt/resources/web/mqtt_overview.tmpl b/plugins/hal-mqtt/resources/web/mqtt_overview.tmpl deleted file mode 100644 index 548070c3..00000000 --- a/plugins/hal-mqtt/resources/web/mqtt_overview.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -

MQTT Overview

- -
-
-
-
Published Topic Data
-
- - - - - - {{#topics}} - - - - - {{/topics}} -
TopicPublished Data
{{.getKey()}}
{{.getValue()}}
-
-
-
-
\ No newline at end of file diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java deleted file mode 100644 index 8063ba1c..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/HalMqttController.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt; - -import se.hal.HalServer; -import se.hal.daemon.HalMulticastDnsDaemon; -import se.hal.intf.*; -import se.hal.plugin.mqtt.detector.HalMqttDetector; -import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; -import se.hal.plugin.mqtt.device.HalMqttEventConfig; -import zutil.InetUtil; -import zutil.ObjectUtil; -import zutil.log.LogUtil; -import zutil.net.mqtt.MqttBroker; -import zutil.net.mqtt.MqttSubscriptionListener; - -import java.io.IOException; -import java.net.InetAddress; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class HalMqttController implements HalAutostartController, MqttSubscriptionListener, HalEventController { - private final Logger logger = LogUtil.getLogger(); - - private MqttBroker mqttBroker; - - private List detectors = Collections.emptyList(); - private Map> topics = new HashMap<>(); - private List deviceListeners = new CopyOnWriteArrayList<>(); - - // -------------------------- - // Lifecycle methods - // -------------------------- - - @Override - public void initialize() { - try { - if (HalMulticastDnsDaemon.getInstance() != null) { - logger.info("Register MQTT in mDNS"); - InetAddress serverIp = InetUtil.getLocalInet4Address().get(0); - HalMulticastDnsDaemon.getInstance().addDnsEntry("_mqtt._tcp.local", serverIp); - } else { - logger.info("mDNS not available"); - } - - logger.info("Starting up MQTT Server"); - mqttBroker = new MqttBroker(); - mqttBroker.addGlobalSubscriber(this); - mqttBroker.start(); - - detectors = HalServer.getPluginManager().getEnabledPluginSingletons(HalMqttDetector.class); - - } catch (IOException e) { - logger.log(Level.SEVERE, "Unable to initialize MQTT plugin.", e); - - close(); - } - } - - @Override - public void close(){ - if (mqttBroker != null) { - logger.info("Shutting down MQTT Server"); - mqttBroker.close(); - mqttBroker = null; - } - } - - // -------------------------- - // MQTT Methods - // -------------------------- - - @Override - public void dataPublished(String topicName, byte[] data) { - if (data == null) - data = new byte[0]; - logger.finest("MQTT data published(topic: " + topicName + "): " + new String(data, StandardCharsets.UTF_8)); - - topicName = topicName.trim(); - List registeredDevices = topics.get(topicName); - - // Handle existing devices - - if (!ObjectUtil.isEmpty(registeredDevices)) { - for (HalMqttDeviceConfig deviceConfig : registeredDevices) { - HalDeviceData deviceData = deviceConfig.getDeviceData(data); - - if (deviceListeners != null) { - for (HalDeviceReportListener deviceListener : deviceListeners) { - deviceListener.reportReceived(deviceConfig, deviceData); - } - } - } - } - - // Handle detection of new devices - - for (HalMqttDetector detector : detectors) { - List detectedDevices = detector.parseTopic(topicName, data); - - // Check if we already know the device - if (!ObjectUtil.isEmpty(detectedDevices)) { - for (HalMqttDeviceConfig detectedDeviceConfig : detectedDevices) { - // Only handle unknown devices - if (registeredDevices == null || !registeredDevices.contains(detectedDeviceConfig)) { - HalDeviceData deviceData = detectedDeviceConfig.getDeviceData(data); - - if (deviceListeners != null) { - for (HalDeviceReportListener deviceListener : deviceListeners) { - deviceListener.reportReceived(detectedDeviceConfig, deviceData); - } - } - } - } - } - } - } - - public MqttBroker getBroker() { - return mqttBroker; - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public void register(HalDeviceConfig deviceConfig) { - if (deviceConfig instanceof HalMqttDeviceConfig) { - HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) deviceConfig; - - if (!topics.containsKey(mqttEvent.getTopicName())) - topics.put(mqttEvent.getTopicName(), new ArrayList<>()); - topics.get(mqttEvent.getTopicName()).add(mqttEvent); - } else { - throw new IllegalArgumentException( - "Device config is not an instance of " + HalMqttDeviceConfig.class + ": " + deviceConfig.getClass()); - } - } - - @Override - public void deregister(HalDeviceConfig deviceConfig) { - if (deviceConfig instanceof HalMqttDeviceConfig) { - HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) deviceConfig; - if (topics.containsKey(mqttEvent.getTopicName())) - topics.get(mqttEvent.getTopicName()).remove(deviceConfig); - } - } - - @Override - public void send(HalEventConfig eventConfig, HalEventData eventData) { - if (eventConfig instanceof HalMqttEventConfig) { - HalMqttEventConfig mqttEvent = (HalMqttEventConfig) eventConfig; - mqttBroker.publish(mqttEvent.getWriteTopicName(), mqttEvent.getMqttPublishPayload(eventData)); - } else if (eventConfig instanceof HalMqttDeviceConfig) { - HalMqttDeviceConfig mqttEvent = (HalMqttDeviceConfig) eventConfig; - mqttBroker.publish(mqttEvent.getTopicName(), Double.toString(eventData.getData()).getBytes()); - } else - throw new IllegalArgumentException( - "Device config is not an instance of " + HalMqttDeviceConfig.class + ": " + eventConfig.getClass()); - } - - @Override - public int size() { - int size = 0; - for (List devices : topics.values()) { - size += devices.size(); - } - return size; - } - - @Override - public void addListener(HalDeviceReportListener listener) { - if (!deviceListeners.contains(listener)) - deviceListeners.add(listener); - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java deleted file mode 100644 index 6c81d21e..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/GenericMqttDetector.java +++ /dev/null @@ -1,89 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import se.hal.plugin.mqtt.device.*; -import zutil.ObjectUtil; -import zutil.parser.DataNode; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * This detector will handle any MQTT topics and messages that have a generic structure. - * Supported formats: - * - *
  • Topics ending with: temperature, humidity, ppm
  • - *
  • Topics where the payload is a JSON object containing the fields: temperature, humidity, ppm
  • - *
    - */ -public class GenericMqttDetector implements HalMqttDetector { - - @Override - public List parseTopic(String topic, byte[] data) { - if (ObjectUtil.isEmpty(topic)) - return Collections.emptyList(); - - List detectedDeviceConfigs = new ArrayList<>(); - String[] topicSections = topic.split("/"); - - // Check if the topic ends with specific keywords - - if (topicSections.length > 1) { - String keyword = topicSections[topicSections.length - 1]; - HalMqttDeviceConfig config = null; - - switch (keyword) { - case "temperature": - config = new HalMqttTemperatureDeviceConfig(topic); - break; - case "humidity": - config = new HalMqttHumidityDeviceConfig(topic); - break; - case "pm25": - config = new HalMqttParticularMatterDeviceConfig(topic); - break; - case "voltage": - config = new HalMqttVoltageDeviceConfig(topic); - break; - case "current": - config = new HalMqttCurrentDeviceConfig(topic); - break; - } - - if (config != null) { - detectedDeviceConfigs.add(config); - return detectedDeviceConfigs; - } - } - - // Check if the payload is in JSON format - - DataNode jsonPayload = JSONParser.read(new String(data, StandardCharsets.UTF_8)); - if (jsonPayload != null) { - if (jsonPayload.get("temperature") != null) { - HalMqttDeviceConfig config = new HalMqttTemperatureDeviceConfig(topic, "$.temperature"); - detectedDeviceConfigs.add(config); - } - if (jsonPayload.get("humidity") != null) { - HalMqttDeviceConfig config = new HalMqttHumidityDeviceConfig(topic, "$.humidity"); - detectedDeviceConfigs.add(config); - } - if (jsonPayload.get("pm25") != null) { - HalMqttDeviceConfig config = new HalMqttParticularMatterDeviceConfig(topic, "$.pm25"); - detectedDeviceConfigs.add(config); - } - if (jsonPayload.get("voltage") != null) { - HalMqttVoltageDeviceConfig sensor = new HalMqttVoltageDeviceConfig(topic, "$.voltage"); - detectedDeviceConfigs.add(sensor); - } - if (jsonPayload.get("current") != null) { - HalMqttCurrentDeviceConfig event = new HalMqttCurrentDeviceConfig(topic, "$.current"); - detectedDeviceConfigs.add(event); - } - } - - return detectedDeviceConfigs; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/HalMqttDetector.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/HalMqttDetector.java deleted file mode 100644 index 6055cace..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/HalMqttDetector.java +++ /dev/null @@ -1,15 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; - -import java.util.List; - -/** - * This interface defines the interface required by a MQTT Device detector. - * The purpose of the implementing classes is to simplify creation of devices based on - * MQTT by automatically detecting and parsing devices from reported topics. - */ -public interface HalMqttDetector { - - List parseTopic(String topic, byte[] data); -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/TasmotaMqttDetector.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/TasmotaMqttDetector.java deleted file mode 100644 index 88740c81..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/TasmotaMqttDetector.java +++ /dev/null @@ -1,26 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalDeviceReportListener; -import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; -import zutil.ObjectUtil; - -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; - -public class TasmotaMqttDetector implements HalMqttDetector { - - - @Override - public List parseTopic(String topic, byte[] data) { - if (ObjectUtil.isEmpty(topic) || - ! topic.startsWith("zigbee2mqtt/") || - topic.startsWith("zigbee2mqtt/bridge/")) - return Collections.emptyList(); - - List detectedDeviceConfigs = new ArrayList<>(); - return detectedDeviceConfigs; - } - -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/Zigbee2mqttDetector.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/Zigbee2mqttDetector.java deleted file mode 100644 index 3d74af5f..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/detector/Zigbee2mqttDetector.java +++ /dev/null @@ -1,46 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalDeviceReportListener; -import se.hal.plugin.mqtt.device.*; -import zutil.ObjectUtil; -import zutil.parser.DataNode; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html - */ -public class Zigbee2mqttDetector implements HalMqttDetector { - - @Override - public List parseTopic(String topic, byte[] data) { - if (ObjectUtil.isEmpty(topic) || - ! topic.startsWith("zigbee2mqtt/") || - topic.endsWith("/set") || - topic.startsWith("zigbee2mqtt/bridge/")) - return Collections.emptyList(); - - List detectedDeviceConfigs = new ArrayList<>(); - DataNode json = JSONParser.read(new String(data, StandardCharsets.UTF_8)); - - if (json.getString("state") != null) { - HalMqttOnOffEventConfig event = new HalMqttOnOffEventConfig(topic, "$.state"); - event.setWriteTopicName(topic + "/set/state"); - event.setValueOnString("ON"); - event.setValueOffString("OFF"); - detectedDeviceConfigs.add(event); - } - - if (json.getString("power") != null) { - HalMqttPowerConsumptionDeviceConfig event = new HalMqttPowerConsumptionDeviceConfig(topic, "$.power"); - detectedDeviceConfigs.add(event); - } - - return detectedDeviceConfigs; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttCurrentDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttCurrentDeviceConfig.java deleted file mode 100644 index 291813cd..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttCurrentDeviceConfig.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.CurrentSensorData; -import se.hal.struct.devicedata.VoltageSensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttCurrentDeviceConfig extends HalMqttSensorConfig { - - public HalMqttCurrentDeviceConfig() {} - public HalMqttCurrentDeviceConfig(String topic) { - super(topic); - } - public HalMqttCurrentDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return CurrentSensorData.class; - } - - @Override - public CurrentSensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new CurrentSensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new CurrentSensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java deleted file mode 100644 index 92275858..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttDeviceConfig.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventController; -import se.hal.intf.HalSensorConfig; -import se.hal.plugin.mqtt.HalMqttController; -import zutil.ui.conf.Configurator; - -import java.util.Objects; - -public abstract class HalMqttDeviceConfig implements HalDeviceConfig { - - @Configurator.Configurable(value = "MQTT Topic Name") - private String topicName; - @Configurator.Configurable(value = "JSON Path", description = "If the value of the topic is a JSON then this parameter can be used to specify the path to the e.g. temperature value." + - "
    THe parameter uses the JSON-Path syntax where it always starts with $ and object fields can be accessed by . and a array element by [index]") - private String jsonPath; - - - public HalMqttDeviceConfig() {} - - /** - * @param topicName is the topic associated to this device - */ - public HalMqttDeviceConfig(String topicName) { - this.topicName = topicName; - } - - /** - * @param topicName is the topic associated to this device. - * @param jsonPath indicates that the payload is of JSON format and the data should be extracted from this path. - */ - public HalMqttDeviceConfig(String topicName, String jsonPath) { - this.topicName = topicName; - this.jsonPath = jsonPath; - } - - - public String getTopicName() { - return topicName; - } - public void setTopicName(String topicName) { - this.topicName = topicName; - } - - public String getJsonPath() { - return jsonPath; - } - public void setJsonPath(String jsonPath) { - this.jsonPath = jsonPath; - } - - // -------------------------- - // Hal Methods - // -------------------------- - - public Class getDeviceControllerClass() { - return HalMqttController.class; - } - - /** - * @param data the data published to the MQTT topic. - * @return a new data object instance containing the device data based on the input data. - */ - public abstract HalDeviceData getDeviceData(byte[] data); - - // -------------------------- - // Java Methods - // -------------------------- - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - HalMqttDeviceConfig that = (HalMqttDeviceConfig) o; - - if (!Objects.equals(topicName, that.topicName)) return false; - return Objects.equals(jsonPath, that.jsonPath); - } - - @Override - public int hashCode() { - int result = topicName != null ? topicName.hashCode() : 0; - result = 31 * result + (jsonPath != null ? jsonPath.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Topic: " + topicName + ", JSON Path: " + jsonPath; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttEventConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttEventConfig.java deleted file mode 100644 index dc76ea43..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttEventConfig.java +++ /dev/null @@ -1,64 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventConfig; -import se.hal.intf.HalEventController; -import se.hal.intf.HalSensorConfig; -import se.hal.plugin.mqtt.HalMqttController; -import zutil.ui.conf.Configurator; - -import java.util.Objects; - -public abstract class HalMqttEventConfig extends HalMqttDeviceConfig implements HalEventConfig { - - @Configurator.Configurable(value = "MQTT Write Topic Name") - private String writeTopicName; - - - public HalMqttEventConfig() {} - public HalMqttEventConfig(String topic) { - super(topic); - } - public HalMqttEventConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - - public String getWriteTopicName() { - return writeTopicName; - } - - public void setWriteTopicName(String writeTopicName) { - this.writeTopicName = writeTopicName; - } - - /** - * Generates the payload that will be added to a MQTT publish message. - * - * @param data the data that should be converted to a payload. - * @return a byte array representing the given data, null if the conversion was not possible. - */ - public abstract byte[] getMqttPublishPayload(HalDeviceData data); - - // -------------------------- - // Java Methods - // -------------------------- - - @Override - public final boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof HalMqttEventConfig)) return false; - if (!super.equals(o)) return false; - - HalMqttEventConfig that = (HalMqttEventConfig) o; - return Objects.equals(writeTopicName, that.writeTopicName); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + Objects.hashCode(writeTopicName); - return result; - } - -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfig.java deleted file mode 100644 index 76bf33c4..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfig.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventController; -import se.hal.plugin.mqtt.HalMqttController; -import se.hal.struct.devicedata.HumiditySensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttHumidityDeviceConfig extends HalMqttSensorConfig { - - public HalMqttHumidityDeviceConfig() {} - public HalMqttHumidityDeviceConfig(String topic) { - super(topic); - } - public HalMqttHumidityDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return HumiditySensorData.class; - } - - @Override - public HumiditySensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new HumiditySensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new HumiditySensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfig.java deleted file mode 100644 index c5c92241..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.OnOffEventData; -import zutil.ObjectUtil; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; -import zutil.ui.conf.Configurator; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -public class HalMqttOnOffEventConfig extends HalMqttEventConfig { - - @Configurator.Configurable(value = "Match value for state ON") - private String valueOnString = "ON"; - @Configurator.Configurable(value = "Match value for state OFF") - private String valueOffString = "OFF"; - - - public HalMqttOnOffEventConfig() {} - public HalMqttOnOffEventConfig(String topic) { - super(topic); - } - public HalMqttOnOffEventConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - - - public String getValueOnString() { - return valueOnString; - } - - public void setValueOnString(String valueOnString) { - this.valueOnString = valueOnString; - } - - public String getValueOffString() { - return valueOffString; - } - - public void setValueOffString(String valueOffString) { - this.valueOffString = valueOffString; - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return OnOffEventData.class; - } - - @Override - public HalDeviceData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - String dataStr = new String(data, StandardCharsets.UTF_8); - - if (!ObjectUtil.isEmpty(getJsonPath())) { - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (ObjectUtil.isEmpty(deviceDataValue)) { - return null; - } - - dataStr = deviceDataValue.getString().toUpperCase(); - } - - if (valueOnString.equalsIgnoreCase(dataStr)) { - return new OnOffEventData(true, System.currentTimeMillis()); - } else if (valueOffString.equalsIgnoreCase(dataStr)) { - return new OnOffEventData(false, System.currentTimeMillis()); - } else { - return null; - } - } - - @Override - public byte[] getMqttPublishPayload(HalDeviceData data) { - if (data instanceof OnOffEventData) { - if (((OnOffEventData) data).isOn()) { - return valueOnString.getBytes(StandardCharsets.UTF_8); - } else { - return valueOffString.getBytes(StandardCharsets.UTF_8); - } - } - - return null; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfig.java deleted file mode 100644 index f5c028fe..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfig.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventController; -import se.hal.plugin.mqtt.HalMqttController; -import se.hal.struct.devicedata.ParticulateMatterSensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttParticularMatterDeviceConfig extends HalMqttSensorConfig { - - public HalMqttParticularMatterDeviceConfig() {} - public HalMqttParticularMatterDeviceConfig(String topic) { - super(topic); - } - public HalMqttParticularMatterDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return ParticulateMatterSensorData.class; - } - - @Override - public ParticulateMatterSensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new ParticulateMatterSensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new ParticulateMatterSensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttPowerConsumptionDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttPowerConsumptionDeviceConfig.java deleted file mode 100644 index f7e9c52c..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttPowerConsumptionDeviceConfig.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.HumiditySensorData; -import se.hal.struct.devicedata.PowerConsumptionSensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttPowerConsumptionDeviceConfig extends HalMqttSensorConfig { - - public HalMqttPowerConsumptionDeviceConfig() {} - public HalMqttPowerConsumptionDeviceConfig(String topic) { - super(topic); - } - public HalMqttPowerConsumptionDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return PowerConsumptionSensorData.class; - } - - @Override - public PowerConsumptionSensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new PowerConsumptionSensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new PowerConsumptionSensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttSensorConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttSensorConfig.java deleted file mode 100644 index 4cd6e056..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttSensorConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalEventController; -import se.hal.intf.HalSensorConfig; -import se.hal.plugin.mqtt.HalMqttController; - -public abstract class HalMqttSensorConfig extends HalMqttDeviceConfig implements HalSensorConfig { - - public HalMqttSensorConfig() {} - public HalMqttSensorConfig(String topic) { - super(topic); - } - public HalMqttSensorConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceControllerClass() { - return HalMqttController.class; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfig.java deleted file mode 100644 index 29010e42..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfig.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.intf.HalEventController; -import se.hal.plugin.mqtt.HalMqttController; -import se.hal.struct.devicedata.TemperatureSensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttTemperatureDeviceConfig extends HalMqttSensorConfig { - - public HalMqttTemperatureDeviceConfig() {} - public HalMqttTemperatureDeviceConfig(String topic) { - super(topic); - } - public HalMqttTemperatureDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return TemperatureSensorData.class; - } - - @Override - public TemperatureSensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new TemperatureSensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new TemperatureSensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttVoltageDeviceConfig.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttVoltageDeviceConfig.java deleted file mode 100644 index b6598c98..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/device/HalMqttVoltageDeviceConfig.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.mqtt.device; - -import se.hal.intf.HalDeviceData; -import se.hal.struct.devicedata.PowerConsumptionSensorData; -import se.hal.struct.devicedata.VoltageSensorData; -import zutil.ObjectUtil; -import zutil.converter.Converter; -import zutil.parser.DataNode; -import zutil.parser.DataNodePath; -import zutil.parser.json.JSONParser; - -import java.nio.charset.StandardCharsets; - -public class HalMqttVoltageDeviceConfig extends HalMqttSensorConfig { - - public HalMqttVoltageDeviceConfig() {} - public HalMqttVoltageDeviceConfig(String topic) { - super(topic); - } - public HalMqttVoltageDeviceConfig(String topic, String jsonPath) { - super(topic, jsonPath); - } - - // -------------------------- - // Hal Methods - // -------------------------- - - @Override - public Class getDeviceDataClass() { - return VoltageSensorData.class; - } - - @Override - public VoltageSensorData getDeviceData(byte[] data) { - if(ObjectUtil.isEmpty(data)) { - return null; - } - - if (!ObjectUtil.isEmpty(getJsonPath())) { - String dataStr = new String(data, StandardCharsets.UTF_8); - DataNode json = JSONParser.read(dataStr); - DataNode deviceDataValue = DataNodePath.search(getJsonPath(), json); - - if (deviceDataValue != null) { - return new VoltageSensorData(deviceDataValue.getDouble(), System.currentTimeMillis()); - } else { - return null; - } - } - return new VoltageSensorData(Converter.toInt(data), System.currentTimeMillis()); - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.AVERAGE; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/page/MqttOverviewPage.java b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/page/MqttOverviewPage.java deleted file mode 100644 index d43d1017..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/page/MqttOverviewPage.java +++ /dev/null @@ -1,49 +0,0 @@ -package se.hal.plugin.mqtt.page; - -import se.hal.HalContext; -import se.hal.intf.HalAbstractControllerManager; -import se.hal.intf.HalWebPage; -import se.hal.plugin.mqtt.HalMqttController; -import zutil.io.file.FileUtil; -import zutil.net.mqtt.MqttBroker; -import zutil.net.mqtt.MqttSubscriptionListener; -import zutil.parser.Templator; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - - -public class MqttOverviewPage extends HalWebPage implements MqttSubscriptionListener { - private static final String TEMPLATE = HalContext.RESOURCE_WEB_ROOT + "/mqtt_overview.tmpl"; - - private Map topicData = new HashMap<>(); - - - public MqttOverviewPage() { - super("mqtt_overview"); - super.getRootNav().createSubNav("Settings").createSubNav(this.getId(), "MQTT Overview").setWeight(9_000); - - HalMqttController controller = HalAbstractControllerManager.getController(HalMqttController.class); - MqttBroker broker = controller.getBroker(); - broker.addGlobalSubscriber(this); - } - - - @Override - public synchronized void dataPublished(String topic, byte[] data) { - topicData.put(topic, new String(data, StandardCharsets.UTF_8)); - } - - @Override - public Templator httpRespond( - Map session, - Map cookie, - Map request) - throws Exception { - - Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); - tmpl.set("topics", topicData.entrySet()); - return tmpl; - } -} diff --git a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/plugin.json b/plugins/hal-mqtt/src/se/hal/plugin/mqtt/plugin.json deleted file mode 100644 index 8c8eabdf..00000000 --- a/plugins/hal-mqtt/src/se/hal/plugin/mqtt/plugin.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 0.1, - "name": "Hal-MQTT", - "interfaces": [ - {"se.hal.intf.HalAutostartController": "se.hal.plugin.mqtt.HalMqttController"}, - - {"se.hal.plugin.mqtt.detector.HalMqttDetector": "se.hal.plugin.mqtt.detector.GenericMqttDetector"}, - {"se.hal.plugin.mqtt.detector.HalMqttDetector": "se.hal.plugin.mqtt.detector.TasmotaMqttDetector"}, - {"se.hal.plugin.mqtt.detector.HalMqttDetector": "se.hal.plugin.mqtt.detector.Zigbee2mqttDetector"}, - - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttCurrentDeviceConfig"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttHumidityDeviceConfig"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttParticularMatterDeviceConfig"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttPowerConsumptionDeviceConfig"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttTemperatureDeviceConfig"}, - {"se.hal.intf.HalSensorConfig": "se.hal.plugin.mqtt.device.HalMqttVoltageDeviceConfig"}, - - {"se.hal.intf.HalEventConfig": "se.hal.plugin.mqtt.device.HalMqttOnOffEventConfig"}, - - {"se.hal.intf.HalWebPage": "se.hal.plugin.mqtt.page.MqttOverviewPage"} - ] -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java deleted file mode 100644 index febc0266..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/GenericMqttDetectorTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import org.junit.Test; -import se.hal.plugin.mqtt.device.*; -import se.hal.struct.devicedata.HumiditySensorData; -import se.hal.struct.devicedata.ParticulateMatterSensorData; -import se.hal.struct.devicedata.TemperatureSensorData; -import se.hal.test.MockHalDeviceReportListener; -import zutil.converter.Converter; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -import static org.junit.Assert.*; - - -public class GenericMqttDetectorTest { - - @Test - public void ignoredTopics() { - GenericMqttDetector detector = new GenericMqttDetector(); - - assertEquals(0, detector.parseTopic("", new byte[]{}).size()); - assertEquals(0, detector.parseTopic("invalid/topic", new byte[]{}).size()); - } - - @Test - public void parseTemperature() { - GenericMqttDetector detector = new GenericMqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality/temperature", - Converter.toBytes(26)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttTemperatureDeviceConfig("zigbee2mqtt/Kitchen air quality/temperature"), - devices.get(0)); - - devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"temperature\": 26}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttTemperatureDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.temperature"), - devices.get(0)); - } - - @Test - public void parseHumidity() { - GenericMqttDetector detector = new GenericMqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality/humidity", - Converter.toBytes(51)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttHumidityDeviceConfig("zigbee2mqtt/Kitchen air quality/humidity"), - devices.get(0)); - - devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"humidity\": 51}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttHumidityDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.humidity"), - devices.get(0)); - } - - @Test - public void parseParticularMatter() { - GenericMqttDetector detector = new GenericMqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality/pm25", - Converter.toBytes(1)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttParticularMatterDeviceConfig("zigbee2mqtt/Kitchen air quality/pm25"), - devices.get(0)); - - devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"pm25\": 1}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttParticularMatterDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.pm25"), - devices.get(0)); - } - - @Test - public void parseVoltage() { - GenericMqttDetector detector = new GenericMqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality/voltage", - Converter.toBytes(1)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttVoltageDeviceConfig("zigbee2mqtt/Kitchen air quality/voltage"), - devices.get(0)); - - devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"voltage\": 1}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttVoltageDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.voltage"), - devices.get(0)); - } - - @Test - public void parseCurrent() { - GenericMqttDetector detector = new GenericMqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality/current", - Converter.toBytes(1)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttCurrentDeviceConfig("zigbee2mqtt/Kitchen air quality/current"), - devices.get(0)); - - devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"current\": 1}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttCurrentDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.current"), - devices.get(0)); - } -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java deleted file mode 100644 index f670e602..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/detector/Zigbee2mqttDetectorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package se.hal.plugin.mqtt.detector; - -import org.junit.Test; -import se.hal.plugin.mqtt.device.HalMqttDeviceConfig; -import se.hal.plugin.mqtt.device.HalMqttOnOffEventConfig; -import se.hal.plugin.mqtt.device.HalMqttParticularMatterDeviceConfig; -import se.hal.plugin.mqtt.device.HalMqttPowerConsumptionDeviceConfig; -import se.hal.test.MockHalDeviceReportListener; -import zutil.converter.Converter; - -import java.nio.charset.StandardCharsets; -import java.util.List; - -import static org.junit.Assert.*; - - -public class Zigbee2mqttDetectorTest { - - @Test - public void ignoredTopics() { - Zigbee2mqttDetector detector = new Zigbee2mqttDetector(); - - List devices = detector.parseTopic("", new byte[]{}); - assertEquals(0, devices.size()); - - devices = detector.parseTopic("invalid/topic", new byte[]{}); - assertEquals(0, devices.size()); - - - } - - @Test - public void parseState() { - Zigbee2mqttDetector detector = new Zigbee2mqttDetector(); - - List devices = devices = detector.parseTopic( - "zigbee2mqtt/Kitchen Plant Light", - "{\"invalid_power\":10.48,\"state\":\"ON\"}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - - HalMqttOnOffEventConfig actualOnOff = new HalMqttOnOffEventConfig("zigbee2mqtt/Kitchen Plant Light", "$.state"); - actualOnOff.setWriteTopicName("zigbee2mqtt/Kitchen Plant Light/set/state"); - assertEquals(actualOnOff, devices.get(0)); - } - - @Test - public void parsePower() { - Zigbee2mqttDetector detector = new Zigbee2mqttDetector(); - - List devices = detector.parseTopic( - "zigbee2mqtt/Kitchen air quality", - "{\"power\": 1}".getBytes(StandardCharsets.UTF_8)); - assertEquals(1, devices.size()); - assertEquals( - new HalMqttPowerConsumptionDeviceConfig("zigbee2mqtt/Kitchen air quality", "$.power"), - devices.get(0)); - } -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfigTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfigTest.java deleted file mode 100644 index 62fb519f..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttHumidityDeviceConfigTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import org.junit.Test; -import se.hal.struct.devicedata.HumiditySensorData; -import se.hal.struct.devicedata.TemperatureSensorData; -import zutil.converter.Converter; - -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.*; -import static se.hal.test.HalAssert.assertEqualsIgnoreTimestamp; - -public class HalMqttHumidityDeviceConfigTest { - - @Test - public void getDeviceDataRaw() { - HalMqttHumidityDeviceConfig config = new HalMqttHumidityDeviceConfig(); - - assertEqualsIgnoreTimestamp(null, config.getDeviceData(null)); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("".getBytes(StandardCharsets.UTF_8))); - - assertEqualsIgnoreTimestamp(new HumiditySensorData(0, 0), config.getDeviceData(Converter.toBytes(0))); - assertEqualsIgnoreTimestamp(new HumiditySensorData(10, 0), config.getDeviceData(Converter.toBytes(10))); - } -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfigTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfigTest.java deleted file mode 100644 index 8b7896c7..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttOnOffEventConfigTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import org.junit.Test; -import se.hal.struct.devicedata.OnOffEventData; - -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.*; -import static se.hal.test.HalAssert.assertEqualsIgnoreTimestamp; - -public class HalMqttOnOffEventConfigTest { - private static final OnOffEventData ON_EVENT = new OnOffEventData(true, 0); - private static final OnOffEventData OFF_EVENT = new OnOffEventData(false, 0); - - @Test - public void getDeviceDataRaw() { - HalMqttOnOffEventConfig config = new HalMqttOnOffEventConfig(); - - assertEqualsIgnoreTimestamp(null, config.getDeviceData(null)); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("unknown".getBytes(StandardCharsets.UTF_8))); - - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("OFF".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("off".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("ON".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("on".getBytes(StandardCharsets.UTF_8))); - - config.setValueOnString("online"); - config.setValueOffString("offline"); - - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("OFFLINE".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("offline".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("ONLINE".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("online".getBytes(StandardCharsets.UTF_8))); - } - - @Test - public void getDeviceDataJson() { - HalMqttOnOffEventConfig config = new HalMqttOnOffEventConfig(); - config.setJsonPath("$.state"); - - assertEqualsIgnoreTimestamp(null, config.getDeviceData("{\"power\":10.48,\"state\":\"\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("{\"power\":10.48,\"state\":\"unknown\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"OFF\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"off\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"ON\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"on\"}".getBytes(StandardCharsets.UTF_8))); - - config.setValueOnString("online"); - config.setValueOffString("offline"); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"OFFLINE\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(OFF_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"offline\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"ONLINE\"}".getBytes(StandardCharsets.UTF_8))); - assertEqualsIgnoreTimestamp(ON_EVENT, config.getDeviceData("{\"power\":10.48,\"state\":\"online\"}".getBytes(StandardCharsets.UTF_8))); - - } - - @Test - public void getMqttPublishPayload() { - HalMqttOnOffEventConfig config = new HalMqttOnOffEventConfig(); - - assertEquals("ON", new String(config.getMqttPublishPayload(ON_EVENT))); - assertEquals("OFF", new String(config.getMqttPublishPayload(OFF_EVENT))); - - config.setValueOnString("online"); - config.setValueOffString("offline"); - - assertEquals("online", new String(config.getMqttPublishPayload(ON_EVENT))); - assertEquals("offline", new String(config.getMqttPublishPayload(OFF_EVENT))); - } -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfigTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfigTest.java deleted file mode 100644 index cd321d1e..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttParticularMatterDeviceConfigTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import org.junit.Test; -import se.hal.struct.devicedata.HumiditySensorData; -import se.hal.struct.devicedata.ParticulateMatterSensorData; -import zutil.converter.Converter; - -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.*; -import static se.hal.test.HalAssert.assertEqualsIgnoreTimestamp; - -public class HalMqttParticularMatterDeviceConfigTest { - - @Test - public void getDeviceDataRaw() { - HalMqttParticularMatterDeviceConfig config = new HalMqttParticularMatterDeviceConfig(); - - assertEqualsIgnoreTimestamp(null, config.getDeviceData(null)); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("".getBytes(StandardCharsets.UTF_8))); - - assertEqualsIgnoreTimestamp(new ParticulateMatterSensorData(0, 0), config.getDeviceData(Converter.toBytes(0))); - assertEqualsIgnoreTimestamp(new ParticulateMatterSensorData(10, 0), config.getDeviceData(Converter.toBytes(10))); - } -} \ No newline at end of file diff --git a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfigTest.java b/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfigTest.java deleted file mode 100644 index 5bd7889f..00000000 --- a/plugins/hal-mqtt/test/se/hal/plugin/mqtt/device/HalMqttTemperatureDeviceConfigTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package se.hal.plugin.mqtt.device; - -import org.junit.Test; -import se.hal.struct.devicedata.TemperatureSensorData; -import zutil.converter.Converter; - -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.*; -import static se.hal.test.HalAssert.assertEqualsIgnoreTimestamp; - -public class HalMqttTemperatureDeviceConfigTest { - - @Test - public void getDeviceDataRaw() { - HalMqttTemperatureDeviceConfig config = new HalMqttTemperatureDeviceConfig(); - - assertEqualsIgnoreTimestamp(null, config.getDeviceData(null)); - assertEqualsIgnoreTimestamp(null, config.getDeviceData("".getBytes(StandardCharsets.UTF_8))); - - assertEqualsIgnoreTimestamp(new TemperatureSensorData(0, 0), config.getDeviceData(Converter.toBytes(0))); - assertEqualsIgnoreTimestamp(new TemperatureSensorData(10, 0), config.getDeviceData(Converter.toBytes(10))); - } -} \ No newline at end of file diff --git a/plugins/hal-netscan/build.gradle b/plugins/hal-netscan/build.gradle deleted file mode 100644 index 81fb360f..00000000 --- a/plugins/hal-netscan/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation project(':hal-core') -} diff --git a/plugins/hal-netscan/src/se/hal/plugin/netscan/NetScanController.java b/plugins/hal-netscan/src/se/hal/plugin/netscan/NetScanController.java deleted file mode 100644 index de1adec2..00000000 --- a/plugins/hal-netscan/src/se/hal/plugin/netscan/NetScanController.java +++ /dev/null @@ -1,159 +0,0 @@ -package se.hal.plugin.netscan; - -import se.hal.HalContext; -import se.hal.intf.*; -import se.hal.struct.devicedata.AvailabilityEventData; -import zutil.InetUtil; -import zutil.log.LogUtil; -import zutil.net.InetScanner; -import zutil.net.InetScanner.InetScanListener; -import zutil.osal.MultiCommandExecutor; - -import java.net.InetAddress; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class NetScanController implements - HalEventController, - HalAutostartController, - HalScannableController, - InetScanListener, - Runnable { - public static Logger logger = LogUtil.getLogger(); - - private static final int NETWORK_SYNC_INTERVAL = 3 * 60 * 60 * 1000; // 3 hours - private static final int PING_INTERVAL = 10 * 1000; // 10 sec - - private ScheduledExecutorService executor; - private List deviceListeners = new CopyOnWriteArrayList<>(); - /** A register and a cache of previous state **/ - private HashMap devices = new HashMap<>(); - private boolean scanRunning = false; - - - @Override - public boolean isAvailable() { - return ! InetUtil.getLocalInet4Address().isEmpty(); - } - - @Override - public void initialize() { - executor = Executors.newScheduledThreadPool(2); - executor.scheduleAtFixedRate(NetScanController.this, 10_000, PING_INTERVAL, TimeUnit.MILLISECONDS); - } - - @Override - public void run() { - try(MultiCommandExecutor executor = new MultiCommandExecutor()){ - for (Map.Entry entry : devices.entrySet()) { - NetworkDevice device = entry.getKey(); - AvailabilityEventData prevData = entry.getValue(); - - if (!deviceListeners.isEmpty()) { - // We ping two times to increase reliability - boolean ping = false; - ping |= InetScanner.isReachable(device.getHost(), executor); - if (!ping) - ping |= InetScanner.isReachable(device.getHost(), executor); - - // Should we report? - if (prevData == null || prevData.isAvailable() != ping) { - AvailabilityEventData newData = new AvailabilityEventData(ping, System.currentTimeMillis()); - entry.setValue(newData); - logger.fine("IP " + device.getHost() + " state has changed to " + newData); - - for (HalDeviceReportListener deviceListener : deviceListeners) { - deviceListener.reportReceived(device, newData); - } - } - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } - } - - @Override - public void foundInetAddress(InetAddress ip) { - logger.fine("Auto Detected ip: " + ip.getHostAddress()); - - for (HalDeviceReportListener deviceListener : deviceListeners) { - deviceListener.reportReceived( - new NetworkDevice(ip.getHostAddress()), - new AvailabilityEventData(true, System.currentTimeMillis())); - } - } - - - @Override - public synchronized void startScan() { - if (!scanRunning) { - scanRunning = true; - executor.schedule(new Runnable() { - @Override - public void run() { - try { - logger.fine("Starting network scan..."); - InetScanner scanner = new InetScanner(); - scanner.setListener(NetScanController.this); - scanner.scan(InetUtil.getLocalInet4Address().get(0)); - logger.fine("Network scan done"); - } catch (Exception e) { - logger.log(Level.SEVERE, null, e); - } finally { - scanRunning = false; - } - } - }, 0, TimeUnit.MILLISECONDS); - } - } - - @Override - public boolean isScanning() { - return scanRunning; - } - - - @Override - public void register(HalDeviceConfig deviceConfig) { - if (deviceConfig instanceof NetworkDevice) - devices.put((NetworkDevice) deviceConfig, null); - } - - @Override - public void deregister(HalDeviceConfig deviceConfig) { - devices.remove(deviceConfig); - } - - @Override - public int size() { - return devices.size(); - } - - - @Override - public void send(HalEventConfig eventConfig, HalEventData eventData) { } - - - @Override - public void addListener(HalDeviceReportListener listener) { - if (!deviceListeners.contains(listener)) - deviceListeners.add(listener); - } - - - @Override - public void close() { - if (executor != null){ - executor.shutdown(); - executor = null; - } - } -} diff --git a/plugins/hal-netscan/src/se/hal/plugin/netscan/NetworkDevice.java b/plugins/hal-netscan/src/se/hal/plugin/netscan/NetworkDevice.java deleted file mode 100644 index fef5c7ad..00000000 --- a/plugins/hal-netscan/src/se/hal/plugin/netscan/NetworkDevice.java +++ /dev/null @@ -1,47 +0,0 @@ -package se.hal.plugin.netscan; - -import se.hal.intf.HalEventConfig; -import se.hal.intf.HalEventController; -import se.hal.intf.HalEventData; -import se.hal.struct.devicedata.AvailabilityEventData; -import zutil.ui.conf.Configurator; - -public class NetworkDevice implements HalEventConfig { - - @Configurator.Configurable("IP Address") - private String host; - - - - public NetworkDevice() { } - public NetworkDevice(String hostAddress) { - this.host = hostAddress; - } - - - public String getHost() { - return host; - } - - - @Override - public Class getDeviceControllerClass() { - return NetScanController.class; - } - @Override - public Class getDeviceDataClass() { - return AvailabilityEventData.class; - } - - - @Override - public boolean equals(Object obj){ - if (obj instanceof NetworkDevice) - return host != null && host.equals(((NetworkDevice) obj).host); - return false; - } - @Override - public String toString(){ - return "Host: "+ host; - } -} diff --git a/plugins/hal-netscan/src/se/hal/plugin/netscan/plugin.json b/plugins/hal-netscan/src/se/hal/plugin/netscan/plugin.json deleted file mode 100644 index 7b08f8f2..00000000 --- a/plugins/hal-netscan/src/se/hal/plugin/netscan/plugin.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "version": 1.0, - "name": "Hal-Network Scanner", - "interfaces": [ - {"se.hal.intf.HalAutostartController": "se.hal.plugin.netscan.NetScanController"}, - {"se.hal.intf.HalEventConfig": "se.hal.plugin.netscan.NetworkDevice"} - ] -} \ No newline at end of file diff --git a/plugins/hal-nutups/build.gradle b/plugins/hal-nutups/build.gradle deleted file mode 100644 index 81fb360f..00000000 --- a/plugins/hal-nutups/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation project(':hal-core') -} diff --git a/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsController.java b/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsController.java deleted file mode 100644 index cfb4d04a..00000000 --- a/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsController.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.nutups; - -import se.hal.HalContext; -import se.hal.intf.HalAutostartController; -import se.hal.intf.HalDeviceConfig; -import se.hal.intf.HalDeviceReportListener; -import se.hal.intf.HalSensorController; -import zutil.log.LogUtil; -import zutil.osal.linux.app.NutUPSClient; - -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - - -public class NutUpsController implements HalSensorController, HalAutostartController, Runnable { - public static Logger logger = LogUtil.getLogger(); - - private static final int SYNC_INTERVAL = 60 * 1000; - public static final String CONFIG_HOST = "hal_nutups.host"; - public static final String CONFIG_PORT = "hal_nutups.port"; - - private HashMap registeredDevices = new HashMap<>(); - private NutUPSClient client; - private ScheduledExecutorService executor; - private List deviceListeners = new CopyOnWriteArrayList<>(); - - - - @Override - public boolean isAvailable() { - return HalContext.containsProperty(CONFIG_HOST); - } - @Override - public void initialize() throws Exception { - if (client == null) { - int port = NutUPSClient.DEFAULT_PORT; - if (HalContext.containsProperty(CONFIG_PORT)) - port = HalContext.getIntegerProperty(CONFIG_PORT); - - client = new NutUPSClient(HalContext.getStringProperty(CONFIG_HOST), port); - - executor = Executors.newScheduledThreadPool(1); - executor.scheduleAtFixedRate(this, 5000, SYNC_INTERVAL, TimeUnit.MILLISECONDS); - } - } - - - @Override - public void addListener(HalDeviceReportListener listener) { - if (!deviceListeners.contains(listener)) - deviceListeners.add(listener); - } - - - @Override - public void run() { - try { - if (client != null) { - for (HalDeviceReportListener deviceListener : deviceListeners) { - for (NutUPSClient.UPSDevice ups : client.getUPSList()) { - NutUpsDevice device = registeredDevices.get(ups.getId()); - if (device == null) - device = new NutUpsDevice(ups); - - deviceListener.reportReceived(device, device.read(ups)); - } - } - } - } catch (Exception e){ - logger.log(Level.SEVERE, "NutUps thread crashed", e); - } - } - - @Override - public void close() { - client = null; - executor.shutdownNow(); - } - - - @Override - public void register(HalDeviceConfig deviceConfig) { - if (deviceConfig instanceof NutUpsDevice) - registeredDevices.put(((NutUpsDevice) deviceConfig).getUpsId(), (NutUpsDevice) deviceConfig); - } - - @Override - public void deregister(HalDeviceConfig deviceConfig) { - registeredDevices.remove(((NutUpsDevice) deviceConfig).getUpsId()); - } - - @Override - public int size() { - return registeredDevices.size(); - } - -} diff --git a/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsDevice.java b/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsDevice.java deleted file mode 100644 index 1d879f73..00000000 --- a/plugins/hal-nutups/src/se/hal/plugin/nutups/NutUpsDevice.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * The MIT License (MIT) - * - * Copyright (c) 2020 Ziver Koc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package se.hal.plugin.nutups; - -import se.hal.intf.HalSensorConfig; -import se.hal.intf.HalSensorController; -import se.hal.intf.HalSensorData; -import se.hal.struct.devicedata.PowerConsumptionSensorData; -import zutil.osal.linux.app.NutUPSClient; -import zutil.ui.conf.Configurator; - -public class NutUpsDevice implements HalSensorConfig{ - - @Configurator.Configurable("UPS id") - private String upsId; - - - public NutUpsDevice(){} - - protected NutUpsDevice(NutUPSClient.UPSDevice ups){ - this.upsId = ups.getId(); - } - - - protected HalSensorData read(NutUPSClient.UPSDevice ups){ - PowerConsumptionSensorData data = new PowerConsumptionSensorData(); - data.setTimestamp(System.currentTimeMillis()); - data.setData(ups.getPowerUsage() * 1/60.0); // Convert watt minutes to watt hour - return data; - } - - - public String getUpsId(){ - return upsId; - } - - - @Override - public long getDataInterval(){ - return 60*1000; // 1 min - } - - @Override - public AggregationMethod getAggregationMethod() { - return AggregationMethod.SUM; - } - @Override - public Class getDeviceControllerClass() { - return NutUpsController.class; - } - @Override - public Class getDeviceDataClass() { - return PowerConsumptionSensorData.class; - } - - @Override - public boolean equals(Object obj){ - if (obj instanceof NutUpsDevice) - return upsId != null && upsId.equals(((NutUpsDevice)obj).upsId); - return false; - } - @Override - public String toString(){ - return "upsId: "+ upsId; - } - -} diff --git a/plugins/hal-nvr/build.gradle b/plugins/hal-nvr/build.gradle deleted file mode 100644 index 3abf8b2c..00000000 --- a/plugins/hal-nvr/build.gradle +++ /dev/null @@ -1,4 +0,0 @@ -dependencies { - implementation project(':hal-core') - -} diff --git a/plugins/hal-nvr/resources/bin/linux/ffmpeg b/plugins/hal-nvr/resources/bin/linux/ffmpeg deleted file mode 100644 index 275005dc..00000000 Binary files a/plugins/hal-nvr/resources/bin/linux/ffmpeg and /dev/null differ diff --git a/plugins/hal-nvr/resources/bin/mac/ffmpeg b/plugins/hal-nvr/resources/bin/mac/ffmpeg deleted file mode 100644 index 95e99a2b..00000000 Binary files a/plugins/hal-nvr/resources/bin/mac/ffmpeg and /dev/null differ diff --git a/plugins/hal-nvr/resources/bin/win/ffmpeg.exe b/plugins/hal-nvr/resources/bin/win/ffmpeg.exe deleted file mode 100644 index a06e7964..00000000 Binary files a/plugins/hal-nvr/resources/bin/win/ffmpeg.exe and /dev/null differ diff --git a/plugins/hal-nvr/resources/hal-nvr-reference.db b/plugins/hal-nvr/resources/hal-nvr-reference.db deleted file mode 100644 index 92282859..00000000 Binary files a/plugins/hal-nvr/resources/hal-nvr-reference.db and /dev/null differ diff --git a/plugins/hal-nvr/resources/web/camera_config.tmpl b/plugins/hal-nvr/resources/web/camera_config.tmpl deleted file mode 100644 index 97280806..00000000 --- a/plugins/hal-nvr/resources/web/camera_config.tmpl +++ /dev/null @@ -1,134 +0,0 @@ -

    Camera Configuration

    - -
    -
    -
    Cameras
    -
    -

    This is a list of all cameras connected to this server.

    - - - - - - - - - {{#cameras}} - - - - - - - {{/cameras}} -
    NameTypeConfiguration - -
    {{.getName()}}{{.getType()}}{{.getDeviceConfig()}} -
    - - -
    - - - -
    -
    -
    -
    -
    -
    - - - - - - - - - diff --git a/plugins/hal-nvr/resources/web/camera_detail.tmpl b/plugins/hal-nvr/resources/web/camera_detail.tmpl deleted file mode 100644 index b97096d0..00000000 --- a/plugins/hal-nvr/resources/web/camera_detail.tmpl +++ /dev/null @@ -1,56 +0,0 @@ -

    Details for {{camera.getName()}}

    - - - -
    - -
    - -
    -
    -
    Configuration
    -
    - - - - - - - - - - - - - - - - - - - - {{#camera.getDeviceConfigurator().getConfiguration()}} - - - - - {{/camera.getDeviceConfigurator().getConfiguration()}} -
    Camera ID:{{camera.getId()}}
    Name:{{camera.getName()}}
    Type:{{camera.getDeviceConfig().getClass().getSimpleName()}}
    Owner:{{camera.getUser().getUsername()}}

    {{.getNiceName()}}:{{.getString()}}
    -
    -
    -
    - - - - \ No newline at end of file diff --git a/plugins/hal-nvr/resources/web/camera_monitor.tmpl b/plugins/hal-nvr/resources/web/camera_monitor.tmpl deleted file mode 100644 index 6fd717d3..00000000 --- a/plugins/hal-nvr/resources/web/camera_monitor.tmpl +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - -
    - - - -
    - - - -
    - - - - \ No newline at end of file diff --git a/plugins/hal-nvr/resources/web/camera_overview.tmpl b/plugins/hal-nvr/resources/web/camera_overview.tmpl deleted file mode 100644 index 7e2d1434..00000000 --- a/plugins/hal-nvr/resources/web/camera_overview.tmpl +++ /dev/null @@ -1,22 +0,0 @@ -

    Camera Overview

    - -
    -
    -
    Cameras
    -
    - - - - - - - {{#cameras}} - - - - - {{/cameras}} -
    NameType
    {{.getName()}}{{.getDeviceConfig().getClass().getSimpleName()}}
    -
    -
    -
    diff --git a/plugins/hal-nvr/resources/web/css/lib/video-js.min.css b/plugins/hal-nvr/resources/web/css/lib/video-js.min.css deleted file mode 100644 index 9bef57e3..00000000 --- a/plugins/hal-nvr/resources/web/css/lib/video-js.min.css +++ /dev/null @@ -1 +0,0 @@ -@charset "UTF-8";.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-modal-dialog,.vjs-button>.vjs-icon-placeholder:before,.vjs-modal-dialog .vjs-modal-dialog-content{position:absolute;top:0;left:0;width:100%;height:100%}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.vjs-button>.vjs-icon-placeholder:before{text-align:center}@font-face{font-family:VideoJS;src:url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAABDkAAsAAAAAG6gAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3hY21hcAAAAYQAAADaAAADPv749/pnbHlmAAACYAAAC3AAABHQZg6OcWhlYWQAAA3QAAAAKwAAADYZw251aGhlYQAADfwAAAAdAAAAJA+RCLFobXR4AAAOHAAAABMAAACM744AAGxvY2EAAA4wAAAASAAAAEhF6kqubWF4cAAADngAAAAfAAAAIAE0AIFuYW1lAAAOmAAAASUAAAIK1cf1oHBvc3QAAA/AAAABJAAAAdPExYuNeJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGS7wTiBgZWBgaWQ5RkDA8MvCM0cwxDOeI6BgYmBlZkBKwhIc01hcPjI+FGJHcRdyA4RZgQRADK3CxEAAHic7dFZbsMgAEXRS0ycyZnnOeG7y+qC8pU1dHusIOXxuoxaOlwZYWQB0Aea4quIEN4E9LzKbKjzDeM6H/mua6Lmc/p8yhg0lvdYx15ZG8uOLQOGjMp3EzqmzJizYMmKNRu27Nhz4MiJMxeu3Ljz4Ekqm7T8P52G8PP3lnTOVk++Z6iN6QZzNN1F7ptuN7eGOjDUoaGODHVsuvU8MdTO9Hd5aqgzQ50b6sJQl4a6MtS1oW4MdWuoO0PdG+rBUI+GejLUs6FeDPVqqDdDvRvqw1CfhpqM9At0iFLaAAB4nJ1YDXBTVRZ+5/22TUlJ8we0pHlJm7RJf5O8F2j6EymlSPkpxaL8U2xpa3DKj0CBhc2IW4eWKSokIoLsuMqssM64f+jA4HSdWXXXscBq67IOs3FXZ1ZYWVyRFdo899yXtIBQZ90k7717zz3v3HPPOfd854YCCj9cL9dL0RQFOqCbGJnrHb5EayiKIWN8iA/hWBblo6hUWm8TtCDwE80WMJus/irwyxOdxeB0MDb14VNJHnXYoLLSl6FfCUYO9nYPTA8Epg9090LprfbBbZ2hY0UlJUXHQp3/vtWkS6EBv8+rPMq5u9692f/dNxJNiqwC1xPE9TCUgCsSdQWgE3XQD25lkG4CN2xmTcOXWBOyser6RN6KnGbKSbmQ3+d0OI1m2W8QzLLkI2sykrWAgJJEtA8vGGW/2Q+CmT3n8zS9wZwu2DCvtuZKZN3xkrLh36yCZuUomQSqGpY8t/25VfHVhw8z4ebGBtfLb0ya9PCaDc+8dGTvk2dsh6z7WzvowlXKUSWo9MJ15a3KrEP2loOr2Ojhw6iW6hf2BDdEccQvZGpaAy7YovSwq8kr7HGllxpd71rkS6G0Sf11sl9OvMK1+jwPPODxjUwkOim9CU3ix1wNjXDfmJSEn618Bs6lpWwUpU+8PCqLMY650zjq8VhCIP17NEKTx3eaLL+s5Pi6yJWaWjTHLR1jYzPSV9VF/6Ojdb/1kO3Mk3uhHC0x6gc1BjlKQ+nQFxTYdaJkZ7ySVxLBbhR1dsboNXp1tCYKW2LRaEzpYcIx2BKNxaL0ZaUnSqfFoiNhHKR/GkX6PWUSAaJelQaqZL1EpoHNsajSEyPSoJ9IjhIxTdjHLmwZvhRDOiFTY/YeQnvrVZmiTQtGncECXtFTBZLOVwwMRgoXHAkXzMzPn1nAJJ8jYSbMDaqN2waGLzNhih/bZynUBMpIWSg7VYi7DRx2m8ALkIdRCJwI6ArJx2EI8kaDWeTQKeAFk9fjl/1AvwktjQ1P7NjyMGQyfd4vjipX6M/i52D7Cq80kqlcxEcGXRr/FEcgs0u5uGgB4VWuMFfpdn2Re6Hi3PqzmxWKsz6+ae2Pn9hXXw/fqM859UiGC0oKYYILJBqJrsn1Z1E5qOs9rQCiUQRREjm8yJcbHF5cUJufX1vAHlefw0XgUoboS3ETfQlTxBC4SOtuE8VPRJTBSCQSjZCpk7Gqzu+masaZ2y7Zjehho4F3g82BNDkAHpORG4+OCS+f6JTPmtRn/PH1kch6d04sp7AQb25aQ/pqUyXeQ8vrebG8OYQdXOQ+585u0sdW9rqalzRURiJ+9F4MweRFrKUjl1GUYhH1A27WOHw5cTFSFPMo9EeUIGnQTZHIaJ7AHLaOKsOODaNF9jkBjYG2QEsQ2xjMUAx2bBEbeTBWMHwskBjngq56S/yfgkBnWBa4K9sqKtq2t1UI8S9He5XuBRbawAdatrQEAi30Aks2+LM8WeCbalVZkWNylvJ+dqJnzVb+OHlSoKW8nPCP7Rd+CcZ2DdWAGqJ2CBFOphgywFFCFBNtfAbGtNPBCwxvygHeYMZMY9ZboBqwq/pVrsbgN5tkv152ODlbMfiqwGMBgxa4Exz3QhovRIUp6acqZmQzRq0ypDXS2TPLT02YIkQETnOE445oOGxOmXAqUJNNG7XgupMjPq2ua9asrj5yY/yuKteO1Kx0YNJTufrirLe1mZnat7OL6rnUdCWenpW6I8mAnbsY8KWs1PuSovCW9A/Z25PQ24a7cNOqgmTkLmBMgh4THgc4b9k2IVv1/g/F5nGljwPLfOgHAzJzh45V/4+WenTzmMtR5Z7us2Tys909UHqrPY7KbckoxRvRHhmVc3cJGE97uml0R1S0jdULVl7EvZtDFVBF35N9cEdjpgmAiOlFZ+Dtoh93+D3zzHr8RRNZQhnCNMNbcegOvpEwZoL+06cJQ07h+th3fZ/7PVbVC6ngTAV/KoLFuO6+2KFcU651gEb5ugPSIb1D+Xp8V4+k3sEIGnw5mYe4If4k1lFYr6SCzmM2EQ8iWtmwjnBI9kTwe1TlfAmXh7H02by9fW2gsjKwtv0aaURKil4OdV7rDL1MXIFNrhdxohcZXYTnq47WisrKitaObbf5+yvkLi5J6lCNZZ+B6GC38VNBZBDidSS/+mSvh6s+srgC8pyKMvDtt+de3c9fU76ZPfuM8ud4Kv0fyP/LqfepMT/3oZxSqpZaTa1DaQYLY8TFsHYbWYsPoRhRWfL5eSSQbhUGgGC3YLbVMk6PitTFNGpAsNrC6D1VNBKgBHMejaiuRWEWGgsSDBTJjqWIl8kJLlsaLJ2tXDr6xGfT85bM2Q06a46x2HTgvdnV8z5YDy/27J4zt6x2VtkzjoYpkq36kaBr4eQSg7tyiVweWubXZugtadl58ydapfbORfKsDTuZ0OBgx4cfdjCf5tbWNITnL120fdOi1RV1C3uKGzNdwYLcMvZ3BxoPyTOCD1XvXTp7U10gWCVmTV9b3r2z0SkGWovb2hp9I89O8a2smlyaO8muMU+dRmtzp60IzAoFpjLr1n388boLyf0dRvxhsHZ0qbWqDkwqvvpkj4l0fY6EIXRi5sQSrAvsVYwXRy4qJ2EVtD1AN7a0HWth9ymvL1xc3WTUKK/TAHA/bXDVtVWfOMfuGxGZv4Ln/jVr9jc3j1yMv0tndmyt9Vq88Y9gH1wtLX3KWjot5++jWHgAoZZkQ14wGQ20Fli71UmKJAy4xKMSTGbVdybW7FDDAut9XpD5AzWrYO7zQ8qffqF8+Ynd/clrHcdyxGy3a/3+mfNnzC/cBsveTjnTvXf1o6vzOlZw7WtqtdmPK/Errz/6NNtD72zmNOZfbmYdTGHfoofqI79Oc+R2n1lrnL6pOm0Up7kwxhTW12Amm7WYkXR2qYrF2AmgmbAsxZjwy1xpg/m1Je2vrp8v/nz2xpmlBg4E9hrMU341wVpTOh/OfmGvAnra8q6uctr60ZQHV3Q+WMQJykMj8ZsWn2QBOmmHMB+m5pDIpTFonYigiaKAhGEiAHF7EliVnQkjoLVIMPtJpBKHYd3A8GYH9jJzrWwmHx5Qjp7vDAX0suGRym1vtm/9W1/HyR8vczfMs6Sk8DSv855/5dlX9oQq52hT8syyp2rx5Id17IAyAM3wIjQPMOHzytEB64q6D5zT91yNbnx3V/nqnd017S9Y0605k3izoXLpsxde2n38yoOV9s1LcjwzNjbdX6asnBVaBj/6/DwKwPkpcqbDG7BnsXoSqWnUAmottYF6jMSdVyYZh3zVXCjwTiwwHH6sGuRiEHQGzuRX6whZkp123oy1BWE2mEfJ/tvIRtM4ZM5bDXiMsPMaAKOTyc5uL57rqyyc5y5JE5pm1i2S2iUX0CcaQ6lC6Zog7JqSqZmYlosl2K6pwNA84zRnQW6SaALYZQGW5lhCtU/W34N6o+bKfZ8cf3/Cl/+iTX3wBzpOY4mRkeNf3rptycGSshQWgGbYt5jFc2e0+DglIrwl6DVWQ7BuwaJ3Xk1J4VL5urnLl/Wf+gHU/hZoZdKNym6lG+I34FaNeZKcSpJIo2IeCVvpdsDGfKvzJnAwmeD37Ow65ZWwSowpgwX5T69s/rB55dP5BcpgDKFV8p7q2sn/1uc93bVzT/w6UrCqDTWvfCq/oCD/qZXNoUj8BL5Kp6GU017frfNXkAtiiyf/SOCEeLqnd8R/Ql9GlCRfctS6k5chvIBuQ1zCCjoCHL2DHNHIXxMJ3kQeO8lbsUXONeSfA5EjcG6/E+KdhN4bP04vBhdi883+BFBzQbxFbvZzQeY9LNBZc0FNfn5NwfDn6rCTnTw6R8o+gfpf5hCom33cRuiTlss3KHmZjD+BPN+5gXuA2ziS/Q73mLxUkpbKN/eqwz5uK0X9F3h2d1V4nGNgZGBgAOJd776+iue3+crAzc4AAje5Bfcg0xz9YHEOBiYQBQA8FQlFAHicY2BkYGBnAAGOPgaG//85+hkYGVCBMgBGGwNYAAAAeJxjYGBgYB8EmKOPgQEAQ04BfgAAAAAAAA4AaAB+AMwA4AECAUIBbAGYAcICGAJYArQC4AMwA7AD3gQwBJYE3AUkBWYFigYgBmYGtAbqB1gIEghYCG4IhAi2COh4nGNgZGBgUGYoZWBnAAEmIOYCQgaG/2A+AwAYCQG2AHicXZBNaoNAGIZfE5PQCKFQ2lUps2oXBfOzzAESyDKBQJdGR2NQR3QSSE/QE/QEPUUPUHqsvsrXjTMw83zPvPMNCuAWP3DQDAejdm1GjzwS7pMmwi75XngAD4/CQ/oX4TFe4Qt7uMMbOzjuDc0EmXCP/C7cJ38Iu+RP4QEe8CU8pP8WHmOPX2EPz87TPo202ey2OjlnQSXV/6arOjWFmvszMWtd6CqwOlKHq6ovycLaWMWVydXKFFZnmVFlZU46tP7R2nI5ncbi/dDkfDtFBA2DDXbYkhKc+V0Bqs5Zt9JM1HQGBRTm/EezTmZNKtpcAMs9Yu6AK9caF76zoLWIWcfMGOSkVduvSWechqZsz040Ib2PY3urxBJTzriT95lipz+TN1fmAAAAeJxtkMl2wjAMRfOAhABlKm2h80C3+ajgCKKDY6cegP59TYBzukAL+z1Zsq8ctaJTTKPrsUQLbXQQI0EXKXroY4AbDDHCGBNMcYsZ7nCPB8yxwCOe8IwXvOIN7/jAJ76wxHfUqWX+OzgumWAjJMV17i0Ndlr6irLKO+qftdT7i6y4uFSUvCknay+lFYZIZaQcmfH/xIFdYn98bqhra1aKTM/6lWMnyaYirx1rFUQZFBkb2zJUtoXeJCeg0WnLtHeSFc3OtrnozNwqi0TkSpBMDB1nSde5oJXW23hTS2/T0LilglXX7dmFVxLnq5U0vYATHFk3zX3BOisoQHNDFDeZnqKDy9hRNawN7Vh727hFzcJ5c8TILrKZfH7tIPxAFP0BpLeJPA==) format("woff");font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder,.vjs-icon-play{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-big-play-button .vjs-icon-placeholder:before,.video-js .vjs-play-control .vjs-icon-placeholder:before,.vjs-icon-play:before{content:"\f101"}.vjs-icon-play-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-play-circle:before{content:"\f102"}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder,.vjs-icon-pause{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before,.vjs-icon-pause:before{content:"\f103"}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder,.vjs-icon-volume-mute{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before,.vjs-icon-volume-mute:before{content:"\f104"}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder,.vjs-icon-volume-low{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before,.vjs-icon-volume-low:before{content:"\f105"}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder,.vjs-icon-volume-mid{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before,.vjs-icon-volume-mid:before{content:"\f106"}.video-js .vjs-mute-control .vjs-icon-placeholder,.vjs-icon-volume-high{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control .vjs-icon-placeholder:before,.vjs-icon-volume-high:before{content:"\f107"}.video-js .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-enter:before{content:"\f108"}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder,.vjs-icon-fullscreen-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before,.vjs-icon-fullscreen-exit:before{content:"\f109"}.vjs-icon-square{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-square:before{content:"\f10a"}.vjs-icon-spinner{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-spinner:before{content:"\f10b"}.video-js .vjs-subs-caps-button .vjs-icon-placeholder,.video-js .vjs-subtitles-button .vjs-icon-placeholder,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-subtitles{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js .vjs-subtitles-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-subtitles:before{content:"\f10c"}.video-js .vjs-captions-button .vjs-icon-placeholder,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder,.vjs-icon-captions{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-captions-button .vjs-icon-placeholder:before,.video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder:before,.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder:before,.vjs-icon-captions:before{content:"\f10d"}.video-js .vjs-chapters-button .vjs-icon-placeholder,.vjs-icon-chapters{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-chapters-button .vjs-icon-placeholder:before,.vjs-icon-chapters:before{content:"\f10e"}.vjs-icon-share{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-share:before{content:"\f10f"}.vjs-icon-cog{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-cog:before{content:"\f110"}.video-js .vjs-play-progress,.video-js .vjs-volume-level,.vjs-icon-circle,.vjs-seek-to-live-control .vjs-icon-placeholder{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-progress:before,.video-js .vjs-volume-level:before,.vjs-icon-circle:before,.vjs-seek-to-live-control .vjs-icon-placeholder:before{content:"\f111"}.vjs-icon-circle-outline{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-outline:before{content:"\f112"}.vjs-icon-circle-inner-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-inner-circle:before{content:"\f113"}.vjs-icon-hd{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-hd:before{content:"\f114"}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder,.vjs-icon-cancel{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder:before,.vjs-icon-cancel:before{content:"\f115"}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder,.vjs-icon-replay{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before,.vjs-icon-replay:before{content:"\f116"}.vjs-icon-facebook{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-facebook:before{content:"\f117"}.vjs-icon-gplus{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-gplus:before{content:"\f118"}.vjs-icon-linkedin{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-linkedin:before{content:"\f119"}.vjs-icon-twitter{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-twitter:before{content:"\f11a"}.vjs-icon-tumblr{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-tumblr:before{content:"\f11b"}.vjs-icon-pinterest{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-pinterest:before{content:"\f11c"}.video-js .vjs-descriptions-button .vjs-icon-placeholder,.vjs-icon-audio-description{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-descriptions-button .vjs-icon-placeholder:before,.vjs-icon-audio-description:before{content:"\f11d"}.video-js .vjs-audio-button .vjs-icon-placeholder,.vjs-icon-audio{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-audio-button .vjs-icon-placeholder:before,.vjs-icon-audio:before{content:"\f11e"}.vjs-icon-next-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-next-item:before{content:"\f11f"}.vjs-icon-previous-item{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-previous-item:before{content:"\f120"}.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder,.vjs-icon-picture-in-picture-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before,.vjs-icon-picture-in-picture-enter:before{content:"\f121"}.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder,.vjs-icon-picture-in-picture-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder:before,.vjs-icon-picture-in-picture-exit:before{content:"\f122"}.video-js{display:block;vertical-align:top;box-sizing:border-box;color:#fff;background-color:#000;position:relative;padding:0;font-size:10px;line-height:1;font-weight:400;font-style:normal;font-family:Arial,Helvetica,sans-serif;word-break:initial}.video-js:-moz-full-screen{position:absolute}.video-js:-webkit-full-screen{width:100%!important;height:100%!important}.video-js[tabindex="-1"]{outline:0}.video-js *,.video-js :after,.video-js :before{box-sizing:inherit}.video-js ul{font-family:inherit;font-size:inherit;line-height:inherit;list-style-position:outside;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}.video-js.vjs-16-9,.video-js.vjs-4-3,.video-js.vjs-fluid{width:100%;max-width:100%;height:0}.video-js.vjs-16-9{padding-top:56.25%}.video-js.vjs-4-3{padding-top:75%}.video-js.vjs-fill{width:100%;height:100%}.video-js .vjs-tech{position:absolute;top:0;left:0;width:100%;height:100%}body.vjs-full-window{padding:0;margin:0;height:100%}.vjs-full-window .video-js.vjs-fullscreen{position:fixed;overflow:hidden;z-index:1000;left:0;top:0;bottom:0;right:0}.video-js.vjs-fullscreen:not(.vjs-ios-native-fs){width:100%!important;height:100%!important;padding-top:0!important}.video-js.vjs-fullscreen.vjs-user-inactive{cursor:none}.vjs-hidden{display:none!important}.vjs-disabled{opacity:.5;cursor:default}.video-js .vjs-offscreen{height:1px;left:-9999px;position:absolute;top:0;width:1px}.vjs-lock-showing{display:block!important;opacity:1;visibility:visible}.vjs-no-js{padding:20px;color:#fff;background-color:#000;font-size:18px;font-family:Arial,Helvetica,sans-serif;text-align:center;width:300px;height:150px;margin:0 auto}.vjs-no-js a,.vjs-no-js a:visited{color:#66a8cc}.video-js .vjs-big-play-button{font-size:3em;line-height:1.5em;height:1.63332em;width:3em;display:block;position:absolute;top:10px;left:10px;padding:0;cursor:pointer;opacity:1;border:.06666em solid #fff;background-color:#2b333f;background-color:rgba(43,51,63,.7);border-radius:.3em;transition:all .4s}.vjs-big-play-centered .vjs-big-play-button{top:50%;left:50%;margin-top:-.81666em;margin-left:-1.5em}.video-js .vjs-big-play-button:focus,.video-js:hover .vjs-big-play-button{border-color:#fff;background-color:#73859f;background-color:rgba(115,133,159,.5);transition:all 0s}.vjs-controls-disabled .vjs-big-play-button,.vjs-error .vjs-big-play-button,.vjs-has-started .vjs-big-play-button,.vjs-using-native-controls .vjs-big-play-button{display:none}.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause .vjs-big-play-button{display:block}.video-js button{background:0 0;border:none;color:inherit;display:inline-block;font-size:inherit;line-height:inherit;text-transform:none;text-decoration:none;transition:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.vjs-control .vjs-button{width:100%;height:100%}.video-js .vjs-control.vjs-close-button{cursor:pointer;height:3em;position:absolute;right:0;top:.5em;z-index:2}.video-js .vjs-modal-dialog{background:rgba(0,0,0,.8);background:linear-gradient(180deg,rgba(0,0,0,.8),rgba(255,255,255,0));overflow:auto}.video-js .vjs-modal-dialog>*{box-sizing:border-box}.vjs-modal-dialog .vjs-modal-dialog-content{font-size:1.2em;line-height:1.5;padding:20px 24px;z-index:1}.vjs-menu-button{cursor:pointer}.vjs-menu-button.vjs-disabled{cursor:default}.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu{display:none}.vjs-menu .vjs-menu-content{display:block;padding:0;margin:0;font-family:Arial,Helvetica,sans-serif;overflow:auto}.vjs-menu .vjs-menu-content>*{box-sizing:border-box}.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu{display:none}.vjs-menu li{list-style:none;margin:0;padding:.2em 0;line-height:1.4em;font-size:1.2em;text-align:center;text-transform:lowercase}.js-focus-visible .vjs-menu li.vjs-menu-item:hover,.vjs-menu li.vjs-menu-item:focus,.vjs-menu li.vjs-menu-item:hover{background-color:#73859f;background-color:rgba(115,133,159,.5)}.js-focus-visible .vjs-menu li.vjs-selected:hover,.vjs-menu li.vjs-selected,.vjs-menu li.vjs-selected:focus,.vjs-menu li.vjs-selected:hover{background-color:#fff;color:#2b333f}.vjs-menu li.vjs-menu-title{text-align:center;text-transform:uppercase;font-size:1em;line-height:2em;padding:0;margin:0 0 .3em 0;font-weight:700;cursor:default}.vjs-menu-button-popup .vjs-menu{display:none;position:absolute;bottom:0;width:10em;left:-3em;height:0;margin-bottom:1.5em;border-top-color:rgba(43,51,63,.7)}.vjs-menu-button-popup .vjs-menu .vjs-menu-content{background-color:#2b333f;background-color:rgba(43,51,63,.7);position:absolute;width:100%;bottom:1.5em;max-height:15em}.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:5em}.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:10em}.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:14em}.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content,.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content{max-height:25em}.vjs-menu-button-popup .vjs-menu.vjs-lock-showing,.vjs-workinghover .vjs-menu-button-popup.vjs-hover .vjs-menu{display:block}.video-js .vjs-menu-button-inline{transition:all .4s;overflow:hidden}.video-js .vjs-menu-button-inline:before{width:2.222222222em}.video-js .vjs-menu-button-inline.vjs-slider-active,.video-js .vjs-menu-button-inline:focus,.video-js .vjs-menu-button-inline:hover,.video-js.vjs-no-flex .vjs-menu-button-inline{width:12em}.vjs-menu-button-inline .vjs-menu{opacity:0;height:100%;width:auto;position:absolute;left:4em;top:0;padding:0;margin:0;transition:all .4s}.vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-menu-button-inline:focus .vjs-menu,.vjs-menu-button-inline:hover .vjs-menu{display:block;opacity:1}.vjs-no-flex .vjs-menu-button-inline .vjs-menu{display:block;opacity:1;position:relative;width:auto}.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu{width:auto}.vjs-menu-button-inline .vjs-menu-content{width:auto;height:100%;margin:0;overflow:hidden}.video-js .vjs-control-bar{display:none;width:100%;position:absolute;bottom:0;left:0;right:0;height:3em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.vjs-has-started .vjs-control-bar{display:flex;visibility:visible;opacity:1;transition:visibility .1s,opacity .1s}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:visible;opacity:0;transition:visibility 1s,opacity 1s}.vjs-controls-disabled .vjs-control-bar,.vjs-error .vjs-control-bar,.vjs-using-native-controls .vjs-control-bar{display:none!important}.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{opacity:1;visibility:visible}.vjs-has-started.vjs-no-flex .vjs-control-bar{display:table}.video-js .vjs-control{position:relative;text-align:center;margin:0;padding:0;height:100%;width:4em;flex:none}.vjs-button>.vjs-icon-placeholder:before{font-size:1.8em;line-height:1.67}.video-js .vjs-control:focus,.video-js .vjs-control:focus:before,.video-js .vjs-control:hover:before{text-shadow:0 0 1em #fff}.video-js .vjs-control-text{border:0;clip:rect(0 0 0 0);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.vjs-no-flex .vjs-control{display:table-cell;vertical-align:middle}.video-js .vjs-custom-control-spacer{display:none}.video-js .vjs-progress-control{cursor:pointer;flex:auto;display:flex;align-items:center;min-width:4em;touch-action:none}.video-js .vjs-progress-control.disabled{cursor:default}.vjs-live .vjs-progress-control{display:none}.vjs-liveui .vjs-progress-control{display:flex;align-items:center}.vjs-no-flex .vjs-progress-control{width:auto}.video-js .vjs-progress-holder{flex:auto;transition:all .2s;height:.3em}.video-js .vjs-progress-control .vjs-progress-holder{margin:0 10px}.video-js .vjs-progress-control:hover .vjs-progress-holder{font-size:1.6666666667em}.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled{font-size:1em}.video-js .vjs-progress-holder .vjs-load-progress,.video-js .vjs-progress-holder .vjs-load-progress div,.video-js .vjs-progress-holder .vjs-play-progress{position:absolute;display:block;height:100%;margin:0;padding:0;width:0}.video-js .vjs-play-progress{background-color:#fff}.video-js .vjs-play-progress:before{font-size:.9em;position:absolute;right:-.5em;top:-.3333333333em;z-index:1}.video-js .vjs-load-progress{background:rgba(115,133,159,.5)}.video-js .vjs-load-progress div{background:rgba(115,133,159,.75)}.video-js .vjs-time-tooltip{background-color:#fff;background-color:rgba(255,255,255,.8);border-radius:.3em;color:#000;float:right;font-family:Arial,Helvetica,sans-serif;font-size:1em;padding:6px 8px 8px 8px;pointer-events:none;position:absolute;top:-3.4em;visibility:hidden;z-index:1}.video-js .vjs-progress-holder:focus .vjs-time-tooltip{display:none}.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip,.video-js .vjs-progress-control:hover .vjs-time-tooltip{display:block;font-size:.6em;visibility:visible}.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip{font-size:1em}.video-js .vjs-progress-control .vjs-mouse-display{display:none;position:absolute;width:1px;height:100%;background-color:#000;z-index:1}.vjs-no-flex .vjs-progress-control .vjs-mouse-display{z-index:0}.video-js .vjs-progress-control:hover .vjs-mouse-display{display:block}.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display{visibility:hidden;opacity:0;transition:visibility 1s,opacity 1s}.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display{display:none}.vjs-mouse-display .vjs-time-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.video-js .vjs-slider{position:relative;cursor:pointer;padding:0;margin:0 .45em 0 .45em;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#73859f;background-color:rgba(115,133,159,.5)}.video-js .vjs-slider.disabled{cursor:default}.video-js .vjs-slider:focus{text-shadow:0 0 1em #fff;box-shadow:0 0 1em #fff}.video-js .vjs-mute-control{cursor:pointer;flex:none}.video-js .vjs-volume-control{cursor:pointer;margin-right:1em;display:flex}.video-js .vjs-volume-control.vjs-volume-horizontal{width:5em}.video-js .vjs-volume-panel .vjs-volume-control{visibility:visible;opacity:0;width:1px;height:1px;margin-left:-1px}.video-js .vjs-volume-panel{transition:width 1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active,.video-js .vjs-volume-panel .vjs-volume-control:active,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control,.video-js .vjs-volume-panel:active .vjs-volume-control,.video-js .vjs-volume-panel:focus .vjs-volume-control{visibility:visible;opacity:1;position:relative;transition:visibility .1s,opacity .1s,height .1s,width .1s,left 0s,top 0s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal{width:5em;height:3em;margin-right:0}.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-vertical,.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-vertical,.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control~.vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-vertical,.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-vertical{left:-3.5em;transition:left 0s}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active{width:10em;transition:width .1s}.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only{width:4em}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{height:8em;width:3em;left:-3000em;transition:visibility 1s,opacity 1s,height 1s 1s,width 1s 1s,left 1s 1s,top 1s 1s}.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal{transition:visibility 1s,opacity 1s,height 1s 1s,width 1s,left 1s 1s,top 1s 1s}.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal{width:5em;height:3em;visibility:visible;opacity:1;position:relative;transition:none}.video-js.vjs-no-flex .vjs-volume-control.vjs-volume-vertical,.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical{position:absolute;bottom:3em;left:.5em}.video-js .vjs-volume-panel{display:flex}.video-js .vjs-volume-bar{margin:1.35em .45em}.vjs-volume-bar.vjs-slider-horizontal{width:5em;height:.3em}.vjs-volume-bar.vjs-slider-vertical{width:.3em;height:5em;margin:1.35em auto}.video-js .vjs-volume-level{position:absolute;bottom:0;left:0;background-color:#fff}.video-js .vjs-volume-level:before{position:absolute;font-size:.9em}.vjs-slider-vertical .vjs-volume-level{width:.3em}.vjs-slider-vertical .vjs-volume-level:before{top:-.5em;left:-.3em}.vjs-slider-horizontal .vjs-volume-level{height:.3em}.vjs-slider-horizontal .vjs-volume-level:before{top:-.3em;right:-.5em}.video-js .vjs-volume-panel.vjs-volume-panel-vertical{width:4em}.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level{height:100%}.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level{width:100%}.video-js .vjs-volume-vertical{width:3em;height:8em;bottom:8em;background-color:#2b333f;background-color:rgba(43,51,63,.7)}.video-js .vjs-volume-horizontal .vjs-menu{left:-2em}.vjs-poster{display:inline-block;vertical-align:middle;background-repeat:no-repeat;background-position:50% 50%;background-size:contain;background-color:#000;cursor:pointer;margin:0;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;height:100%}.vjs-has-started .vjs-poster{display:none}.vjs-audio.vjs-has-started .vjs-poster{display:block}.vjs-using-native-controls .vjs-poster{display:none}.video-js .vjs-live-control{display:flex;align-items:flex-start;flex:auto;font-size:1em;line-height:3em}.vjs-no-flex .vjs-live-control{display:table-cell;width:auto;text-align:left}.video-js.vjs-liveui .vjs-live-control,.video-js:not(.vjs-live) .vjs-live-control{display:none}.video-js .vjs-seek-to-live-control{align-items:center;cursor:pointer;flex:none;display:inline-flex;height:100%;padding-left:.5em;padding-right:.5em;font-size:1em;line-height:3em;width:auto;min-width:4em}.vjs-no-flex .vjs-seek-to-live-control{display:table-cell;width:auto;text-align:left}.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control,.video-js:not(.vjs-live) .vjs-seek-to-live-control{display:none}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge{cursor:auto}.vjs-seek-to-live-control .vjs-icon-placeholder{margin-right:.5em;color:#888}.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder{color:red}.video-js .vjs-time-control{flex:none;font-size:1em;line-height:3em;min-width:2em;width:auto;padding-left:1em;padding-right:1em}.vjs-live .vjs-time-control{display:none}.video-js .vjs-current-time,.vjs-no-flex .vjs-current-time{display:none}.video-js .vjs-duration,.vjs-no-flex .vjs-duration{display:none}.vjs-time-divider{display:none;line-height:3em}.vjs-live .vjs-time-divider{display:none}.video-js .vjs-play-control{cursor:pointer}.video-js .vjs-play-control .vjs-icon-placeholder{flex:none}.vjs-text-track-display{position:absolute;bottom:3em;left:0;right:0;top:0;pointer-events:none}.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display{bottom:1em}.video-js .vjs-text-track{font-size:1.4em;text-align:center;margin-bottom:.1em}.vjs-subtitles{color:#fff}.vjs-captions{color:#fc6}.vjs-tt-cue{display:block}video::-webkit-media-text-track-display{transform:translateY(-3em)}.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display{transform:translateY(-1.5em)}.video-js .vjs-picture-in-picture-control{cursor:pointer;flex:none}.video-js .vjs-fullscreen-control{cursor:pointer;flex:none}.vjs-playback-rate .vjs-playback-rate-value,.vjs-playback-rate>.vjs-menu-button{position:absolute;top:0;left:0;width:100%;height:100%}.vjs-playback-rate .vjs-playback-rate-value{pointer-events:none;font-size:1.5em;line-height:2;text-align:center}.vjs-playback-rate .vjs-menu{width:4em;left:0}.vjs-error .vjs-error-display .vjs-modal-dialog-content{font-size:1.4em;text-align:center}.vjs-error .vjs-error-display:before{color:#fff;content:"X";font-family:Arial,Helvetica,sans-serif;font-size:4em;left:0;line-height:1;margin-top:-.5em;position:absolute;text-shadow:.05em .05em .1em #000;text-align:center;top:50%;vertical-align:middle;width:100%}.vjs-loading-spinner{display:none;position:absolute;top:50%;left:50%;margin:-25px 0 0 -25px;opacity:.85;text-align:left;border:6px solid rgba(43,51,63,.7);box-sizing:border-box;background-clip:padding-box;width:50px;height:50px;border-radius:25px;visibility:hidden}.vjs-seeking .vjs-loading-spinner,.vjs-waiting .vjs-loading-spinner{display:block;-webkit-animation:vjs-spinner-show 0s linear .3s forwards;animation:vjs-spinner-show 0s linear .3s forwards}.vjs-loading-spinner:after,.vjs-loading-spinner:before{content:"";position:absolute;margin:-6px;box-sizing:inherit;width:inherit;height:inherit;border-radius:inherit;opacity:1;border:inherit;border-color:transparent;border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:before{-webkit-animation:vjs-spinner-spin 1.1s cubic-bezier(.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite;animation:vjs-spinner-spin 1.1s cubic-bezier(.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite}.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:before{border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:after{border-top-color:#fff;-webkit-animation-delay:.44s;animation-delay:.44s}@keyframes vjs-spinner-show{to{visibility:visible}}@-webkit-keyframes vjs-spinner-show{to{visibility:visible}}@keyframes vjs-spinner-spin{100%{transform:rotate(360deg)}}@-webkit-keyframes vjs-spinner-spin{100%{-webkit-transform:rotate(360deg)}}@keyframes vjs-spinner-fade{0%{border-top-color:#73859f}20%{border-top-color:#73859f}35%{border-top-color:#fff}60%{border-top-color:#73859f}100%{border-top-color:#73859f}}@-webkit-keyframes vjs-spinner-fade{0%{border-top-color:#73859f}20%{border-top-color:#73859f}35%{border-top-color:#fff}60%{border-top-color:#73859f}100%{border-top-color:#73859f}}.vjs-chapters-button .vjs-menu ul{width:24em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-subs-caps-button+.vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:"ï„";font-size:1.5em;line-height:inherit}.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder{vertical-align:middle;display:inline-block;margin-bottom:-.1em}.video-js .vjs-audio-button+.vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before{font-family:VideoJS;content:" ï„";font-size:1.5em;line-height:inherit}.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-control,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-control,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-audio-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-captions-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-chapters-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-current-time,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-descriptions-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-duration,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-playback-rate,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-remaining-time,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-subtitles-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-time-divider,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-control{display:none}.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:hover,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:active,.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover{width:auto;width:initial}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subs-caps-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-live) .vjs-subs-caps-button,.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-liveui) .vjs-subs-caps-button{display:none}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-custom-control-spacer,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-custom-control-spacer{flex:auto;display:block}.video-js:not(.vjs-fullscreen).vjs-layout-tiny.vjs-no-flex .vjs-custom-control-spacer,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui.vjs-no-flex .vjs-custom-control-spacer{width:auto}.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-progress-control,.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-progress-control{display:none}.vjs-modal-dialog.vjs-text-track-settings{background-color:#2b333f;background-color:rgba(43,51,63,.75);color:#fff;height:70%}.vjs-text-track-settings .vjs-modal-dialog-content{display:table}.vjs-text-track-settings .vjs-track-settings-colors,.vjs-text-track-settings .vjs-track-settings-controls,.vjs-text-track-settings .vjs-track-settings-font{display:table-cell}.vjs-text-track-settings .vjs-track-settings-controls{text-align:right;vertical-align:bottom}@supports (display:grid){.vjs-text-track-settings .vjs-modal-dialog-content{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr;padding:20px 24px 0 24px}.vjs-track-settings-controls .vjs-default-button{margin-bottom:20px}.vjs-text-track-settings .vjs-track-settings-controls{grid-column:1/-1}.vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content,.vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content{grid-template-columns:1fr}}.vjs-track-setting>select{margin-right:1em;margin-bottom:.5em}.vjs-text-track-settings fieldset{margin:5px;padding:3px;border:none}.vjs-text-track-settings fieldset span{display:inline-block}.vjs-text-track-settings fieldset span>select{max-width:7.3em}.vjs-text-track-settings legend{color:#fff;margin:0 0 5px 0}.vjs-text-track-settings .vjs-label{position:absolute;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);display:block;margin:0 0 5px 0;padding:0;border:0;height:1px;width:1px;overflow:hidden}.vjs-track-settings-controls button:active,.vjs-track-settings-controls button:focus{outline-style:solid;outline-width:medium;background-image:linear-gradient(0deg,#fff 88%,#73859f 100%)}.vjs-track-settings-controls button:hover{color:rgba(43,51,63,.75)}.vjs-track-settings-controls button{background-color:#fff;background-image:linear-gradient(-180deg,#fff 88%,#73859f 100%);color:#2b333f;cursor:pointer;border-radius:2px}.vjs-track-settings-controls .vjs-default-button{margin-right:1em}@media print{.video-js>:not(.vjs-tech):not(.vjs-poster){visibility:hidden}}.vjs-resize-manager{position:absolute;top:0;left:0;width:100%;height:100%;border:none;z-index:-1000}.js-focus-visible .video-js :focus:not(.focus-visible){outline:0;background:0 0}.video-js .vjs-menu :focus:not(:focus-visible),.video-js :focus:not(:focus-visible){outline:0;background:0 0} \ No newline at end of file diff --git a/plugins/hal-nvr/resources/web/fonts/VideoJS.svg b/plugins/hal-nvr/resources/web/fonts/VideoJS.svg deleted file mode 100644 index f1ae8262..00000000 --- a/plugins/hal-nvr/resources/web/fonts/VideoJS.svg +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/hal-nvr/resources/web/fonts/VideoJS.ttf b/plugins/hal-nvr/resources/web/fonts/VideoJS.ttf deleted file mode 100644 index 2f3c5110..00000000 Binary files a/plugins/hal-nvr/resources/web/fonts/VideoJS.ttf and /dev/null differ diff --git a/plugins/hal-nvr/resources/web/fonts/VideoJS.woff b/plugins/hal-nvr/resources/web/fonts/VideoJS.woff deleted file mode 100644 index 4b846ae5..00000000 Binary files a/plugins/hal-nvr/resources/web/fonts/VideoJS.woff and /dev/null differ diff --git a/plugins/hal-nvr/resources/web/js/lib/video.min.js b/plugins/hal-nvr/resources/web/js/lib/video.min.js deleted file mode 100644 index 72cd788b..00000000 --- a/plugins/hal-nvr/resources/web/js/lib/video.min.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @license - * Video.js 7.11.4 - * Copyright Brightcove, Inc. - * Available under Apache License Version 2.0 - * - * - * Includes vtt.js - * Available under Apache License Version 2.0 - * - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).videojs=t()}(this,function(){"use strict";var d="7.11.4",e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function i(e,t){return e(t={exports:{}},t.exports),t.exports}var p="undefined"!=typeof window?window:"undefined"!=typeof e?e:"undefined"!=typeof self?self:{},n={},r="undefined"!=typeof e?e:"undefined"!=typeof window?window:{},h="undefined"!=typeof document?document:r["__GLOBAL_DOCUMENT_CACHE@4"]||(r["__GLOBAL_DOCUMENT_CACHE@4"]=n),l=[],s=function(o,u){return function(e,t,i){var n=u.levels[t],r=new RegExp("^("+n+")$");if("log"!==e&&i.unshift(e.toUpperCase()+":"),i.unshift(o+":"),l){l.push([].concat(i));var a=l.length-1e3;l.splice(0,0',i=n.firstChild,n.setAttribute("style","display:none; position:absolute;"),h.body.appendChild(n));for(var a={},s=0;sx',e=t.firstChild.href}return e}function zt(e){if("string"==typeof e){var t=/^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/.exec(e);if(t)return t.pop().toLowerCase()}return""}function Gt(e,t){void 0===t&&(t=p.location);var i=qt(e);return(":"===i.protocol?t.protocol:i.protocol)+i.host!==t.protocol+t.host}var Xt=function(n){function e(e){var t;void 0===e&&(e=[]);for(var i=e.length-1;0<=i;i--)if(e[i].enabled){Vt(e,e[i]);break}return(t=n.call(this,e)||this).changing_=!1,t}$e(e,n);var t=e.prototype;return t.addTrack=function(e){var t=this;e.enabled&&Vt(this,e),n.prototype.addTrack.call(this,e),e.addEventListener&&(e.enabledChange_=function(){t.changing_||(t.changing_=!0,Vt(t,e),t.changing_=!1,t.trigger("change"))},e.addEventListener("enabledchange",e.enabledChange_))},t.removeTrack=function(e){n.prototype.removeTrack.call(this,e),e.removeEventListener&&e.enabledChange_&&(e.removeEventListener("enabledchange",e.enabledChange_),e.enabledChange_=null)},e}(Ft),Kt=function(n){function e(e){var t;void 0===e&&(e=[]);for(var i=e.length-1;0<=i;i--)if(e[i].selected){Ht(e,e[i]);break}return(t=n.call(this,e)||this).changing_=!1,Object.defineProperty(ze(t),"selectedIndex",{get:function(){for(var e=0;e - * Copyright (c) 2014 David Björklund - * Available under the MIT license - * - */,si=Object.prototype.toString;var oi=function(e){var r={};return e&&e.trim().split("\n").forEach(function(e){var t=e.indexOf(":"),i=e.slice(0,t).trim().toLowerCase(),n=e.slice(t+1).trim();"undefined"==typeof r[i]?r[i]=n:Array.isArray(r[i])?r[i].push(n):r[i]=[r[i],n]}),r},ui=di,li=di;function ci(e,t,i){var n=e;return ai(t)?(i=t,"string"==typeof e&&(n={uri:e})):n=g({},t,{uri:e}),n.callback=i,n}function di(e,t,i){return hi(t=ci(e,t,i))}function hi(n){if("undefined"==typeof n.callback)throw new Error("callback argument missing");var r=!1,a=function(e,t,i){r||(r=!0,n.callback(e,t,i))};function t(e){return clearTimeout(o),e instanceof Error||(e=new Error(""+(e||"Unknown XMLHttpRequest Error"))),e.statusCode=0,a(e,m)}function e(){if(!s){var e;clearTimeout(o),e=n.useXDR&&void 0===u.status?200:1223===u.status?204:u.status;var t=m,i=null;return 0!==e?(t={body:function(){var e=void 0;if(e=u.response?u.response:u.responseText||function(e){try{if("document"===e.responseType)return e.responseXML;var t=e.responseXML&&"parsererror"===e.responseXML.documentElement.nodeName;if(""===e.responseType&&!t)return e.responseXML}catch(e){}return null}(u),f)try{e=JSON.parse(e)}catch(e){}return e}(),statusCode:e,method:c,headers:{},url:l,rawRequest:u},u.getAllResponseHeaders&&(t.headers=oi(u.getAllResponseHeaders()))):i=new Error("Internal XMLHttpRequest Error"),a(i,t,t.body)}}var i,s,o,u=n.xhr||null,l=(u=u||(n.cors||n.useXDR?new di.XDomainRequest:new di.XMLHttpRequest)).url=n.uri||n.url,c=u.method=n.method||"GET",d=n.body||n.data,h=u.headers=n.headers||{},p=!!n.sync,f=!1,m={body:void 0,headers:{},statusCode:0,method:c,url:l,rawRequest:u};if("json"in n&&!1!==n.json&&(f=!0,h.accept||h.Accept||(h.Accept="application/json"),"GET"!==c&&"HEAD"!==c&&(h["content-type"]||h["Content-Type"]||(h["Content-Type"]="application/json"),d=JSON.stringify(!0===n.json?d:n.json))),u.onreadystatechange=function(){4===u.readyState&&setTimeout(e,0)},u.onload=e,u.onerror=t,u.onprogress=function(){},u.onabort=function(){s=!0},u.ontimeout=t,u.open(c,l,!p,n.username,n.password),p||(u.withCredentials=!!n.withCredentials),!p&&0=e?t.push(r):r.startTime===r.endTime&&r.startTime<=e&&r.startTime+.5>=e&&t.push(r)}if(o=!1,t.length!==this.activeCues_.length)o=!0;else for(var a=0;a]*>?)?/);return e=t[1]?t[1]:t[2],i=i.substr(e.length),e}function t(e,t){var i=Pi[e];if(!i)return null;var n=a.document.createElement(i),r=Di[e];return r&&t&&(n[r]=t.trim()),n}for(var n,r,s,o,u=a.document.createElement("div"),l=u,c=[];null!==(n=e());)if("<"!==n[0])l.appendChild(a.document.createTextNode((r=n,xi.innerHTML=r,r=xi.textContent,xi.textContent="",r)));else{if("/"===n[1]){c.length&&c[c.length-1]===n.substr(2).replace(">","")&&(c.pop(),l=l.parentNode);continue}var d,h=wi(n.substr(1,n.length-2));if(h){d=a.document.createProcessingInstruction("timestamp",h),l.appendChild(d);continue}var p=n.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);if(!p)continue;if(!(d=t(p[1],p[3])))continue;if(s=l,Oi[(o=d).localName]&&Oi[o.localName]!==s.localName)continue;if(p[2]){var f=p[2].split(".");f.forEach(function(e){var t=/^bg_/.test(e),i=t?e.slice(3):e;if(Li.hasOwnProperty(i)){var n=t?"background-color":"color",r=Li[i];d.style[n]=r}}),d.className=f.join(" ")}c.push(p[1]),l.appendChild(d),l=d}return u}var Mi=[[1470,1470],[1472,1472],[1475,1475],[1478,1478],[1488,1514],[1520,1524],[1544,1544],[1547,1547],[1549,1549],[1563,1563],[1566,1610],[1645,1647],[1649,1749],[1765,1766],[1774,1775],[1786,1805],[1807,1808],[1810,1839],[1869,1957],[1969,1969],[1984,2026],[2036,2037],[2042,2042],[2048,2069],[2074,2074],[2084,2084],[2088,2088],[2096,2110],[2112,2136],[2142,2142],[2208,2208],[2210,2220],[8207,8207],[64285,64285],[64287,64296],[64298,64310],[64312,64316],[64318,64318],[64320,64321],[64323,64324],[64326,64449],[64467,64829],[64848,64911],[64914,64967],[65008,65020],[65136,65140],[65142,65276],[67584,67589],[67592,67592],[67594,67637],[67639,67640],[67644,67644],[67647,67669],[67671,67679],[67840,67867],[67872,67897],[67903,67903],[67968,68023],[68030,68031],[68096,68096],[68112,68115],[68117,68119],[68121,68147],[68160,68167],[68176,68184],[68192,68223],[68352,68405],[68416,68437],[68440,68466],[68472,68479],[68608,68680],[126464,126467],[126469,126495],[126497,126498],[126500,126500],[126503,126503],[126505,126514],[126516,126519],[126521,126521],[126523,126523],[126530,126530],[126535,126535],[126537,126537],[126539,126539],[126541,126543],[126545,126546],[126548,126548],[126551,126551],[126553,126553],[126555,126555],[126557,126557],[126559,126559],[126561,126562],[126564,126564],[126567,126570],[126572,126578],[126580,126583],[126585,126588],[126590,126590],[126592,126601],[126603,126619],[126625,126627],[126629,126633],[126635,126651],[1114109,1114109]];function Ni(e){for(var t=0;t=i[0]&&e<=i[1])return!0}return!1}function Ui(){}function Bi(e,t,i){Ui.call(this),this.cue=t,this.cueDiv=Ri(e,t.text);var n={color:"rgba(255, 255, 255, 1)",backgroundColor:"rgba(0, 0, 0, 0.8)",position:"relative",left:0,right:0,top:0,bottom:0,display:"inline",writingMode:""===t.vertical?"horizontal-tb":"lr"===t.vertical?"vertical-lr":"vertical-rl",unicodeBidi:"plaintext"};this.applyStyles(n,this.cueDiv),this.div=e.document.createElement("div"),n={direction:function(e){var t=[],i="";if(!e||!e.childNodes)return"ltr";function r(e,t){for(var i=t.childNodes.length-1;0<=i;i--)e.push(t.childNodes[i])}function a(e){if(!e||!e.length)return null;var t=e.pop(),i=t.textContent||t.innerText;if(i){var n=i.match(/^.*(\n|\r)/);return n?n[e.length=0]:i}return"ruby"===t.tagName?a(e):t.childNodes?(r(e,t),a(e)):void 0}for(r(t,e);i=a(t);)for(var n=0;nd&&(c=c<0?-1:1,c*=Math.ceil(d/l)*l),r<0&&(c+=""===n.vertical?o.height:o.width,a=a.reverse()),i.move(h,c)}else{var p=i.lineHeight/o.height*100;switch(n.lineAlign){case"center":r-=p/2;break;case"end":r-=p}switch(n.vertical){case"":t.applyStyles({top:t.formatStyle(r,"%")});break;case"rl":t.applyStyles({left:t.formatStyle(r,"%")});break;case"lr":t.applyStyles({right:t.formatStyle(r,"%")})}a=["+y","-x","+x","-y"],i=new Fi(t)}var f=function(e,t){for(var i,n=new Fi(e),r=1,a=0;ae.left&&this.tope.top},Fi.prototype.overlapsAny=function(e){for(var t=0;t=e.top&&this.bottom<=e.bottom&&this.left>=e.left&&this.right<=e.right},Fi.prototype.overlapsOppositeAxis=function(e,t){switch(t){case"+x":return this.lefte.right;case"+y":return this.tope.bottom}},Fi.prototype.intersectPercentage=function(e){return Math.max(0,Math.min(this.right,e.right)-Math.max(this.left,e.left))*Math.max(0,Math.min(this.bottom,e.bottom)-Math.max(this.top,e.top))/(this.height*this.width)},Fi.prototype.toCSSCompatValues=function(e){return{top:this.top-e.top,bottom:e.bottom-this.bottom,left:this.left-e.left,right:e.right-this.right,height:this.height,width:this.width}},Fi.getSimpleBoxPosition=function(e){var t=e.div?e.div.offsetHeight:e.tagName?e.offsetHeight:0,i=e.div?e.div.offsetWidth:e.tagName?e.offsetWidth:0,n=e.div?e.div.offsetTop:e.tagName?e.offsetTop:0;return{left:(e=e.div?e.div.getBoundingClientRect():e.tagName?e.getBoundingClientRect():e).left,right:e.right,top:e.top||n,height:e.height||t,bottom:e.bottom||n+(e.height||t),width:e.width||i}},Vi.StringDecoder=function(){return{decode:function(e){if(!e)return"";if("string"!=typeof e)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(e))}}},Vi.convertCueToDOMTree=function(e,t){return e&&t?Ri(e,t):null};Vi.processCues=function(n,r,e){if(!n||!r||!e)return null;for(;e.firstChild;)e.removeChild(e.firstChild);var a=n.document.createElement("div");if(a.style.position="absolute",a.style.left="0",a.style.right="0",a.style.top="0",a.style.bottom="0",a.style.margin="1.5%",e.appendChild(a),function(e){for(var t=0;t