diff --git a/.gitignore b/.gitignore
index 35a16d43c1d..c398279d095 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,7 @@ captures
/.recommenders/
/java/.recommenders/
java/com.sap.sailing.selenium.test/com.sap.sailing.selenium.test (No Proxy) individual.launch
+
+## Kubernetes
+kubernetes/aws-alb-ingress-controller.yaml
+java/com.sap.sailing.gwt.ui/360_VR_Wimbachklamm.mp4
diff --git a/build.gradle b/build.gradle
index 5ff17d04b84..5b766598191 100755
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
}
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
+ classpath 'com.android.tools.build:gradle:3.6.4'
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.3"
classpath "org.ajoberstar:grgit:2.3.0"
}
@@ -45,10 +45,10 @@ allprojects {
// dependency versions for all sub modules
ext {
// regular
- buildTools = "28.0.3"
- compileSdk = 28
+ buildTools = "29.0.3"
+ compileSdk = 29
minSdk = 16
- targetSdk = 28
+ targetSdk = 29
sourceCompatibilityVersion = JavaVersion.VERSION_1_8
targetCompatibilityVersion = JavaVersion.VERSION_1_8
diff --git a/cfg/VERSION b/cfg/VERSION
index f7bd9847470..8d9b8b2df90 100644
--- a/cfg/VERSION
+++ b/cfg/VERSION
@@ -1 +1 @@
-1.4.63
+1.4.91
diff --git a/cfg/files2sign.json b/cfg/files2sign.json
index ed0af5de200..a3be8a1e089 100644
--- a/cfg/files2sign.json
+++ b/cfg/files2sign.json
@@ -1,22 +1,22 @@
-{
- "ANDROID":[
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.racecommittee.app-unsigned.apk",
- "artifactid": "com.sap.sailing.racecommittee.app"
- },
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.tracking.app-unsigned.apk",
- "artifactid": "com.sap.sailing.android.tracking.app"
- },
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.buoy.positioning.app-unsigned.apk",
- "artifactid": "com.sap.sailing.android.buoy.positioning.app"
- }
- ]
-}
+{
+ "ANDROID":[
+ {
+ "version": "1.4.91",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.racecommittee.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.racecommittee.app"
+ },
+ {
+ "version": "1.4.91",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.tracking.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.android.tracking.app"
+ },
+ {
+ "version": "1.4.91",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.buoy.positioning.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.android.buoy.positioning.app"
+ }
+ ]
+}
diff --git a/cfg/import.ais b/cfg/import.ais
index a16f8f8346d..cfacf491ecd 100644
--- a/cfg/import.ais
+++ b/cfg/import.ais
@@ -5,17 +5,17 @@ imports dslVersion:"1.3", {
artifactFile "com.oracle.download.java:jdk:tar.gz:linux-x64:1.8.0_66-sap-01"
artifactFile "com.google.download.android:tools:zip:linux:26.1.1"
- artifactFile "com.google.download.android:platform-tools:zip:linux:28.0.0"
- artifactFile "com.google.download.android:build-tools:zip:linux:28.0.3"
- artifactFile "com.google.download.android:platform:zip:28.6"
+ artifactFile "com.google.download.android:platform-tools:zip:linux:30.0.5"
+ artifactFile "com.google.download.android:build-tools:zip:linux:29.0.3"
+ artifactFile "com.google.download.android:platform:zip:29.5"
// optional dependencies for Android SDK - use when required (e.g. appcompat-v7)
artifactFile "com.google.download.android:google-repository:zip:58"
// tools required to sign + zipalign APKs
unzip {
- artifactFile "com.sap.prd.codesign.mavenclient:com.sap.prd.codesign.mavenclient.dist.cli:zip:dist:1.1.1"
- artifactFile "com.google.download.android:build-tools:zip:linux:27.0.3"
+ artifactFile "com.sap.prd.codesign.mavenclient:com.sap.prd.codesign.mavenclient.dist.cli:zip:dist:1.4.0"
+ artifactFile "com.google.download.android:build-tools:zip:linux:27.0.3"
}
artifactFile "com.sap.ldi.signing:localSigningKeystore:jks:1.0.0"
}
diff --git a/configuration/archive_instance_setup/mountnvmeswap.initd b/configuration/archive_instance_setup/mountnvmeswap.initd
new file mode 100755
index 00000000000..537dd85e0f6
--- /dev/null
+++ b/configuration/archive_instance_setup/mountnvmeswap.initd
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# mountnvmeswap Formats and mounts a yet unpartitioned NVMe volume as swap
+#
+# chkconfig: 2345 95 10
+# description: Formats and mounts a yet unpartitioned NVMe volume as swap
+#
+### BEGIN INIT INFO
+# Provides: mountnvmeswap
+# Required-Start: $local_fs $network
+# Should-Start:
+# Required-Stop:
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Formats and mounts a yet unpartitioned NVMe volume as swap
+# Description: Formats and mounts a yet unpartitioned NVMe volume as swap
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+RETVAL=0
+
+# See how we were called.
+case "$1" in
+ start)
+ /usr/local/bin/mountnvmeswap
+ ;;
+ stop)
+ ;;
+ status)
+ swapon -s | grep /dev/nvme0n1
+ RETVAL=$?
+ ;;
+ *)
+ echo $"Usage: $0 {start|status|stop}"
+ RETVAL=3
+esac
+
+exit $RETVAL
diff --git a/configuration/aws-automation/launch-mongodb-replica.sh b/configuration/aws-automation/launch-mongodb-replica.sh
index 120923fbdbb..cb1a397924f 100755
--- a/configuration/aws-automation/launch-mongodb-replica.sh
+++ b/configuration/aws-automation/launch-mongodb-replica.sh
@@ -12,8 +12,15 @@
# that the new replica never becomes PRIMARY by setting the priority to 0
#
AVAILABILITY_ZONE=eu-west-1c
-INSTANCE_TYPE=i2.xlarge
-IMAGE_ID=ami-0e5f927ef86e00bd2
+INSTANCE_TYPE=i3.xlarge
+# eu-west-1:
+IMAGE_ID=ami-054a4c20d297ab81e
+# eu-west-2:
+#IMAGE_ID=ami-0c0907685eae2dbab
+# eu-west-1:
+SECURITY_GROUP_ID=sg-0a9bc2fb61f10a342
+# eu-west-2:
+#SECURITY_GROUP_ID=sg-02649c35a73ee0ae5
KEY_NAME=Axel
REPLICA_SET_NAME=
REPLICA_SET_PRIMARY=
@@ -57,8 +64,7 @@ do
exit 4;;
esac
done
-
-aws ec2 run-instances --placement AvailabilityZone=$AVAILABILITY_ZONE --instance-type $INSTANCE_TYPE --security-group-ids sg-0a9bc2fb61f10a342 --image-id $IMAGE_ID --count 1 --user-data "REPLICA_SET_NAME=$REPLICA_SET_NAME
+aws ec2 run-instances --placement AvailabilityZone=$AVAILABILITY_ZONE --instance-type $INSTANCE_TYPE --security-group-ids ${SECURITY_GROUP_ID} --image-id $IMAGE_ID --count 1 --user-data "REPLICA_SET_NAME=$REPLICA_SET_NAME
REPLICA_SET_PRIMARY=$REPLICA_SET_PRIMARY
REPLICA_SET_PRIORITY=$REPLICA_SET_PRIORITY
-REPLICA_SET_VOTES=$REPLICA_SET_VOTES" --ebs-optimized --key-name $KEY_NAME --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]" "ResourceType=volume,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]"
+REPLICA_SET_VOTES=$REPLICA_SET_VOTES" --ebs-optimized --key-name $KEY_NAME --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY},{Key=mongo-replica-sets,Value=$REPLICA_SET_NAME}]" "ResourceType=volume,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]"
diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh
index b093eb7f418..1abd6cfdce5 100755
--- a/configuration/buildAndUpdateProduct.sh
+++ b/configuration/buildAndUpdateProduct.sh
@@ -277,11 +277,13 @@ if [[ "$@" == "release" ]]; then
cp -v $PROJECT_HOME/java/target/refreshInstance.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
+ cp -v $PROJECT_HOME/java/target/env-default-rules.sh $ACDIR/
+ cp -v $PROJECT_HOME/java/target/defineReverseProxyMappings.sh $ACDIR/
cp -v $p2PluginRepository/configuration/config.ini configuration/
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-http.xml configuration/jetty/etc
- cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml configuration/jetty/etc
+ cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deploy.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
cp -v $PROJECT_HOME/java/target/configuration/mail.properties configuration/
@@ -335,7 +337,7 @@ INSTALL_FROM_RELEASE=$SIMPLE_VERSION_INFO
SSH_CMD="ssh $REMOTE_SERVER_LOGIN"
SCP_CMD="scp -r"
- echo "Packaged release $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO.tar.gz! I've put an env.sh that matches the current branch to $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/env.sh!"
+ echo "Packaged release $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO.tar.gz! I've put an env.sh that matches the current branch to $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/env.sh!"
echo "Checking the remote connection..."
REMOTE_HOME=`ssh $REMOTE_SERVER_LOGIN 'echo $HOME/releases'`
@@ -811,11 +813,13 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $p2PluginRepository/configuration/config.ini configuration/
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-http.xml configuration/jetty/etc
- cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml configuration/jetty/etc
+ cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deploy.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
if [ ! -f "$ACDIR/env.sh" ]; then
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
+ cp -v $PROJECT_HOME/java/target/env-default-rules.sh $ACDIR/
+ cp -v $PROJECT_HOME/java/target/defineReverseProxyMappings.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties $ACDIR/configuration/
cp -v $PROJECT_HOME/java/target/configuration/mail.properties $ACDIR/configuration/
cp -v $PROJECT_HOME/java/target/configuration/logging.properties $ACDIR/configuration/
@@ -831,6 +835,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
# make sure to read the information from env.sh
. $ACDIR/env.sh
+ . $ACDIR/env-default-rules.sh
echo "$VERSION_INFO System:" > $ACDIR/configuration/jetty/version.txt
@@ -895,12 +900,14 @@ if [[ "$@" == "remote-deploy" ]]; then
$SCP_CMD $p2PluginRepository/configuration/config.ini $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
$SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
$SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-http.xml $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
- $SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
+ $SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deploy.xml $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
$SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
$SCP_CMD $PROJECT_HOME/java/target/configuration/monitoring.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
$SCP_CMD $PROJECT_HOME/java/target/configuration/mail.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
$SCP_CMD $PROJECT_HOME/java/target/env.sh $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
+ $SCP_CMD $PROJECT_HOME/java/target/env-default-rules.sh $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
+ $SCP_CMD $PROJECT_HOME/java/target/defineReverseProxyMappings.sh $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
$SCP_CMD $PROJECT_HOME/java/target/start $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
$SCP_CMD $PROJECT_HOME/java/target/stop $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
$SCP_CMD $PROJECT_HOME/java/target/status $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
diff --git a/configuration/httpd/conf.d/000-macros.conf b/configuration/httpd/conf.d/000-macros.conf
new file mode 100755
index 00000000000..625ed3a550a
--- /dev/null
+++ b/configuration/httpd/conf.d/000-macros.conf
@@ -0,0 +1,837 @@
+
+# new ARCHIVE, based on i3.2xlarge, 64GB RAM and 1.9TB swap
+ Use Rewrite 172.31.20.241 8888
+# new ARCHIVE, based on i3.2xlarge, 64GB RAM and 1.9TB swap
+# Use Rewrite 172.31.26.29 8888
+
+
+
+ SetEnvIf Request_Method POST REQUEST_IS_POST=1
+ SetEnvIf Request_URI ^/gateway.* REQUEST_IS_POST=1
+ SetEnvIf Request_URI ^/gwt/RegattaOverview.* REQUEST_IS_POST=1
+ SetEnvIf Request_URI ^/?$ REQUEST_IS_POST=1
+
+ Header set Expires "Sun, 19 Nov 1978 05:00:00 GMT" env=REQUEST_IS_POST
+ Header set Cache-Control no-cache,no-store,must-revalidate,post-check=0,pre-check=0 env=REQUEST_IS_POST
+
+ AddOutputFilterByType DEFLATE text/plain
+ AddOutputFilterByType DEFLATE text/html
+ AddOutputFilterByType DEFLATE text/xml
+ AddOutputFilterByType DEFLATE text/json
+ AddOutputFilterByType DEFLATE application/json
+ AddOutputFilterByType DEFLATE text/css
+ AddOutputFilterByType DEFLATE image/gif
+ AddOutputFilterByType DEFLATE image/png
+ AddOutputFilterByType DEFLATE image/jpeg
+ AddOutputFilterByType DEFLATE application/xml
+ AddOutputFilterByType DEFLATE application/xhtml+xml
+ AddOutputFilterByType DEFLATE application/rss+xml
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE application/x-javascript
+
+ AllowEncodedSlashes NoDecode
+
+
+
+ AllowEncodedSlashes On
+
+ SetEnvIf Request_Method POST REQUEST_IS_POST=1
+ SetEnvIf Request_URI ^/?$ REQUEST_IS_POST=1
+
+ Header set Expires "Sun, 19 Nov 1978 05:00:00 GMT" env=REQUEST_IS_POST
+ Header set Cache-Control no-cache,no-store,must-revalidate,post-check=0,pre-check=0 env=REQUEST_IS_POST
+
+
+ AddType text/css .css
+ AddType application/x-javascript .js
+ AddType text/x-component .htc
+ AddType text/html .html .htm
+ AddType text/richtext .rtf .rtx
+ AddType image/svg+xml .svg .svgz
+ AddType text/plain .txt
+ AddType text/xsd .xsd
+ AddType text/xsl .xsl
+ AddType text/xml .xml
+ AddType video/asf .asf .asx .wax .wmv .wmx
+ AddType video/avi .avi
+ AddType image/bmp .bmp
+ AddType application/java .class
+ AddType video/divx .divx
+ AddType application/msword .doc .docx
+ AddType application/vnd.ms-fontobject .eot
+ AddType application/x-msdownload .exe
+ AddType image/gif .gif
+ AddType application/x-gzip .gz .gzip
+ AddType image/x-icon .ico
+ AddType image/jpeg .jpg .jpeg .jpe
+ AddType application/vnd.ms-access .mdb
+ AddType audio/midi .mid .midi
+ AddType video/quicktime .mov .qt
+ AddType audio/mpeg .mp3 .m4a
+ AddType video/mp4 .mp4 .m4v
+ AddType video/mpeg .mpeg .mpg .mpe
+ AddType application/vnd.ms-project .mpp
+ AddType application/x-font-otf .otf
+ AddType application/vnd.oasis.opendocument.database .odb
+ AddType application/vnd.oasis.opendocument.chart .odc
+ AddType application/vnd.oasis.opendocument.formula .odf
+ AddType application/vnd.oasis.opendocument.graphics .odg
+ AddType application/vnd.oasis.opendocument.presentation .odp
+ AddType application/vnd.oasis.opendocument.spreadsheet .ods
+ AddType application/vnd.oasis.opendocument.text .odt
+ AddType audio/ogg .ogg
+ AddType application/pdf .pdf
+ AddType image/png .png
+ AddType application/vnd.ms-powerpoint .pot .pps .ppt .pptx
+ AddType audio/x-realaudio .ra .ram
+ AddType application/x-shockwave-flash .swf
+ AddType application/x-tar .tar
+ AddType image/tiff .tif .tiff
+ AddType application/x-font-ttf .ttf .ttc
+ AddType audio/wav .wav
+ AddType audio/wma .wma
+ AddType application/vnd.ms-write .wri
+ AddType application/vnd.ms-excel .xla .xls .xlsx .xlt .xlw
+ AddType application/zip .zip
+
+
+
+ # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
+
+
+ SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+ RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+
+
+
+
+ # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
+ FilterDeclare COMPRESS
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/html'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/css'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/plain'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/x-component'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/javascript'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/json'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xhtml+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/rss+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/atom+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/vnd.ms-fontobject'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/svg+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/x-icon'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/gif'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/jpeg'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/png'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/x-font-ttf'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$font/opentype'"
+ FilterChain COMPRESS
+ FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no
+
+
+
+ # Legacy versions of Apache
+ AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
+ AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
+ AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
+
+
+
+
+
+ ProxyPass "/" "http://$IP:$PORT/" nocanon
+ # This is what is was before, requiring AllowEncodedSlashes On instead of NoDecode:
+ # RewriteRule ^/(.*)$ http://$IP:$PORT/$1 [P,L]
+ ProxyPassReverse / http://$IP:$PORT/
+ ProxyPreserveHost On
+
+
+
+ SSLEngine On
+ SSLCertificateFile /etc/pki/tls/certs/star_sapsailing_com.crt
+ SSLCertificateKeyFile /etc/pki/tls/private/star_sapsailing_com.key
+ SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
+
+
+
+ SSLEngine On
+ SSLCertificateFile /etc/letsencrypt/live/sail-insight.com/cert.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/sail-insight.com/privkey.pem
+ SSLCertificateChainFile /etc/letsencrypt/live/sail-insight.com/fullchain.pem
+
+
+
+ SSLEngine On
+ SSLCertificateFile /etc/pki/tls/certs/star_saptennis_com.crt
+ SSLCertificateKeyFile /etc/pki/tls/private/star_saptennis_com.key
+ SSLCertificateChainFile /etc/pki/tls/certs/saptennis_chain.crt
+
+
+
+
+ ServerName $HOSTNAME
+
+ SetHandler server-status
+ Order deny,allow
+ Allow from all
+
+ CustomLog logs/services_log combined
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+
+ SetHandler server-status
+ Order deny,allow
+ Allow from all
+
+ CustomLog logs/services_log combined
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+ RewriteEngine on
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html" [L,NE]
+ Use ArchiveRewrite
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Home-ARCHIVE-SSL $HOSTNAME
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers-Tennis
+ Use SSL-Tennis
+ RewriteEngine on
+ RewriteRule ^(/)?$ https://$HOSTNAME/$PATH [L,NE]
+ RewriteRule ^/(.*)$ http://$IP:$PORT/$1 [P,L]
+ ProxyPassReverse / http://$IP:$PORT/
+ ProxyPreserveHost On
+ CustomLog logs/tennis_log combined
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers-Tennis
+ Use SSL-Tennis
+ RewriteEngine on
+ RewriteRule ^/(.*)$ http://$IP:$PORT/$1 [P,L]
+ ProxyPassReverse / http://$IP:$PORT/
+ ProxyPreserveHost On
+ CustomLog logs/tennis_log combined
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+ RewriteEngine on
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html" [L,NE]
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+ RewriteEngine on
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html" [L,NE]
+ Use Rewrite $IP $PORT
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Home-SSL $HOSTNAME $IP $PORT
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+
+
+
+
+ ServerName $HOSTNAME
+ RedirectPermanent / https://www.$HOSTNAME/
+ CustomLog logs/tennis_log combined
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+ RewriteEngine on
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html" [L,NE]
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers
+ RewriteEngine on
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers
+ RewriteEngine on
+ Use Rewrite $IP $PORT
+ CustomLog logs/tennis_log combined
+
+
+
+
+ ServerName $HOSTNAME
+ RedirectPermanent / https://$HOSTNAME/
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Plain-SSL $HOSTNAME $IP $PORT
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+ RewriteEngine on
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers
+ RedirectMatch /(.*)$ $TARGETURL/
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?locale=de#/event/:eventId=$EVENTUUID" [L,NE]
+
+ RewriteCond %{REQUEST_URI} "^/en$"
+ RewriteRule ^(/en)?$ "http://$HOSTNAME/gwt/Home.html?locale=en#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?locale=de#/event/:eventId=$EVENTUUID" [L,NE]
+
+ RewriteCond %{REQUEST_URI} "^/en$"
+ RewriteRule ^(/en)?$ "http://$HOSTNAME/gwt/Home.html?locale=en#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/dashboards/RibDashboard.html?leaderboardName=$LEADERBOARD_NAME&%1" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID&locale=$LANG&enableLeaderboard=true" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID&locale=$LANG" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID&enableLeaderboard=true" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Event-SSL $HOSTNAME $EVENTUUID $IP $PORT
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/series/events/:seriesId=$SERIESUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Series-SSL $HOSTNAME $EVENTUUID $IP $PORT
+
+
+
+
+ ServerName $HOSTNAME
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/series/events/:seriesId=$SERIESUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html#/regatta/overview/:eventId=$EVENTUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?locale=$LANG#EventPlace:eventId=$EVENTUUID" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Series-ARCHIVE-SSL $HOSTNAME $SERIESUUID
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/series/events/:leaderboardGroupId=$SERIESUUID" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/series/events/:leaderboardGroupId=$SERIESUUID" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Event-ARCHIVE-SSL $HOSTNAME $EVENTUUID
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ # The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
+ RewriteCond %{QUERY_STRING} "^(.*)$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID&%1" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/events/$EVENTNAME/$EXTRA" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&$EXTRA" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&showRaceDetails=true&locale=$LANG" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&showRaceDetails=true" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Leaderboard.html?name=$NAME&leaderboardGroupName=$GROUPNAME&showRaceDetails=true&root=leaderboardGroupPanel" [L,NE]
+
+ Use Rewrite $IP $PORT
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Leaderboard.html?name=$NAME&leaderboardGroupName=$GROUPNAME&$EXTRA" [L,NE]
+
+ Use Rewrite-ARCHIVE1
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&showRaceDetails=true" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+ Use RedirectPermanentToSSL $HOSTNAME
+ Use Spectator-ARCHIVE-SSL $HOSTNAME "$GROUPNAME"
+
+
+
+
+ ServerName $HOSTNAME
+ Use SSL
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&showRaceDetails=true" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&$EXTRA" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/events/$EVENTNAME/$EXTRA" [L,NE]
+
+ Use ArchiveRewrite
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&showRaceDetails=true" [L,NE]
+
+ Use Rewrite 172.31.18.218 8888
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Spectator.html?leaderboardGroupName=$GROUPNAME&$EXTRA" [L,NE]
+
+ Use Rewrite 172.31.18.218 8888
+
+
+
+
+
+ ServerName $HOSTNAME
+
+ Use Headers
+
+ RewriteEngine on
+
+ RewriteCond %{REQUEST_URI} "^(/)?$"
+ RewriteRule ^(/)?$ "http://$HOSTNAME/events/$EVENTNAME/$EXTRA" [L,NE]
+
+ Use Rewrite 172.31.18.218 8888
+
+
diff --git a/configuration/httpd/conf.d/000-main.conf b/configuration/httpd/conf.d/000-main.conf
new file mode 100644
index 00000000000..60321f7393f
--- /dev/null
+++ b/configuration/httpd/conf.d/000-main.conf
@@ -0,0 +1,263 @@
+# Default - NEVER move this or change anything here unless you know what you're doing
+# This first VirtualHost catches all port 80 HTTP traffic that is not caught by
+# any explicitly matching VirtualHost for port 80 declared further down below.
+# It redirects to the URI used, replacing https for http
+
+ AllowEncodedSlashes NoDecode
+ ServerName somenonexistingsubdomainname.sapsailing.com
+# New approach: a single RewriteRule that catches all HTTP requests, mapping them to corresponding HTTPS requests
+ RewriteEngine on
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteCond %{HTTPS} off
+ RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
+ CustomLog logs/redirect_log sapsailing_httpd_redirect_combined env=!original_client_ip
+ CustomLog logs/redirect_log sapsailing_httpd_redirect_combined_first_forwarded_for_ip env=original_client_ip
+
+
+# The explicit rewrite from http://sapsailing.com with missing subdomain name to https://www.sapsailing.com:
+
+ ServerName sapsailing.com
+# New approach: a single RewriteRule that catches all HTTP requests, mapping them to corresponding HTTPS requests
+ RewriteEngine on
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteCond %{HTTPS} off
+ RewriteRule ^.*$ https://www.sapsailing.com%{REQUEST_URI} [L,R=301,NE]
+ CustomLog logs/access_log sapsailing_httpd_redirect_combined env=!original_client_ip
+ CustomLog logs/access_log sapsailing_httpd_redirect_combined_first_forwarded_for_ip env=original_client_ip
+# Old approach: RedirectPermanent for each :80 VirtualHost
+# RedirectPermanent / https://www.sapsailing.com/
+
+
+# WWW as the default virtual host for HTTPS access to port 443 because it's listed as the first on :443
+Use Home-ARCHIVE-SSL-Redirect www.sapsailing.com
+
+
+# a rewrite rule to map sapsailing.com to www.sapsailing.com for HTTPS requests
+ ServerName sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^.*$ https://www.sapsailing.com%{REQUEST_URI} [L,R=301,NE]
+ CustomLog logs/access_log sapsailing_httpd_redirect_combined env=!original_client_ip
+ CustomLog logs/access_log sapsailing_httpd_redirect_combined_first_forwarded_for_ip env=original_client_ip
+
+
+
+ ServerName jobs.sapsailing.com
+ Use SSL
+ CustomLog logs/jobs_log combined env=!original_client_ip
+ CustomLog logs/jobs_log first_forwarded_for_ip env=original_client_ip
+ DocumentRoot /home/trac/static/jobs
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName kw2013.sapsailing.com
+ RedirectPermanent / http://livecenter.kieler-woche.de/en/
+
+
+# sail-insight.com
+
+ ServerName sail-insight.com
+ ServerAlias www.sail-insight.com
+ Use SSL-sail-insight
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+ Alias / /home/trac/sail-insight-website/
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
+# P2 repository
+
+ ServerName p2.sapsailing.com
+ Use SSL
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+ Alias /p2 /home/trac/p2-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+# Git Viewer -->
+
+ ServerName gitlist.sapsailing.com
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+ DocumentRoot /var/www/gitlist
+ AddHandler php5-script .php
+ AddType text/html .php
+ DirectoryIndex index.php
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
+# Maven Repositories -->
+
+ ServerName maven.sapsailing.com
+ Use SSL
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+ Alias /maven /home/trac/maven-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName bugzilla.sapsailing.com
+ Use SSL
+ RewriteEngine On
+ RewriteRule ^/?$ https://bugzilla.sapsailing.com/bugzilla [L]
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+ Alias /bugzilla /usr/share/bugzilla
+
+ AddHandler cgi-script .cgi
+ Options +Indexes +ExecCGI +FollowSymLinks
+ DirectoryIndex index.cgi
+ AllowOverride Limit Options FileInfo Indexes AuthConfig
+
+ # Additional Perl modules
+
+ SetEnv PERL5LIB /root/perl5/lib/perl5:/root/perl5/lib/perl5/x86_64-linux-thread-multi
+
+
+
+# Error pages -->
+
+ ServerName status.sapsailing.com
+ Use SSL
+ DocumentRoot /var/www/errorpages
+
+
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.html
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+
+
+
+ SetHandler server-status
+ Order deny,allow
+ Deny from all
+ Allow from all
+ #Allow from 46.142.162.18 localhost 127.0.0.1 *
+
+
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+# PIWIK analytics -->
+
+ ServerName analysis.sapsailing.com
+ Alias / /var/www/piwik/
+
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.php
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+# Wiki -->
+
+ ServerName wiki.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://localhost:4567/$1 [P,L]
+ ProxyPassReverse / http://localhost:4567/
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+
+ ServerName stg.static.sapsailing.com
+ RedirectPermanent / http://static.sapsailing.com/
+
+
+
+ ServerName releases.sapsailing.com
+ DocumentRoot /home/trac/releases
+
+
+ Options +Indexes +FollowSymLinks
+
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+
+ ServerName static.sapsailing.com
+ Use SSL
+ DocumentRoot /var/www/static
+
+
+ Order Deny,Allow
+ Allow from all
+ Options -Indexes +FollowSymLinks -ExecCGI
+ AllowOverride All
+
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+
+ ServerName dev.sapsailing.com
+ RedirectPermanent / https://dev.sapsailing.com/
+
+
+
+ ServerName hudson.sapsailing.com
+ RedirectPermanent / https://hudson.sapsailing.com/
+
+
+
+ ServerName hudson.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://172.31.28.17:8080/$1 [P,L]
+ ProxyPassReverse / http://172.31.28.17:8080/
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
+
+ ServerName jenkins.sapsailing.com
+ RedirectPermanent / https://jenkins.sapsailing.com/
+
+
+
+ ServerName jenkins.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://172.31.17.141:8080/$1 [P,L]
+ ProxyPassReverse / http://172.31.17.141:8080/
+ CustomLog logs/services_log combined env=!original_client_ip
+ CustomLog logs/services_log first_forwarded_for_ip env=original_client_ip
+
+
diff --git a/configuration/httpd/conf.d/000-main.conf.bak b/configuration/httpd/conf.d/000-main.conf.bak
new file mode 100644
index 00000000000..e7d4f7a5d8a
--- /dev/null
+++ b/configuration/httpd/conf.d/000-main.conf.bak
@@ -0,0 +1,338 @@
+
+# Default - NEVER move this or change anything here
+
+ ServerName sapsailing.com
+ RedirectPermanent / https://www.sapsailing.com/
+
+
+
+ ServerName jobs.sapsailing.com
+ RedirectPermanent / https://jobs.sapsailing.com/
+
+
+
+ ServerName jobs.sapsailing.com
+ Use SSL
+ CustomLog logs/jobs_log combined
+ DocumentRoot /home/trac/static/jobs
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName a.dummy.sapsailing.com
+ RewriteEngine on
+ RewriteCond %{HTTP:X-Forwarded-Proto} !https
+ RewriteCond %{HTTPS} off
+ RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
+
+
+
+ ServerName kw2013.sapsailing.com
+ RedirectPermanent / http://livecenter.kieler-woche.de/en/
+
+
+# P2 repository
+
+ ServerName p2.sapsailing.com
+ CustomLog logs/services_log combined
+ Alias /p2 /home/trac/p2-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName p2.sapsailing.com
+ Use SSL
+ CustomLog logs/services_log combined
+ Alias /p2 /home/trac/p2-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+# Git Viewer -->
+
+ ServerName gitlist.sapsailing.com
+ CustomLog logs/services_log combined
+ DocumentRoot /var/www/gitlist
+ AddHandler php5-script .php
+ AddType text/html .php
+ DirectoryIndex index.php
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
+# Maven Repositories -->
+
+ ServerName maven.sapsailing.com
+ CustomLog logs/services_log combined
+ Alias /maven /home/trac/maven-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName maven.sapsailing.com
+ Use SSL
+ CustomLog logs/services_log combined
+ Alias /maven /home/trac/maven-repositories
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+# Bugzilla -->
+Alias /bugzilla/var/lib/bugzilla/data/webdot /var/lib/bugzilla/data/webdot
+Alias /bugzilla /usr/share/bugzilla
+
+
+ AddHandler cgi-script .cgi
+ Options +Indexes +ExecCGI +FollowSymLinks
+ DirectoryIndex index.cgi index.html
+ AllowOverride Limit Options FileInfo
+ AddType application/vnd.mozilla.xul+xml .xul
+ AddType application/rdf+xml .rdf
+
+
+
+ ServerName bugzilla.sapsailing.com
+ RedirectPermanent / https://bugzilla.sapsailing.com/
+
+
+
+ ServerName bugzilla.sapsailing.com
+ Use SSL
+ CustomLog logs/services_log combined
+ #ServerPath /bugzilla
+ #DocumentRoot /usr/share/bugzilla
+ Alias /bugzilla /usr/share/bugzilla
+
+ AddHandler cgi-script .cgi
+ Options +Indexes +ExecCGI +FollowSymLinks
+ DirectoryIndex index.cgi
+ AllowOverride Limit Options FileInfo
+
+
+
+# Various Services like p2 repository and bugzilla -->
+
+ ServerName bugzilla.sapsailing.com
+
+ CustomLog logs/services_log combined
+ #ServerPath /bugzilla
+ #DocumentRoot /usr/share/bugzilla
+
+ Alias /bugzilla /usr/share/bugzilla
+ Alias /p2 /home/trac/p2-repositories
+ Alias /maven /home/trac/maven-repositories
+ Alias /maven-riccardo /home/riccardo/maven-repositories
+ Alias /mrtg /var/www/mrtg
+
+
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.html
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+
+
+
+ AddHandler cgi-script .cgi
+ Options +Indexes +ExecCGI +FollowSymLinks
+ DirectoryIndex index.cgi
+ AllowOverride Limit Options FileInfo
+
+
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+# Error pages -->
+
+ ServerName status.sapsailing.com
+ DocumentRoot /var/www/errorpages
+
+
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.html
+ AllowOverride None
+ Order allow,deny
+ Allow from all
+
+
+
+ SetHandler server-status
+ Order deny,allow
+ Deny from all
+ Allow from all
+ #Allow from 46.142.162.18 localhost 127.0.0.1 *
+
+
+ CustomLog logs/services_log combined
+
+
+# PIWIK analytics -->
+
+ ServerName analysis.sapsailing.com
+ Alias / /var/www/piwik/
+
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.php
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+ CustomLog logs/services_log combined
+
+
+# Wiki -->
+
+ ServerName wiki.sapsailing.com
+ RedirectPermanent / https://wiki.sapsailing.com/
+
+
+
+ ServerName wiki.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://localhost:4567/$1 [P,L]
+ ProxyPassReverse / http://localhost:4567/
+ CustomLog logs/services_log combined
+
+
+
+ ServerName mrtg.sapsailing.com
+ RedirectPermanent / http://static.sapsailing.com/
+
+
+
+ ServerName stg.static.sapsailing.com
+ RedirectPermanent / http://static.sapsailing.com/
+
+
+
+ ServerName releases.sapsailing.com
+ DocumentRoot /home/trac/releases
+
+
+ Order Deny,Allow
+ Allow from all
+ Options Indexes FollowSymLinks -ExecCGI
+ AllowOverride All
+
+ CustomLog logs/services_log combined
+
+
+# Static files -->
+
+ ServerName static.sapsailing.com
+ DocumentRoot /var/www/static
+
+
+ Order Deny,Allow
+ Allow from all
+ Options -Indexes FollowSymLinks ExecCGI
+ AllowOverride All
+
+ CustomLog logs/services_log combined
+
+
+
+ ServerName erlebniswelt.sapsailing.com
+ DocumentRoot /var/www/static/fcb
+
+
+ Options +Indexes -FollowSymLinks +MultiViews
+ AllowOverride None
+ Order allow,deny
+ allow from all
+
+
+
+
+ ServerName static.sapsailing.com
+ Use SSL
+ DocumentRoot /var/www/static
+
+
+ Order Deny,Allow
+ Allow from all
+ Options -Indexes FollowSymLinks ExecCGI
+ AllowOverride All
+
+ CustomLog logs/services_log combined
+
+
+
+ ServerName dev.sapsailing.com
+ RedirectPermanent / https://dev.sapsailing.com/
+
+
+
+ ServerName hudson.sapsailing.com
+ RedirectPermanent / https://hudson.sapsailing.com/
+
+
+
+ ServerName hudson.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://172.31.28.17:8080/$1 [P,L]
+ ProxyPassReverse / http://172.31.28.17:8080/
+ CustomLog logs/services_log combined
+
+
+
+ ServerName jenkins.sapsailing.com
+ RedirectPermanent / https://jenkins.sapsailing.com/
+
+
+
+ ServerName jenkins.sapsailing.com
+ Use SSL
+ RewriteEngine on
+ RewriteRule ^/(.*) http://172.31.17.141:8080/$1 [P,L]
+ ProxyPassReverse / http://172.31.17.141:8080/
+ CustomLog logs/services_log combined
+
+
diff --git a/configuration/httpd/conf.d/001-events.conf b/configuration/httpd/conf.d/001-events.conf
new file mode 100644
index 00000000000..3adc67a7c72
--- /dev/null
+++ b/configuration/httpd/conf.d/001-events.conf
@@ -0,0 +1,443 @@
+#
+# Spectator $IP $PORT $HOSTNAME $GROUPNAME
+#
+# Spectator-WithParam $IP $PORT $HOSTNAME $GROUPNAME $EXTRA
+#
+# Leaderboard $IP $PORT $HOSTNAME $GROUPNAME $NAME
+#
+# Leaderboard-WithParam $IP $PORT $HOSTNAME $GROUPNAME $NAME $EXTRA
+#
+# Make sure to always use "" as delimiters for strings containing spaces
+# After a change please perform a service httpd reload
+#
+# ATT: There is a special macro called Spectator-ARCHIVE that contains
+# the fixed IP adress and port for the archive server. You can find it
+# in /etc/httpd/conf.d/000-macros.conf at the beginning of the file. Feel free
+# to add other macros there.
+
+###################################
+############ DEDICATED ###########
+
+## DEV Server
+Use Plain-SSL-Redirect dev.sapsailing.com 172.31.28.17 8888
+Use Plain-SSL dualstack-dev.sapsailing.com 172.31.28.17 8888
+
+###################################
+######### SHARED INSTANCE #########
+
+
+###################################
+############# ARCHIVE #############
+
+# 2.4mR IDM 2015
+Use Event-ARCHIVE-SSL-Redirect idm24mR2015.sapsailing.com "c51c9915-a8f0-4a31-a0e2-127d557b7ea0"
+
+# 29er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 29erWorlds2016.sapsailing.com "4b381745-1d29-4bda-b388-4a0b7e94dc98"
+
+####### 49er Worlds - Euros ######
+
+## 49er Oceania 2019
+Use Series-ARCHIVE-SSL-Redirect 49eroceania2019.sapsailing.com "b4a6e4ef-9a07-4832-9268-05cce3941b46"
+
+## 49er Euros 2020
+Use Event-ARCHIVE-SSL-Redirect 49erEuros2020.sapsailing.com "bc806190-1c04-4222-b21b-898a08b97e54"
+
+# 49er / FX Princess Sofia Trophy 2016
+Use Event-ARCHIVE-SSL-Redirect trofeoprincesasofia2016.sapsailing.com "445d072c-fe95-4cc3-ba49-874d4cd43325"
+# ATC Regatta 2015
+Use Event-ARCHIVE-SSL-Redirect atcregatta2015.sapsailing.com "9c1d57bf-29c8-440f-becd-4862bc0f43fe"
+
+# 49er JWC 2015
+Use Event-ARCHIVE-SSL-Redirect 49erJWC2015.sapsailing.com "9605d85c-867e-438c-a74b-47da896f476e"
+
+# 49er Euros
+Use Event-ARCHIVE-SSL-Redirect 49er-n17-euros2017.sapsailing.com "2288808b-2697-48e9-893a-76ccf00ee94d"
+Use Event-Archive-SSL-Redirect 49erEuros2016.sapsailing.com "ccb1b062-fe59-4a1c-a15e-f30795ac3c7f"
+Use Event-ARCHIVE-SSL-Redirect 49ereuros2015.sapsailing.com "2b684db8-683d-4479-925b-5962085b7ae1"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2013.sapsailing.com "232ce9c3-ac84-47c8-b77d-eec782400877"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2012.sapsailing.com "22c7942e-eb3d-462a-9d59-600468044bb2"
+
+# 49er Worlds
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2020.sapsailing.com "ee70229c-9836-4025-9096-1b5050152b1e"
+Use Series-ARCHIVE-SSL-Redirect 49erworlds2019.sapsailing.com "e5a65f0a-b9b4-4630-b0b0-ad1fd7727518"
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2016.sapsailing.com "98e52dd5-2765-4f71-8ebc-82284be07fbc"
+
+# 505 Worlds & IDMs
+Use Event-ARCHIVE-SSL-Redirect 505worlds2019.sapsailing.com "fbfa43cc-ff0d-42b9-96c5-6d218c636a62"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018.sapsailing.com "4d2909ab-d05a-4a94-80b1-dba50bd4729e"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2017.sapsailing.com "4e070be8-51a8-4633-87fc-533f3f54f7d4"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2016.sapsailing.com "8f5daf0f-210f-435f-97bd-c6aff11af3d2"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2015.sapsailing.com "b1e78f52-ce52-4b71-ac30-452599faf4a1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014.sapsailing.com "94e1e240-8a3d-466b-9f39-419f311eaace"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2013.sapsailing.com "35dc9389-b59d-4ebd-8f6b-432065642f40"
+Use Event-ARCHIVE-SSL-Redirect 505idm2013.sapsailing.com "9130f36f-d1c3-4080-ac5b-a2104b8fbfd0"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2012.sapsailing.com "64e2b631-1a97-4270-9383-14481c8e960f"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2011.sapsailing.com "a10c4fad-c07c-44f5-8241-614a165daae1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2010.sapsailing.com "ba1f5e28-7552-4277-ac32-e440a3432fb7"
+
+## 6mR Worlds 2017 Vancouver
+Use Event-ARCHIVE-SSL-Redirect 6mworlds2017.sapsailing.com "3fbe1757-652c-48d6-a7a5-3a611c82589b"
+
+## 8 meter worlds 2017
+Use Event-ARCHIVE-SSL-Redirect 8mworlds2017.sapsailing.com "bd1df974-aa7d-4d56-98ca-de0a50bbe0b6"
+
+## Aeroe Rund 2018
+Use Event-ARCHIVE-SSL-Redirect aeroe-rund2018.sapsailing.com "7a16b434-6952-457b-9b7c-010875f6bfd3"
+
+# Audi Business Cup
+Use Event-ARCHIVE-SSL-Redirect audibusinesscup2015.sapsailing.com "080b6c09-0110-4b51-a209-dc3cf8b9f669"
+
+## BAR Test Oman
+Use Event-ARCHIVE-SSL-Redirect bar-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+Use Event-ARCHIVE-SSL-Redirect ACWS-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+
+# Bart's Bash
+Use Event-ARCHIVE-SSL-Redirect bartsbash2015.sapsailing.com "905457f5-5ccb-4fea-8219-8300eed637ad"
+Use Event-ARCHIVE-SSL-Redirect bartsbash2014.sapsailing.com "b2e2b264-134c-4ef2-b592-53562fd0f835"
+
+# Bodenseewoche 2015
+Use Event-ARCHIVE-SSL-Redirect bodenseewoche2015.sapsailing.com "fef6c510-eefe-470f-a9a9-dcf6293ec116"
+
+## CRW 2016
+Use Event-ARCHIVE-SSL-Redirect crw2016.sapsailing.com "56f62045-de57-4c5e-be6c-a2339d9c9ece"
+
+## Eastern Economic Forum 2016
+Use Event-ARCHIVE-SSL-Redirect forumvostok2016.sapsailing.com "5093220d-875f-4909-9ad0-46476639ee97"
+
+# ESS
+Use Event-ARCHIVE-SSL-Redirect ess2018.sapsailing.com "61af2433-21b4-4a74-8f82-58b582db3944"
+Use Event-ARCHIVE-SSL-Redirect ess2017.sapsailing.com "467b55e8-eab8-4f4b-bf5e-830221fe25dc"
+Use Event-ARCHIVE-SSL-Redirect ess2016.sapsailing.com "a6025457-2880-4fe9-b9d6-ad943ef78025"
+Use Event-ARCHIVE-SSL-Redirect ess40-2015.sapsailing.com "70054aa2-b381-4e77-8e17-840ee1b6190d"
+Use Event-ARCHIVE-SSL-Redirect ess40-2014.sapsailing.com "8e5bef73-7d55-45ed-951a-72ef375ae1b6"
+Use Event-ARCHIVE-SSL-Redirect ess40-2013.sapsailing.com "1487bc65-be3b-4dbc-951d-a1390e9619e0"
+Use Event-ARCHIVE-SSL-Redirect ess40-2012.sapsailing.com "863a5c30-cbea-4f1c-8fb1-54fffa6b7319"
+
+##Hansesail 2017 (operated by melanie.coldewey@sap.com)
+Use Event-ARCHIVE-SSL-Redirect hansesail2017.sapsailing.com "0e76df1e-c61f-4845-8681-4b60d4de7cb3"
+
+## Helga Cup 2019
+Use Event-ARCHIVE-SSL-Redirect helgacup2019.sapsailing.com "94f7932a-4518-4c58-9f77-d43682b002f6"
+
+## IDM Drachen 2012
+Use Event-ARCHIVE-SSL-Redirect drachenidm2012.sapsailing.com "a42beb95-4885-45f1-9ed7-91159a6e4f6a"
+
+## IDM FINN 2017
+Use Event-ARCHIVE-SSL-Redirect idmfinn2017.sapsailing.com "9af4c6fd-a9ff-43b6-9927-323db03bab31"
+
+## IDM Star
+Use Event-ARCHIVE-SSL-Redirect idmstar2016.sapsailing.com "5a05f9b9-2f70-46f7-99e1-c069b418bc20"
+Use Event-ARCHIVE-SSL-Redirect staridm2013.sapsailing.com "a43b8f96-1e77-448a-9b8a-0f586f1bb18f"
+
+## IDM Travemünde 2011
+Use Event-ARCHIVE-SSL-Redirect idm2011.sapsailing.com "9577d5ae-b27f-4e76-bf26-580950baf904"
+
+## Kiellauf 2017
+Use Event-ARCHIVE-SSL-Redirect kiellauf2017.sapsailing.com "acccaf32-3173-4ee2-afd2-5e09a00cdf53"
+
+## Kieler Woche
+Use Event-ARCHIVE-SSL-Redirect kw2020.sapsailing.com "246dcd4a-c33e-482b-b63f-4e389f9c554a"
+Use Event-ARCHIVE-SSL-Redirect kw2019.sapsailing.com "ecf57f2a-9d4b-4918-aef3-07543ed0c4ec"
+Use Event-ARCHIVE-SSL-Redirect kw2018.sapsailing.com "1a73aaef-7386-4178-8a11-14ced71e1d8c"
+Use Event-ARCHIVE-SSL-Redirect kw2017.sapsailing.com "4d3df446-43bc-46a1-9146-3e1caf6ed757"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2016.sapsailing.com "cc12361a-236b-4ddf-b68d-42ca785cd4d6"
+Use Event-ARCHIVE-SSL-Redirect audiultracup2016.sapsailing.com "c4f9793f-c492-46a0-8af2-a543d5dab588"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2015.sapsailing.com "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2014.sapsailing.com "3713fbd7-a62b-439b-bec1-bea732c5d895"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2013.sapsailing.com "3e4145a7-5473-4670-83e6-fd5ee364f7eb"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2012.sapsailing.com "0c0db838-b199-4309-a2a4-90bebe85ffe8"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2011.sapsailing.com "8be013de-d49d-49ea-9e73-4e7c7970aec0"
+
+## Kornati Cup 2019
+Use Event-ARCHIVE-SSL-Redirect kornaticup2019.sapsailing.com "b3735a2e-bdd6-4cf7-91a7-87d388c954b4"
+
+## Medemblik Regatta
+Use Event-ARCHIVE-SSL-Redirect medemblik2019.sapsailing.com "c7e7589c-b5ef-4e7d-93ff-3cd18527976b"
+Use Event-ARCHIVE-SSL-Redirect medemblik2018.sapsailing.com "36b3a1ff-34ef-48ab-86e3-aebbbcac55c4"
+
+## Mini 650 Pacific Challenge 2013
+Use Spectator-ARCHIVE-SSL-Redirect mini650pc2013.sapsailing.com "Mini 650 Pacific Challenge 2013"
+
+## Nord Stream Race
+Use Event-ARCHIVE-SSL-Redirect nordstreamrace2019.sapsailing.com "1fc9069a-0ee8-4ca9-ba62-4ab6c410c909"
+Use Event-ARCHIVE-SSL-Redirect nordstreamrace2018.sapsailing.com "9791acc7-79f7-4c3b-9cde-7b5dede49cc2"
+
+## Norwegian Grundig Cup (on norwegian league server)
+Use Event-ARCHIVE-SSL-Redirect grundigcup2016-norway.sapsailing.com "6caf564b-f2b4-4b51-8667-50049ecb8581"
+
+## Olympic Games
+Use Event-ARCHIVE-SSL-Redirect olympics2016.sapsailing.com "e6dbae49-aa2f-49fc-a7eb-82e24cdc307f"
+Use Event-ARCHIVE-SSL-Redirect olympics2012.sapsailing.com "901963ed-743d-41db-9727-1c3b73e78ddd"
+
+## Olympic Testevent 2019 - Enoshima
+Use Event-ARCHIVE-SSL-Redirect tokyo2019.sapsailing.com "6389fb9d-12e1-47ef-9831-ddab8cf9598f"
+
+## Pitter Croatia Coast Cup 2019
+Use Event-ARCHIVE-SSL-Redirect croatiacoastcup2019.sapsailing.com "8b798fbb-65b0-49d6-b126-cfd02b1c1fd3"
+
+## Red Bull Foiling Generation 2018 Final
+Use Event-ARCHIVE-SSL-Redirect rbfg2018.sapsailing.com "1e330c0e-9dbf-4fa0-9817-1e11e17bea55"
+
+## RosAtom
+Use Event-ARCHIVE-SSL-Redirect rsc2016.sapsailing.com "3aab9f92-0d83-411c-bfc9-c9032079a749"
+Use Event-ARCHIVE-SSL-Redirect rosatomcup2015.sapsailing.com "f3a8c574-d23d-44d8-9316-3afb7ee0d39c"
+
+## SAP Opti Cup SCGN 2015
+Use Event-ARCHIVE-SSL-Redirect sapopticup2015.sapsailing.com "dff7d969-9858-4dff-b2f2-a45018a2f755"
+
+## SCGN Test 2016
+Use Event-ARCHIVE-SSL-Redirect scgn-test.sapsailing.com "5ca2b18a-0a55-4591-910c-1f1f35be51f2"
+
+# SCGN Training
+Use Event-ARCHIVE-SSL-Redirect scgn-training.sapsailing.com "004147c6-3991-4a02-bc50-72b8e5b8027c"
+
+## Sejlerskole Cup 2014
+Use Event-ARCHIVE-SSL-Redirect sejlerskolecup-2014.sapsailing.com "d75063dd-f579-4a46-836c-5cc017a71e44"
+
+## STG meets Berlin Match Race 2012
+Use Event-ARCHIVE-SSL-Redirect obmr2012.sapsailing.com "2dd8de86-6564-4189-9aa0-8666f26dbb5b"
+
+# Surfing World Cup Sylt 2015
+Use Event-ARCHIVE-SSL-Redirect worldcupsylt2015.sapsailing.com "9eb910b8-bb61-4113-8263-1667cd37ac76"
+
+## Tempest Worlds 2019
+Use Event-ARCHIVE-SSL-Redirect tempestworlds2019.sapsailing.com "72038d9e-5add-40f8-beea-5116d568facb"
+
+## Thetis 2018
+Use Event-ARCHIVE-SSL-Redirect thetis2018.sapsailing.com "8964d8e0-0368-4581-8df8-6ade3543a94a"
+
+## Travemünder Woche
+Use Event-ARCHIVE-SSL-Redirect tw2018.sapsailing.com "4e71526d-a25f-4f41-adc0-d92c60738134"
+Use Event-ARCHIVE-SSL-Redirect tw2017.sapsailing.com "7784129f-7832-49f1-a5f0-b8d66aa5560c"
+Use Event-ARCHIVE-SSL-Redirect tw2016.sapsailing.com "86e9e60c-0b16-4941-b14a-166ed4dcabff"
+Use Event-ARCHIVE-SSL-Redirect tw2015.sapsailing.com "82c5adb5-2dac-40fb-aa18-3b8057365e57"
+Use Event-ARCHIVE-SSL-Redirect tw2014.sapsailing.com "5133df37-0092-42c8-a9cb-7a087ae00219"
+Use Event-ARCHIVE-SSL-Redirect tw2013.sapsailing.com "b0a721cf-89c3-486c-9a84-25e1aaeb3225"
+
+## USODA 2016
+Use Event-ARCHIVE-SSL-Redirect usoda2016.sapsailing.com "46c85236-087e-418f-84f9-aee24dac948f"
+
+## VANPro
+Use Event-ARCHIVE-SSL-Redirect VANpro2016.sapsailing.com "2316ce7d-be93-4ffc-8613-cb6ffa475153"
+
+## Vulcain Trophy 2014 - SAP Grand Prix
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014.sapsailing.com "09f39909-5d72-40d2-b6e9-4ea7c908a0dc"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014-proam.sapsailing.com "ca9daa7d-c966-4e89-bc50-ce598e2f2a56"
+
+## WMRT 2016
+Use Event-ARCHIVE-SSL-Redirect wmrt2016.sapsailing.com "cfbe9100-75d6-474a-8d58-3ef8b19cdf25"
+
+## World Sailing Conference Cup 2019
+Use Event-ARCHIVE-SSL-Redirect bermuda2019.sapsailing.com "a865eda3-b8df-4cf8-bcf1-6bd6a1ca45bb"
+
+# YES 2015
+Use Event-ARCHIVE-SSL-Redirect yes2015.sapsailing.com "fb44d803-15d3-4fe1-852d-28346346ebc3"
+Use Event-ARCHIVE-SSL-Redirect yes2014.sapsailing.com "6b3dfa41-8553-4abc-8b02-7e3423a59041"
+Use Event-ARCHIVE-SSL-Redirect yes2012.sapsailing.com "7732cfba-23ff-44a7-9b5d-733d1d92e467"
+
+
+######### World Cups #############
+
+## Hempel World Cup Series 2020
+Use Event-ARCHIVE-SSL-Redirect hwcs2020-round2.sapsailing.com "70bf91ae-17d9-43af-b36b-e9b0cc172d48"
+Use Event-ARCHIVE-SSL-Redirect hwcs2020-round1.sapsailing.com "2d42185c-c914-4042-814f-14108218b3a3"
+
+## World Cup Series 2019
+Use Event-ARCHIVE-SSL-Redirect wcs2019-marseille.sapsailing.com "ba606057-ced3-444d-9425-fd89ae36ca18"
+Use Event-ARCHIVE-SSL-Redirect wcs2019-genoa.sapsailing.com "587e5fef-53ea-47f0-a71b-1fc29053b4f0"
+Use Event-ARCHIVE-SSL-Redirect wcs2019-miami.sapsailing.com "f393a743-dd49-450f-ab8b-d11522b266f2"
+Use Event-ARCHIVE-SSL-Redirect wcs2019-enoshima.sapsailing.com "bb4c7908-56a4-4b6e-9942-4ffd20c59fdc"
+
+## Hempel Sailing World Championships Aarhus 2018
+Use Event-ARCHIVE-SSL-Redirect worlds2018.sapsailing.com "4544f986-8235-426d-ad3e-6a3500b12edc"
+
+## World Cup Series 2018
+Use Event-ARCHIVE-SSL-Redirect wcs2018-marseille.sapsailing.com "d5c7440a-27fb-4b9f-a64d-5beb1a2907fd"
+Use Event-ARCHIVE-SSL-Redirect wcs2018-hyeres.sapsailing.com "74e27348-ee53-4b1c-b042-3a6f09c27bb2"
+Use Event-ARCHIVE-SSL-Redirect wcs2018-miami.sapsailing.com "f90391b0-022d-47a1-9dec-293acaa8b3c0"
+Use Event-ARCHIVE-SSL-Redirect wcs2018-gamagori.sapsailing.com "1e8a9ae8-4a48-4de2-99c5-308169c37b76"
+
+## Sailing World Championships Test Event 2017 Aarhus
+Use Event-ARCHIVE-SSL-Redirect swc2017-aarhus.sapsailing.com "c452f5e8-1bec-4e7b-b459-32f6b820add6"
+
+## Sailing World Cup 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-santander.sapsailing.com "3b79f093-d52f-440b-9fc4-2fb9e0cc70c9"
+Use Event-ARCHIVE-SSL-Redirect swc2017-hyeres.sapsailing.com "8962f211-6e79-422a-85c5-397aaff8d050"
+Use Event-ARCHIVE-SSL-Redirect swc2017-miami.sapsailing.com "b82f9012-47d8-4e9e-b3b2-a690c1db0fe3"
+
+## SWC Worldcup Finale Melbourne 2016
+Use Event-ARCHIVE-SSL-Redirect swc2016-melbourne.sapsailing.com "dd2edeed-b9eb-483e-b3e9-d4761d0d58d2"
+
+## SWC Qingdao
+Use Event-ARCHIVE-SSL-Redirect swc2016-qingdao.sapsailing.com "ea0caed8-3182-424e-91a7-6add65e1357d"
+
+####### Championsleague ##########
+
+## Alandia Nordic Club Sailing Championship 2019
+Use Event-ARCHIVE-SSL-Redirect ncs2019-championships.sapsailing.com "ead70d8e-0fca-4aa0-bf83-e66c19de8ab3"
+
+## Sailing Champions League 2019
+Use Event-ARCHIVE-SSL-Redirect scl2019-final.sapsailing.com "e59f2681-94ca-4a9d-b815-c4995b17d26e"
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier3.sapsailing.com "ccd72544-b7c1-4775-949a-ad5ac26b562f"
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier2.sapsailing.com "dd4eb3ce-dce0-4e8f-bfdb-ff1219f6fde1"
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier1.sapsailing.com "5b3d782a-d6eb-420f-852e-44e6d36b8869"
+
+## SAILING Champions League 2018
+Use Event-ARCHIVE-SSL-Redirect scl2018-final.sapsailing.com "0503a3d7-2b50-4825-84b4-7bcfd6cd4730"
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal2.sapsailing.com "74f7e1c6-9d19-4751-9083-60f00c9b8053"
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal1.sapsailing.com "ccd8a484-f2be-4dbd-9474-c457fefd6536"
+
+##Sailing Champions League 2017
+Use Event-ARCHIVE-SSL-Redirect scl2017.sapsailing.com "e6da37a1-773f-4dc5-b7e8-7753d3308386"
+
+## Sailing Championsleague 2016 (Petersburg / Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2016.sapsailing.com "810a5132-849f-49e0-828c-ace23289f0ac"
+
+# Championsleague 2015
+Use Event-ARCHIVE-SSL-Redirect championsleague2015.sapsailing.com "f40d9162-9008-4516-9110-33ab0f17d6a0"
+Use Event-ARCHIVE-SSL-Redirect championsleague2014.sapsailing.com "eb326f1b-81e8-468b-89cb-10131a8d8459"
+
+############ Leagues #############
+
+## Austrian Sailing League
+Use Series-ARCHIVE-SSL-Redirect austrianleague2019.sapsailing.com "882b8061-d7bc-4d76-a3c5-daa57296b0f8"
+Use Series-ARCHIVE-SSL-Redirect austrianleague2018.sapsailing.com "d5b4fba4-903e-4e0f-86e7-93162c527640"
+Use Series-ARCHIVE-SSL-Redirect austrianleague2017.sapsailing.com "94d23c5e-4cef-47d7-92c7-f8ed77ec486c"
+Use Series-ARCHIVE-SSL-Redirect austrianleague2016.sapsailing.com "526d2f9e-7abf-42b1-af7f-212f0ec4c3e6"
+Use Series-ARCHIVE-SSL-Redirect austrianleague2015.sapsailing.com "9543fd96-9bf4-4795-960a-f4d96b9d8a38"
+
+## German Sailing League
+Use Series-ARCHIVE-SSL-Redirect bundesliga2019.sapsailing.com "e8c27965-7636-4bb6-883b-98a29df31f50"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2019.sapsailing.com "8c59828d-cad7-4e84-b86b-433d844010cc"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2018.sapsailing.com "2ebdd4fe-a111-4b9e-87ed-03f4bc3d9805"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2018.sapsailing.com "537ad3d5-e0bd-43af-86c4-ed2c0c3eabc9"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2017.sapsailing.com "9ee75a6e-60c2-4a69-993c-b38f45be29ad"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2017.sapsailing.com "b2567e08-26d9-45c1-b5e0-8c410c8db18b"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2016.sapsailing.com "39cae2e8-6243-4b34-8c23-18e9d82b6cc4"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2016.sapsailing.com "4cd7f524-fa47-45b7-ae48-71e693337415"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2016-relegation.sapsailing.com "3b54a7ce-7585-4935-8564-522988484fb2"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2015.sapsailing.com "e63c2a3e-5afe-45ae-847f-ba98a1707f04"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2015.sapsailing.com "aa99d93f-9444-4611-bb5e-be9cca0de6a7"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2015-relegation.sapsailing.com "0fb650c5-51c8-44c9-8b4f-cb5d3e2def9a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2014.sapsailing.com "be6aaa29-1754-4de0-bc82-5c0c395f080e"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2014.sapsailing.com "8b982dc7-d20e-45f2-9941-f3a48fb3a0f3"
+Use Event-ARCHIVE-SSL-Redirect businesscup2014-hamburg.sapsailing.com "fd2e442b-6e79-4cc7-af23-01eb8eeb061e"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2013.sapsailing.com "a6abca6e-9711-44dc-9c64-929bc869aa12"
+
+## DSL Pokal
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2019.sapsailing.com "c6fd851c-4335-4c65-831f-c4d424b0f86f"
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2018.sapsailing.com "fc4ca7c9-5d11-4444-9f80-462a3e536e08"
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2017.sapsailing.com "f1860c95-0518-4044-ada2-3594a351dd13"
+
+## Danish Sailing League
+Use Series-ARCHIVE-SSL-Redirect danishleague2019.sapsailing.com "1894888b-a5cc-4d3d-9335-7d4374b72e15"
+Use Series-ARCHIVE-SSL-Redirect danishleague2018.sapsailing.com "c3dc2b53-b8f3-4657-93a5-e3c0bab7af80"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2018.sapsailing.com "f39e70a1-d31d-4f1d-87f5-c7832557f8d8"
+Use Series-ARCHIVE-SSL-Redirect danishleague2017.sapsailing.com "81ea69d1-111f-448f-8a83-394db3662b37"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2017.sapsailing.com "d21fe68c-f72f-4d11-b595-4a9426167b7c"
+Use Series-ARCHIVE-SSL-Redirect danishleague2016.sapsailing.com "845682ab-b8e5-4a2d-b1c2-d4e03f31963a"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2016.sapsailing.com "32a9c63b-1f00-4dba-bbe0-924a9b4a876d"
+Use Series-ARCHIVE-SSL-Redirect danishleague2015.sapsailing.com "94c730da-c0c0-44c8-b31e-0d3363e668a2"
+Use Series-ARCHIVE-SSL-Redirect sejlsportsligaen2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+Use Series-ARCHIVE-SSL-Redirect danishleague2014.sapsailing.com "cdea0732-5f0f-483b-9c32-266e2a95884e"
+
+## Dutch Sailing League
+Use Series-ARCHIVE-SSL-Redirect dutchleague2018.sapsailing.com "cdfc4a3f-ef43-42fb-91fd-589b71cc1815"
+Use Series-ARCHIVE-SSL-Redirect dutchleague2017.sapsailing.com "07ebdd71-324b-4944-9dc8-790a7de49d36"
+Use Series-ARCHIVE-SSL-Redirect dutchleague2016.sapsailing.com "a975259d-f513-43f6-9e8c-e358c944f175"
+Use Event-ARCHIVE-SSL-Redirect dutchleague2015.sapsailing.com "4f863d14-1025-44ec-853b-7ef5d45fb19d"
+
+## Junioren Liga
+Use Series-ARCHIVE-SSL-Redirect juniorenliga2019.sapsailing.com "5a81ddaf-b839-4ba7-aeaf-57699124b5f6"
+Use Event-ARCHIVE-SSL-Redirect juniorenliga2016.sapsailing.com "71ef7523-b900-4ea5-bb48-b83e1db98a8e"
+
+## Finnish Sailing League
+## Finnish League 2019
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019-final.sapsailing.com "1175aef1-1459-4794-b0ca-402a92785e01"
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019-qualifier2.sapsailing.com "681b850d-64be-43e8-9a2e-8e1384198878"
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019.sapsailing.com "869f49cb-230a-4849-947f-7f8b21d7d5d8"
+
+Use Series-ARCHIVE-SSL-Redirect finnishleague2018.sapsailing.com "268a2f86-6722-4d26-8be1-08c0eb0b9306"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2017.sapsailing.com "2828a793-aa4b-428e-80f5-cdea2f92558e"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2016.sapsailing.com "e5a45e4d-5026-4a38-b585-e51a6deecc11"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2015.sapsailing.com "9d679af1-99f7-492c-88dc-1e2d0e1e3d8d"
+
+## French Sailing League
+Use Series-ARCHIVE-SSL-Redirect frenchleague2019.sapsailing.com "540cba66-192e-4392-8cb5-33cd20a7ce50"
+Use Series-ARCHIVE-SSL-Redirect frenchleague2018.sapsailing.com "aa75fb08-a973-45ec-9d71-f4c7e1a7a04a"
+Use Event-ARCHIVE-SSL-Redirect frenchleague2017.sapsailing.com "d366af4f-246d-4369-945a-f26505f9d77a"
+Use Event-ARCHIVE-SSL-Redirect frenchleague2016.sapsailing.com "6755bbe9-b761-42a2-9f85-a23dea926a3b"
+
+## Norwegian Sailing League
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2019.sapsailing.com "984b1bb8-eff1-484c-8275-44488e9f37ce"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2018.sapsailing.com "b60934bb-01d5-45d8-b05d-8ca0ccf33d82"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2017.sapsailing.com "b74b76b6-9f2e-4e30-abcf-3285af7b7cf0"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2-2017.sapsailing.com "852ecc8d-17b2-4d8d-81ad-20a34da07174"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2016.sapsailing.com "e3134c8b-bd61-457a-a039-3b277af70725"
+Use Event-ARCHIVE-SSL-Redirect norwegianleague2015.sapsailing.com "29c81874-4e3f-4970-9ae3-1c65eca8889b"
+Use Event-ARCHIVE-SSL-Redirect mesternes-mester-2015.sapsailing.com "8a5a7dcd-3c19-464f-a828-d0c3898b38d5"
+
+## Polish League
+Use Series-ARCHIVE-SSL-Redirect polishleague2019.sapsailing.com "006e7569-7c5b-49de-a973-addf288e599e"
+Use Series-ARCHIVE-SSL-Redirect polishleague2018.sapsailing.com "aa60bdfc-6931-4919-9647-6ae61284f1da"
+Use Series-ARCHIVE-SSL-Redirect polishleague2017.sapsailing.com "862eb0bb-4db1-42cd-a857-5a0bcd13c7ad"
+
+## Swedish Sailing League
+Use Series-ARCHIVE-SSL-Redirect swedishleague2019.sapsailing.com "fc141444-5c7b-4e65-af4e-72bf6b60199e"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2018.sapsailing.com "9bc7be83-496f-4e30-a5f5-ebf634a8cdc1"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2017.sapsailing.com "25179060-9139-4c2a-a71f-db11970548f0"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2016.sapsailing.com "83db3f7a-b213-45a2-a772-4fc40ae10742"
+Use Series-ARCHIVE-SSL-Redirect allsvenskan2015.sapsailing.com "dd9fcadd-687b-43d5-8917-ed87e1d38633"
+Use Event-ARCHIVE-SSL-Redirect mastarnas-mastare-2015.sapsailing.com "f27b8b88-a7a3-4694-9374-7e5caebb0328"
+
+###### SAP Sailing Experience ######
+
+## SAP Now 2019 Experience
+Use Event-ARCHIVE-SSL-Redirect sapnow2019.sapsailing.com "30f90ec0-0ed6-4a94-9ade-aee464824d2d"
+
+## SAP Sailing Experience 2019 - NRV
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019-nrv.sapsailing.com "a7c0ec9b-f6bf-400e-98b6-be51ab541abe"
+
+## SAP Sailing Experience 2019 - Travemünder Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019tw.sapsailing.com "2412609f-cb83-4832-9346-ccab59c7c3a8"
+
+## SAP Sailing Experience 2019 - Kieler Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019kw.sapsailing.com "f21d34d8-6c40-4804-872b-0e5fdfb5ce64"
+
+## SAP Sailing Experience 2018 Travemuender Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2018tw.sapsailing.com "03937740-7da2-4644-92f9-9cb12c40fbb4"
+
+## 505 Worlds 2018 - SAP Poland team race (SAP Experience)
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018-SAP-Poland.sapsailing.com "eeeddabb-31c3-425c-abd5-0913781f879e"
+
+## VIP SAP 2017 - DENMARK
+Use Event-ARCHIVE-SSL-Redirect vipsap2017.sapsailing.com "00b7dc6a-0f2a-42d1-8869-2483cde9d5ad"
+
+## TW 2016
+Use Event-ARCHIVE-SSL-Redirect TWguestsailing2016.sapsailing.com "bd4e1319-f431-423a-a922-3215ea0e6fe9"
+
+## VIP Racing Denmark Skovshoved 2016
+Use Event-ARCHIVE-SSL-Redirect vipsailingleaguedenmark2016.sapsailing.com "819c71d3-dd74-44fa-a397-24a38d2d76c5"
+
+# Travemuender Woche 2015
+Use Event-ARCHIVE-SSL-Redirect saptw2015.sapsailing.com "eb58ae5d-0460-4574-a1b8-2e4ef8cf8e15"
+
+## SAP Sailing Experience
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019portocervo.sapsailing.com "3d8dabf1-8900-4b49-8b20-1036207d41e1"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017saintmoritz.sapsailing.com "2828748a-735b-426c-883d-3e0d0c612449"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017tw.sapsailing.com "c89b99bb-f156-45aa-8eaa-6a5835c00037"
+Use Event-ARCHIVE-SSL-Redirect audisailingexperience2016.sapsailing.com "2744dfa0-e833-475e-afe3-a70807fa75f4"
+
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests.sapsailing.com "001a5d8f-9704-4400-b5df-6c78e8037b8b"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests2.sapsailing.com "6c8231c3-cd1e-4535-a021-2fed1fc30471"
+
+Use Spectator-ARCHIVE-SSL-Redirect captainsdinner2013.sapsailing.com "Captain's Dinner 2013"
+Use Spectator-ARCHIVE-SSL-Redirect winners.sapsailing.com "SAP Winners Circle"
+Use Spectator-ARCHIVE-SSL-Redirect winners2013.sapsailing.com "SAP Winners Circle 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505guestsailing2013.sapsailing.com "505 Worlds 2013 Guest Program"
+Use Spectator-ARCHIVE-SSL-Redirect 505worlds2012-guests.sapsailing.com "505 Worlds 2012 Guests"
+Use Spectator-ARCHIVE-SSL-Redirect arenal2012.sapsailing.com "Arenal 2012"
+Use Spectator-ARCHIVE-SSL-Redirect isafwc2014.sapsailing.com "ISAF World Cup Hyeres 2014"
+Use Event-ARCHIVE-SSL-Redirect mdm2011.sapsailing.com "b06d2e5c-0a35-4205-910f-d8387bbdd350"
diff --git a/configuration/httpd/conf.d/001-events.conf.bak b/configuration/httpd/conf.d/001-events.conf.bak
new file mode 100644
index 00000000000..520d6d087cf
--- /dev/null
+++ b/configuration/httpd/conf.d/001-events.conf.bak
@@ -0,0 +1,381 @@
+#
+# Spectator $IP $PORT $HOSTNAME $GROUPNAME
+#
+# Spectator-WithParam $IP $PORT $HOSTNAME $GROUPNAME $EXTRA
+#
+# Leaderboard $IP $PORT $HOSTNAME $GROUPNAME $NAME
+#
+# Leaderboard-WithParam $IP $PORT $HOSTNAME $GROUPNAME $NAME $EXTRA
+#
+# Make sure to always use "" as delimiters for strings containing spaces
+# After a change please perform a service httpd reload
+#
+# ATT: There is a special macro called Spectator-ARCHIVE that contains
+# the fixed IP adress and port for the archive server. You can find it
+# in /etc/httpd/conf.d/000-macros.conf at the end of the file. Feel free
+# to add other macros there.
+
+###################################
+############ DEDICATED ###########
+
+## Finnish Sailing League 2017
+Use Plain-SSL-Redirect finnishleague2017-master.sapsailing.com 172.31.30.80 8888
+
+## Danish Sailing League 2017
+Use Plain-SSL-Redirect danishleague2017-master.sapsailing.com 172.31.23.126 8888
+
+## Swedish Sailing League 2017
+Use Plain-SSL-Redirect swedishleague2017-master.sapsailing.com 172.31.18.186 8888
+
+## German Sailing League 2017
+Use Plain-SSL-Redirect bundesliga1-2017-master.sapsailing.com 172.31.18.63 8888
+Use Plain-SSL-Redirect bundesliga2-2017-master.sapsailing.com 172.31.26.48 8888
+
+## Norwegian Sailing Server NOR
+Use Event-SSL-Redirect nor.sapsailing.com "33324b88-da0c-4c8f-a4e5-aa036da12da3" 172.31.21.60 8888
+
+## Austrian Sailing League 2017
+Use Event-SSL-Redirect austrianleague2017.sapsailing.com "94d23c5e-4cef-47d7-92c7-f8ed77ec486c" 172.31.21.61 8888
+
+## French Sailing League 2017
+Use Event-SSL-Redirect frenchleague2017.sapsailing.com "d366af4f-246d-4369-945a-f26505f9d77a" 172.31.22.171 8888
+
+## Polish Sailing League 2017
+Use Event-SSL-Redirect polishleague2017.sapsailing.com "2899b154-41c7-4bb4-bc23-2bf83e927ea3" 172.31.30.38 8888
+
+## Dutch Sailing League 2017
+Use Plain-SSL-Redirect dutchleague2017-master.sapsailing.com 172.31.24.29 8888
+
+## Norwegian Sailing League 2017
+Use Plain-SSL-Redirect norwegianleague2017-master.sapsailing.com 172.31.27.133 8888
+
+## ESS 2017
+Use Series-SSL-Redirect ess2017.sapsailing.com "d6ffa3c7-946b-4bef-9c22-2faf0fef429a" 172.31.11.96 8888
+
+## Test ESS
+Use Plain-SSL-Redirect test-ess.sapsailing.com 172.31.47.179 8888
+
+## Dutch Federation, first event probably North See Regatta
+Use Plain-SSL-Redirect dutch-federation.sapsailing.com 172.31.23.151 8888
+Use Event-SSL-Redirect nsr2017.sapsailing.com "b519045d-368f-4071-80e0-c6bdd47a3944" 172.31.23.151 8888
+
+###################################
+######### SHARED INSTANCE #########
+
+# 49er Analytics Instance
+Use Plain-SSL-Redirect 49er.sapsailing.com 172.31.30.84 8897
+
+# Abeam (Japanese Partner) training system
+Use Plain-SSL-Redirect abeam-training.sapsailing.com 172.31.30.84 8890
+
+# SAP Extreme Sailing Team Training Server
+Use Home-SSL-Redirect extreme-team.sapsailing.com 172.31.30.84 8892
+
+# Luebecker Yacht-Club (LYC)
+Use Home-SSL-Redirect lyc.sapsailing.com 172.31.30.84 8898
+
+# Bayerischer Yacht-Club (BYC)
+Use Home-SSL-Redirect byc.sapsailing.com 172.31.30.84 8894
+
+# TracTrac Test
+Use Plain-SSL-Redirect tractractest.sapsailing.com 172.31.30.84 8891
+
+# Ubilabs-Testserver
+Use Plain-SSL-Redirect ubilabstest.sapsailing.com 172.31.30.84 8895
+
+# USSailing
+Use Home-SSL-Redirect ussailing.sapsailing.com 172.31.30.84 8889
+
+# VSaW
+Use Home-SSL-Redirect vsaw.sapsailing.com 172.31.30.84 8893
+
+# IRISHDBSCDW3
+Use Home-SSL-Redirect irishdbscdw3.sapsailing.com 172.31.30.84 8896
+
+# Keppel Bay Sailing Club (KBSC)
+Use Home-SSL-Redirect kbsc.sapsailing.com 172.31.30.84 8899
+
+# Seebrucker Regattaverein am Chiemsee (SRV)
+Use Home-SSL-Redirect srv.sapsailing.com 172.31.30.84 8900
+
+# Heinz Nixdorf Verein zur Foerderung des Segelsports e.V. (HNV)
+Use Home-SSL-Redirect hnv.sapsailing.com 172.31.30.84 8901
+
+# Israeli Sailing Association / Carmel Sailing Society
+Use Home-SSL-Redirect isr.sapsailing.com 172.31.30.84 8902
+Use Home-SSL-Redirect cyc.sapsailing.com 172.31.30.84 8902
+
+# Sailtracks Test Server
+Use Home-SSL-Redirect sailtracks.sapsailing.com 172.31.30.84 8903
+
+## Norwegian Sailing Federation
+## Use Home-SSL-Redirect nor.sapsailing.com 172.31.30.84 8904
+
+# Segelvereinigung Rhein-Neckar
+Use Home-SSL-Redirect srn.sapsailing.com 172.31.30.84 8906
+
+# Yacht-Club Luzern (Arnet, Ivo [mailto:ivo.arnet@itready.net])
+Use Home-SSL-Redirect ycl.sapsailing.com 172.31.30.84 8907
+
+# Segelzentrum der Unviversität Kiel (Hamann, Jan [mailto:jan.hamann@email.uni-kiel.de])
+Use Home-SSL-Redirect segelzentrum.sapsailing.com 172.31.30.84 8908
+
+###################################
+############# ARCHIVE #############
+
+## Kieler Woche 2017
+Use Event-ARCHIVE-SSL-Redirect kw2017.sapsailing.com "4d3df446-43bc-46a1-9146-3e1caf6ed757"
+
+## Sailing World Cup Santander 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-santander.sapsailing.com "3b79f093-d52f-440b-9fc4-2fb9e0cc70c9"
+
+## SWC 2017 Hyeres
+Use Event-ARCHIVE-SSL-Redirect swc2017-hyeres.sapsailing.com "8962f211-6e79-422a-85c5-397aaff8d050"
+
+# SWC Worldcup Miami 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-miami.sapsailing.com "b82f9012-47d8-4e9e-b3b2-a690c1db0fe3"
+
+## ESS 2016
+Use Series-ARCHIVE-SSL-Redirect ess2016.sapsailing.com "7f7ea575-261a-4bde-9d32-0d867e02fe20"
+
+## SWC Worldcup Finale Melbourne 2016
+Use Event-ARCHIVE-SSL-Redirect swc2016-melbourne.sapsailing.com "dd2edeed-b9eb-483e-b3e9-d4761d0d58d2"
+
+## Swedish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect swedishleague2016.sapsailing.com "396bccc4-b72e-4edf-a1d2-80f8cfdb259f"
+
+## Danish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect danishleague2016.sapsailing.com "ba493472-a1bd-49cc-ba4d-52eb723f0a71"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2016.sapsailing.com "8140b59e-3202-41b9-90d2-c75e85ef2ace"
+## VIP Racing Denmark Skovshoved 2016
+Use Event-ARCHIVE-SSL-Redirect vipsailingleaguedenmark2016.sapsailing.com "819c71d3-dd74-44fa-a397-24a38d2d76c5"
+
+## Norwegian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2016.sapsailing.com "2cb0eb1d-1a3c-45c6-8d73-caf907086b8a"
+
+## Dutch Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect dutchleague2016.sapsailing.com "6d2fb3c9-de3a-4aa5-b893-1634e50710a0"
+
+## Austrian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect austrianleague2016.sapsailing.com "137fbbc3-7b42-4d3c-a85a-0f5e94f55375"
+
+## Finish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect finnishleague2016.sapsailing.com "86b0afe6-0675-4525-8701-7da02971f4f9"
+
+## SWC Qingdao
+Use Event-ARCHIVE-SSL-Redirect swc2016-qingdao.sapsailing.com "ea0caed8-3182-424e-91a7-6add65e1357d"
+
+## Norwegian Grundig Cup (on norwegian league server)
+Use Event-ARCHIVE-SSL-Redirect grundigcup2016-norway.sapsailing.com "6caf564b-f2b4-4b51-8667-50049ecb8581"
+
+## Sailing Championsleague 2016 (Petersburg / Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2016.sapsailing.com "810a5132-849f-49e0-828c-ace23289f0ac"
+
+## USODA 2016
+Use Event-ARCHIVE-SSL-Redirect usoda2016.sapsailing.com "46c85236-087e-418f-84f9-aee24dac948f"
+
+## BAR Test Oman
+USE Event-ARCHIVE-SSL-Redirect bar-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+USE Event-ARCHIVE-SSL-Redirect ACWS-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+
+## Test new archive in Availability zone A
+USE Event-SSL-Redirect bar-test-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03" 172.31.40.107 8888
+
+## SCGN Test 2016
+USE Event-ARCHIVE-SSL-Redirect scgn-test.sapsailing.com "5ca2b18a-0a55-4591-910c-1f1f35be51f2"
+
+# SCGN Training
+Use Event-ARCHIVE-SSL-Redirect scgn-training.sapsailing.com "004147c6-3991-4a02-bc50-72b8e5b8027c"
+
+## TW 2016
+Use Event-ARCHIVE-SSL-Redirect tw2016.sapsailing.com "86e9e60c-0b16-4941-b14a-166ed4dcabff"
+Use Event-ARCHIVE-SSL-Redirect TWguestsailing2016.sapsailing.com "bd4e1319-f431-423a-a922-3215ea0e6fe9"
+
+## VANPro
+Use Event-ARCHIVE-SSL-Redirect VANpro2016.sapsailing.com "2316ce7d-be93-4ffc-8613-cb6ffa475153"
+
+## Eastern Economic Forum 2016 (on multievent instance)
+Use Event-ARCHIVE-SSL-Redirect forumvostok2016.sapsailing.com "5093220d-875f-4909-9ad0-46476639ee97"
+
+## IDM Star
+Use Event-ARCHIVE-SSL-Redirect idmstar2016.sapsailing.com "5a05f9b9-2f70-46f7-99e1-c069b418bc20"
+
+# 505 Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 505worlds2016.sapsailing.com "8f5daf0f-210f-435f-97bd-c6aff11af3d2"
+
+# 29er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 29erWorlds2016.sapsailing.com "4b381745-1d29-4bda-b388-4a0b7e94dc98"
+
+## WMRT 2016
+Use Event-ARCHIVE-SSL-Redirect wmrt2016.sapsailing.com "cfbe9100-75d6-474a-8d58-3ef8b19cdf25"
+
+## French Sailing League 2016
+Use Event-ARCHIVE-SSL-Redirect frenchleague2016.sapsailing.com "6755bbe9-b761-42a2-9f85-a23dea926a3b"
+
+## RosAtom 2016
+Use Event-ARCHIVE-SSL-Redirect rsc2016.sapsailing.com "3aab9f92-0d83-411c-bfc9-c9032079a749"
+
+## CRW 2016
+Use Event-ARCHIVE-SSL-Redirect crw2016.sapsailing.com "56f62045-de57-4c5e-be6c-a2339d9c9ece"
+
+# Kieler Woche 2016
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2016.sapsailing.com "cc12361a-236b-4ddf-b68d-42ca785cd4d6"
+Use Event-ARCHIVE-SSL-Redirect audiultracup2016.sapsailing.com "c4f9793f-c492-46a0-8af2-a543d5dab588"
+
+## 49erEuros 2016
+Use Event-Archive-SSL-Redirect 49erEuros2016.sapsailing.com "ccb1b062-fe59-4a1c-a15e-f30795ac3c7f"
+
+# 49er / FX Princess Sofia Trophy 2016
+Use Event-ARCHIVE-SSL-Redirect trofeoprincesasofia2016.sapsailing.com "445d072c-fe95-4cc3-ba49-874d4cd43325"
+
+# 49er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2016.sapsailing.com "98e52dd5-2765-4f71-8ebc-82284be07fbc"
+
+# 2.4mR IDM 2015
+Use Event-ARCHIVE-SSL-Redirect idm24mR2015.sapsailing.com "c51c9915-a8f0-4a31-a0e2-127d557b7ea0"
+
+# Surfing World Cup Sylt 2015
+Use Event-ARCHIVE-SSL-Redirect worldcupsylt2015.sapsailing.com "9eb910b8-bb61-4113-8263-1667cd37ac76"
+
+# Norwegian League 2015
+Use Event-ARCHIVE-SSL-Redirect norwegianleague2015.sapsailing.com "29c81874-4e3f-4970-9ae3-1c65eca8889b"
+Use Event-ARCHIVE-SSL-Redirect mesternes-mester-2015.sapsailing.com "8a5a7dcd-3c19-464f-a828-d0c3898b38d5"
+
+# Dutch League 2015
+Use Event-ARCHIVE-SSL-Redirect dutchleague2015.sapsailing.com "4f863d14-1025-44ec-853b-7ef5d45fb19d"
+
+# Championsleague 2015
+Use Event-ARCHIVE-SSL-Redirect championsleague2015.sapsailing.com "f40d9162-9008-4516-9110-33ab0f17d6a0"
+
+# Bart's Bash
+Use Event-ARCHIVE-SSL-Redirect bartsbash2015.sapsailing.com "905457f5-5ccb-4fea-8219-8300eed637ad"
+
+# 49er JWC 2015
+Use Event-ARCHIVE-SSL-Redirect 49erJWC2015.sapsailing.com "9605d85c-867e-438c-a74b-47da896f476e"
+
+# Travemuender Woche 2015
+Use Event-ARCHIVE-SSL-Redirect tw2015.sapsailing.com "82c5adb5-2dac-40fb-aa18-3b8057365e57"
+Use Event-ARCHIVE-SSL-Redirect saptw2015.sapsailing.com "eb58ae5d-0460-4574-a1b8-2e4ef8cf8e15"
+
+# Audi Business Cup
+Use Event-ARCHIVE-SSL-Redirect audibusinesscup2015.sapsailing.com "080b6c09-0110-4b51-a209-dc3cf8b9f669"
+
+# 49er Euros 2015
+Use Event-ARCHIVE-SSL-Redirect 49ereuros2015.sapsailing.com "2b684db8-683d-4479-925b-5962085b7ae1"
+
+# KW 2015
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2015.sapsailing.com "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+Use LiveCenterWithLanguageSubstitution-ARCHIVE livecenter.kieler-woche.de "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+
+# YES 2015
+Use Event-ARCHIVE-SSL-Redirect yes2015.sapsailing.com "fb44d803-15d3-4fe1-852d-28346346ebc3"
+
+# SAP Opti Cup SCGN 2015
+Use Event-ARCHIVE-SSL-Redirect sapopticup2015.sapsailing.com "dff7d969-9858-4dff-b2f2-a45018a2f755"
+
+# RCApp Test Server
+Use Plain rcapptest.sapsailing.com 172.31.31.114 8888
+
+# ESS 2015
+Use Event-ARCHIVE-SSL-Redirect ess40-2015.sapsailing.com "70054aa2-b381-4e77-8e17-840ee1b6190d"
+
+# Danish League 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2015.sapsailing.com "270696d1-d7cc-49c4-a022-bb6bce0f2dcc"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2015.sapsailing.com "5e7d5183-bdb1-425e-b144-a8322ea49c98"
+
+# Swedish League 2015
+Use Series-ARCHIVE-SSL-Redirect allsvenskan2015.sapsailing.com "9627bbdf-1e8e-4a5a-8641-afc2218699b9"
+Use Series-ARCHIVE-SSL-Redirect mastarnas-mastare-2015.sapsailing.com "f27b8b88-a7a3-4694-9374-7e5caebb0328"
+
+# Finnish League 2015
+Use Series-ARCHIVE-SSL-Redirect finnishleague2015.sapsailing.com "51168037-06d1-4907-bbfe-3d7bd0b23304"
+
+# Austrian League 2015
+Use Series-ARCHIVE-SSL-Redirect austrianleague2015.sapsailing.com "85e8bd66-299a-4f5c-929e-746dbd081f13"
+
+# Bodenseewoche 2015
+Use Event-ARCHIVE-SSL-Redirect bodenseewoche2015.sapsailing.com "fef6c510-eefe-470f-a9a9-dcf6293ec116"
+
+# RosAtom Cup 2015
+Use Event-ARCHIVE-SSL-Redirect rosatomcup2015.sapsailing.com "f3a8c574-d23d-44d8-9316-3afb7ee0d39c"
+
+# 505 Worlds 2015
+Use Event-ARCHIVE-SSL-Redirect 505worlds2015.sapsailing.com "b1e78f52-ce52-4b71-ac30-452599faf4a1"
+
+# ATC on Archive:
+Use Event-ARCHIVE-SSL-Redirect atcregatta2015.sapsailing.com "9c1d57bf-29c8-440f-becd-4862bc0f43fe"
+
+# DEV Server
+Use Plain-SSL dev.sapsailing.com 172.31.28.17 8888
+Use Plain-SSL dualstack-dev.sapsailing.com 172.31.28.17 8888
+
+# WWW
+Use Home-ARCHIVE-SSL-Redirect sapsailing.com
+Use Home-ARCHIVE-SSL-Redirect www.sapsailing.com
+
+# DEMO Server on DEV
+Use Plain-SSL-Redirect demo.sapsailing.com 172.31.28.17 8889
+
+# Bundesliga 2014 2015 2016
+Use Series-ARCHIVE-SSL-Redirect bundesliga2014.sapsailing.com "2b2659c9-6c3b-46fb-a83d-9c9f1d2eabca"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2014.sapsailing.com "e455a068-06e5-410a-aea5-d1295f66f61e"
+Use Series-ARCHIVE-SSL-Redirect businesscup2014-hamburg.sapsailing.com "fd2e442b-6e79-4cc7-af23-01eb8eeb061e"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2015-relegation.sapsailing.com "0fb650c5-51c8-44c9-8b4f-cb5d3e2def9a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2015.sapsailing.com "95932bc5-5762-4951-b597-61f0f615734e"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2015.sapsailing.com "ba09f02f-98e7-4a8f-8e90-1f6c72459e82"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2016-relegation.sapsailing.com "3b54a7ce-7585-4935-8564-522988484fb2"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2016.sapsailing.com "880c3122-8c63-4485-84cb-7115c58d35ea"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2016.sapsailing.com "e7cfefaf-8942-4341-a314-368c2a2d0437"
+Use Event-ARCHIVE-SSL-Redirect juniorenliga2016.sapsailing.com "71ef7523-b900-4ea5-bb48-b83e1db98a8e"
+Use Event-ARCHIVE-SSL-Redirect audisailingexperience2016.sapsailing.com "2744dfa0-e833-475e-afe3-a70807fa75f4"
+
+# Danish events 2014: League 2014, Dansish sailing school event, qualification league 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2014.sapsailing.com "abab896f-f49f-46f2-9908-c4d6ede79e1e"
+Use Series-ARCHIVE-SSL-Redirect sejlsportsligaen2015.sapsailing.com "bad1d47c-9bf3-4d31-ac07-f0391cc117da"
+Use Event-ARCHIVE-SSL-Redirect sejlerskolecup-2014.sapsailing.com "d75063dd-f579-4a46-836c-5cc017a71e44"
+
+Use Event-ARCHIVE-SSL-Redirect championsleague2014.sapsailing.com "eb326f1b-81e8-468b-89cb-10131a8d8459"
+Use Event-ARCHIVE-SSL-Redirect ess40-2014.sapsailing.com "8e5bef73-7d55-45ed-951a-72ef375ae1b6"
+Use Event-ARCHIVE-SSL-Redirect bartsbash2014.sapsailing.com "b2e2b264-134c-4ef2-b592-53562fd0f835"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014.sapsailing.com "94e1e240-8a3d-466b-9f39-419f311eaace"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests.sapsailing.com "001a5d8f-9704-4400-b5df-6c78e8037b8b"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests2.sapsailing.com "6c8231c3-cd1e-4535-a021-2fed1fc30471"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014.sapsailing.com "09f39909-5d72-40d2-b6e9-4ea7c908a0dc"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014-proam.sapsailing.com "ca9daa7d-c966-4e89-bc50-ce598e2f2a56"
+Use Event-ARCHIVE-SSL-Redirect tw2014.sapsailing.com "5133df37-0092-42c8-a9cb-7a087ae00219"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2014.sapsailing.com "3713fbd7-a62b-439b-bec1-bea732c5d895"
+Use Event-ARCHIVE-SSL-Redirect yes2014.sapsailing.com "6b3dfa41-8553-4abc-8b02-7e3423a59041"
+Use Event-ARCHIVE-SSL-Redirect olympics2012.sapsailing.com "901963ed-743d-41db-9727-1c3b73e78ddd"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2013.sapsailing.com "a6abca6e-9711-44dc-9c64-929bc869aa12"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2010.sapsailing.com "ba1f5e28-7552-4277-ac32-e440a3432fb7"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2011.sapsailing.com "a10c4fad-c07c-44f5-8241-614a165daae1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2012.sapsailing.com "64e2b631-1a97-4270-9383-14481c8e960f"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2013.sapsailing.com "35dc9389-b59d-4ebd-8f6b-432065642f40"
+Use Event-ARCHIVE-SSL-Redirect drachenidm2012.sapsailing.com "a42beb95-4885-45f1-9ed7-91159a6e4f6a"
+Use Event-ARCHIVE-SSL-Redirect ess40-2012.sapsailing.com "863a5c30-cbea-4f1c-8fb1-54fffa6b7319"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2012.sapsailing.com "22c7942e-eb3d-462a-9d59-600468044bb2"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2013.sapsailing.com "232ce9c3-ac84-47c8-b77d-eec782400877"
+Use Event-ARCHIVE-SSL-Redirect idm2011.sapsailing.com "9577d5ae-b27f-4e76-bf26-580950baf904"
+Use Event-ARCHIVE-SSL-Redirect staridm2013.sapsailing.com "a43b8f96-1e77-448a-9b8a-0f586f1bb18f"
+Use Event-ARCHIVE-SSL-Redirect tw2013.sapsailing.com "b0a721cf-89c3-486c-9a84-25e1aaeb3225"
+Use Event-ARCHIVE-SSL-Redirect 505idm2013.sapsailing.com "9130f36f-d1c3-4080-ac5b-a2104b8fbfd0"
+Use Event-ARCHIVE-SSL-Redirect ess40-2013.sapsailing.com "1487bc65-be3b-4dbc-951d-a1390e9619e0"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2012.sapsailing.com "0c0db838-b199-4309-a2a4-90bebe85ffe8"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2013.sapsailing.com "3e4145a7-5473-4670-83e6-fd5ee364f7eb"
+Use Event-ARCHIVE-SSL-Redirect mdm2011.sapsailing.com "b06d2e5c-0a35-4205-910f-d8387bbdd350"
+Use Event-ARCHIVE-SSL-Redirect obmr2012.sapsailing.com "2dd8de86-6564-4189-9aa0-8666f26dbb5b"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2011.sapsailing.com "8be013de-d49d-49ea-9e73-4e7c7970aec0"
+Use Event-ARCHIVE-SSL-Redirect yes2012.sapsailing.com "7732cfba-23ff-44a7-9b5d-733d1d92e467"
+
+Use Spectator-ARCHIVE-SSL-Redirect captainsdinner2013.sapsailing.com "Captain's Dinner 2013"
+Use Spectator-ARCHIVE-SSL-Redirect winners.sapsailing.com "SAP Winners Circle"
+Use Spectator-ARCHIVE-SSL-Redirect winners2013.sapsailing.com "SAP Winners Circle 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505guestsailing2013.sapsailing.com "505 Worlds 2013 Guest Program"
+Use Spectator-ARCHIVE-SSL-Redirect 505worlds2012-guests.sapsailing.com "505 Worlds 2012 Guests"
+Use Spectator-ARCHIVE-SSL-Redirect arenal2012.sapsailing.com "Arenal 2012"
+Use Spectator-ARCHIVE-SSL-Redirect mini650pc2013.sapsailing.com "Mini 650 Pacific Challenge 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505.sapsailing.com "505"
+Use Spectator-ARCHIVE-SSL-Redirect isafwc2014.sapsailing.com "ISAF World Cup Hyeres 2014"
+
+############# ARCHIVE #############
+###################################
diff --git a/configuration/httpd/conf.d/001-events.conf.bak2 b/configuration/httpd/conf.d/001-events.conf.bak2
new file mode 100644
index 00000000000..0521a4d1275
--- /dev/null
+++ b/configuration/httpd/conf.d/001-events.conf.bak2
@@ -0,0 +1,450 @@
+#
+# Spectator $IP $PORT $HOSTNAME $GROUPNAME
+#
+# Spectator-WithParam $IP $PORT $HOSTNAME $GROUPNAME $EXTRA
+#
+# Leaderboard $IP $PORT $HOSTNAME $GROUPNAME $NAME
+#
+# Leaderboard-WithParam $IP $PORT $HOSTNAME $GROUPNAME $NAME $EXTRA
+#
+# Make sure to always use "" as delimiters for strings containing spaces
+# After a change please perform a service httpd reload
+#
+# ATT: There is a special macro called Spectator-ARCHIVE that contains
+# the fixed IP adress and port for the archive server. You can find it
+# in /etc/httpd/conf.d/000-macros.conf at the end of the file. Feel free
+# to add other macros there.
+
+###################################
+############ DEDICATED ###########
+
+## North Stream Race 2017
+Use Series-SSL-Redirect nordstreamrace2017.sapsailing.com "189e402f-5de5-463e-ad90-a1199c01ffa6" 172.31.21.227 8888
+
+## Norwegian Sailing Server NOR
+Use Event-SSL-Redirect nor.sapsailing.com "13ded7b5-7e81-44df-84e1-566c0cefc8f2" 172.31.17.231 8888
+
+## ESS 2017
+Use Series-SSL-Redirect ess2017.sapsailing.com "d6ffa3c7-946b-4bef-9c22-2faf0fef429a" 172.31.18.190 8888
+
+## Dutch Federation, first event probably North See Regatta
+Use Plain-SSL-Redirect dutch-federation.sapsailing.com 172.31.23.151 8888
+Use Event-SSL-Redirect nsr2017.sapsailing.com "b519045d-368f-4071-80e0-c6bdd47a3944" 172.31.23.151 8888
+
+## Team Phoenix TP52
+Use Plain-SSL-Redirect phoenix.sapsailing.com 172.31.30.173 8888
+
+###################################
+######### SHARED INSTANCE #########
+
+# USSailing
+Use Home-SSL-Redirect ussailing.sapsailing.com 172.31.30.84 8889
+
+# Abeam (Japanese Partner) training system
+Use Plain-SSL-Redirect abeam-training.sapsailing.com 172.31.30.84 8890
+
+# TracTrac Test (now through Application Load Balancer (ALB))
+#Use Plain-SSL-Redirect tractractest.sapsailing.com 172.31.30.84 8891
+
+# SAP Extreme Sailing Team Training Server
+Use Home-SSL-Redirect extreme-team.sapsailing.com 172.31.30.84 8892
+
+# VSaW
+Use Home-SSL-Redirect vsaw.sapsailing.com 172.31.30.84 8893
+
+# Bayerischer Yacht-Club (BYC)
+Use Home-SSL-Redirect byc.sapsailing.com 172.31.30.84 8894
+
+# Ubilabs-Testserver (now through Application Load Balancer (ALB))
+#Use Plain-SSL-Redirect ubilabstest.sapsailing.com 172.31.30.84 8895
+
+# IRISHDBSCDW3
+Use Home-SSL-Redirect irishdbscdw3.sapsailing.com 172.31.30.84 8896
+
+# 49er Analytics Instance
+Use Plain-SSL-Redirect 49er.sapsailing.com 172.31.30.84 8897
+
+# Luebecker Yacht-Club (LYC)
+Use Home-SSL-Redirect lyc.sapsailing.com 172.31.30.84 8898
+
+# Keppel Bay Sailing Club (KBSC)
+Use Home-SSL-Redirect kbsc.sapsailing.com 172.31.30.84 8899
+
+# Seebrucker Regattaverein am Chiemsee (SRV)
+Use Home-SSL-Redirect srv.sapsailing.com 172.31.30.84 8900
+
+# Heinz Nixdorf Verein zur Foerderung des Segelsports e.V. (HNV)
+Use Home-SSL-Redirect hnv.sapsailing.com 172.31.30.84 8901
+
+# Israeli Sailing Association / Carmel Sailing Society
+Use Home-SSL-Redirect isr.sapsailing.com 172.31.30.84 8902
+Use Home-SSL-Redirect cyc.sapsailing.com 172.31.30.84 8902
+
+# Sailtracks Test Server
+Use Home-SSL-Redirect sailtracks.sapsailing.com 172.31.30.84 8903
+
+## Norwegian Sailing Federation
+## Use Home-SSL-Redirect nor.sapsailing.com 172.31.30.84 8904
+
+# Jojos Nacra Campaign (jpolgar@aol.com)
+Use Plain-SSL-Redirect jojopolgar.sapsailing.com 172.31.30.84 8905
+
+# Segelvereinigung Rhein-Neckar
+Use Home-SSL-Redirect srn.sapsailing.com 172.31.30.84 8906
+
+# Yacht-Club Luzern (Arnet, Ivo [mailto:ivo.arnet@itready.net])
+Use Home-SSL-Redirect ycl.sapsailing.com 172.31.30.84 8907
+
+#### Segelzentrum der Unviversität Kiel (Hamann, Jan [mailto:jan.hamann@email.uni-kiel.de])
+Use Home-SSL-Redirect segelzentrum.sapsailing.com 172.31.30.84 8908
+
+# Robert Davis Sailing, Canadian Sailing Team (Robert Davis )
+Use Plain-SSL-Redirect rdsailing.sapsailing.com 172.31.30.84 8909
+
+# Keith Johnson Softalk Computer Services (Keith Johnson )
+Use Plain-SSL-Redirect kjscs.sapsailing.com 172.31.30.84 8910
+
+# KSSS (Royal Swedish Sailing Club, Stefan Rahm )
+Use Plain-SSL-Redirect ksss.sapsailing.com 172.31.30.84 8911
+
+# Sailing Academy South Africa (instructor@sailingacademy.co.za, Neil Ashton)
+Use Plain-SSL-Redirect sailingacademy.sapsailing.com 172.31.30.84 8912
+
+# StarLab (Fabiano Avancini [mailto:fabiano.avancini@gmail.com], https://www.facebook.com/events/845403545622630/)
+Use Plain-SSL-Redirect starlab.sapsailing.com 172.31.30.84 8913
+
+# SingaporeSailing (Lim Han Ee )
+Use Plain-SSL-Redirect singaporesailing.sapsailing.com 172.31.30.84 8914
+
+# Club Nautic de Sitges (pa.ubach@gmail.com; on behalf of; Pere-Andreu Ubach )
+Use Plain-SSL-Redirect sitges.sapsailing.com 172.31.30.84 8915
+
+# Akademischer Seglerverein Aachen (Axel Schmidt, axel.sebastian.schmidt@rwth-aachen.de)
+Use Plain-SSL-Redirect asvia.sapsailing.com 172.31.30.84 8916
+
+# Schwartauer Segler-Verein, www.ssv-net.de, Alexander Probst, webmaster@alexprobst.de
+Use Plain-SSL-Redirect ssv.sapsailing.com 172.31.30.84 8917
+
+# SailCanada (Canadian Sailing Federation, Ken Dool, ken@sailing.ca)
+Use Plain-SSL-Redirect sailcanada.sapsailing.com 172.31.30.84 8918
+
+###################################
+############# ARCHIVE #############
+
+## World Cup Gamagori 2017
+Use Event-ARCHIVE-SSL-Redirect wcs2018-gamagori.sapsailing.com "1e8a9ae8-4a48-4de2-99c5-308169c37b76"
+
+## Dutch Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect dutchleague2017.sapsailing.com "0acd49d7-5fb3-46d2-a174-7ef7a63dbe66"
+
+## 505 Worlds 2017
+Use Event-ARCHIVE-SSL-Redirect 505worlds2017.sapsailing.com "4e070be8-51a8-4633-87fc-533f3f54f7d4"
+
+## Norwegian Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2017.sapsailing.com "b9879cc4-5bc7-4a2e-867c-299eaae28f25"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2-2017.sapsailing.com "80577cb6-11ee-4154-8aed-3917044554b9"
+
+
+## Danish Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect danishleague2017.sapsailing.com "9da19281-a0ce-4871-a23e-3b6ef04a6706"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2017.sapsailing.com "f0003d0e-0db4-4009-9478-d7868f5c432c"
+Use Event-ARCHIVE-SSL-Redirect vipsap2017.sapsailing.com "00b7dc6a-0f2a-42d1-8869-2483cde9d5ad"
+
+## Austrian Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect austrianleague2017.sapsailing.com "94d23c5e-4cef-47d7-92c7-f8ed77ec486c"
+
+## Kiellauf 2017
+Use Event-ARCHIVE-SSL-Redirect kiellauf2017.sapsailing.com "acccaf32-3173-4ee2-afd2-5e09a00cdf53"
+
+## 6mR Worlds 2017 Vancouver
+Use Event-ARCHIVE-SSL-Redirect 6mworlds2017.sapsailing.com "3fbe1757-652c-48d6-a7a5-3a611c82589b"
+
+## French Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect frenchleague2017.sapsailing.com "d366af4f-246d-4369-945a-f26505f9d77a"
+
+## IDM FINN 2017
+Use Event-ARCHIVE-SSL-Redirect idmfinn2017.sapsailing.com "9af4c6fd-a9ff-43b6-9927-323db03bab31"
+
+## Polish Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect polishleague2017.sapsailing.com "dc8306e0-38dd-48e7-9010-21ace668d0b7"
+
+## 8 meter worlds 2017
+Use Event-ARCHIVE-SSL-Redirect 8mworlds2017.sapsailing.com "bd1df974-aa7d-4d56-98ca-de0a50bbe0b6"
+
+##Sailing Champions League 2017
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017saintmoritz.sapsailing.com "2828748a-735b-426c-883d-3e0d0c612449"
+Use Event-ARCHIVE-SSL-Redirect scl2017.sapsailing.com "e6da37a1-773f-4dc5-b7e8-7753d3308386"
+
+##Hansesail 2017 (operated by melanie.coldewey@sap.com)
+Use Event-ARCHIVE-SSL-Redirect hansesail2017.sapsailing.com "0e76df1e-c61f-4845-8681-4b60d4de7cb3"
+
+## 49er, 49FX, Nacra17 Europeans Championships 2017
+Use Event-ARCHIVE-SSL-Redirect 49er-n17-euros2017.sapsailing.com "2288808b-2697-48e9-893a-76ccf00ee94d"
+
+# Sailing World Championships Test Event 2017 Aarhus
+Use Event-ARCHIVE-SSL-Redirect swc2017-aarhus.sapsailing.com "c452f5e8-1bec-4e7b-b459-32f6b820add6"
+
+## Travemuender Woche 2017
+Use Event-ARCHIVE-SSL-Redirect tw2017.sapsailing.com "7784129f-7832-49f1-a5f0-b8d66aa5560c"
+Use Event-ARCHIVE-SSL-Redirect experiencetw2017.sapsailing.com "c89b99bb-f156-45aa-8eaa-6a5835c00037"
+
+## Kieler Woche 2017
+Use Event-ARCHIVE-SSL-Redirect kw2017.sapsailing.com "4d3df446-43bc-46a1-9146-3e1caf6ed757"
+
+## Sailing World Cup Santander 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-santander.sapsailing.com "3b79f093-d52f-440b-9fc4-2fb9e0cc70c9"
+
+## SWC 2017 Hyeres
+Use Event-ARCHIVE-SSL-Redirect swc2017-hyeres.sapsailing.com "8962f211-6e79-422a-85c5-397aaff8d050"
+
+# SWC Worldcup Miami 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-miami.sapsailing.com "b82f9012-47d8-4e9e-b3b2-a690c1db0fe3"
+
+## ESS 2016
+Use Series-ARCHIVE-SSL-Redirect ess2016.sapsailing.com "7f7ea575-261a-4bde-9d32-0d867e02fe20"
+
+## SWC Worldcup Finale Melbourne 2016
+Use Event-ARCHIVE-SSL-Redirect swc2016-melbourne.sapsailing.com "dd2edeed-b9eb-483e-b3e9-d4761d0d58d2"
+
+## Swedish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect swedishleague2016.sapsailing.com "396bccc4-b72e-4edf-a1d2-80f8cfdb259f"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2017.sapsailing.com "8c3467b4-7592-46cc-b70f-45dab79bc2da"
+
+## Danish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect danishleague2016.sapsailing.com "ba493472-a1bd-49cc-ba4d-52eb723f0a71"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2016.sapsailing.com "8140b59e-3202-41b9-90d2-c75e85ef2ace"
+## VIP Racing Denmark Skovshoved 2016
+Use Event-ARCHIVE-SSL-Redirect vipsailingleaguedenmark2016.sapsailing.com "819c71d3-dd74-44fa-a397-24a38d2d76c5"
+
+## Norwegian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2016.sapsailing.com "2cb0eb1d-1a3c-45c6-8d73-caf907086b8a"
+
+## Dutch Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect dutchleague2016.sapsailing.com "6d2fb3c9-de3a-4aa5-b893-1634e50710a0"
+
+## Austrian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect austrianleague2016.sapsailing.com "137fbbc3-7b42-4d3c-a85a-0f5e94f55375"
+
+## Finish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect finnishleague2016.sapsailing.com "86b0afe6-0675-4525-8701-7da02971f4f9"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2017.sapsailing.com "a8cd2cfd-83e8-42a9-804b-ff867dab82ad"
+
+## SWC Qingdao
+Use Event-ARCHIVE-SSL-Redirect swc2016-qingdao.sapsailing.com "ea0caed8-3182-424e-91a7-6add65e1357d"
+
+## Norwegian Grundig Cup (on norwegian league server)
+Use Event-ARCHIVE-SSL-Redirect grundigcup2016-norway.sapsailing.com "6caf564b-f2b4-4b51-8667-50049ecb8581"
+
+## Sailing Championsleague 2016 (Petersburg / Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2016.sapsailing.com "810a5132-849f-49e0-828c-ace23289f0ac"
+
+## USODA 2016
+Use Event-ARCHIVE-SSL-Redirect usoda2016.sapsailing.com "46c85236-087e-418f-84f9-aee24dac948f"
+
+## BAR Test Oman
+USE Event-ARCHIVE-SSL-Redirect bar-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+USE Event-ARCHIVE-SSL-Redirect ACWS-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+
+## Test new archive in Availability zone A
+USE Event-SSL-Redirect bar-test-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03" 172.31.40.107 8888
+
+## SCGN Test 2016
+USE Event-ARCHIVE-SSL-Redirect scgn-test.sapsailing.com "5ca2b18a-0a55-4591-910c-1f1f35be51f2"
+
+# SCGN Training
+Use Event-ARCHIVE-SSL-Redirect scgn-training.sapsailing.com "004147c6-3991-4a02-bc50-72b8e5b8027c"
+
+## TW 2016
+Use Event-ARCHIVE-SSL-Redirect tw2016.sapsailing.com "86e9e60c-0b16-4941-b14a-166ed4dcabff"
+Use Event-ARCHIVE-SSL-Redirect TWguestsailing2016.sapsailing.com "bd4e1319-f431-423a-a922-3215ea0e6fe9"
+
+## VANPro
+Use Event-ARCHIVE-SSL-Redirect VANpro2016.sapsailing.com "2316ce7d-be93-4ffc-8613-cb6ffa475153"
+
+## Eastern Economic Forum 2016
+Use Event-ARCHIVE-SSL-Redirect forumvostok2016.sapsailing.com "5093220d-875f-4909-9ad0-46476639ee97"
+
+## IDM Star
+Use Event-ARCHIVE-SSL-Redirect idmstar2016.sapsailing.com "5a05f9b9-2f70-46f7-99e1-c069b418bc20"
+
+# 505 Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 505worlds2016.sapsailing.com "8f5daf0f-210f-435f-97bd-c6aff11af3d2"
+
+# 29er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 29erWorlds2016.sapsailing.com "4b381745-1d29-4bda-b388-4a0b7e94dc98"
+
+## WMRT 2016
+Use Event-ARCHIVE-SSL-Redirect wmrt2016.sapsailing.com "cfbe9100-75d6-474a-8d58-3ef8b19cdf25"
+
+## French Sailing League 2016
+Use Event-ARCHIVE-SSL-Redirect frenchleague2016.sapsailing.com "6755bbe9-b761-42a2-9f85-a23dea926a3b"
+
+## RosAtom 2016
+Use Event-ARCHIVE-SSL-Redirect rsc2016.sapsailing.com "3aab9f92-0d83-411c-bfc9-c9032079a749"
+
+## CRW 2016
+Use Event-ARCHIVE-SSL-Redirect crw2016.sapsailing.com "56f62045-de57-4c5e-be6c-a2339d9c9ece"
+
+# Kieler Woche 2016
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2016.sapsailing.com "cc12361a-236b-4ddf-b68d-42ca785cd4d6"
+Use Event-ARCHIVE-SSL-Redirect audiultracup2016.sapsailing.com "c4f9793f-c492-46a0-8af2-a543d5dab588"
+
+## 49erEuros 2016
+Use Event-Archive-SSL-Redirect 49erEuros2016.sapsailing.com "ccb1b062-fe59-4a1c-a15e-f30795ac3c7f"
+
+# 49er / FX Princess Sofia Trophy 2016
+Use Event-ARCHIVE-SSL-Redirect trofeoprincesasofia2016.sapsailing.com "445d072c-fe95-4cc3-ba49-874d4cd43325"
+
+# 49er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2016.sapsailing.com "98e52dd5-2765-4f71-8ebc-82284be07fbc"
+
+# 2.4mR IDM 2015
+Use Event-ARCHIVE-SSL-Redirect idm24mR2015.sapsailing.com "c51c9915-a8f0-4a31-a0e2-127d557b7ea0"
+
+# Surfing World Cup Sylt 2015
+Use Event-ARCHIVE-SSL-Redirect worldcupsylt2015.sapsailing.com "9eb910b8-bb61-4113-8263-1667cd37ac76"
+
+# Norwegian League 2015
+Use Event-ARCHIVE-SSL-Redirect norwegianleague2015.sapsailing.com "29c81874-4e3f-4970-9ae3-1c65eca8889b"
+Use Event-ARCHIVE-SSL-Redirect mesternes-mester-2015.sapsailing.com "8a5a7dcd-3c19-464f-a828-d0c3898b38d5"
+
+# Dutch League 2015
+Use Event-ARCHIVE-SSL-Redirect dutchleague2015.sapsailing.com "4f863d14-1025-44ec-853b-7ef5d45fb19d"
+
+# Championsleague 2015
+Use Event-ARCHIVE-SSL-Redirect championsleague2015.sapsailing.com "f40d9162-9008-4516-9110-33ab0f17d6a0"
+
+# Bart's Bash
+Use Event-ARCHIVE-SSL-Redirect bartsbash2015.sapsailing.com "905457f5-5ccb-4fea-8219-8300eed637ad"
+
+# 49er JWC 2015
+Use Event-ARCHIVE-SSL-Redirect 49erJWC2015.sapsailing.com "9605d85c-867e-438c-a74b-47da896f476e"
+
+# Travemuender Woche 2015
+Use Event-ARCHIVE-SSL-Redirect tw2015.sapsailing.com "82c5adb5-2dac-40fb-aa18-3b8057365e57"
+Use Event-ARCHIVE-SSL-Redirect saptw2015.sapsailing.com "eb58ae5d-0460-4574-a1b8-2e4ef8cf8e15"
+
+# Audi Business Cup
+Use Event-ARCHIVE-SSL-Redirect audibusinesscup2015.sapsailing.com "080b6c09-0110-4b51-a209-dc3cf8b9f669"
+
+# 49er Euros 2015
+Use Event-ARCHIVE-SSL-Redirect 49ereuros2015.sapsailing.com "2b684db8-683d-4479-925b-5962085b7ae1"
+
+# KW 2015
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2015.sapsailing.com "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+Use LiveCenterWithLanguageSubstitution-ARCHIVE livecenter.kieler-woche.de "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+
+# YES 2015
+Use Event-ARCHIVE-SSL-Redirect yes2015.sapsailing.com "fb44d803-15d3-4fe1-852d-28346346ebc3"
+
+# SAP Opti Cup SCGN 2015
+Use Event-ARCHIVE-SSL-Redirect sapopticup2015.sapsailing.com "dff7d969-9858-4dff-b2f2-a45018a2f755"
+
+# RCApp Test Server
+Use Plain rcapptest.sapsailing.com 172.31.31.114 8888
+
+# ESS 2015
+Use Event-ARCHIVE-SSL-Redirect ess40-2015.sapsailing.com "70054aa2-b381-4e77-8e17-840ee1b6190d"
+
+# Danish League 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2015.sapsailing.com "270696d1-d7cc-49c4-a022-bb6bce0f2dcc"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2015.sapsailing.com "5e7d5183-bdb1-425e-b144-a8322ea49c98"
+
+# Swedish League 2015
+Use Series-ARCHIVE-SSL-Redirect allsvenskan2015.sapsailing.com "9627bbdf-1e8e-4a5a-8641-afc2218699b9"
+Use Series-ARCHIVE-SSL-Redirect mastarnas-mastare-2015.sapsailing.com "f27b8b88-a7a3-4694-9374-7e5caebb0328"
+
+# Finnish League 2015
+Use Series-ARCHIVE-SSL-Redirect finnishleague2015.sapsailing.com "51168037-06d1-4907-bbfe-3d7bd0b23304"
+
+# Austrian League 2015
+Use Series-ARCHIVE-SSL-Redirect austrianleague2015.sapsailing.com "85e8bd66-299a-4f5c-929e-746dbd081f13"
+
+# Bodenseewoche 2015
+Use Event-ARCHIVE-SSL-Redirect bodenseewoche2015.sapsailing.com "fef6c510-eefe-470f-a9a9-dcf6293ec116"
+
+# RosAtom Cup 2015
+Use Event-ARCHIVE-SSL-Redirect rosatomcup2015.sapsailing.com "f3a8c574-d23d-44d8-9316-3afb7ee0d39c"
+
+# 505 Worlds 2015
+Use Event-ARCHIVE-SSL-Redirect 505worlds2015.sapsailing.com "b1e78f52-ce52-4b71-ac30-452599faf4a1"
+
+# ATC on Archive:
+Use Event-ARCHIVE-SSL-Redirect atcregatta2015.sapsailing.com "9c1d57bf-29c8-440f-becd-4862bc0f43fe"
+
+# DEV Server
+Use Plain-SSL dev.sapsailing.com 172.31.28.17 8888
+Use Plain-SSL dualstack-dev.sapsailing.com 172.31.28.17 8888
+
+# WWW is now defined in 000-main.conf so it can be the default virtual host; please see there
+
+# DEMO Server on DEV
+Use Plain-SSL-Redirect demo.sapsailing.com 172.31.28.17 8889
+
+# Bundesliga 2014 2015 2016 2017
+Use Series-ARCHIVE-SSL-Redirect bundesliga2014.sapsailing.com "2b2659c9-6c3b-46fb-a83d-9c9f1d2eabca"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2014.sapsailing.com "e455a068-06e5-410a-aea5-d1295f66f61e"
+Use Series-ARCHIVE-SSL-Redirect businesscup2014-hamburg.sapsailing.com "fd2e442b-6e79-4cc7-af23-01eb8eeb061e"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2015-relegation.sapsailing.com "0fb650c5-51c8-44c9-8b4f-cb5d3e2def9a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2015.sapsailing.com "95932bc5-5762-4951-b597-61f0f615734e"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2015.sapsailing.com "ba09f02f-98e7-4a8f-8e90-1f6c72459e82"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2016-relegation.sapsailing.com "3b54a7ce-7585-4935-8564-522988484fb2"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2016.sapsailing.com "880c3122-8c63-4485-84cb-7115c58d35ea"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2016.sapsailing.com "e7cfefaf-8942-4341-a314-368c2a2d0437"
+Use Event-ARCHIVE-SSL-Redirect juniorenliga2016.sapsailing.com "71ef7523-b900-4ea5-bb48-b83e1db98a8e"
+Use Event-ARCHIVE-SSL-Redirect audisailingexperience2016.sapsailing.com "2744dfa0-e833-475e-afe3-a70807fa75f4"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2017.sapsailing.com "7268b8a4-7c86-431c-935d-efa9e6d16e8a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2017.sapsailing.com "25d32dea-7bc6-4b7d-ac83-a3522cc8574b"
+## DSL Pokal 2017
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2017.sapsailing.com "f1860c95-0518-4044-ada2-3594a351dd13"
+
+
+# Danish events 2014: League 2014, Dansish sailing school event, qualification league 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2014.sapsailing.com "abab896f-f49f-46f2-9908-c4d6ede79e1e"
+Use Series-ARCHIVE-SSL-Redirect sejlsportsligaen2015.sapsailing.com "bad1d47c-9bf3-4d31-ac07-f0391cc117da"
+Use Event-ARCHIVE-SSL-Redirect sejlerskolecup-2014.sapsailing.com "d75063dd-f579-4a46-836c-5cc017a71e44"
+
+Use Event-ARCHIVE-SSL-Redirect championsleague2014.sapsailing.com "eb326f1b-81e8-468b-89cb-10131a8d8459"
+Use Event-ARCHIVE-SSL-Redirect ess40-2014.sapsailing.com "8e5bef73-7d55-45ed-951a-72ef375ae1b6"
+Use Event-ARCHIVE-SSL-Redirect bartsbash2014.sapsailing.com "b2e2b264-134c-4ef2-b592-53562fd0f835"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014.sapsailing.com "94e1e240-8a3d-466b-9f39-419f311eaace"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests.sapsailing.com "001a5d8f-9704-4400-b5df-6c78e8037b8b"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests2.sapsailing.com "6c8231c3-cd1e-4535-a021-2fed1fc30471"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014.sapsailing.com "09f39909-5d72-40d2-b6e9-4ea7c908a0dc"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014-proam.sapsailing.com "ca9daa7d-c966-4e89-bc50-ce598e2f2a56"
+Use Event-ARCHIVE-SSL-Redirect tw2014.sapsailing.com "5133df37-0092-42c8-a9cb-7a087ae00219"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2014.sapsailing.com "3713fbd7-a62b-439b-bec1-bea732c5d895"
+Use Event-ARCHIVE-SSL-Redirect yes2014.sapsailing.com "6b3dfa41-8553-4abc-8b02-7e3423a59041"
+Use Event-ARCHIVE-SSL-Redirect olympics2012.sapsailing.com "901963ed-743d-41db-9727-1c3b73e78ddd"
+Use Event-ARCHIVE-SSL-Redirect olympics2016.sapsailing.com "e6dbae49-aa2f-49fc-a7eb-82e24cdc307f"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2013.sapsailing.com "a6abca6e-9711-44dc-9c64-929bc869aa12"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2010.sapsailing.com "ba1f5e28-7552-4277-ac32-e440a3432fb7"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2011.sapsailing.com "a10c4fad-c07c-44f5-8241-614a165daae1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2012.sapsailing.com "64e2b631-1a97-4270-9383-14481c8e960f"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2013.sapsailing.com "35dc9389-b59d-4ebd-8f6b-432065642f40"
+Use Event-ARCHIVE-SSL-Redirect drachenidm2012.sapsailing.com "a42beb95-4885-45f1-9ed7-91159a6e4f6a"
+Use Event-ARCHIVE-SSL-Redirect ess40-2012.sapsailing.com "863a5c30-cbea-4f1c-8fb1-54fffa6b7319"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2012.sapsailing.com "22c7942e-eb3d-462a-9d59-600468044bb2"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2013.sapsailing.com "232ce9c3-ac84-47c8-b77d-eec782400877"
+Use Event-ARCHIVE-SSL-Redirect idm2011.sapsailing.com "9577d5ae-b27f-4e76-bf26-580950baf904"
+Use Event-ARCHIVE-SSL-Redirect staridm2013.sapsailing.com "a43b8f96-1e77-448a-9b8a-0f586f1bb18f"
+Use Event-ARCHIVE-SSL-Redirect tw2013.sapsailing.com "b0a721cf-89c3-486c-9a84-25e1aaeb3225"
+Use Event-ARCHIVE-SSL-Redirect 505idm2013.sapsailing.com "9130f36f-d1c3-4080-ac5b-a2104b8fbfd0"
+Use Event-ARCHIVE-SSL-Redirect ess40-2013.sapsailing.com "1487bc65-be3b-4dbc-951d-a1390e9619e0"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2012.sapsailing.com "0c0db838-b199-4309-a2a4-90bebe85ffe8"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2013.sapsailing.com "3e4145a7-5473-4670-83e6-fd5ee364f7eb"
+Use Event-ARCHIVE-SSL-Redirect mdm2011.sapsailing.com "b06d2e5c-0a35-4205-910f-d8387bbdd350"
+Use Event-ARCHIVE-SSL-Redirect obmr2012.sapsailing.com "2dd8de86-6564-4189-9aa0-8666f26dbb5b"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2011.sapsailing.com "8be013de-d49d-49ea-9e73-4e7c7970aec0"
+Use Event-ARCHIVE-SSL-Redirect yes2012.sapsailing.com "7732cfba-23ff-44a7-9b5d-733d1d92e467"
+
+Use Spectator-ARCHIVE-SSL-Redirect captainsdinner2013.sapsailing.com "Captain's Dinner 2013"
+Use Spectator-ARCHIVE-SSL-Redirect winners.sapsailing.com "SAP Winners Circle"
+Use Spectator-ARCHIVE-SSL-Redirect winners2013.sapsailing.com "SAP Winners Circle 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505guestsailing2013.sapsailing.com "505 Worlds 2013 Guest Program"
+Use Spectator-ARCHIVE-SSL-Redirect 505worlds2012-guests.sapsailing.com "505 Worlds 2012 Guests"
+Use Spectator-ARCHIVE-SSL-Redirect arenal2012.sapsailing.com "Arenal 2012"
+Use Spectator-ARCHIVE-SSL-Redirect mini650pc2013.sapsailing.com "Mini 650 Pacific Challenge 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505.sapsailing.com "505"
+Use Spectator-ARCHIVE-SSL-Redirect isafwc2014.sapsailing.com "ISAF World Cup Hyeres 2014"
+
diff --git a/configuration/httpd/conf.d/001-events.conf.bak3 b/configuration/httpd/conf.d/001-events.conf.bak3
new file mode 100644
index 00000000000..a77049da827
--- /dev/null
+++ b/configuration/httpd/conf.d/001-events.conf.bak3
@@ -0,0 +1,1059 @@
+#
+# Spectator $IP $PORT $HOSTNAME $GROUPNAME
+#
+# Spectator-WithParam $IP $PORT $HOSTNAME $GROUPNAME $EXTRA
+#
+# Leaderboard $IP $PORT $HOSTNAME $GROUPNAME $NAME
+#
+# Leaderboard-WithParam $IP $PORT $HOSTNAME $GROUPNAME $NAME $EXTRA
+#
+# Make sure to always use "" as delimiters for strings containing spaces
+# After a change please perform a service httpd reload
+#
+# ATT: There is a special macro called Spectator-ARCHIVE that contains
+# the fixed IP adress and port for the archive server. You can find it
+# in /etc/httpd/conf.d/000-macros.conf at the beginning of the file. Feel free
+# to add other macros there.
+
+###################################
+############ DEDICATED ###########
+
+## Norwegian Sailing Server NOR
+Use Event-SSL-Redirect nor.sapsailing.com "13ded7b5-7e81-44df-84e1-566c0cefc8f2" 172.31.17.231 8888
+
+## Team Phoenix TP52
+Use Plain-SSL-Redirect phoenix.sapsailing.com 172.31.30.173 8888
+
+###################################
+######### SHARED INSTANCE #########
+
+## Dutch Federation, first event probably North See Regatta
+Use Event-SSL-Redirect nsr2017.sapsailing.com "b519045d-368f-4071-80e0-c6bdd47a3944" 172.31.30.84 8926
+
+# USSailing
+Use Home-SSL-Redirect ussailing.sapsailing.com 172.31.30.84 8889
+
+# Abeam (Japanese Partner) training system
+Use Plain-SSL-Redirect abeam-training.sapsailing.com 172.31.30.84 8890
+
+# TracTrac Test (now through Application Load Balancer (ALB))
+Use Plain-SSL-Redirect tractractest.sapsailing.com 172.31.30.84 8891
+
+# SAP Extreme Sailing Team Training Server
+Use Home-SSL-Redirect extreme-team.sapsailing.com 172.31.30.84 8892
+
+# VSaW
+Use Home-SSL-Redirect vsaw.sapsailing.com 172.31.30.84 8893
+
+# Bayerischer Yacht-Club (BYC)
+Use Home-SSL-Redirect byc.sapsailing.com 172.31.30.84 8894
+
+# Ubilabs-Testserver (now through Application Load Balancer (ALB))
+#Use Plain-SSL-Redirect ubilabstest.sapsailing.com 172.31.30.84 8895
+
+# IRISHDBSCDW3
+Use Home-SSL-Redirect irishdbscdw3.sapsailing.com 172.31.30.84 8896
+
+# 49er Analytics Instance
+Use Plain-SSL-Redirect 49er.sapsailing.com 172.31.30.84 8897
+
+# Luebecker Yacht-Club (LYC)
+Use Home-SSL-Redirect lyc.sapsailing.com 172.31.30.84 8898
+
+# Keppel Bay Sailing Club (KBSC)
+Use Home-SSL-Redirect kbsc.sapsailing.com 172.31.30.84 8899
+
+# Seebrucker Regattaverein am Chiemsee (SRV)
+Use Home-SSL-Redirect srv.sapsailing.com 172.31.30.84 8900
+
+# Heinz Nixdorf Verein zur Foerderung des Segelsports e.V. (HNV)
+Use Home-SSL-Redirect hnv.sapsailing.com 172.31.30.84 8901
+
+# Israeli Sailing Association / Carmel Sailing Society
+Use Home-SSL-Redirect isr.sapsailing.com 172.31.30.84 8902
+Use Home-SSL-Redirect cyc.sapsailing.com 172.31.30.84 8902
+
+# Sailtracks Test Server
+Use Home-SSL-Redirect sailtracks.sapsailing.com 172.31.30.84 8903
+
+## Norwegian Sailing Federation
+Use Home-SSL-Redirect nor.sapsailing.com 172.31.26.158 8904
+
+# Jojos Nacra Campaign (jpolgar@aol.com)
+Use Plain-SSL-Redirect jojopolgar.sapsailing.com 172.31.30.84 8905
+
+# Segelvereinigung Rhein-Neckar
+Use Home-SSL-Redirect srn.sapsailing.com 172.31.30.84 8906
+
+# Yacht-Club Luzern (Arnet, Ivo [mailto:ivo.arnet@itready.net])
+Use Home-SSL-Redirect ycl.sapsailing.com 172.31.30.84 8907
+
+#### Segelzentrum der Unviversität Kiel (Hamann, Jan [mailto:jan.hamann@email.uni-kiel.de])
+Use Home-SSL-Redirect segelzentrum.sapsailing.com 172.31.30.84 8908
+
+# Robert Davis Sailing, Canadian Sailing Team (Robert Davis )
+Use Plain-SSL-Redirect rdsailing.sapsailing.com 172.31.30.84 8909
+
+# Keith Johnson Softalk Computer Services (Keith Johnson )
+Use Plain-SSL-Redirect kjscs.sapsailing.com 172.31.30.84 8910
+
+# KSSS (Royal Swedish Sailing Club, Stefan Rahm )
+Use Plain-SSL-Redirect ksss.sapsailing.com 172.31.30.84 8911
+
+# Sailing Academy South Africa (instructor@sailingacademy.co.za, Neil Ashton)
+Use Plain-SSL-Redirect sailingacademy.sapsailing.com 172.31.30.84 8912
+
+# StarLab (Fabiano Avancini [mailto:fabiano.avancini@gmail.com], https://www.facebook.com/events/845403545622630/)
+Use Plain-SSL-Redirect starlab.sapsailing.com 172.31.30.84 8913
+
+# SingaporeSailing (Lim Han Ee )
+Use Plain-SSL-Redirect singaporesailing.sapsailing.com 172.31.30.84 8914
+
+# Club Nautic de Sitges (pa.ubach@gmail.com; on behalf of; Pere-Andreu Ubach )
+Use Plain-SSL-Redirect sitges.sapsailing.com 172.31.30.84 8915
+
+# Akademischer Seglerverein Aachen (Axel Schmidt, axel.sebastian.schmidt@rwth-aachen.de)
+Use Plain-SSL-Redirect asvia.sapsailing.com 172.31.30.84 8916
+
+# Schwartauer Segler-Verein, www.ssv-net.de, Alexander Probst, webmaster@alexprobst.de
+Use Plain-SSL-Redirect ssv.sapsailing.com 172.31.30.84 8917
+
+# SailCanada (Canadian Sailing Federation, Ken Dool, ken@sailing.ca)
+Use Plain-SSL-Redirect sailcanada.sapsailing.com 172.31.30.84 8918
+
+###################################
+############# ARCHIVE #############
+
+## 49er Worlds 2019
+Use Series-ARCHIVE-SSL-Redirect 49erworlds2019.sapsailing.com "e5a65f0a-b9b4-4630-b0b0-ad1fd7727518"
+
+## 49er Oceania 2019
+Use Series-ARCHIVE-SSL-Redirect 49eroceania2019.sapsailing.com "b4a6e4ef-9a07-4832-9268-05cce3941b46"
+
+## Norwegian Sailing League 2019
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2019.sapsailing.com "984b1bb8-eff1-484c-8275-44488e9f37ce"
+
+## French Sailing League 2019
+Use Series-ARCHIVE-SSL-Redirect frenchleague2019.sapsailing.com "540cba66-192e-4392-8cb5-33cd20a7ce50"
+
+## Swedish Sailing League 2019
+Use Series-ARCHIVE-SSL-Redirect swedishleague2019.sapsailing.com "fc141444-5c7b-4e65-af4e-72bf6b60199e"
+
+## Danish Sailing League 2019
+Use Series-ARCHIVE-SSL-Redirect danishleague2019.sapsailing.com "1894888b-a5cc-4d3d-9335-7d4374b72e15"
+
+## Österreichische Segelbundesliga 2019
+Use Series-ARCHIVE-SSL-Redirect austrianleague2019.sapsailing.com "882b8061-d7bc-4d76-a3c5-daa57296b0f8"
+
+## 1. Segel-Bundesliga 2019
+Use Series-ARCHIVE-SSL-Redirect bundesliga2019.sapsailing.com "e8c27965-7636-4bb6-883b-98a29df31f50"
+
+## 2. Segel-Bundesliga 2019
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2019.sapsailing.com "8c59828d-cad7-4e84-b86b-433d844010cc"
+
+## Deutsche Junioren Segel-Liga 2019
+Use Series-ARCHIVE-SSL-Redirect juniorenliga2019.sapsailing.com "5a81ddaf-b839-4ba7-aeaf-57699124b5f6"
+
+## Finnish League 2019 - qualifier 2
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019-qualifier2.sapsailing.com "681b850d-64be-43e8-9a2e-8e1384198878"
+
+## Finnish League 2019 - final
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019-final.sapsailing.com "1175aef1-1459-4794-b0ca-402a92785e01"
+
+## Alandia Nordic Club Sailing Championship 2019
+Use Event-ARCHIVE-SSL-Redirect ncs2019-championships.sapsailing.com "ead70d8e-0fca-4aa0-bf83-e66c19de8ab3"
+
+## Polish League 2019
+Use Series-ARCHIVE-SSL-Redirect polishleague2019.sapsailing.com "006e7569-7c5b-49de-a973-addf288e599e"
+
+## World Sailing Conference Cup 2019
+Use Event-ARCHIVE-SSL-Redirect bermuda2019.sapsailing.com "a865eda3-b8df-4cf8-bcf1-6bd6a1ca45bb"
+
+## Pitter Croatia Coast Cup 2019
+Use Event-ARCHIVE-SSL-Redirect croatiacoastcup2019.sapsailing.com "8b798fbb-65b0-49d6-b126-cfd02b1c1fd3"
+
+## Tempest Worlds 2019
+Use Event-ARCHIVE-SSL-Redirect tempestworlds2019.sapsailing.com "72038d9e-5add-40f8-beea-5116d568facb"
+
+## SAP Now 2019 Experience
+Use Event-ARCHIVE-SSL-Redirect sapnow2019.sapsailing.com "30f90ec0-0ed6-4a94-9ade-aee464824d2d"
+
+## Olympic Testevent 2019 - Enoshima
+Use Event-ARCHIVE-SSL-Redirect tokyo2019.sapsailing.com "6389fb9d-12e1-47ef-9831-ddab8cf9598f"
+
+## Sailing Champions League 2019 - Final St. Moritz
+Use Event-ARCHIVE-SSL-Redirect scl2019-final.sapsailing.com "e59f2681-94ca-4a9d-b815-c4995b17d26e"
+
+## Hempel World Cup Series 2020 - Round 1
+Use Event-ARCHIVE-SSL-Redirect hwcs2020-round1.sapsailing.com "2d42185c-c914-4042-814f-14108218b3a3"
+
+## Nord Stream Race 2019
+Use Event-ARCHIVE-SSL-Redirect nordstreamrace2019.sapsailing.com "1fc9069a-0ee8-4ca9-ba62-4ab6c410c909"
+
+## Sailing Champions League 2019 Qualifier 3 St. Petersburg
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier3.sapsailing.com "ccd72544-b7c1-4775-949a-ad5ac26b562f"
+
+## Helga Cup 2019
+Use Event-ARCHIVE-SSL-Redirect helgacup2019.sapsailing.com "94f7932a-4518-4c58-9f77-d43682b002f6"
+
+## Kieler Woche 2019
+Use Event-ARCHIVE-SSL-Redirect kw2019.sapsailing.com "ecf57f2a-9d4b-4918-aef3-07543ed0c4ec"
+
+## SAP Sailing Experience 2019 - NRV
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019-nrv.sapsailing.com "a7c0ec9b-f6bf-400e-98b6-be51ab541abe"
+
+## SAP Sailing Experience 2019 - Travemünder Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019tw.sapsailing.com "2412609f-cb83-4832-9346-ccab59c7c3a8"
+
+## SAP Sailing Experience 2019 - Kieler Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019kw.sapsailing.com "f21d34d8-6c40-4804-872b-0e5fdfb5ce64"
+
+## Medemblik Regatta 2019
+Use Event-ARCHIVE-SSL-Redirect medemblik2019.sapsailing.com "c7e7589c-b5ef-4e7d-93ff-3cd18527976b"
+
+## Sailing Champions League 2019 - Qualifier 1 Palma
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier1.sapsailing.com "5b3d782a-d6eb-420f-852e-44e6d36b8869"
+
+## Sailing Champions League 2019 - Qualifier 2 Porto Cervo
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier2.sapsailing.com "dd4eb3ce-dce0-4e8f-bfdb-ff1219f6fde1"
+
+## Kornati Cup 2019
+Use Event-ARCHIVE-SSL-Redirect kornaticup2019.sapsailing.com "b3735a2e-bdd6-4cf7-91a7-87d388c954b4"
+
+## 505 Worlds 2019 - Fremantle
+Use Event-ARCHIVE-SSL-Redirect 505worlds2019.sapsailing.com "fbfa43cc-ff0d-42b9-96c5-6d218c636a62"
+
+## Polska Ekstraklasa Żeglarska 2018
+Use Series-ARCHIVE-SSL-Redirect polishleague2018.sapsailing.com "aa60bdfc-6931-4919-9647-6ae61284f1da"
+
+## Allsvenskan Segling 2018
+Use Series-ARCHIVE-SSL-Redirect swedishleague2018.sapsailing.com "9bc7be83-496f-4e30-a5f5-ebf634a8cdc1"
+
+## Red Bull Foiling Generation 2018 Final
+Use Event-ARCHIVE-SSL-Redirect rbfg2018.sapsailing.com "1e330c0e-9dbf-4fa0-9817-1e11e17bea55"
+
+## Finnish Sailing League 2018
+Use Event-ARCHIVE-SSL-Redirect finnishleague2019.sapsailing.com "869f49cb-230a-4849-947f-7f8b21d7d5d8"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2018.sapsailing.com "268a2f86-6722-4d26-8be1-08c0eb0b9306"
+
+## Dutch Sailing League 2018
+use Series-ARCHIVE-SSL-Redirect dutchleague2018.sapsailing.com "cdfc4a3f-ef43-42fb-91fd-589b71cc1815"
+
+## Österreichische Segelbundesliga 2018
+use Series-ARCHIVE-SSL-Redirect austrianleague2018.sapsailing.com "d5b4fba4-903e-4e0f-86e7-93162c527640"
+
+## Danish Sailing League 2018
+Use Series-ARCHIVE-SSL-Redirect danishleague2018.sapsailing.com "c3dc2b53-b8f3-4657-93a5-e3c0bab7af80"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2018.sapsailing.com "f39e70a1-d31d-4f1d-87f5-c7832557f8d8"
+
+## Ligue Nationale De Voile 2018
+Use Series-ARCHIVE-SSL-Redirect frenchleague2018.sapsailing.com "aa75fb08-a973-45ec-9d71-f4c7e1a7a04a"
+
+## SAILING Champions League 2018 - Final
+use Event-ARCHIVE-SSL-Redirect scl2018-final.sapsailing.com "0503a3d7-2b50-4825-84b4-7bcfd6cd4730"
+
+## SAILING Champions League 2018 - Semifinal 2
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal2.sapsailing.com "74f7e1c6-9d19-4751-9083-60f00c9b8053"
+
+## Hempel Sailing World Championships Aarhus 2018
+Use Event-ARCHIVE-SSL-Redirect worlds2018.sapsailing.com "4544f986-8235-426d-ad3e-6a3500b12edc"
+
+## Travemünder Woche 2018
+Use Event-ARCHIVE-SSL-Redirect tw2018.sapsailing.com "4e71526d-a25f-4f41-adc0-d92c60738134"
+
+## SAP Sailing Experience 2018 Travemuender Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2018tw.sapsailing.com "03937740-7da2-4644-92f9-9cb12c40fbb4"
+
+## 505 Worlds 2018 - SAP Poland team race
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018-SAP-Poland.sapsailing.com "eeeddabb-31c3-425c-abd5-0913781f879e"
+
+## Nord Stream Race 2018
+Use Event-ARCHIVE-SSL-Redirect nordstreamrace2018.sapsailing.com "9791acc7-79f7-4c3b-9cde-7b5dede49cc2"
+
+## Kieler Woche 2018
+Use Event-ARCHIVE-SSL-Redirect kw2018.sapsailing.com "1a73aaef-7386-4178-8a11-14ced71e1d8c"
+
+## Sailing Championsleague 2018 (Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal1.sapsailing.com "ccd8a484-f2be-4dbd-9474-c457fefd6536"
+
+## World Cup Series 2019 - Marseille
+Use Event-ARCHIVE-SSL-Redirect wcs2019-marseille.sapsailing.com "ba606057-ced3-444d-9425-fd89ae36ca18"
+
+## World Cup Series 2019 - Genoa
+Use Event-ARCHIVE-SSL-Redirect wcs2019-genoa.sapsailing.com "587e5fef-53ea-47f0-a71b-1fc29053b4f0"
+
+## World Cup Series 2019 - Miami
+Use Event-ARCHIVE-SSL-Redirect wcs2019-miami.sapsailing.com "f393a743-dd49-450f-ab8b-d11522b266f2"
+
+## World Cup Series 2019 - Enoshima
+Use Event-ARCHIVE-SSL-Redirect wcs2019-enoshima.sapsailing.com "bb4c7908-56a4-4b6e-9942-4ffd20c59fdc"
+
+## World Cup final 2018 Marseille
+Use Event-ARCHIVE-SSL-Redirect wcs2018-marseille.sapsailing.com "d5c7440a-27fb-4b9f-a64d-5beb1a2907fd"
+
+## Medemblik 2018
+Use Event-ARCHIVE-SSL-Redirect medemblik2018.sapsailing.com "36b3a1ff-34ef-48ab-86e3-aebbbcac55c4"
+
+## Aeroe Rund 2018
+Use Event-ARCHIVE-SSL-Redirect aeroe-rund2018.sapsailing.com "7a16b434-6952-457b-9b7c-010875f6bfd3"
+
+## Thetis 2018
+Use Event-ARCHIVE-SSL-Redirect thetis2018.sapsailing.com "8964d8e0-0368-4581-8df8-6ade3543a94a"
+
+## World Cup Hyeres 2018
+Use Event-ARCHIVE-SSL-Redirect wcs2018-hyeres.sapsailing.com "74e27348-ee53-4b1c-b042-3a6f09c27bb2"
+
+## World Cup Miami 2018
+Use Event-ARCHIVE-SSL-Redirect wcs2018-miami.sapsailing.com "f90391b0-022d-47a1-9dec-293acaa8b3c0"
+
+# 505 Worlds
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018.sapsailing.com "4d2909ab-d05a-4a94-80b1-dba50bd4729e"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2017.sapsailing.com "4e070be8-51a8-4633-87fc-533f3f54f7d4"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2016.sapsailing.com "8f5daf0f-210f-435f-97bd-c6aff11af3d2"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2015.sapsailing.com "b1e78f52-ce52-4b71-ac30-452599faf4a1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014.sapsailing.com "94e1e240-8a3d-466b-9f39-419f311eaace"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2013.sapsailing.com "35dc9389-b59d-4ebd-8f6b-432065642f40"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2012.sapsailing.com "64e2b631-1a97-4270-9383-14481c8e960f"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2011.sapsailing.com "a10c4fad-c07c-44f5-8241-614a165daae1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2010.sapsailing.com "ba1f5e28-7552-4277-ac32-e440a3432fb7"
+
+## World Cup Gamagori 2017
+Use Event-ARCHIVE-SSL-Redirect wcs2018-gamagori.sapsailing.com "1e8a9ae8-4a48-4de2-99c5-308169c37b76"
+
+## Dutch Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect dutchleague2017.sapsailing.com "07ebdd71-324b-4944-9dc8-790a7de49d36"
+
+
+## Norwegian Sailing League
+use Series-ARCHIVE-SSL-Redirect norwegianleague2018.sapsailing.com "b60934bb-01d5-45d8-b05d-8ca0ccf33d82"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2017.sapsailing.com "b74b76b6-9f2e-4e30-abcf-3285af7b7cf0"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2-2017.sapsailing.com "852ecc8d-17b2-4d8d-81ad-20a34da07174"
+
+
+## Danish Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect danishleague2017.sapsailing.com "81ea69d1-111f-448f-8a83-394db3662b37"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2017.sapsailing.com "d21fe68c-f72f-4d11-b595-4a9426167b7c"
+Use Event-ARCHIVE-SSL-Redirect vipsap2017.sapsailing.com "00b7dc6a-0f2a-42d1-8869-2483cde9d5ad"
+
+## Austrian Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect austrianleague2017.sapsailing.com "94d23c5e-4cef-47d7-92c7-f8ed77ec486c"
+
+## Kiellauf 2017
+Use Event-ARCHIVE-SSL-Redirect kiellauf2017.sapsailing.com "acccaf32-3173-4ee2-afd2-5e09a00cdf53"
+
+## 6mR Worlds 2017 Vancouver
+Use Event-ARCHIVE-SSL-Redirect 6mworlds2017.sapsailing.com "3fbe1757-652c-48d6-a7a5-3a611c82589b"
+
+## French Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect frenchleague2018.sapsailing.com "1d6b816e-ff44-4529-951c-703a0c05fb74"
+Use Event-ARCHIVE-SSL-Redirect frenchleague2017.sapsailing.com "d366af4f-246d-4369-945a-f26505f9d77a"
+
+## IDM FINN 2017
+Use Event-ARCHIVE-SSL-Redirect idmfinn2017.sapsailing.com "9af4c6fd-a9ff-43b6-9927-323db03bab31"
+
+## Polish Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect polishleague2017.sapsailing.com "dc8306e0-38dd-48e7-9010-21ace668d0b7"
+
+## 8 meter worlds 2017
+Use Event-ARCHIVE-SSL-Redirect 8mworlds2017.sapsailing.com "bd1df974-aa7d-4d56-98ca-de0a50bbe0b6"
+
+##Sailing Champions League 2017
+Use Event-ARCHIVE-SSL-Redirect scl2017.sapsailing.com "e6da37a1-773f-4dc5-b7e8-7753d3308386"
+
+##Hansesail 2017 (operated by melanie.coldewey@sap.com)
+Use Event-ARCHIVE-SSL-Redirect hansesail2017.sapsailing.com "0e76df1e-c61f-4845-8681-4b60d4de7cb3"
+
+## 49er, 49FX, Nacra17 Europeans Championships 2017
+Use Event-ARCHIVE-SSL-Redirect 49er-n17-euros2017.sapsailing.com "2288808b-2697-48e9-893a-76ccf00ee94d"
+
+## Sailing World Championships Test Event 2017 Aarhus
+Use Event-ARCHIVE-SSL-Redirect swc2017-aarhus.sapsailing.com "c452f5e8-1bec-4e7b-b459-32f6b820add6"
+
+## Travemuender Woche 2017
+Use Event-ARCHIVE-SSL-Redirect tw2017.sapsailing.com "7784129f-7832-49f1-a5f0-b8d66aa5560c"
+
+## Kieler Woche 2017
+Use Event-ARCHIVE-SSL-Redirect kw2017.sapsailing.com "4d3df446-43bc-46a1-9146-3e1caf6ed757"
+
+## Sailing World Cup Santander 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-santander.sapsailing.com "3b79f093-d52f-440b-9fc4-2fb9e0cc70c9"
+
+## SWC 2017 Hyeres
+Use Event-ARCHIVE-SSL-Redirect swc2017-hyeres.sapsailing.com "8962f211-6e79-422a-85c5-397aaff8d050"
+
+## SWC Worldcup Miami 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-miami.sapsailing.com "b82f9012-47d8-4e9e-b3b2-a690c1db0fe3"
+
+## SWC Worldcup Finale Melbourne 2016
+Use Event-ARCHIVE-SSL-Redirect swc2016-melbourne.sapsailing.com "dd2edeed-b9eb-483e-b3e9-d4761d0d58d2"
+
+## Swedish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect swedishleague2016.sapsailing.com "83db3f7a-b213-45a2-a772-4fc40ae10742"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2017.sapsailing.com "25179060-9139-4c2a-a71f-db11970548f0"
+
+## Danish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect danishleague2016.sapsailing.com "845682ab-b8e5-4a2d-b1c2-d4e03f31963a"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2016.sapsailing.com "32a9c63b-1f00-4dba-bbe0-924a9b4a876d"
+## VIP Racing Denmark Skovshoved 2016
+Use Event-ARCHIVE-SSL-Redirect vipsailingleaguedenmark2016.sapsailing.com "819c71d3-dd74-44fa-a397-24a38d2d76c5"
+
+## Norwegian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2016.sapsailing.com "e3134c8b-bd61-457a-a039-3b277af70725"
+
+## Dutch Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect dutchleague2016.sapsailing.com "a975259d-f513-43f6-9e8c-e358c944f175"
+
+## Austrian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect austrianleague2016.sapsailing.com "526d2f9e-7abf-42b1-af7f-212f0ec4c3e6"
+
+## Finish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect finnishleague2016.sapsailing.com "e5a45e4d-5026-4a38-b585-e51a6deecc11"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2017.sapsailing.com "2828a793-aa4b-428e-80f5-cdea2f92558e"
+
+## SWC Qingdao
+Use Event-ARCHIVE-SSL-Redirect swc2016-qingdao.sapsailing.com "ea0caed8-3182-424e-91a7-6add65e1357d"
+
+## Norwegian Grundig Cup (on norwegian league server)
+Use Event-ARCHIVE-SSL-Redirect grundigcup2016-norway.sapsailing.com "6caf564b-f2b4-4b51-8667-50049ecb8581"
+
+## Sailing Championsleague 2016 (Petersburg / Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2016.sapsailing.com "810a5132-849f-49e0-828c-ace23289f0ac"
+
+## USODA 2016
+Use Event-ARCHIVE-SSL-Redirect usoda2016.sapsailing.com "46c85236-087e-418f-84f9-aee24dac948f"
+
+## BAR Test Oman
+USE Event-ARCHIVE-SSL-Redirect bar-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+USE Event-ARCHIVE-SSL-Redirect ACWS-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+
+## Test new archive in Availability zone A
+USE Event-SSL-Redirect bar-test-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03" 172.31.40.107 8888
+
+## SCGN Test 2016
+USE Event-ARCHIVE-SSL-Redirect scgn-test.sapsailing.com "5ca2b18a-0a55-4591-910c-1f1f35be51f2"
+
+# SCGN Training
+Use Event-ARCHIVE-SSL-Redirect scgn-training.sapsailing.com "004147c6-3991-4a02-bc50-72b8e5b8027c"
+
+## TW 2016
+Use Event-ARCHIVE-SSL-Redirect tw2016.sapsailing.com "86e9e60c-0b16-4941-b14a-166ed4dcabff"
+Use Event-ARCHIVE-SSL-Redirect TWguestsailing2016.sapsailing.com "bd4e1319-f431-423a-a922-3215ea0e6fe9"
+
+## VANPro
+Use Event-ARCHIVE-SSL-Redirect VANpro2016.sapsailing.com "2316ce7d-be93-4ffc-8613-cb6ffa475153"
+
+## Eastern Economic Forum 2016
+Use Event-ARCHIVE-SSL-Redirect forumvostok2016.sapsailing.com "5093220d-875f-4909-9ad0-46476639ee97"
+
+## IDM Star
+Use Event-ARCHIVE-SSL-Redirect idmstar2016.sapsailing.com "5a05f9b9-2f70-46f7-99e1-c069b418bc20"
+
+# 29er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 29erWorlds2016.sapsailing.com "4b381745-1d29-4bda-b388-4a0b7e94dc98"
+
+## WMRT 2016
+Use Event-ARCHIVE-SSL-Redirect wmrt2016.sapsailing.com "cfbe9100-75d6-474a-8d58-3ef8b19cdf25"
+
+## French Sailing League 2016
+Use Event-ARCHIVE-SSL-Redirect frenchleague2016.sapsailing.com "6755bbe9-b761-42a2-9f85-a23dea926a3b"
+
+## RosAtom 2016
+Use Event-ARCHIVE-SSL-Redirect rsc2016.sapsailing.com "3aab9f92-0d83-411c-bfc9-c9032079a749"
+
+## CRW 2016
+Use Event-ARCHIVE-SSL-Redirect crw2016.sapsailing.com "56f62045-de57-4c5e-be6c-a2339d9c9ece"
+
+## Kieler Woche 2016
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2016.sapsailing.com "cc12361a-236b-4ddf-b68d-42ca785cd4d6"
+Use Event-ARCHIVE-SSL-Redirect audiultracup2016.sapsailing.com "c4f9793f-c492-46a0-8af2-a543d5dab588"
+
+## 49erEuros 2016
+Use Event-Archive-SSL-Redirect 49erEuros2016.sapsailing.com "ccb1b062-fe59-4a1c-a15e-f30795ac3c7f"
+
+# 49er / FX Princess Sofia Trophy 2016
+Use Event-ARCHIVE-SSL-Redirect trofeoprincesasofia2016.sapsailing.com "445d072c-fe95-4cc3-ba49-874d4cd43325"
+
+# 49er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2016.sapsailing.com "98e52dd5-2765-4f71-8ebc-82284be07fbc"
+
+# 2.4mR IDM 2015
+Use Event-ARCHIVE-SSL-Redirect idm24mR2015.sapsailing.com "c51c9915-a8f0-4a31-a0e2-127d557b7ea0"
+
+# Surfing World Cup Sylt 2015
+Use Event-ARCHIVE-SSL-Redirect worldcupsylt2015.sapsailing.com "9eb910b8-bb61-4113-8263-1667cd37ac76"
+
+# Norwegian League 2015
+Use Event-ARCHIVE-SSL-Redirect norwegianleague2015.sapsailing.com "29c81874-4e3f-4970-9ae3-1c65eca8889b"
+Use Event-ARCHIVE-SSL-Redirect mesternes-mester-2015.sapsailing.com "8a5a7dcd-3c19-464f-a828-d0c3898b38d5"
+
+# Dutch League 2015
+Use Event-ARCHIVE-SSL-Redirect dutchleague2015.sapsailing.com "4f863d14-1025-44ec-853b-7ef5d45fb19d"
+
+# Championsleague 2015
+Use Event-ARCHIVE-SSL-Redirect championsleague2015.sapsailing.com "f40d9162-9008-4516-9110-33ab0f17d6a0"
+
+# Bart's Bash
+Use Event-ARCHIVE-SSL-Redirect bartsbash2015.sapsailing.com "905457f5-5ccb-4fea-8219-8300eed637ad"
+
+# 49er JWC 2015
+Use Event-ARCHIVE-SSL-Redirect 49erJWC2015.sapsailing.com "9605d85c-867e-438c-a74b-47da896f476e"
+
+# Travemuender Woche 2015
+Use Event-ARCHIVE-SSL-Redirect tw2015.sapsailing.com "82c5adb5-2dac-40fb-aa18-3b8057365e57"
+Use Event-ARCHIVE-SSL-Redirect saptw2015.sapsailing.com "eb58ae5d-0460-4574-a1b8-2e4ef8cf8e15"
+
+# Audi Business Cup
+Use Event-ARCHIVE-SSL-Redirect audibusinesscup2015.sapsailing.com "080b6c09-0110-4b51-a209-dc3cf8b9f669"
+
+# 49er Euros 2015
+Use Event-ARCHIVE-SSL-Redirect 49ereuros2015.sapsailing.com "2b684db8-683d-4479-925b-5962085b7ae1"
+
+# KW 2015
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2015.sapsailing.com "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+Use LiveCenterWithLanguageSubstitution-ARCHIVE livecenter.kieler-woche.de "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+
+# YES 2015
+Use Event-ARCHIVE-SSL-Redirect yes2015.sapsailing.com "fb44d803-15d3-4fe1-852d-28346346ebc3"
+
+# SAP Opti Cup SCGN 2015
+Use Event-ARCHIVE-SSL-Redirect sapopticup2015.sapsailing.com "dff7d969-9858-4dff-b2f2-a45018a2f755"
+
+# RCApp Test Server
+Use Plain rcapptest.sapsailing.com 172.31.31.114 8888
+
+# ESS
+Use Event-ARCHIVE-SSL-Redirect ess2018.sapsailing.com "61af2433-21b4-4a74-8f82-58b582db3944"
+Use Event-ARCHIVE-SSL-Redirect ess2017.sapsailing.com "467b55e8-eab8-4f4b-bf5e-830221fe25dc"
+Use Event-ARCHIVE-SSL-Redirect ess2016.sapsailing.com "a6025457-2880-4fe9-b9d6-ad943ef78025"
+Use Event-ARCHIVE-SSL-Redirect ess40-2015.sapsailing.com "70054aa2-b381-4e77-8e17-840ee1b6190d"
+Use Event-ARCHIVE-SSL-Redirect ess40-2014.sapsailing.com "8e5bef73-7d55-45ed-951a-72ef375ae1b6"
+Use Event-ARCHIVE-SSL-Redirect ess40-2013.sapsailing.com "1487bc65-be3b-4dbc-951d-a1390e9619e0"
+Use Event-ARCHIVE-SSL-Redirect ess40-2012.sapsailing.com "863a5c30-cbea-4f1c-8fb1-54fffa6b7319"
+
+# Danish League 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2015.sapsailing.com "94c730da-c0c0-44c8-b31e-0d3363e668a2"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+
+# Swedish League 2015
+Use Series-ARCHIVE-SSL-Redirect allsvenskan2015.sapsailing.com "dd9fcadd-687b-43d5-8917-ed87e1d38633"
+Use Event-ARCHIVE-SSL-Redirect mastarnas-mastare-2015.sapsailing.com "f27b8b88-a7a3-4694-9374-7e5caebb0328"
+
+# Finnish League 2015
+Use Series-ARCHIVE-SSL-Redirect finnishleague2015.sapsailing.com "9d679af1-99f7-492c-88dc-1e2d0e1e3d8d"
+
+# Austrian League 2015
+Use Series-ARCHIVE-SSL-Redirect austrianleague2015.sapsailing.com "9543fd96-9bf4-4795-960a-f4d96b9d8a38"
+
+# Bodenseewoche 2015
+Use Event-ARCHIVE-SSL-Redirect bodenseewoche2015.sapsailing.com "fef6c510-eefe-470f-a9a9-dcf6293ec116"
+
+# RosAtom Cup 2015
+Use Event-ARCHIVE-SSL-Redirect rosatomcup2015.sapsailing.com "f3a8c574-d23d-44d8-9316-3afb7ee0d39c"
+
+# ATC on Archive:
+Use Event-ARCHIVE-SSL-Redirect atcregatta2015.sapsailing.com "9c1d57bf-29c8-440f-becd-4862bc0f43fe"
+
+## DEV Server
+Use Plain-SSL-Redirect dev.sapsailing.com 172.31.28.17 8888
+Use Plain-SSL dualstack-dev.sapsailing.com 172.31.28.17 8888
+
+## WWW is now defined in 000-main.conf so it can be the default virtual host; please see there
+
+## DEMO Server on DEV
+Use Plain-SSL-Redirect demo.sapsailing.com 172.31.28.17 8889
+
+## Bundesliga 2014 2015 2016 2017 2018 2019
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2018.sapsailing.com "2ebdd4fe-a111-4b9e-87ed-03f4bc3d9805"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2018.sapsailing.com "537ad3d5-e0bd-43af-86c4-ed2c0c3eabc9"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2017.sapsailing.com "9ee75a6e-60c2-4a69-993c-b38f45be29ad"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2017.sapsailing.com "b2567e08-26d9-45c1-b5e0-8c410c8db18b"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2016.sapsailing.com "39cae2e8-6243-4b34-8c23-18e9d82b6cc4"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2016.sapsailing.com "4cd7f524-fa47-45b7-ae48-71e693337415"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2016-relegation.sapsailing.com "3b54a7ce-7585-4935-8564-522988484fb2"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2015.sapsailing.com "e63c2a3e-5afe-45ae-847f-ba98a1707f04"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2015.sapsailing.com "aa99d93f-9444-4611-bb5e-be9cca0de6a7"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2015-relegation.sapsailing.com "0fb650c5-51c8-44c9-8b4f-cb5d3e2def9a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2014.sapsailing.com "be6aaa29-1754-4de0-bc82-5c0c395f080e"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2014.sapsailing.com "8b982dc7-d20e-45f2-9941-f3a48fb3a0f3"
+Use Event-ARCHIVE-SSL-Redirect businesscup2014-hamburg.sapsailing.com "fd2e442b-6e79-4cc7-af23-01eb8eeb061e"
+
+## DSL Pokal 2017 2018 2019
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2017.sapsailing.com "f1860c95-0518-4044-ada2-3594a351dd13"
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2018.sapsailing.com "fc4ca7c9-5d11-4444-9f80-462a3e536e08"
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2019.sapsailing.com "c6fd851c-4335-4c65-831f-c4d424b0f86f"
+
+## Junioren Liga
+Use Event-ARCHIVE-SSL-Redirect juniorenliga2016.sapsailing.com "71ef7523-b900-4ea5-bb48-b83e1db98a8e"
+
+## SAP Sailing Experience
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019portocervo.sapsailing.com "3d8dabf1-8900-4b49-8b20-1036207d41e1"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017saintmoritz.sapsailing.com "2828748a-735b-426c-883d-3e0d0c612449"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017tw.sapsailing.com "c89b99bb-f156-45aa-8eaa-6a5835c00037"
+Use Event-ARCHIVE-SSL-Redirect audisailingexperience2016.sapsailing.com "2744dfa0-e833-475e-afe3-a70807fa75f4"
+
+## Danish events 2014: League 2014, Dansish sailing school event, qualification league 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2014.sapsailing.com "cdea0732-5f0f-483b-9c32-266e2a95884e"
+Use Series-ARCHIVE-SSL-Redirect sejlsportsligaen2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+Use Event-ARCHIVE-SSL-Redirect sejlerskolecup-2014.sapsailing.com "d75063dd-f579-4a46-836c-5cc017a71e44"
+Use Event-ARCHIVE-SSL-Redirect championsleague2014.sapsailing.com "eb326f1b-81e8-468b-89cb-10131a8d8459"
+Use Event-ARCHIVE-SSL-Redirect bartsbash2014.sapsailing.com "b2e2b264-134c-4ef2-b592-53562fd0f835"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests.sapsailing.com "001a5d8f-9704-4400-b5df-6c78e8037b8b"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests2.sapsailing.com "6c8231c3-cd1e-4535-a021-2fed1fc30471"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014.sapsailing.com "09f39909-5d72-40d2-b6e9-4ea7c908a0dc"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014-proam.sapsailing.com "ca9daa7d-c966-4e89-bc50-ce598e2f2a56"
+Use Event-ARCHIVE-SSL-Redirect tw2014.sapsailing.com "5133df37-0092-42c8-a9cb-7a087ae00219"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2014.sapsailing.com "3713fbd7-a62b-439b-bec1-bea732c5d895"
+Use Event-ARCHIVE-SSL-Redirect yes2014.sapsailing.com "6b3dfa41-8553-4abc-8b02-7e3423a59041"
+Use Event-ARCHIVE-SSL-Redirect olympics2012.sapsailing.com "901963ed-743d-41db-9727-1c3b73e78ddd"
+Use Event-ARCHIVE-SSL-Redirect olympics2016.sapsailing.com "e6dbae49-aa2f-49fc-a7eb-82e24cdc307f"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2013.sapsailing.com "a6abca6e-9711-44dc-9c64-929bc869aa12"
+Use Event-ARCHIVE-SSL-Redirect drachenidm2012.sapsailing.com "a42beb95-4885-45f1-9ed7-91159a6e4f6a"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2012.sapsailing.com "22c7942e-eb3d-462a-9d59-600468044bb2"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2013.sapsailing.com "232ce9c3-ac84-47c8-b77d-eec782400877"
+Use Event-ARCHIVE-SSL-Redirect idm2011.sapsailing.com "9577d5ae-b27f-4e76-bf26-580950baf904"
+Use Event-ARCHIVE-SSL-Redirect staridm2013.sapsailing.com "a43b8f96-1e77-448a-9b8a-0f586f1bb18f"
+Use Event-ARCHIVE-SSL-Redirect tw2013.sapsailing.com "b0a721cf-89c3-486c-9a84-25e1aaeb3225"
+Use Event-ARCHIVE-SSL-Redirect 505idm2013.sapsailing.com "9130f36f-d1c3-4080-ac5b-a2104b8fbfd0"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2012.sapsailing.com "0c0db838-b199-4309-a2a4-90bebe85ffe8"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2013.sapsailing.com "3e4145a7-5473-4670-83e6-fd5ee364f7eb"
+Use Event-ARCHIVE-SSL-Redirect mdm2011.sapsailing.com "b06d2e5c-0a35-4205-910f-d8387bbdd350"
+Use Event-ARCHIVE-SSL-Redirect obmr2012.sapsailing.com "2dd8de86-6564-4189-9aa0-8666f26dbb5b"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2011.sapsailing.com "8be013de-d49d-49ea-9e73-4e7c7970aec0"
+Use Event-ARCHIVE-SSL-Redirect yes2012.sapsailing.com "7732cfba-23ff-44a7-9b5d-733d1d92e467"
+
+Use Spectator-ARCHIVE-SSL-Redirect captainsdinner2013.sapsailing.com "Captain's Dinner 2013"
+Use Spectator-ARCHIVE-SSL-Redirect winners.sapsailing.com "SAP Winners Circle"
+Use Spectator-ARCHIVE-SSL-Redirect winners2013.sapsailing.com "SAP Winners Circle 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505guestsailing2013.sapsailing.com "505 Worlds 2013 Guest Program"
+Use Spectator-ARCHIVE-SSL-Redirect 505worlds2012-guests.sapsailing.com "505 Worlds 2012 Guests"
+Use Spectator-ARCHIVE-SSL-Redirect arenal2012.sapsailing.com "Arenal 2012"
+Use Spectator-ARCHIVE-SSL-Redirect mini650pc2013.sapsailing.com "Mini 650 Pacific Challenge 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505.sapsailing.com "505"
+Use Spectator-ARCHIVE-SSL-Redirect isafwc2014.sapsailing.com "ISAF World Cup Hyeres 2014"
+
+## SAP Sailing Experience 2019 - Kieler Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019kw.sapsailing.com "f21d34d8-6c40-4804-872b-0e5fdfb5ce64"
+
+## Medemblik Regatta 2019
+Use Event-ARCHIVE-SSL-Redirect medemblik2019.sapsailing.com "c7e7589c-b5ef-4e7d-93ff-3cd18527976b"
+
+## Sailing Champions League 2019 - Qualifier 1 Palma
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier1.sapsailing.com "5b3d782a-d6eb-420f-852e-44e6d36b8869"
+
+## Sailing Champions League 2019 - Qualifier 2 Porto Cervo
+Use Event-ARCHIVE-SSL-Redirect scl2019-qualifier2.sapsailing.com "dd4eb3ce-dce0-4e8f-bfdb-ff1219f6fde1"
+
+## Kornati Cup 2019
+Use Event-ARCHIVE-SSL-Redirect kornaticup2019.sapsailing.com "b3735a2e-bdd6-4cf7-91a7-87d388c954b4"
+
+## 505 Worlds 2019 - Fremantle
+Use Event-ARCHIVE-SSL-Redirect 505worlds2019.sapsailing.com "fbfa43cc-ff0d-42b9-96c5-6d218c636a62"
+
+## Polska Ekstraklasa Żeglarska 2018
+Use Series-ARCHIVE-SSL-Redirect polishleague2018.sapsailing.com "aa60bdfc-6931-4919-9647-6ae61284f1da"
+
+## Allsvenskan Segling 2018
+Use Series-ARCHIVE-SSL-Redirect swedishleague2018.sapsailing.com "9bc7be83-496f-4e30-a5f5-ebf634a8cdc1"
+
+## Red Bull Foiling Generation 2018 Final
+Use Event-ARCHIVE-SSL-Redirect rbfg2018.sapsailing.com "1e330c0e-9dbf-4fa0-9817-1e11e17bea55"
+
+## Finnish Sailing League 2018
+Use Series-ARCHIVE-SSL-Redirect finnishleague2018.sapsailing.com "268a2f86-6722-4d26-8be1-08c0eb0b9306"
+
+## Dutch Sailing League 2018
+use Series-ARCHIVE-SSL-Redirect dutchleague2018.sapsailing.com "cdfc4a3f-ef43-42fb-91fd-589b71cc1815"
+
+## Österreichische Segelbundesliga 2018
+use Series-ARCHIVE-SSL-Redirect austrianleague2018.sapsailing.com "d5b4fba4-903e-4e0f-86e7-93162c527640"
+
+## Danish Sailing League 2018
+Use Series-ARCHIVE-SSL-Redirect danishleague2018.sapsailing.com "c3dc2b53-b8f3-4657-93a5-e3c0bab7af80"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2018.sapsailing.com "f39e70a1-d31d-4f1d-87f5-c7832557f8d8"
+
+## DSL-Pokal 2018
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2018.sapsailing.com "fc4ca7c9-5d11-4444-9f80-462a3e536e08"
+
+## Ligue Nationale De Voile 2018
+Use Series-ARCHIVE-SSL-Redirect frenchleague2018.sapsailing.com "aa75fb08-a973-45ec-9d71-f4c7e1a7a04a"
+
+## SAILING Champions League 2018 - Final
+use Event-ARCHIVE-SSL-Redirect scl2018-final.sapsailing.com "0503a3d7-2b50-4825-84b4-7bcfd6cd4730"
+
+## SAILING Champions League 2018 - Semifinal 2
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal2.sapsailing.com "74f7e1c6-9d19-4751-9083-60f00c9b8053"
+
+## Hempel Sailing World Championships Aarhus 2018
+Use Event-ARCHIVE-SSL-Redirect worlds2018.sapsailing.com "4544f986-8235-426d-ad3e-6a3500b12edc"
+
+## Travemünder Woche 2018
+Use Event-ARCHIVE-SSL-Redirect tw2018.sapsailing.com "4e71526d-a25f-4f41-adc0-d92c60738134"
+
+## SAP Sailing Experience 2018 Travemuender Woche
+Use Event-ARCHIVE-SSL-Redirect sapexperience2018tw.sapsailing.com "03937740-7da2-4644-92f9-9cb12c40fbb4"
+
+## 505 Worlds 2018 - SAP Poland team race
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018-SAP-Poland.sapsailing.com "eeeddabb-31c3-425c-abd5-0913781f879e"
+
+## Nord Stream Race 2018
+Use Event-ARCHIVE-SSL-Redirect nordstreamrace2018.sapsailing.com "9791acc7-79f7-4c3b-9cde-7b5dede49cc2"
+
+## Kieler Woche 2018
+Use Event-ARCHIVE-SSL-Redirect kw2018.sapsailing.com "1a73aaef-7386-4178-8a11-14ced71e1d8c"
+
+## Sailing Championsleague 2018 (Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2018-semifinal1.sapsailing.com "ccd8a484-f2be-4dbd-9474-c457fefd6536"
+
+## World Cup Series 2019 - Marseille
+Use Event-ARCHIVE-SSL-Redirect wcs2019-marseille.sapsailing.com "ba606057-ced3-444d-9425-fd89ae36ca18"
+
+## World Cup Series 2019 - Genoa
+Use Event-ARCHIVE-SSL-Redirect wcs2019-genoa.sapsailing.com "587e5fef-53ea-47f0-a71b-1fc29053b4f0"
+
+## World Cup Series 2019 - Miami
+Use Event-ARCHIVE-SSL-Redirect wcs2019-miami.sapsailing.com "f393a743-dd49-450f-ab8b-d11522b266f2"
+
+## World Cup Series 2019 - Enoshima
+Use Event-ARCHIVE-SSL-Redirect wcs2019-enoshima.sapsailing.com "bb4c7908-56a4-4b6e-9942-4ffd20c59fdc"
+
+## World Cup final 2018 Marseille
+Use Event-ARCHIVE-SSL-Redirect wcs2018-marseille.sapsailing.com "d5c7440a-27fb-4b9f-a64d-5beb1a2907fd"
+
+## Medemblik 2018
+Use Event-ARCHIVE-SSL-Redirect medemblik2018.sapsailing.com "36b3a1ff-34ef-48ab-86e3-aebbbcac55c4"
+
+## Aeroe Rund 2018
+Use Event-ARCHIVE-SSL-Redirect aeroe-rund2018.sapsailing.com "7a16b434-6952-457b-9b7c-010875f6bfd3"
+
+## Thetis 2018
+Use Event-ARCHIVE-SSL-Redirect thetis2018.sapsailing.com "8964d8e0-0368-4581-8df8-6ade3543a94a"
+
+## World Cup Hyeres 2018
+Use Event-ARCHIVE-SSL-Redirect wcs2018-hyeres.sapsailing.com "74e27348-ee53-4b1c-b042-3a6f09c27bb2"
+
+## World Cup Miami 2018
+Use Event-ARCHIVE-SSL-Redirect wcs2018-miami.sapsailing.com "f90391b0-022d-47a1-9dec-293acaa8b3c0"
+
+# 505 Worlds
+Use Event-ARCHIVE-SSL-Redirect 505worlds2018.sapsailing.com "4d2909ab-d05a-4a94-80b1-dba50bd4729e"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2017.sapsailing.com "4e070be8-51a8-4633-87fc-533f3f54f7d4"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2016.sapsailing.com "8f5daf0f-210f-435f-97bd-c6aff11af3d2"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2015.sapsailing.com "b1e78f52-ce52-4b71-ac30-452599faf4a1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014.sapsailing.com "94e1e240-8a3d-466b-9f39-419f311eaace"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2013.sapsailing.com "35dc9389-b59d-4ebd-8f6b-432065642f40"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2012.sapsailing.com "64e2b631-1a97-4270-9383-14481c8e960f"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2011.sapsailing.com "a10c4fad-c07c-44f5-8241-614a165daae1"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2010.sapsailing.com "ba1f5e28-7552-4277-ac32-e440a3432fb7"
+
+## World Cup Gamagori 2017
+Use Event-ARCHIVE-SSL-Redirect wcs2018-gamagori.sapsailing.com "1e8a9ae8-4a48-4de2-99c5-308169c37b76"
+
+## Dutch Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect dutchleague2017.sapsailing.com "07ebdd71-324b-4944-9dc8-790a7de49d36"
+
+
+## Norwegian Sailing League
+use Series-ARCHIVE-SSL-Redirect norwegianleague2018.sapsailing.com "b60934bb-01d5-45d8-b05d-8ca0ccf33d82"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2017.sapsailing.com "b74b76b6-9f2e-4e30-abcf-3285af7b7cf0"
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2-2017.sapsailing.com "852ecc8d-17b2-4d8d-81ad-20a34da07174"
+
+
+## Danish Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect danishleague2017.sapsailing.com "81ea69d1-111f-448f-8a83-394db3662b37"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2017.sapsailing.com "d21fe68c-f72f-4d11-b595-4a9426167b7c"
+Use Event-ARCHIVE-SSL-Redirect vipsap2017.sapsailing.com "00b7dc6a-0f2a-42d1-8869-2483cde9d5ad"
+
+## Austrian Sailing League 2017
+Use Series-ARCHIVE-SSL-Redirect austrianleague2017.sapsailing.com "94d23c5e-4cef-47d7-92c7-f8ed77ec486c"
+
+## Kiellauf 2017
+Use Event-ARCHIVE-SSL-Redirect kiellauf2017.sapsailing.com "acccaf32-3173-4ee2-afd2-5e09a00cdf53"
+
+## 6mR Worlds 2017 Vancouver
+Use Event-ARCHIVE-SSL-Redirect 6mworlds2017.sapsailing.com "3fbe1757-652c-48d6-a7a5-3a611c82589b"
+
+## French Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect frenchleague2018.sapsailing.com "1d6b816e-ff44-4529-951c-703a0c05fb74"
+Use Event-ARCHIVE-SSL-Redirect frenchleague2017.sapsailing.com "d366af4f-246d-4369-945a-f26505f9d77a"
+
+## IDM FINN 2017
+Use Event-ARCHIVE-SSL-Redirect idmfinn2017.sapsailing.com "9af4c6fd-a9ff-43b6-9927-323db03bab31"
+
+## Polish Sailing League 2017
+Use Event-ARCHIVE-SSL-Redirect polishleague2017.sapsailing.com "dc8306e0-38dd-48e7-9010-21ace668d0b7"
+
+## 8 meter worlds 2017
+Use Event-ARCHIVE-SSL-Redirect 8mworlds2017.sapsailing.com "bd1df974-aa7d-4d56-98ca-de0a50bbe0b6"
+
+##Sailing Champions League 2017
+Use Event-ARCHIVE-SSL-Redirect scl2017.sapsailing.com "e6da37a1-773f-4dc5-b7e8-7753d3308386"
+
+##Hansesail 2017 (operated by melanie.coldewey@sap.com)
+Use Event-ARCHIVE-SSL-Redirect hansesail2017.sapsailing.com "0e76df1e-c61f-4845-8681-4b60d4de7cb3"
+
+## 49er, 49FX, Nacra17 Europeans Championships 2017
+Use Event-ARCHIVE-SSL-Redirect 49er-n17-euros2017.sapsailing.com "2288808b-2697-48e9-893a-76ccf00ee94d"
+
+## Sailing World Championships Test Event 2017 Aarhus
+Use Event-ARCHIVE-SSL-Redirect swc2017-aarhus.sapsailing.com "c452f5e8-1bec-4e7b-b459-32f6b820add6"
+
+## Travemuender Woche 2017
+Use Event-ARCHIVE-SSL-Redirect tw2017.sapsailing.com "7784129f-7832-49f1-a5f0-b8d66aa5560c"
+
+## Kieler Woche 2017
+Use Event-ARCHIVE-SSL-Redirect kw2017.sapsailing.com "4d3df446-43bc-46a1-9146-3e1caf6ed757"
+
+## Sailing World Cup Santander 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-santander.sapsailing.com "3b79f093-d52f-440b-9fc4-2fb9e0cc70c9"
+
+## SWC 2017 Hyeres
+Use Event-ARCHIVE-SSL-Redirect swc2017-hyeres.sapsailing.com "8962f211-6e79-422a-85c5-397aaff8d050"
+
+## SWC Worldcup Miami 2017
+Use Event-ARCHIVE-SSL-Redirect swc2017-miami.sapsailing.com "b82f9012-47d8-4e9e-b3b2-a690c1db0fe3"
+
+## SWC Worldcup Finale Melbourne 2016
+Use Event-ARCHIVE-SSL-Redirect swc2016-melbourne.sapsailing.com "dd2edeed-b9eb-483e-b3e9-d4761d0d58d2"
+
+## Swedish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect swedishleague2016.sapsailing.com "83db3f7a-b213-45a2-a772-4fc40ae10742"
+Use Series-ARCHIVE-SSL-Redirect swedishleague2017.sapsailing.com "25179060-9139-4c2a-a71f-db11970548f0"
+
+## Danish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect danishleague2016.sapsailing.com "845682ab-b8e5-4a2d-b1c2-d4e03f31963a"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2016.sapsailing.com "32a9c63b-1f00-4dba-bbe0-924a9b4a876d"
+## VIP Racing Denmark Skovshoved 2016
+Use Event-ARCHIVE-SSL-Redirect vipsailingleaguedenmark2016.sapsailing.com "819c71d3-dd74-44fa-a397-24a38d2d76c5"
+
+## Norwegian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect norwegianleague2016.sapsailing.com "e3134c8b-bd61-457a-a039-3b277af70725"
+
+## Dutch Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect dutchleague2016.sapsailing.com "a975259d-f513-43f6-9e8c-e358c944f175"
+
+## Austrian Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect austrianleague2016.sapsailing.com "526d2f9e-7abf-42b1-af7f-212f0ec4c3e6"
+
+## Finish Sailing League 2016
+Use Series-ARCHIVE-SSL-Redirect finnishleague2016.sapsailing.com "e5a45e4d-5026-4a38-b585-e51a6deecc11"
+Use Series-ARCHIVE-SSL-Redirect finnishleague2017.sapsailing.com "2828a793-aa4b-428e-80f5-cdea2f92558e"
+
+## SWC Qingdao
+Use Event-ARCHIVE-SSL-Redirect swc2016-qingdao.sapsailing.com "ea0caed8-3182-424e-91a7-6add65e1357d"
+
+## Norwegian Grundig Cup (on norwegian league server)
+Use Event-ARCHIVE-SSL-Redirect grundigcup2016-norway.sapsailing.com "6caf564b-f2b4-4b51-8667-50049ecb8581"
+
+## Sailing Championsleague 2016 (Petersburg / Porto Cervo)
+Use Event-ARCHIVE-SSL-Redirect scl2016.sapsailing.com "810a5132-849f-49e0-828c-ace23289f0ac"
+
+## USODA 2016
+Use Event-ARCHIVE-SSL-Redirect usoda2016.sapsailing.com "46c85236-087e-418f-84f9-aee24dac948f"
+
+## BAR Test Oman
+USE Event-ARCHIVE-SSL-Redirect bar-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+USE Event-ARCHIVE-SSL-Redirect ACWS-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03"
+
+## Test new archive in Availability zone A
+USE Event-SSL-Redirect bar-test-test.sapsailing.com "ec3dd898-9f02-472c-a783-06907cb9ef03" 172.31.40.107 8888
+
+## SCGN Test 2016
+USE Event-ARCHIVE-SSL-Redirect scgn-test.sapsailing.com "5ca2b18a-0a55-4591-910c-1f1f35be51f2"
+
+# SCGN Training
+Use Event-ARCHIVE-SSL-Redirect scgn-training.sapsailing.com "004147c6-3991-4a02-bc50-72b8e5b8027c"
+
+## TW 2016
+Use Event-ARCHIVE-SSL-Redirect tw2016.sapsailing.com "86e9e60c-0b16-4941-b14a-166ed4dcabff"
+Use Event-ARCHIVE-SSL-Redirect TWguestsailing2016.sapsailing.com "bd4e1319-f431-423a-a922-3215ea0e6fe9"
+
+## VANPro
+Use Event-ARCHIVE-SSL-Redirect VANpro2016.sapsailing.com "2316ce7d-be93-4ffc-8613-cb6ffa475153"
+
+## Eastern Economic Forum 2016
+Use Event-ARCHIVE-SSL-Redirect forumvostok2016.sapsailing.com "5093220d-875f-4909-9ad0-46476639ee97"
+
+## IDM Star
+Use Event-ARCHIVE-SSL-Redirect idmstar2016.sapsailing.com "5a05f9b9-2f70-46f7-99e1-c069b418bc20"
+
+# 29er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 29erWorlds2016.sapsailing.com "4b381745-1d29-4bda-b388-4a0b7e94dc98"
+
+## WMRT 2016
+Use Event-ARCHIVE-SSL-Redirect wmrt2016.sapsailing.com "cfbe9100-75d6-474a-8d58-3ef8b19cdf25"
+
+## French Sailing League 2016
+Use Event-ARCHIVE-SSL-Redirect frenchleague2016.sapsailing.com "6755bbe9-b761-42a2-9f85-a23dea926a3b"
+
+## RosAtom 2016
+Use Event-ARCHIVE-SSL-Redirect rsc2016.sapsailing.com "3aab9f92-0d83-411c-bfc9-c9032079a749"
+
+## CRW 2016
+Use Event-ARCHIVE-SSL-Redirect crw2016.sapsailing.com "56f62045-de57-4c5e-be6c-a2339d9c9ece"
+
+## Kieler Woche 2016
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2016.sapsailing.com "cc12361a-236b-4ddf-b68d-42ca785cd4d6"
+Use Event-ARCHIVE-SSL-Redirect audiultracup2016.sapsailing.com "c4f9793f-c492-46a0-8af2-a543d5dab588"
+
+## 49erEuros 2016
+Use Event-Archive-SSL-Redirect 49erEuros2016.sapsailing.com "ccb1b062-fe59-4a1c-a15e-f30795ac3c7f"
+
+# 49er / FX Princess Sofia Trophy 2016
+Use Event-ARCHIVE-SSL-Redirect trofeoprincesasofia2016.sapsailing.com "445d072c-fe95-4cc3-ba49-874d4cd43325"
+
+# 49er Worlds 2016
+Use Event-ARCHIVE-SSL-Redirect 49erworlds2016.sapsailing.com "98e52dd5-2765-4f71-8ebc-82284be07fbc"
+
+# 2.4mR IDM 2015
+Use Event-ARCHIVE-SSL-Redirect idm24mR2015.sapsailing.com "c51c9915-a8f0-4a31-a0e2-127d557b7ea0"
+
+# Surfing World Cup Sylt 2015
+Use Event-ARCHIVE-SSL-Redirect worldcupsylt2015.sapsailing.com "9eb910b8-bb61-4113-8263-1667cd37ac76"
+
+# Norwegian League 2015
+Use Event-ARCHIVE-SSL-Redirect norwegianleague2015.sapsailing.com "29c81874-4e3f-4970-9ae3-1c65eca8889b"
+Use Event-ARCHIVE-SSL-Redirect mesternes-mester-2015.sapsailing.com "8a5a7dcd-3c19-464f-a828-d0c3898b38d5"
+
+# Dutch League 2015
+Use Event-ARCHIVE-SSL-Redirect dutchleague2015.sapsailing.com "4f863d14-1025-44ec-853b-7ef5d45fb19d"
+
+# Championsleague 2015
+Use Event-ARCHIVE-SSL-Redirect championsleague2015.sapsailing.com "f40d9162-9008-4516-9110-33ab0f17d6a0"
+
+# Bart's Bash
+Use Event-ARCHIVE-SSL-Redirect bartsbash2015.sapsailing.com "905457f5-5ccb-4fea-8219-8300eed637ad"
+
+# 49er JWC 2015
+Use Event-ARCHIVE-SSL-Redirect 49erJWC2015.sapsailing.com "9605d85c-867e-438c-a74b-47da896f476e"
+
+# Travemuender Woche 2015
+Use Event-ARCHIVE-SSL-Redirect tw2015.sapsailing.com "82c5adb5-2dac-40fb-aa18-3b8057365e57"
+Use Event-ARCHIVE-SSL-Redirect saptw2015.sapsailing.com "eb58ae5d-0460-4574-a1b8-2e4ef8cf8e15"
+
+# Audi Business Cup
+Use Event-ARCHIVE-SSL-Redirect audibusinesscup2015.sapsailing.com "080b6c09-0110-4b51-a209-dc3cf8b9f669"
+
+# 49er Euros 2015
+Use Event-ARCHIVE-SSL-Redirect 49ereuros2015.sapsailing.com "2b684db8-683d-4479-925b-5962085b7ae1"
+
+# KW 2015
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2015.sapsailing.com "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+Use LiveCenterWithLanguageSubstitution-ARCHIVE livecenter.kieler-woche.de "a9d6c5d5-cac3-47f2-9b5c-506e441819a1"
+
+# YES 2015
+Use Event-ARCHIVE-SSL-Redirect yes2015.sapsailing.com "fb44d803-15d3-4fe1-852d-28346346ebc3"
+
+# SAP Opti Cup SCGN 2015
+Use Event-ARCHIVE-SSL-Redirect sapopticup2015.sapsailing.com "dff7d969-9858-4dff-b2f2-a45018a2f755"
+
+# RCApp Test Server
+Use Plain rcapptest.sapsailing.com 172.31.31.114 8888
+
+# ESS
+Use Event-ARCHIVE-SSL-Redirect ess2018.sapsailing.com "61af2433-21b4-4a74-8f82-58b582db3944"
+Use Event-ARCHIVE-SSL-Redirect ess2017.sapsailing.com "467b55e8-eab8-4f4b-bf5e-830221fe25dc"
+Use Event-ARCHIVE-SSL-Redirect ess2016.sapsailing.com "a6025457-2880-4fe9-b9d6-ad943ef78025"
+Use Event-ARCHIVE-SSL-Redirect ess40-2015.sapsailing.com "70054aa2-b381-4e77-8e17-840ee1b6190d"
+Use Event-ARCHIVE-SSL-Redirect ess40-2014.sapsailing.com "8e5bef73-7d55-45ed-951a-72ef375ae1b6"
+Use Event-ARCHIVE-SSL-Redirect ess40-2013.sapsailing.com "1487bc65-be3b-4dbc-951d-a1390e9619e0"
+Use Event-ARCHIVE-SSL-Redirect ess40-2012.sapsailing.com "863a5c30-cbea-4f1c-8fb1-54fffa6b7319"
+
+# Danish League 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2015.sapsailing.com "94c730da-c0c0-44c8-b31e-0d3363e668a2"
+Use Series-ARCHIVE-SSL-Redirect danishleague2-2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+
+# Swedish League 2015
+Use Series-ARCHIVE-SSL-Redirect allsvenskan2015.sapsailing.com "dd9fcadd-687b-43d5-8917-ed87e1d38633"
+Use Event-ARCHIVE-SSL-Redirect mastarnas-mastare-2015.sapsailing.com "f27b8b88-a7a3-4694-9374-7e5caebb0328"
+
+# Finnish League 2015
+Use Series-ARCHIVE-SSL-Redirect finnishleague2015.sapsailing.com "9d679af1-99f7-492c-88dc-1e2d0e1e3d8d"
+
+# Austrian League 2015
+Use Series-ARCHIVE-SSL-Redirect austrianleague2015.sapsailing.com "9543fd96-9bf4-4795-960a-f4d96b9d8a38"
+
+# Bodenseewoche 2015
+Use Event-ARCHIVE-SSL-Redirect bodenseewoche2015.sapsailing.com "fef6c510-eefe-470f-a9a9-dcf6293ec116"
+
+# RosAtom Cup 2015
+Use Event-ARCHIVE-SSL-Redirect rosatomcup2015.sapsailing.com "f3a8c574-d23d-44d8-9316-3afb7ee0d39c"
+
+# ATC on Archive:
+Use Event-ARCHIVE-SSL-Redirect atcregatta2015.sapsailing.com "9c1d57bf-29c8-440f-becd-4862bc0f43fe"
+
+## DEV Server
+Use Plain-SSL dev.sapsailing.com 172.31.28.17 8888
+Use Plain-SSL dualstack-dev.sapsailing.com 172.31.28.17 8888
+
+## WWW is now defined in 000-main.conf so it can be the default virtual host; please see there
+
+## DEMO Server on DEV
+Use Plain-SSL-Redirect demo.sapsailing.com 172.31.28.17 8889
+
+## Bundesliga 2014 2015 2016 2017 2018
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2018.sapsailing.com "2ebdd4fe-a111-4b9e-87ed-03f4bc3d9805"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2018.sapsailing.com "537ad3d5-e0bd-43af-86c4-ed2c0c3eabc9"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2017.sapsailing.com "9ee75a6e-60c2-4a69-993c-b38f45be29ad"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2017.sapsailing.com "b2567e08-26d9-45c1-b5e0-8c410c8db18b"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2016.sapsailing.com "39cae2e8-6243-4b34-8c23-18e9d82b6cc4"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2016.sapsailing.com "4cd7f524-fa47-45b7-ae48-71e693337415"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2016-relegation.sapsailing.com "3b54a7ce-7585-4935-8564-522988484fb2"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2015.sapsailing.com "e63c2a3e-5afe-45ae-847f-ba98a1707f04"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2015.sapsailing.com "aa99d93f-9444-4611-bb5e-be9cca0de6a7"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2015-relegation.sapsailing.com "0fb650c5-51c8-44c9-8b4f-cb5d3e2def9a"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2014.sapsailing.com "be6aaa29-1754-4de0-bc82-5c0c395f080e"
+Use Series-ARCHIVE-SSL-Redirect bundesliga2-2014.sapsailing.com "8b982dc7-d20e-45f2-9941-f3a48fb3a0f3"
+Use Event-ARCHIVE-SSL-Redirect businesscup2014-hamburg.sapsailing.com "fd2e442b-6e79-4cc7-af23-01eb8eeb061e"
+
+## DSL Pokal 2017
+Use Event-ARCHIVE-SSL-Redirect dsl-pokal2017.sapsailing.com "f1860c95-0518-4044-ada2-3594a351dd13"
+
+## Junioren Liga
+Use Event-ARCHIVE-SSL-Redirect juniorenliga2016.sapsailing.com "71ef7523-b900-4ea5-bb48-b83e1db98a8e"
+
+## SAP Sailing Experience
+Use Event-ARCHIVE-SSL-Redirect sapexperience2019portocervo.sapsailing.com "3d8dabf1-8900-4b49-8b20-1036207d41e1"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017saintmoritz.sapsailing.com "2828748a-735b-426c-883d-3e0d0c612449"
+Use Event-ARCHIVE-SSL-Redirect sapexperience2017tw.sapsailing.com "c89b99bb-f156-45aa-8eaa-6a5835c00037"
+Use Event-ARCHIVE-SSL-Redirect audisailingexperience2016.sapsailing.com "2744dfa0-e833-475e-afe3-a70807fa75f4"
+
+## Danish events 2014: League 2014, Dansish sailing school event, qualification league 2015
+Use Series-ARCHIVE-SSL-Redirect danishleague2014.sapsailing.com "cdea0732-5f0f-483b-9c32-266e2a95884e"
+Use Series-ARCHIVE-SSL-Redirect sejlsportsligaen2015.sapsailing.com "81370e03-98d5-4b0c-b97a-8cc83ffdb18f"
+Use Event-ARCHIVE-SSL-Redirect sejlerskolecup-2014.sapsailing.com "d75063dd-f579-4a46-836c-5cc017a71e44"
+Use Event-ARCHIVE-SSL-Redirect championsleague2014.sapsailing.com "eb326f1b-81e8-468b-89cb-10131a8d8459"
+Use Event-ARCHIVE-SSL-Redirect bartsbash2014.sapsailing.com "b2e2b264-134c-4ef2-b592-53562fd0f835"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests.sapsailing.com "001a5d8f-9704-4400-b5df-6c78e8037b8b"
+Use Event-ARCHIVE-SSL-Redirect 505worlds2014-guests2.sapsailing.com "6c8231c3-cd1e-4535-a021-2fed1fc30471"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014.sapsailing.com "09f39909-5d72-40d2-b6e9-4ea7c908a0dc"
+Use Event-ARCHIVE-SSL-Redirect vulcaintrophy2014-proam.sapsailing.com "ca9daa7d-c966-4e89-bc50-ce598e2f2a56"
+Use Event-ARCHIVE-SSL-Redirect tw2014.sapsailing.com "5133df37-0092-42c8-a9cb-7a087ae00219"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2014.sapsailing.com "3713fbd7-a62b-439b-bec1-bea732c5d895"
+Use Event-ARCHIVE-SSL-Redirect yes2014.sapsailing.com "6b3dfa41-8553-4abc-8b02-7e3423a59041"
+Use Event-ARCHIVE-SSL-Redirect olympics2012.sapsailing.com "901963ed-743d-41db-9727-1c3b73e78ddd"
+Use Event-ARCHIVE-SSL-Redirect olympics2016.sapsailing.com "e6dbae49-aa2f-49fc-a7eb-82e24cdc307f"
+Use Event-ARCHIVE-SSL-Redirect bundesliga2013.sapsailing.com "a6abca6e-9711-44dc-9c64-929bc869aa12"
+Use Event-ARCHIVE-SSL-Redirect drachenidm2012.sapsailing.com "a42beb95-4885-45f1-9ed7-91159a6e4f6a"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2012.sapsailing.com "22c7942e-eb3d-462a-9d59-600468044bb2"
+Use Event-ARCHIVE-SSL-Redirect 49er-euros2013.sapsailing.com "232ce9c3-ac84-47c8-b77d-eec782400877"
+Use Event-ARCHIVE-SSL-Redirect idm2011.sapsailing.com "9577d5ae-b27f-4e76-bf26-580950baf904"
+Use Event-ARCHIVE-SSL-Redirect staridm2013.sapsailing.com "a43b8f96-1e77-448a-9b8a-0f586f1bb18f"
+Use Event-ARCHIVE-SSL-Redirect tw2013.sapsailing.com "b0a721cf-89c3-486c-9a84-25e1aaeb3225"
+Use Event-ARCHIVE-SSL-Redirect 505idm2013.sapsailing.com "9130f36f-d1c3-4080-ac5b-a2104b8fbfd0"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2012.sapsailing.com "0c0db838-b199-4309-a2a4-90bebe85ffe8"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2013.sapsailing.com "3e4145a7-5473-4670-83e6-fd5ee364f7eb"
+Use Event-ARCHIVE-SSL-Redirect mdm2011.sapsailing.com "b06d2e5c-0a35-4205-910f-d8387bbdd350"
+Use Event-ARCHIVE-SSL-Redirect obmr2012.sapsailing.com "2dd8de86-6564-4189-9aa0-8666f26dbb5b"
+Use Event-ARCHIVE-SSL-Redirect kielerwoche2011.sapsailing.com "8be013de-d49d-49ea-9e73-4e7c7970aec0"
+Use Event-ARCHIVE-SSL-Redirect yes2012.sapsailing.com "7732cfba-23ff-44a7-9b5d-733d1d92e467"
+
+Use Spectator-ARCHIVE-SSL-Redirect captainsdinner2013.sapsailing.com "Captain's Dinner 2013"
+Use Spectator-ARCHIVE-SSL-Redirect winners.sapsailing.com "SAP Winners Circle"
+Use Spectator-ARCHIVE-SSL-Redirect winners2013.sapsailing.com "SAP Winners Circle 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505guestsailing2013.sapsailing.com "505 Worlds 2013 Guest Program"
+Use Spectator-ARCHIVE-SSL-Redirect 505worlds2012-guests.sapsailing.com "505 Worlds 2012 Guests"
+Use Spectator-ARCHIVE-SSL-Redirect arenal2012.sapsailing.com "Arenal 2012"
+Use Spectator-ARCHIVE-SSL-Redirect mini650pc2013.sapsailing.com "Mini 650 Pacific Challenge 2013"
+Use Spectator-ARCHIVE-SSL-Redirect 505.sapsailing.com "505"
+Use Spectator-ARCHIVE-SSL-Redirect isafwc2014.sapsailing.com "ISAF World Cup Hyeres 2014"
+
diff --git a/configuration/httpd/conf.d/003-default.conf b/configuration/httpd/conf.d/003-default.conf
new file mode 100644
index 00000000000..1a75b629957
--- /dev/null
+++ b/configuration/httpd/conf.d/003-default.conf
@@ -0,0 +1,10 @@
+
+# Default site
+#
+# Options -Indexes
+ #ErrorDocument 403 /error/noindex.html
+ #RedirectPermanent / http://www.sapsailing.com/
+#
+
+# Default host
+
diff --git a/configuration/httpd/conf.d/004-git-ssl.conf b/configuration/httpd/conf.d/004-git-ssl.conf
new file mode 100644
index 00000000000..8fdc111a3ea
--- /dev/null
+++ b/configuration/httpd/conf.d/004-git-ssl.conf
@@ -0,0 +1,167 @@
+
+ServerName git.sapsailing.com
+SetEnv GIT_PROJECT_ROOT /home/trac/git
+SetEnv GIT_HTTP_EXPORT_ALL
+ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+
+
+ Options +ExecCGI
+ AuthType Basic
+ AuthName 'Git'
+ AuthUserFile '/etc/httpd/conf/passwd.git'
+ Require valid-user
+
+
+#AuthType Basic
+#AuthName "Git"
+#AuthUserFile /etc/httpd/conf/passwd.git
+#Require valid-user
+
+# Use separate log files for the SSL virtual host; note that LogLevel
+# is not inherited from httpd.conf.
+ErrorLog logs/git_error_log
+TransferLog logs/git_access_log
+LogLevel warn
+
+# SSL Engine Switch:
+# Enable/Disable SSL for this virtual host.
+SSLEngine on
+
+# SSL Protocol support:
+# List the enable protocol levels with which clients will be able to
+# connect. Disable SSLv2 access by default:
+SSLProtocol all -SSLv2
+
+# SSL Cipher Suite:
+# List the ciphers that the client is permitted to negotiate.
+# See the mod_ssl documentation for a complete list.
+# This line with SSLv3 disabled; seems to cause problems for Gerrit replication...
+SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:SSLv3:!RC4:!MD5:+HIGH
+
+# Server Certificate:
+# Point SSLCertificateFile at a PEM encoded certificate. If
+# the certificate is encrypted, then you will be prompted for a
+# pass phrase. Note that a kill -HUP will prompt again. A new
+# certificate can be generated using the genkey(1) command.
+#SSLCertificateFile /etc/pki/tls/certs/ca.crt
+SSLCertificateFile /etc/pki/tls/certs/star_sapsailing_com.crt
+
+# Server Private Key:
+# If the key is not combined with the certificate, use this
+# directive to point at the key file. Keep in mind that if
+# you've both a RSA and a DSA private key you can configure
+# both in parallel (to also allow the use of DSA ciphers, etc.)
+#SSLCertificateKeyFile /etc/pki/tls/private/ca.key
+SSLCertificateKeyFile /etc/pki/tls/private/star_sapsailing_com.key
+
+# Server Certificate Chain:
+# Point SSLCertificateChainFile at a file containing the
+# concatenation of PEM encoded CA certificates which form the
+# certificate chain for the server certificate. Alternatively
+# the referenced file can be the same as SSLCertificateFile
+# when the CA certificates are directly appended to the server
+# certificate for convinience.
+#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
+SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
+
+# Certificate Authority (CA):
+# Set the CA certificate verification path where to find CA
+# certificates for client authentication or alternatively one
+# huge file containing all of them (file must be PEM encoded)
+#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
+
+# Client Authentication (Type):
+# Client certificate verification type and depth. Types are
+# none, optional, require and optional_no_ca. Depth is a
+# number which specifies how deeply to verify the certificate
+# issuer chain before deciding the certificate is not valid.
+#SSLVerifyClient require
+#SSLVerifyDepth 10
+
+# Access Control:
+# With SSLRequire you can do per-directory access control based
+# on arbitrary complex boolean expressions containing server
+# variable checks and other lookup directives. The syntax is a
+# mixture between C and Perl. See the mod_ssl documentation
+# for more details.
+#
+#SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
+# and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \
+# and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \
+# and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \
+# and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \
+# or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
+#
+
+# SSL Engine Options:
+# Set various options for the SSL engine.
+# o FakeBasicAuth:
+# Translate the client X.509 into a Basic Authorisation. This means that
+# the standard Auth/DBMAuth methods can be used for access control. The
+# user name is the `one line' version of the client's X.509 certificate.
+# Note that no password is obtained from the user. Every entry in the user
+# file needs this password: `xxj31ZMTZzkVA'.
+# o ExportCertData:
+# This exports two additional environment variables: SSL_CLIENT_CERT and
+# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
+# server (always existing) and the client (only existing when client
+# authentication is used). This can be used to import the certificates
+# into CGI scripts.
+# o StdEnvVars:
+# This exports the standard SSL/TLS related `SSL_*' environment variables.
+# Per default this exportation is switched off for performance reasons,
+# because the extraction step is an expensive operation and is usually
+# useless for serving static content. So one usually enables the
+# exportation for CGI and SSI requests only.
+# o StrictRequire:
+# This denies access when "SSLRequireSSL" or "SSLRequire" applied even
+# under a "Satisfy any" situation, i.e. when it applies access is denied
+# and no other module can change it.
+# o OptRenegotiate:
+# This enables optimized SSL connection renegotiation handling when SSL
+# directives are used in per-directory context.
+#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
+
+ SSLOptions +StdEnvVars
+
+
+
+ SSLOptions +StdEnvVars
+
+
+# SSL Protocol Adjustments:
+# The safe and default but still SSL/TLS standard compliant shutdown
+# approach is that mod_ssl sends the close notify alert but doesn't wait for
+# the close notify alert from client. When you need a different shutdown
+# approach you can use one of the following variables:
+# o ssl-unclean-shutdown:
+# This forces an unclean shutdown when the connection is closed, i.e. no
+# SSL close notify alert is send or allowed to received. This violates
+# the SSL/TLS standard but is needed for some brain-dead browsers. Use
+# this when you receive I/O errors because of the standard approach where
+# mod_ssl sends the close notify alert.
+# o ssl-accurate-shutdown:
+# This forces an accurate shutdown when the connection is closed, i.e. a
+# SSL close notify alert is send and mod_ssl waits for the close notify
+# alert of the client. This is 100% SSL/TLS standard compliant, but in
+# practice often causes hanging connections with brain-dead browsers. Use
+# this only for browsers where you know that their SSL implementation
+# works correctly.
+# Notice: Most problems of broken clients are also related to the HTTP
+# keep-alive facility, so you usually additionally want to disable
+# keep-alive for those clients, too. Use variable "nokeepalive" for this.
+# Similarly, one has to force some clients to use HTTP/1.0 to workaround
+# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
+# "force-response-1.0" for this.
+SetEnvIf User-Agent ".*MSIE.*" \
+ nokeepalive ssl-unclean-shutdown \
+ downgrade-1.0 force-response-1.0
+
+# Per-Server Logging:
+# The home of a custom SSL log file. Use this when you want a
+# compact non-error SSL logfile on a virtual host basis.
+CustomLog logs/ssl_request_log \
+ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
+
+
+
diff --git a/configuration/httpd/conf.d/005-tennis.conf b/configuration/httpd/conf.d/005-tennis.conf
new file mode 100644
index 00000000000..40ff57b478c
--- /dev/null
+++ b/configuration/httpd/conf.d/005-tennis.conf
@@ -0,0 +1,261 @@
+
+# XXX: Move to saptennis.com
+Use Tennis-Plain tennis-analytics.sapsailing.com 54.234.185.60 8888
+Use Tennis-Plain mobile.tennis-analytics.sapsailing.com 54.234.185.60 8888
+Use Tennis-Plain api.tennis-analytics.sapsailing.com 54.234.185.60 8888
+Use Tennis-Plain dev.tennis-analytics.sapsailing.com 34.242.130.58 8888
+
+# XXX: Move these to SSL
+Use Tennis-Plain saptennis.com 54.234.185.60 8888
+Use Tennis-Plain draw.saptennis.com 54.234.185.60 8888
+Use Tennis-Plain groupedit.saptennis.com 34.253.178.230 8888
+
+Use Tennis-With-SSL-Plain dev.saptennis.com 34.254.157.195 8888
+Use Tennis-With-SSL-Plain api.saptennis.com 54.234.185.60 8888
+Use Tennis-With-SSL-Plain tpc.saptennis.com 34.242.18.47 8888
+Use Tennis-With-SSL media.saptennis.com 34.240.66.211 8888 "media/mediaportal"
+Use Tennis-With-SSL-Plain apps.saptennis.com 34.241.245.147 8888
+
+# Monitoring
+Use Tennis-With-SSL-Plain monitor.saptennis.com 172.31.45.221 3000
+Use Tennis-Plain hellgate.saptennis.com 172.31.45.221 2003
+
+# Piwik
+Use Tennis-With-SSL-Plain analytics.saptennis.com 172.31.45.221 8999
+
+Use Tennis-SSL-Redirect media.saptennis.com
+
+
+ ServerName hellgate.saptennis.com
+ ProxyRequests Off
+
+ Order deny,allow
+ Allow from all
+
+
+
+ ProxyPass http://172.31.45.221:2003/
+ ProxyPassReverse http://172.31.45.221:2003/
+
+
+
+
+ ServerName tpc.wta-playerzone.com
+ Use Headers
+
+ SSLEngine On
+ SSLCertificateFile /etc/pki/tls/certs/tpc.wta-playerzone.com.crt
+ SSLCertificateKeyFile /etc/pki/tls/private/tpc.wta-playerzone.com.key
+
+ RewriteEngine on
+ RewriteRule ^(/)?$ "https://tpc.wta-playerzone.com/tpc/ui5" [L,NE]
+ Use Rewrite 34.242.18.47 8888
+
+
+ AddType text/css .css
+ AddType application/x-javascript .js
+ AddType text/x-component .htc
+ AddType text/html .html .htm
+ AddType text/richtext .rtf .rtx
+ AddType image/svg+xml .svg .svgz
+ AddType text/plain .txt
+ AddType text/xsd .xsd
+ AddType text/xsl .xsl
+ AddType text/xml .xml
+ AddType video/asf .asf .asx .wax .wmv .wmx
+ AddType video/avi .avi
+ AddType image/bmp .bmp
+ AddType application/java .class
+ AddType video/divx .divx
+ AddType application/msword .doc .docx
+ AddType application/vnd.ms-fontobject .eot
+ AddType application/x-msdownload .exe
+ AddType image/gif .gif
+ AddType application/x-gzip .gz .gzip
+ AddType image/x-icon .ico
+ AddType image/jpeg .jpg .jpeg .jpe
+ AddType application/vnd.ms-access .mdb
+ AddType audio/midi .mid .midi
+ AddType video/quicktime .mov .qt
+ AddType audio/mpeg .mp3 .m4a
+ AddType video/mp4 .mp4 .m4v
+ AddType video/mpeg .mpeg .mpg .mpe
+ AddType application/vnd.ms-project .mpp
+ AddType application/x-font-otf .otf
+ AddType application/vnd.oasis.opendocument.database .odb
+ AddType application/vnd.oasis.opendocument.chart .odc
+ AddType application/vnd.oasis.opendocument.formula .odf
+ AddType application/vnd.oasis.opendocument.graphics .odg
+ AddType application/vnd.oasis.opendocument.presentation .odp
+ AddType application/vnd.oasis.opendocument.spreadsheet .ods
+ AddType application/vnd.oasis.opendocument.text .odt
+ AddType audio/ogg .ogg
+ AddType application/pdf .pdf
+ AddType image/png .png
+ AddType application/vnd.ms-powerpoint .pot .pps .ppt .pptx
+ AddType audio/x-realaudio .ra .ram
+ AddType application/x-shockwave-flash .swf
+ AddType application/x-tar .tar
+ AddType image/tiff .tif .tiff
+ AddType application/x-font-ttf .ttf .ttc
+ AddType audio/wav .wav
+ AddType audio/wma .wma
+ AddType application/vnd.ms-write .wri
+ AddType application/vnd.ms-excel .xla .xls .xlsx .xlt .xlw
+ AddType application/zip .zip
+
+
+
+ # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
+
+
+ SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+ RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+
+
+
+
+ # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
+ FilterDeclare COMPRESS
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/html'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/css'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/plain'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/x-component'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/javascript'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/json'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xhtml+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/rss+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/atom+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/vnd.ms-fontobject'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/svg+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/x-icon'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/gif'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/jpeg'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/png'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/x-font-ttf'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$font/opentype'"
+ FilterChain COMPRESS
+ FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no
+
+
+
+ # Legacy versions of Apache
+ AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
+ AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
+ AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
+
+
+
+
+
+ ServerName tpc.wta-playerzone.com
+ Use Headers
+ RewriteEngine on
+ RewriteRule ^(/)?$ "http://tpc.wta-playerzone.com/tpc/ui5" [L,NE]
+ Use Rewrite 34.242.18.47 8888
+
+
+ AddType text/css .css
+ AddType application/x-javascript .js
+ AddType text/x-component .htc
+ AddType text/html .html .htm
+ AddType text/richtext .rtf .rtx
+ AddType image/svg+xml .svg .svgz
+ AddType text/plain .txt
+ AddType text/xsd .xsd
+ AddType text/xsl .xsl
+ AddType text/xml .xml
+ AddType video/asf .asf .asx .wax .wmv .wmx
+ AddType video/avi .avi
+ AddType image/bmp .bmp
+ AddType application/java .class
+ AddType video/divx .divx
+ AddType application/msword .doc .docx
+ AddType application/vnd.ms-fontobject .eot
+ AddType application/x-msdownload .exe
+ AddType image/gif .gif
+ AddType application/x-gzip .gz .gzip
+ AddType image/x-icon .ico
+ AddType image/jpeg .jpg .jpeg .jpe
+ AddType application/vnd.ms-access .mdb
+ AddType audio/midi .mid .midi
+ AddType video/quicktime .mov .qt
+ AddType audio/mpeg .mp3 .m4a
+ AddType video/mp4 .mp4 .m4v
+ AddType video/mpeg .mpeg .mpg .mpe
+ AddType application/vnd.ms-project .mpp
+ AddType application/x-font-otf .otf
+ AddType application/vnd.oasis.opendocument.database .odb
+ AddType application/vnd.oasis.opendocument.chart .odc
+ AddType application/vnd.oasis.opendocument.formula .odf
+ AddType application/vnd.oasis.opendocument.graphics .odg
+ AddType application/vnd.oasis.opendocument.presentation .odp
+ AddType application/vnd.oasis.opendocument.spreadsheet .ods
+ AddType application/vnd.oasis.opendocument.text .odt
+ AddType audio/ogg .ogg
+ AddType application/pdf .pdf
+ AddType image/png .png
+ AddType application/vnd.ms-powerpoint .pot .pps .ppt .pptx
+ AddType audio/x-realaudio .ra .ram
+ AddType application/x-shockwave-flash .swf
+ AddType application/x-tar .tar
+ AddType image/tiff .tif .tiff
+ AddType application/x-font-ttf .ttf .ttc
+ AddType audio/wav .wav
+ AddType audio/wma .wma
+ AddType application/vnd.ms-write .wri
+ AddType application/vnd.ms-excel .xla .xls .xlsx .xlt .xlw
+ AddType application/zip .zip
+
+
+
+ # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
+
+
+ SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+ RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+
+
+
+
+ # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
+ FilterDeclare COMPRESS
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/html'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/css'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/plain'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$text/x-component'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/javascript'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/json'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/xhtml+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/rss+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/atom+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/vnd.ms-fontobject'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/svg+xml'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/x-icon'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/gif'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/jpeg'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$image/png'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$application/x-font-ttf'"
+ FilterProvider COMPRESS DEFLATE "%{Content_Type} = '$font/opentype'"
+ FilterChain COMPRESS
+ FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no
+
+
+
+ # Legacy versions of Apache
+ AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
+ AddOutputFilterByType DEFLATE application/javascript
+ AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
+ AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
+ AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
+
+
+
+
+
diff --git a/configuration/httpd/conf.d/README b/configuration/httpd/conf.d/README
new file mode 100644
index 00000000000..f5e96615a9b
--- /dev/null
+++ b/configuration/httpd/conf.d/README
@@ -0,0 +1,9 @@
+
+This directory holds configuration files for the Apache HTTP Server;
+any files in this directory which have the ".conf" extension will be
+processed as httpd configuration files. The directory is used in
+addition to the directory /etc/httpd/conf.modules.d/, which contains
+configuration files necessary to load modules.
+
+Files are processed in alphabetical order.
+
diff --git a/configuration/httpd/conf.d/autoindex.conf b/configuration/httpd/conf.d/autoindex.conf
new file mode 100644
index 00000000000..2b61ed46db6
--- /dev/null
+++ b/configuration/httpd/conf.d/autoindex.conf
@@ -0,0 +1,93 @@
+#
+# Directives controlling the display of server-generated directory listings.
+#
+# Required modules: mod_authz_core, mod_authz_host,
+# mod_autoindex, mod_alias
+#
+# To see the listing of a directory, the Options directive for the
+# directory must include "Indexes", and the directory must not contain
+# a file matching those listed in the DirectoryIndex directive.
+#
+
+#
+# IndexOptions: Controls the appearance of server-generated directory
+# listings.
+#
+IndexOptions FancyIndexing HTMLTable VersionSort
+
+# We include the /icons/ alias for FancyIndexed directory listings. If
+# you do not use FancyIndexing, you may comment this out.
+#
+Alias /icons/ "/var/www/icons/"
+
+
+ Options Indexes MultiViews FollowSymlinks
+ AllowOverride None
+ Require all granted
+
+
+#
+# AddIcon* directives tell the server which icon to show for different
+# files or filename extensions. These are only displayed for
+# FancyIndexed directories.
+#
+AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
+
+AddIconByType (TXT,/icons/text.gif) text/*
+AddIconByType (IMG,/icons/image2.gif) image/*
+AddIconByType (SND,/icons/sound2.gif) audio/*
+AddIconByType (VID,/icons/movie.gif) video/*
+
+AddIcon /icons/binary.gif .bin .exe
+AddIcon /icons/binhex.gif .hqx
+AddIcon /icons/tar.gif .tar
+AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
+AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
+AddIcon /icons/a.gif .ps .ai .eps
+AddIcon /icons/layout.gif .html .shtml .htm .pdf
+AddIcon /icons/text.gif .txt
+AddIcon /icons/c.gif .c
+AddIcon /icons/p.gif .pl .py
+AddIcon /icons/f.gif .for
+AddIcon /icons/dvi.gif .dvi
+AddIcon /icons/uuencoded.gif .uu
+AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
+AddIcon /icons/tex.gif .tex
+AddIcon /icons/bomb.gif core.
+
+AddIcon /icons/back.gif ..
+AddIcon /icons/hand.right.gif README
+AddIcon /icons/folder.gif ^^DIRECTORY^^
+AddIcon /icons/blank.gif ^^BLANKICON^^
+
+#
+# DefaultIcon is which icon to show for files which do not have an icon
+# explicitly set.
+#
+DefaultIcon /icons/unknown.gif
+
+#
+# AddDescription allows you to place a short description after a file in
+# server-generated indexes. These are only displayed for FancyIndexed
+# directories.
+# Format: AddDescription "description" filename
+#
+#AddDescription "GZIP compressed document" .gz
+#AddDescription "tar archive" .tar
+#AddDescription "GZIP compressed tar archive" .tgz
+
+#
+# ReadmeName is the name of the README file the server will look for by
+# default, and append to directory listings.
+#
+# HeaderName is the name of a file which should be prepended to
+# directory indexes.
+ReadmeName README.html
+HeaderName HEADER.html
+
+#
+# IndexIgnore is a set of filenames which directory indexing should ignore
+# and not include in the listing. Shell-style wildcarding is permitted.
+#
+IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
+
diff --git a/configuration/httpd/conf.d/awstats.conf b/configuration/httpd/conf.d/awstats.conf
new file mode 100644
index 00000000000..3bca4ed1618
--- /dev/null
+++ b/configuration/httpd/conf.d/awstats.conf
@@ -0,0 +1,57 @@
+#
+# Content of this file, with correct values, can be automatically added to
+# your Apache server by using the AWStats configure.pl tool.
+#
+
+
+# If using Windows and Perl ActiveStat, this is to enable Perl script as CGI.
+#ScriptInterpreterSource registry
+
+
+#
+# Directives to add to your Apache conf file to allow use of AWStats as a CGI.
+# Note that path "/usr/share/awstats/" must reflect your AWStats install path.
+#
+Alias /awstatsclasses "/usr/share/awstats/wwwroot/classes/"
+Alias /awstatscss "/usr/share/awstats/wwwroot/css/"
+Alias /awstatsicons "/usr/share/awstats/wwwroot/icon/"
+ScriptAlias /awstats/ "/usr/share/awstats/wwwroot/cgi-bin/"
+
+# Adding access to /var/log/old/cache/unique-ips-per-referrer/stats:
+Alias /unique-visitors/ "/var/log/old/cache/unique-ips-per-referrer/stats/"
+
+#
+# This is to permit URL access to scripts/files in AWStats directory.
+#
+
+ ServerName awstats.sapsailing.com
+ Use SSL
+ CustomLog logs/awstats_access_log combined
+ ErrorLog logs/awstats_error_log
+
+
+ Options None
+# AllowOverride None
+# Order allow,deny
+# Allow from 127.0.0.1
+ AuthType Basic
+ AuthName 'awstats'
+ AuthUserFile /etc/httpd/conf/passwd.awstats
+ Require valid-user
+
+
+
+ Options Indexes
+# AllowOverride None
+# Order allow,deny
+# Allow from 127.0.0.1
+ AuthType Basic
+ AuthName 'awstats'
+ AuthUserFile /etc/httpd/conf/passwd.awstats
+ Require valid-user
+
+# Additional Perl modules
+
+ SetEnv PERL5LIB /usr/share/awstats/lib:/usr/share/awstats/plugins:/root/perl5/lib/perl5
+
+
diff --git a/configuration/httpd/conf.d/mrtg.conf b/configuration/httpd/conf.d/mrtg.conf
new file mode 100644
index 00000000000..b10a71ef1bc
--- /dev/null
+++ b/configuration/httpd/conf.d/mrtg.conf
@@ -0,0 +1,14 @@
+#
+# This configuration file maps the mrtg output (generated daily)
+# into the URL space. By default these results are only accessible
+# from the local host.
+#
+Alias /mrtg /var/www/mrtg
+
+
+ Order deny,allow
+ Deny from all
+ Allow from 127.0.0.1
+ Allow from ::1
+ # Allow from .example.com
+
diff --git a/configuration/httpd/conf.d/notrace.conf b/configuration/httpd/conf.d/notrace.conf
new file mode 100644
index 00000000000..651c89917b5
--- /dev/null
+++ b/configuration/httpd/conf.d/notrace.conf
@@ -0,0 +1,6 @@
+# This directive overrides the behavior of TRACE for both the core server and
+# mod_proxy. The default TraceEnable on permits TRACE requests per RFC 2616,
+# which disallows any request body to accompany the request. TraceEnable off
+# causes the core server and mod_proxy to return a 405 (Method not allowed)
+# error to the client.
+TraceEnable off
diff --git a/configuration/httpd/conf.d/perl.conf b/configuration/httpd/conf.d/perl.conf
new file mode 100644
index 00000000000..30d5d16f3bd
--- /dev/null
+++ b/configuration/httpd/conf.d/perl.conf
@@ -0,0 +1,48 @@
+#
+# Mod_perl incorporates a Perl interpreter into the Apache web server,
+# so that the Apache web server can directly execute Perl code.
+# Mod_perl links the Perl runtime library into the Apache web server
+# and provides an object-oriented Perl interface for Apache's C
+# language API. The end result is a quicker CGI script turnaround
+# process, since no external Perl interpreter has to be started.
+#
+
+# Uncomment this line to globally enable warnings, which will be
+# written to the server's error log. Warnings should be enabled
+# during the development process, but should be disabled on a
+# production server as they affect performance.
+#
+#PerlSwitches -w
+
+# Uncomment this line to enable taint checking globally. When Perl is
+# running in taint mode various checks are performed to reduce the
+# risk of insecure data being passed to a subshell or being used to
+# modify the filesystem. Unfortunately many Perl modules are not
+# taint-safe, so you should exercise care before enabling it on a
+# production server.
+#
+#PerlSwitches -T
+
+# This will allow execution of mod_perl to compile your scripts to
+# subroutines which it will execute directly, avoiding the costly
+# compile process for most requests.
+#
+#Alias /perl /var/www/perl
+#
+# SetHandler perl-script
+# PerlResponseHandler ModPerl::Registry
+# PerlOptions +ParseHeaders
+# Options +ExecCGI
+#
+
+# This will allow remote server configuration reports, with the URL of
+# http://servername/perl-status
+# Change the ".example.com" to match your domain to enable.
+#
+#
+# SetHandler perl-script
+# PerlResponseHandler Apache2::Status
+# Order deny,allow
+# Deny from all
+# Allow from .example.com
+#
diff --git a/configuration/httpd/conf.d/perl.conf.rpmnew b/configuration/httpd/conf.d/perl.conf.rpmnew
new file mode 100644
index 00000000000..087e6751d98
--- /dev/null
+++ b/configuration/httpd/conf.d/perl.conf.rpmnew
@@ -0,0 +1,49 @@
+LoadModule perl_module modules/mod_perl.so
+#
+# Mod_perl incorporates a Perl interpreter into the Apache web server,
+# so that the Apache web server can directly execute Perl code.
+# Mod_perl links the Perl runtime library into the Apache web server
+# and provides an object-oriented Perl interface for Apache's C
+# language API. The end result is a quicker CGI script turnaround
+# process, since no external Perl interpreter has to be started.
+#
+
+# Uncomment this line to globally enable warnings, which will be
+# written to the server's error log. Warnings should be enabled
+# during the development process, but should be disabled on a
+# production server as they affect performance.
+#
+#PerlSwitches -w
+
+# Uncomment this line to enable taint checking globally. When Perl is
+# running in taint mode various checks are performed to reduce the
+# risk of insecure data being passed to a subshell or being used to
+# modify the filesystem. Unfortunately many Perl modules are not
+# taint-safe, so you should exercise care before enabling it on a
+# production server.
+#
+#PerlSwitches -T
+
+# This will allow execution of mod_perl to compile your scripts to
+# subroutines which it will execute directly, avoiding the costly
+# compile process for most requests.
+#
+#Alias /perl /var/www/perl
+#
+# SetHandler perl-script
+# PerlResponseHandler ModPerl::Registry
+# PerlOptions +ParseHeaders
+# Options +ExecCGI
+#
+
+# This will allow remote server configuration reports, with the URL of
+# http://servername/perl-status
+# Change the ".example.com" to match your domain to enable.
+#
+#
+# SetHandler perl-script
+# PerlResponseHandler Apache2::Status
+# Order deny,allow
+# Deny from all
+# Allow from .example.com
+#
diff --git a/configuration/httpd/conf.d/php-conf.7.1 b/configuration/httpd/conf.d/php-conf.7.1
new file mode 100644
index 00000000000..2c8064ed7f1
--- /dev/null
+++ b/configuration/httpd/conf.d/php-conf.7.1
@@ -0,0 +1,57 @@
+#
+# The following lines prevent .user.ini files from being viewed by Web clients.
+#
+
+ Require all denied
+
+
+#
+# Allow php to handle Multiviews
+#
+AddType text/html .php
+
+#
+# Add index.php to the list of files that will be served as directory
+# indexes.
+#
+DirectoryIndex index.php
+
+# mod_php options
+
+ #
+ # Cause the PHP interpreter to handle files with a .php extension.
+ #
+
+ SetHandler application/x-httpd-php
+
+
+ #
+ # Uncomment the following lines to allow PHP to pretty-print .phps
+ # files as PHP source code:
+ #
+ #
+ # SetHandler application/x-httpd-php-source
+ #
+
+ #
+ # Apache specific PHP configuration options
+ # those can be override in each configured vhost
+ #
+ php_value session.save_handler "files"
+ php_value session.save_path "/var/lib/php/7.1/session"
+ php_value soap.wsdl_cache_dir "/var/lib/php/7.1/wsdlcache"
+
+ #php_value opcache.file_cache "/var/lib/php/7.1/opcache"
+
+
+# Redirect to local php-fpm if mod_php is not available
+
+
+ # Enable http authorization headers
+ SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
+
+
+ SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
+
+
+
diff --git a/configuration/httpd/conf.d/php.conf b/configuration/httpd/conf.d/php.conf
new file mode 100644
index 00000000000..2c8064ed7f1
--- /dev/null
+++ b/configuration/httpd/conf.d/php.conf
@@ -0,0 +1,57 @@
+#
+# The following lines prevent .user.ini files from being viewed by Web clients.
+#
+
+ Require all denied
+
+
+#
+# Allow php to handle Multiviews
+#
+AddType text/html .php
+
+#
+# Add index.php to the list of files that will be served as directory
+# indexes.
+#
+DirectoryIndex index.php
+
+# mod_php options
+
+ #
+ # Cause the PHP interpreter to handle files with a .php extension.
+ #
+
+ SetHandler application/x-httpd-php
+
+
+ #
+ # Uncomment the following lines to allow PHP to pretty-print .phps
+ # files as PHP source code:
+ #
+ #
+ # SetHandler application/x-httpd-php-source
+ #
+
+ #
+ # Apache specific PHP configuration options
+ # those can be override in each configured vhost
+ #
+ php_value session.save_handler "files"
+ php_value session.save_path "/var/lib/php/7.1/session"
+ php_value soap.wsdl_cache_dir "/var/lib/php/7.1/wsdlcache"
+
+ #php_value opcache.file_cache "/var/lib/php/7.1/opcache"
+
+
+# Redirect to local php-fpm if mod_php is not available
+
+
+ # Enable http authorization headers
+ SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
+
+
+ SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
+
+
+
diff --git a/configuration/httpd/conf.d/ssl.conf b/configuration/httpd/conf.d/ssl.conf
new file mode 100644
index 00000000000..46893ae0ceb
--- /dev/null
+++ b/configuration/httpd/conf.d/ssl.conf
@@ -0,0 +1,221 @@
+#
+# When we also provide SSL we have to listen to the
+# standard HTTPS port in addition.
+#
+Listen 443 https
+
+##
+## SSL Global Context
+##
+## All SSL configuration in this context applies both to
+## the main server and all SSL-enabled virtual hosts.
+##
+
+# Pass Phrase Dialog:
+# Configure the pass phrase gathering process.
+# The filtering dialog program (`builtin' is a internal
+# terminal dialog) has to provide the pass phrase on stdout.
+SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
+
+# Inter-Process Session Cache:
+# Configure the SSL Session Cache: First the mechanism
+# to use and second the expiring timeout (in seconds).
+SSLSessionCache shmcb:/run/httpd/sslcache(512000)
+SSLSessionCacheTimeout 300
+
+# Pseudo Random Number Generator (PRNG):
+# Configure one or more sources to seed the PRNG of the
+# SSL library. The seed data should be of good random quality.
+# WARNING! On some platforms /dev/random blocks if not enough entropy
+# is available. This means you then cannot use the /dev/random device
+# because it would lead to very long connection times (as long as
+# it requires to make more entropy available). But usually those
+# platforms additionally provide a /dev/urandom device which doesn't
+# block. So, if available, use this one instead. Read the mod_ssl User
+# Manual for more details.
+SSLRandomSeed startup file:/dev/urandom 256
+SSLRandomSeed connect builtin
+#SSLRandomSeed startup file:/dev/random 512
+#SSLRandomSeed connect file:/dev/random 512
+#SSLRandomSeed connect file:/dev/urandom 512
+
+#
+# Use "SSLCryptoDevice" to enable any supported hardware
+# accelerators. Use "openssl engine -v" to list supported
+# engine names. NOTE: If you enable an accelerator and the
+# server does not start, consult the error logs and ensure
+# your accelerator is functioning properly.
+#
+SSLCryptoDevice builtin
+#SSLCryptoDevice ubsec
+
+##
+## SSL Virtual Host Context
+##
+
+
+
+# General setup for the virtual host, inherited from global configuration
+#DocumentRoot "/var/www/html"
+#ServerName www.example.com:443
+
+# Use separate log files for the SSL virtual host; note that LogLevel
+# is not inherited from httpd.conf.
+ErrorLog logs/ssl_error_log
+TransferLog logs/ssl_access_log
+LogLevel warn
+
+# SSL Engine Switch:
+# Enable/Disable SSL for this virtual host.
+SSLEngine on
+
+# List the protocol versions which clients are allowed to connect with.
+# Disable SSLv3 by default (cf. RFC 7525 3.1.1). TLSv1 (1.0) should be
+# disabled as quickly as practical. By the end of 2016, only the TLSv1.2
+# protocol or later should remain in use.
+SSLProtocol all -SSLv3
+SSLProxyProtocol all -SSLv3
+
+# User agents such as web browsers are not configured for the user's
+# own preference of either security or performance, therefore this
+# must be the prerogative of the web server administrator who manages
+# cpu load versus confidentiality, so enforce the server's cipher order.
+SSLHonorCipherOrder on
+
+# SSL Cipher Suite:
+# List the ciphers that the client is permitted to negotiate.
+# See the mod_ssl documentation for a complete list.
+
+# By leaving this directive commented out, the system-wide OpenSSL
+# default is used. See update-crypto-policies(8) for more details.
+#SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
+#SSLProxyCipherSuite HIGH:MEDIUM:!aNULL:!MD5
+
+# Point SSLCertificateFile at a PEM encoded certificate. If
+# the certificate is encrypted, then you will be prompted for a
+# pass phrase. Note that restarting httpd will prompt again. Keep
+# in mind that if you have both an RSA and a DSA certificate you
+# can configure both in parallel (to also allow the use of DSA
+# ciphers, etc.)
+# Some ECC cipher suites (http://www.ietf.org/rfc/rfc4492.txt)
+# require an ECC certificate which can also be configured in
+# parallel.
+SSLCertificateFile /etc/pki/tls/certs/localhost.crt
+
+# Server Private Key:
+# If the key is not combined with the certificate, use this
+# directive to point at the key file. Keep in mind that if
+# you've both a RSA and a DSA private key you can configure
+# both in parallel (to also allow the use of DSA ciphers, etc.)
+# ECC keys, when in use, can also be configured in parallel
+SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
+
+# Server Certificate Chain:
+# Point SSLCertificateChainFile at a file containing the
+# concatenation of PEM encoded CA certificates which form the
+# certificate chain for the server certificate. Alternatively
+# the referenced file can be the same as SSLCertificateFile
+# when the CA certificates are directly appended to the server
+# certificate for convenience.
+#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
+
+# Certificate Authority (CA):
+# Set the CA certificate verification path where to find CA
+# certificates for client authentication or alternatively one
+# huge file containing all of them (file must be PEM encoded)
+#SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
+
+# Client Authentication (Type):
+# Client certificate verification type and depth. Types are
+# none, optional, require and optional_no_ca. Depth is a
+# number which specifies how deeply to verify the certificate
+# issuer chain before deciding the certificate is not valid.
+#SSLVerifyClient require
+#SSLVerifyDepth 10
+
+# Access Control:
+# With SSLRequire you can do per-directory access control based
+# on arbitrary complex boolean expressions containing server
+# variable checks and other lookup directives. The syntax is a
+# mixture between C and Perl. See the mod_ssl documentation
+# for more details.
+#
+#SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
+# and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \
+# and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \
+# and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \
+# and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \
+# or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
+#
+
+# SSL Engine Options:
+# Set various options for the SSL engine.
+# o FakeBasicAuth:
+# Translate the client X.509 into a Basic Authorisation. This means that
+# the standard Auth/DBMAuth methods can be used for access control. The
+# user name is the `one line' version of the client's X.509 certificate.
+# Note that no password is obtained from the user. Every entry in the user
+# file needs this password: `xxj31ZMTZzkVA'.
+# o ExportCertData:
+# This exports two additional environment variables: SSL_CLIENT_CERT and
+# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
+# server (always existing) and the client (only existing when client
+# authentication is used). This can be used to import the certificates
+# into CGI scripts.
+# o StdEnvVars:
+# This exports the standard SSL/TLS related `SSL_*' environment variables.
+# Per default this exportation is switched off for performance reasons,
+# because the extraction step is an expensive operation and is usually
+# useless for serving static content. So one usually enables the
+# exportation for CGI and SSI requests only.
+# o StrictRequire:
+# This denies access when "SSLRequireSSL" or "SSLRequire" applied even
+# under a "Satisfy any" situation, i.e. when it applies access is denied
+# and no other module can change it.
+# o OptRenegotiate:
+# This enables optimized SSL connection renegotiation handling when SSL
+# directives are used in per-directory context.
+#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
+
+ SSLOptions +StdEnvVars
+
+
+ SSLOptions +StdEnvVars
+
+
+# SSL Protocol Adjustments:
+# The safe and default but still SSL/TLS standard compliant shutdown
+# approach is that mod_ssl sends the close notify alert but doesn't wait for
+# the close notify alert from client. When you need a different shutdown
+# approach you can use one of the following variables:
+# o ssl-unclean-shutdown:
+# This forces an unclean shutdown when the connection is closed, i.e. no
+# SSL close notify alert is sent or allowed to be received. This violates
+# the SSL/TLS standard but is needed for some brain-dead browsers. Use
+# this when you receive I/O errors because of the standard approach where
+# mod_ssl sends the close notify alert.
+# o ssl-accurate-shutdown:
+# This forces an accurate shutdown when the connection is closed, i.e. a
+# SSL close notify alert is sent and mod_ssl waits for the close notify
+# alert of the client. This is 100% SSL/TLS standard compliant, but in
+# practice often causes hanging connections with brain-dead browsers. Use
+# this only for browsers where you know that their SSL implementation
+# works correctly.
+# Notice: Most problems of broken clients are also related to the HTTP
+# keep-alive facility, so you usually additionally want to disable
+# keep-alive for those clients, too. Use variable "nokeepalive" for this.
+# Similarly, one has to force some clients to use HTTP/1.0 to workaround
+# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
+# "force-response-1.0" for this.
+BrowserMatch "MSIE [2-5]" \
+ nokeepalive ssl-unclean-shutdown \
+ downgrade-1.0 force-response-1.0
+
+# Per-Server Logging:
+# The home of a custom SSL log file. Use this when you want a
+# compact non-error SSL logfile on a virtual host basis.
+CustomLog logs/ssl_request_log \
+ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
+
+
+
diff --git a/configuration/httpd/conf.d/userdir.conf b/configuration/httpd/conf.d/userdir.conf
new file mode 100644
index 00000000000..b5d7a49ef72
--- /dev/null
+++ b/configuration/httpd/conf.d/userdir.conf
@@ -0,0 +1,36 @@
+#
+# UserDir: The name of the directory that is appended onto a user's home
+# directory if a ~user request is received.
+#
+# The path to the end user account 'public_html' directory must be
+# accessible to the webserver userid. This usually means that ~userid
+# must have permissions of 711, ~userid/public_html must have permissions
+# of 755, and documents contained therein must be world-readable.
+# Otherwise, the client will only receive a "403 Forbidden" message.
+#
+
+ #
+ # UserDir is disabled by default since it can confirm the presence
+ # of a username on the system (depending on home directory
+ # permissions).
+ #
+ UserDir disabled
+
+ #
+ # To enable requests to /~user/ to serve the user's public_html
+ # directory, remove the "UserDir disabled" line above, and uncomment
+ # the following line instead:
+ #
+ #UserDir public_html
+
+
+#
+# Control access to UserDir directories. The following is an example
+# for a site where these directories are restricted to read-only.
+#
+
+ AllowOverride FileInfo AuthConfig Limit Indexes
+ Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+ Require method GET POST OPTIONS
+
+
diff --git a/configuration/httpd/conf.d/webalizer.conf b/configuration/httpd/conf.d/webalizer.conf
new file mode 100644
index 00000000000..556d95ddd87
--- /dev/null
+++ b/configuration/httpd/conf.d/webalizer.conf
@@ -0,0 +1,14 @@
+#
+# This configuration file maps the webalizer log analysis
+# results (generated daily) into the URL space. By default
+# these results are only accessible from the local host.
+#
+Alias /usage /var/www/usage
+
+
+ Order deny,allow
+ Deny from all
+ Allow from 127.0.0.1
+ Allow from ::1
+ # Allow from .example.com
+
diff --git a/configuration/httpd/conf.d/welcome.conf.org b/configuration/httpd/conf.d/welcome.conf.org
new file mode 100644
index 00000000000..f6f00ceec31
--- /dev/null
+++ b/configuration/httpd/conf.d/welcome.conf.org
@@ -0,0 +1,18 @@
+#
+# This configuration file enables the default "Welcome" page if there
+# is no default index page present for the root URL. To disable the
+# Welcome page, comment out all the lines below.
+#
+# NOTE: if this file is removed, it will be restored on upgrades.
+#
+
+ Options -Indexes
+ ErrorDocument 403 /.noindex.html
+
+
+
+ AllowOverride None
+ Require all granted
+
+
+Alias /.noindex.html /var/www/noindex/index.html
diff --git a/configuration/httpd/conf.d/wordpress.conf b/configuration/httpd/conf.d/wordpress.conf
new file mode 100644
index 00000000000..90c998c0369
--- /dev/null
+++ b/configuration/httpd/conf.d/wordpress.conf
@@ -0,0 +1,13 @@
+
+ ServerName blog.sapsailing.com
+ Use SSL
+ CustomLog logs/blog_log combined
+ DocumentRoot /home/wordpress/wordpress
+
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
diff --git a/configuration/httpd/conf/httpd.conf b/configuration/httpd/conf/httpd.conf
new file mode 100644
index 00000000000..87a87b78c1d
--- /dev/null
+++ b/configuration/httpd/conf/httpd.conf
@@ -0,0 +1,938 @@
+# This is the main Apache server configuration file. It contains the
+# configuration directives that give the server its instructions.
+# See for detailed information.
+# In particular, see
+#
+# for a discussion of each configuration directive.
+#
+#
+# Do NOT simply read the instructions in here without understanding
+# what they do. They're here only as hints or reminders. If you are unsure
+# consult the online docs. You have been warned.
+#
+# The configuration directives are grouped into three basic sections:
+# 1. Directives that control the operation of the Apache server process as a
+# whole (the 'global environment').
+# 2. Directives that define the parameters of the 'main' or 'default' server,
+# which responds to requests that aren't handled by a virtual host.
+# These directives also provide default values for the settings
+# of all virtual hosts.
+# 3. Settings for virtual hosts, which allow Web requests to be sent to
+# different IP addresses or hostnames and have them handled by the
+# same Apache server process.
+#
+# Configuration and logfile names: If the filenames you specify for many
+# of the server's control files begin with "/" (or "drive:/" for Win32), the
+# server will use that explicit path. If the filenames do *not* begin
+# with "/", the value of ServerRoot is prepended -- so "logs/foo.log"
+# with ServerRoot set to "/etc/httpd" will be interpreted by the
+# server as "/etc/httpd/logs/foo.log".
+#
+
+### Section 1: Global Environment
+#
+# The directives in this section affect the overall operation of Apache,
+# such as the number of concurrent requests it can handle or where it
+# can find its configuration files.
+#
+
+#
+# Don't give away too much information about all the subcomponents
+# we are running. Comment out this line if you don't mind remote sites
+# finding out what major optional modules you are running
+ServerTokens OS
+
+#
+# ServerRoot: The top of the directory tree under which the server's
+# configuration, error, and log files are kept.
+#
+# NOTE! If you intend to place this on an NFS (or otherwise network)
+# mounted filesystem then please read the LockFile documentation
+# (available at );
+# you will save yourself a lot of trouble.
+#
+# Do NOT add a slash at the end of the directory path.
+#
+ServerRoot "/etc/httpd"
+
+#
+# PidFile: The file in which the server should record its process
+# identification number when it starts.
+#
+PidFile /var/run/httpd/httpd.pid
+
+#
+# Timeout: The number of seconds before receives and sends time out.
+#
+Timeout 6400
+
+#
+# KeepAlive: Whether or not to allow persistent connections (more than
+# one request per connection). Set to "Off" to deactivate.
+#
+KeepAlive Off
+
+#
+# MaxKeepAliveRequests: The maximum number of requests to allow
+# during a persistent connection. Set to 0 to allow an unlimited amount.
+# We recommend you leave this number high, for maximum performance.
+#
+MaxKeepAliveRequests 100
+
+#
+# KeepAliveTimeout: Number of seconds to wait for the next request from the
+# same client on the same connection.
+#
+KeepAliveTimeout 160
+
+##
+## Server-Pool Size Regulation (MPM specific)
+##
+
+# prefork MPM
+# StartServers: number of server processes to start
+# MinSpareServers: minimum number of server processes which are kept spare
+# MaxSpareServers: maximum number of server processes which are kept spare
+# ServerLimit: maximum value for MaxClients for the lifetime of the server
+# MaxClients: maximum number of server processes allowed to start
+# MaxRequestsPerChild: maximum number of requests a server process serves
+
+StartServers 8
+MinSpareServers 10
+MaxSpareServers 50
+ServerLimit 4048
+MaxRequestWorkers 4048
+MaxConnectionsPerChild 3000
+
+
+# worker MPM
+# StartServers: initial number of server processes to start
+# MaxClients: maximum number of simultaneous client connections
+# MinSpareThreads: minimum number of worker threads which are kept spare
+# MaxSpareThreads: maximum number of worker threads which are kept spare
+# ThreadsPerChild: constant number of worker threads in each server process
+# MaxRequestsPerChild: maximum number of requests a server process serves
+
+StartServers 2
+MaxClients 4048
+MinSpareThreads 25
+MaxSpareThreads 75
+ThreadsPerChild 25
+MaxRequestsPerChild 0
+
+
+#
+# Listen: Allows you to bind Apache to specific IP addresses and/or
+# ports, in addition to the default. See also the
+# directive.
+#
+# Change this to Listen on specific IP addresses as shown below to
+# prevent Apache from glomming onto all bound IP addresses (0.0.0.0)
+#
+#Listen 12.34.56.78:80
+Listen 80
+
+#
+# Dynamic Shared Object (DSO) Support
+#
+# To be able to use the functionality of a module which was built as a DSO you
+# have to place corresponding `LoadModule' lines at this location so the
+# directives contained in it are actually available _before_ they are used.
+# Statically compiled modules (those listed by `httpd -l') do not need
+# to be loaded here.
+#
+# Example:
+# LoadModule foo_module modules/mod_foo.so
+#
+Include conf.modules.d/*.conf
+
+LoadModule usertrack_module modules/mod_usertrack.so
+LoadModule speling_module modules/mod_speling.so
+LoadModule php7_module modules/libphp-7.1.so
+
+
+
+ # If php is turned on, we respect .php and .phps files.
+ AddType application/x-httpd-php .php
+ AddType application/x-httpd-php-source .phps
+
+ # Since most users will want index.php to work we
+ # also automatically enable index.php
+
+ DirectoryIndex index.html index.php
+
+
+
+#
+# The following modules are not loaded by default:
+#
+#LoadModule cern_meta_module modules/mod_cern_meta.so
+#LoadModule asis_module modules/mod_asis.so
+
+#
+# Load config files from the config directory "/etc/httpd/conf.d".
+#
+
+Include conf.d/*.conf
+
+#
+# ExtendedStatus controls whether Apache will generate "full" status
+# information (ExtendedStatus On) or just basic information (ExtendedStatus
+# Off) when the "server-status" handler is called. The default is Off.
+#
+ExtendedStatus On
+
+#
+# If you wish httpd to run as a different user or group, you must run
+# httpd as root initially and it will switch.
+#
+# User/Group: The name (or #number) of the user/group to run httpd as.
+# . On SCO (ODT 3) use "User nouser" and "Group nogroup".
+# . On HPUX you may not be able to use shared memory as nobody, and the
+# suggested workaround is to create a user www and use that user.
+# NOTE that some kernels refuse to setgid(Group) or semctl(IPC_SET)
+# when the value of (unsigned)Group is above 60000;
+# don't use Group #-1 on these systems!
+#
+User apache
+Group apache
+
+### Section 2: 'Main' server configuration
+#
+# The directives in this section set up the values used by the 'main'
+# server, which responds to any requests that aren't handled by a
+# definition. These values also provide defaults for
+# any containers you may define later in the file.
+#
+# All of these directives may appear inside containers,
+# in which case these default settings will be overridden for the
+# virtual host being defined.
+#
+
+#
+# ServerAdmin: Your address, where problems with the server should be
+# e-mailed. This address appears on some server-generated pages, such
+# as error documents. e.g. admin@your-domain.com
+#
+ServerAdmin admin@sapsailing.com
+
+#
+# ServerName gives the name and port that the server uses to identify itself.
+# This can often be determined automatically, but we recommend you specify
+# it explicitly to prevent problems during startup.
+#
+# If this is not set to valid DNS name for your host, server-generated
+# redirections will not work. See also the UseCanonicalName directive.
+#
+# If your host doesn't have a registered DNS name, enter its IP address here.
+# You will have to access it by its address anyway, and this will make
+# redirections work in a sensible way.
+#
+ServerName www.sapsailing.com:80
+
+#
+# UseCanonicalName: Determines how Apache constructs self-referencing
+# URLs and the SERVER_NAME and SERVER_PORT variables.
+# When set "Off", Apache will use the Hostname and Port supplied
+# by the client. When set "On", Apache will use the value of the
+# ServerName directive.
+#
+UseCanonicalName Off
+
+#
+# DocumentRoot: The directory out of which you will serve your
+# documents. By default, all requests are taken from this directory, but
+# symbolic links and aliases may be used to point to other locations.
+#
+DocumentRoot "/var/www/html"
+
+#
+# Each directory to which Apache has access can be configured with respect
+# to which services and features are allowed and/or disabled in that
+# directory (and its subdirectories).
+#
+# First, we configure the "default" to be a very restrictive set of
+# features.
+#
+
+ Options FollowSymLinks
+ AllowOverride None
+
+
+#
+# Note that from this point forward you must specifically allow
+# particular features to be enabled - so if something's not working as
+# you might expect, make sure that you have specifically enabled it
+# below.
+#
+
+#
+# This should be changed to whatever you set DocumentRoot to.
+#
+
+
+#
+# Possible values for the Options directive are "None", "All",
+# or any combination of:
+# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
+#
+# Note that "MultiViews" must be named *explicitly* --- "Options All"
+# doesn't give it to you.
+#
+# The Options directive is both complicated and important. Please see
+# http://httpd.apache.org/docs/2.2/mod/core.html#options
+# for more information.
+#
+ Options Indexes FollowSymLinks
+
+#
+# AllowOverride controls what directives may be placed in .htaccess files.
+# It can be "All", "None", or any combination of the keywords:
+# Options FileInfo AuthConfig Limit
+#
+ AllowOverride None
+
+#
+# Controls who can get stuff from this server.
+#
+ Order allow,deny
+ Allow from all
+
+
+
+#
+# UserDir: The name of the directory that is appended onto a user's home
+# directory if a ~user request is received.
+#
+# The path to the end user account 'public_html' directory must be
+# accessible to the webserver userid. This usually means that ~userid
+# must have permissions of 711, ~userid/public_html must have permissions
+# of 755, and documents contained therein must be world-readable.
+# Otherwise, the client will only receive a "403 Forbidden" message.
+#
+# See also: http://httpd.apache.org/docs/misc/FAQ.html#forbidden
+#
+
+ #
+ # UserDir is disabled by default since it can confirm the presence
+ # of a username on the system (depending on home directory
+ # permissions).
+ #
+ UserDir disable
+
+ #
+ # To enable requests to /~user/ to serve the user's public_html
+ # directory, remove the "UserDir disable" line above, and uncomment
+ # the following line instead:
+ #
+ #UserDir public_html
+
+
+
+#
+# Control access to UserDir directories. The following is an example
+# for a site where these directories are restricted to read-only.
+#
+#
+# AllowOverride FileInfo AuthConfig Limit
+# Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+#
+# Order allow,deny
+# Allow from all
+#
+#
+# Order deny,allow
+# Deny from all
+#
+#
+
+#
+# DirectoryIndex: sets the file that Apache will serve if a directory
+# is requested.
+#
+# The index.html.var file (a type-map) is used to deliver content-
+# negotiated documents. The MultiViews Option can be used for the
+# same purpose, but it is much slower.
+#
+DirectoryIndex index.html index.html.var
+
+#
+# AccessFileName: The name of the file to look for in each directory
+# for additional configuration directives. See also the AllowOverride
+# directive.
+#
+AccessFileName .htaccess
+
+#
+# The following lines prevent .htaccess and .htpasswd files from being
+# viewed by Web clients.
+#
+
+ Order allow,deny
+ Deny from all
+
+
+#
+# TypesConfig describes where the mime.types file (or equivalent) is
+# to be found.
+#
+TypesConfig /etc/mime.types
+
+#
+# The mod_mime_magic module allows the server to use various hints from the
+# contents of the file itself to determine its type. The MIMEMagicFile
+# directive tells the module where the hint definitions are located.
+#
+
+# MIMEMagicFile /usr/share/magic.mime
+ MIMEMagicFile conf/magic
+
+
+#
+# HostnameLookups: Log the names of clients or just their IP addresses
+# e.g., www.apache.org (on) or 204.62.129.132 (off).
+# The default is off because it'd be overall better for the net if people
+# had to knowingly turn this feature on, since enabling it means that
+# each client request will result in AT LEAST one lookup request to the
+# nameserver.
+#
+HostnameLookups Off
+
+#
+# EnableMMAP: Control whether memory-mapping is used to deliver
+# files (assuming that the underlying OS supports it).
+# The default is on; turn this off if you serve from NFS-mounted
+# filesystems. On some systems, turning it off (regardless of
+# filesystem) can improve performance; for details, please see
+# http://httpd.apache.org/docs/2.2/mod/core.html#enablemmap
+#
+#EnableMMAP off
+
+#
+# EnableSendfile: Control whether the sendfile kernel support is
+# used to deliver files (assuming that the OS supports it).
+# The default is on; turn this off if you serve from NFS-mounted
+# filesystems. Please see
+# http://httpd.apache.org/docs/2.2/mod/core.html#enablesendfile
+#
+#EnableSendfile off
+
+#
+# ErrorLog: The location of the error log file.
+# If you do not specify an ErrorLog directive within a
+# container, error messages relating to that virtual host will be
+# logged here. If you *do* define an error logfile for a
+# container, that host's errors will be logged there and not here.
+#
+ErrorLog logs/error_log
+
+#
+# LogLevel: Control the number of messages logged to the error_log.
+# Possible values include: debug, info, notice, warn, error, crit,
+# alert, emerg.
+#
+LogLevel warn
+
+#
+# The following directives define some format nicknames for use with
+# a CustomLog directive (see below).
+#
+LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+LogFormat "%{Host}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" sapsailing_httpd_redirect_combined
+LogFormat "%{Host}i %{original_client_ip}e %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" sapsailing_httpd_redirect_combined_first_forwarded_for_ip
+
+# In the request header received from the client look for an X-Forwarded-For header
+# field. If found, log the first IP address in it as the client host; otherwise, use
+# the combined format above which uses %h for the client host.
+SetEnvIf X-Forwarded-For "^([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*).*$" original_client_ip=$1
+
+# The first_forwarded_for_ip log format can be used if the original_client_ip variable is set.
+# This means that a non-empty X-Forwarded-For header field was found and that the original_client_ip
+# variable now contains that client's IP address. Use this format to log the original client's IP
+# address instead of the immediate client's (probably some proxy server).
+LogFormat "%v %{original_client_ip}e %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" first_forwarded_for_ip
+
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+LogFormat "%{Referer}i -> %U" referer
+LogFormat "%{User-agent}i" agent
+
+# "combinedio" includes actual counts of actual bytes received (%I) and sent (%O); this
+# requires the mod_logio module to be loaded.
+#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
+
+#
+# The location and format of the access logfile (Common Logfile Format).
+# If you do not define any access logfiles within a
+# container, they will be logged here. Contrariwise, if you *do*
+# define per- access logfiles, transactions will be
+# logged therein and *not* in this file.
+#
+#CustomLog logs/access_log common
+
+#
+# If you would like to have separate agent and referer logfiles, uncomment
+# the following directives.
+#
+#CustomLog logs/referer_log referer
+#CustomLog logs/agent_log agent
+
+#
+# For a single logfile with access, agent, and referer information
+# (Combined Logfile Format), use the following directive:
+#
+#CustomLog logs/access_log combined
+#
+# Use the following for an Apache that runs behind a load balancer such
+# as an Amazon ELB or some other proxy. It records
+# the header field X-Forwarded-For as the client host IP address if such
+# an address is found, and the immediate client's IP if no such header
+# field is found.
+CustomLog logs/access_log combined env=!original_client_ip
+CustomLog logs/access_log first_forwarded_for_ip env=original_client_ip
+
+#
+# Optionally add a line containing the server version and virtual host
+# name to server-generated pages (internal error documents, FTP directory
+# listings, mod_status and mod_info output etc., but not CGI generated
+# documents or custom error documents).
+# Set to "EMail" to also include a mailto: link to the ServerAdmin.
+# Set to one of: On | Off | EMail
+#
+ServerSignature Off
+
+#
+# WebDAV module configuration section.
+#
+
+ # Location of the WebDAV lock database.
+ DAVLockDB /var/lib/dav/lockdb
+
+
+#
+# ScriptAlias: This controls which directories contain server scripts.
+# ScriptAliases are essentially the same as Aliases, except that
+# documents in the realname directory are treated as applications and
+# run by the server when requested rather than as documents sent to the client.
+# The same rules about trailing "/" apply to ScriptAlias directives as to
+# Alias.
+#
+ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
+
+#
+# "/var/www/cgi-bin" should be changed to whatever your ScriptAliased
+# CGI directory exists, if you have that configured.
+#
+
+ AllowOverride None
+ Options None
+ Order allow,deny
+ Allow from all
+
+
+#
+# Redirect allows you to tell clients about documents which used to exist in
+# your server's namespace, but do not anymore. This allows you to tell the
+# clients where to look for the relocated document.
+# Example:
+# Redirect permanent /foo http://www.example.com/bar
+
+#
+# Directives controlling the display of server-generated directory listings.
+#
+
+#
+# IndexOptions: Controls the appearance of server-generated directory
+# listings.
+#
+IndexOptions FancyIndexing VersionSort NameWidth=* HTMLTable
+
+#
+# AddIcon* directives tell the server which icon to show for different
+# files or filename extensions. These are only displayed for
+# FancyIndexed directories.
+#
+AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
+
+AddIconByType (TXT,/icons/text.gif) text/*
+AddIconByType (IMG,/icons/image2.gif) image/*
+AddIconByType (SND,/icons/sound2.gif) audio/*
+AddIconByType (VID,/icons/movie.gif) video/*
+
+AddIcon /icons/binary.gif .bin .exe
+AddIcon /icons/binhex.gif .hqx
+AddIcon /icons/tar.gif .tar
+AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
+AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
+AddIcon /icons/a.gif .ps .ai .eps
+AddIcon /icons/layout.gif .html .shtml .htm .pdf
+AddIcon /icons/text.gif .txt
+AddIcon /icons/c.gif .c
+AddIcon /icons/p.gif .pl .py
+AddIcon /icons/f.gif .for
+AddIcon /icons/dvi.gif .dvi
+AddIcon /icons/uuencoded.gif .uu
+AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
+AddIcon /icons/tex.gif .tex
+AddIcon /icons/bomb.gif core
+
+AddIcon /icons/back.gif ..
+AddIcon /icons/hand.right.gif README
+AddIcon /icons/folder.gif ^^DIRECTORY^^
+AddIcon /icons/blank.gif ^^BLANKICON^^
+
+#
+# DefaultIcon is which icon to show for files which do not have an icon
+# explicitly set.
+#
+DefaultIcon /icons/unknown.gif
+
+#
+# AddDescription allows you to place a short description after a file in
+# server-generated indexes. These are only displayed for FancyIndexed
+# directories.
+# Format: AddDescription "description" filename
+#
+#AddDescription "GZIP compressed document" .gz
+#AddDescription "tar archive" .tar
+#AddDescription "GZIP compressed tar archive" .tgz
+
+#
+# ReadmeName is the name of the README file the server will look for by
+# default, and append to directory listings.
+#
+# HeaderName is the name of a file which should be prepended to
+# directory indexes.
+ReadmeName README.html
+HeaderName HEADER.html
+
+#
+# IndexIgnore is a set of filenames which directory indexing should ignore
+# and not include in the listing. Shell-style wildcarding is permitted.
+#
+IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
+
+#
+# DefaultLanguage and AddLanguage allows you to specify the language of
+# a document. You can then use content negotiation to give a browser a
+# file in a language the user can understand.
+#
+# Specify a default language. This means that all data
+# going out without a specific language tag (see below) will
+# be marked with this one. You probably do NOT want to set
+# this unless you are sure it is correct for all cases.
+#
+# * It is generally better to not mark a page as
+# * being a certain language than marking it with the wrong
+# * language!
+#
+# DefaultLanguage nl
+#
+# Note 1: The suffix does not have to be the same as the language
+# keyword --- those with documents in Polish (whose net-standard
+# language code is pl) may wish to use "AddLanguage pl .po" to
+# avoid the ambiguity with the common suffix for perl scripts.
+#
+# Note 2: The example entries below illustrate that in some cases
+# the two character 'Language' abbreviation is not identical to
+# the two character 'Country' code for its country,
+# E.g. 'Danmark/dk' versus 'Danish/da'.
+#
+# Note 3: In the case of 'ltz' we violate the RFC by using a three char
+# specifier. There is 'work in progress' to fix this and get
+# the reference data for rfc1766 cleaned up.
+#
+# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl)
+# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de)
+# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja)
+# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn)
+# Norwegian (no) - Polish (pl) - Portugese (pt)
+# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv)
+# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW)
+#
+AddLanguage ca .ca
+AddLanguage cs .cz .cs
+AddLanguage da .dk
+AddLanguage de .de
+AddLanguage el .el
+AddLanguage en .en
+AddLanguage eo .eo
+AddLanguage es .es
+AddLanguage et .et
+AddLanguage fr .fr
+AddLanguage he .he
+AddLanguage hr .hr
+AddLanguage it .it
+AddLanguage ja .ja
+AddLanguage ko .ko
+AddLanguage ltz .ltz
+AddLanguage nl .nl
+AddLanguage nn .nn
+AddLanguage no .no
+AddLanguage pl .po
+AddLanguage pt .pt
+AddLanguage pt-BR .pt-br
+AddLanguage ru .ru
+AddLanguage sv .sv
+AddLanguage zh-CN .zh-cn
+AddLanguage zh-TW .zh-tw
+
+#
+# LanguagePriority allows you to give precedence to some languages
+# in case of a tie during content negotiation.
+#
+# Just list the languages in decreasing order of preference. We have
+# more or less alphabetized them here. You probably want to change this.
+#
+LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW
+
+#
+# ForceLanguagePriority allows you to serve a result page rather than
+# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback)
+# [in case no accepted languages matched the available variants]
+#
+ForceLanguagePriority Prefer Fallback
+
+#
+# Specify a default charset for all content served; this enables
+# interpretation of all content as UTF-8 by default. To use the
+# default browser choice (ISO-8859-1), or to allow the META tags
+# in HTML content to override this choice, comment out this
+# directive:
+#
+AddDefaultCharset UTF-8
+#AddDefaultCharset ISO-8859-1
+
+#
+# AddType allows you to add to or override the MIME configuration
+# file mime.types for specific file types.
+#
+#AddType application/x-tar .tgz
+
+#
+# AddEncoding allows you to have certain browsers uncompress
+# information on the fly. Note: Not all browsers support this.
+# Despite the name similarity, the following Add* directives have nothing
+# to do with the FancyIndexing customization directives above.
+#
+#AddEncoding x-compress .Z
+#AddEncoding x-gzip .gz .tgz
+
+# If the AddEncoding directives above are commented-out, then you
+# probably should define those extensions to indicate media types:
+#
+AddType application/x-compress .Z
+AddType application/x-gzip .gz .tgz
+
+#
+# AddHandler allows you to map certain file extensions to "handlers":
+# actions unrelated to filetype. These can be either built into the server
+# or added with the Action directive (see below)
+#
+# To use CGI scripts outside of ScriptAliased directories:
+# (You will also need to add "ExecCGI" to the "Options" directive.)
+#
+#AddHandler cgi-script .cgi
+
+#
+# For files that include their own HTTP headers:
+#
+#AddHandler send-as-is asis
+
+#
+# For type maps (negotiated resources):
+# (This is enabled by default to allow the Apache "It Worked" page
+# to be distributed in multiple languages.)
+#
+AddHandler type-map var
+
+#
+# Filters allow you to process content before it is sent to the client.
+#
+# To parse .shtml files for server-side includes (SSI):
+# (You will also need to add "Includes" to the "Options" directive.)
+#
+AddType text/html .shtml
+AddOutputFilter INCLUDES .shtml
+
+#
+# Action lets you define media types that will execute a script whenever
+# a matching file is called. This eliminates the need for repeated URL
+# pathnames for oft-used CGI file processors.
+# Format: Action media/type /cgi-script/location
+# Format: Action handler-name /cgi-script/location
+#
+
+#
+# Customizable error responses come in three flavors:
+# 1) plain text 2) local redirects 3) external redirects
+#
+# Some examples:
+ErrorDocument 500 http://status.sapsailing.com/500.html
+ErrorDocument 503 http://status.sapsailing.com/503.html
+#ErrorDocument 404 "Document not found"
+#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
+#ErrorDocument 402 http://www.example.com/subscription_info.html
+#
+
+#
+# Putting this all together, we can internationalize error responses.
+#
+# We use Alias to redirect any /error/HTTP_.html.var response to
+# our collection of by-error message multi-language collections. We use
+# includes to substitute the appropriate text.
+#
+# You can modify the messages' appearance without changing any of the
+# default HTTP_.html.var files by adding the line:
+#
+# Alias /error/include/ "/your/include/path/"
+#
+# which allows you to create your own set of files by starting with the
+# /var/www/error/include/ files and
+# copying them to /your/include/path/, even on a per-VirtualHost basis.
+#
+
+Alias /error/ "/var/www/error/"
+
+
+
+
+ AllowOverride None
+ Options IncludesNoExec
+ AddOutputFilter Includes html
+ AddHandler type-map var
+ Order allow,deny
+ Allow from all
+ LanguagePriority en es de fr
+ ForceLanguagePriority Prefer Fallback
+
+
+# ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
+# ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var
+# ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var
+# ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var
+# ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var
+# ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var
+# ErrorDocument 410 /error/HTTP_GONE.html.var
+# ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var
+# ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var
+# ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
+# ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var
+# ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
+# ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var
+# ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var
+# ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var
+# ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var
+# ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var
+
+
+
+
+#
+# The following directives modify normal HTTP response behavior to
+# handle known problems with browser implementations.
+#
+BrowserMatch "Mozilla/2" nokeepalive
+BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+BrowserMatch "RealPlayer 4\.0" force-response-1.0
+BrowserMatch "Java/1\.0" force-response-1.0
+BrowserMatch "JDK/1\.0" force-response-1.0
+
+#
+# The following directive disables redirects on non-GET requests for
+# a directory that does not include the trailing slash. This fixes a
+# problem with Microsoft WebFolders which does not appropriately handle
+# redirects for folders with DAV methods.
+# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV.
+#
+BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+BrowserMatch "MS FrontPage" redirect-carefully
+BrowserMatch "^WebDrive" redirect-carefully
+BrowserMatch "^WebDAVFS/1.[0123]" redirect-carefully
+BrowserMatch "^gnome-vfs/1.0" redirect-carefully
+BrowserMatch "^XML Spy" redirect-carefully
+BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully
+
+#
+# Allow server status reports generated by mod_status,
+# with the URL of http://servername/server-status
+# Change the ".example.com" to match your domain to enable.
+
+ SetHandler server-status
+ Order deny,allow
+ #Deny from all
+ Allow from all
+
+
+#
+# Allow remote server configuration reports, with the URL of
+# http://servername/server-info (requires that mod_info.c be loaded).
+# Change the ".example.com" to match your domain to enable.
+#
+#
+# SetHandler server-info
+# Order deny,allow
+# Deny from all
+# Allow from .example.com
+#
+
+#
+# Proxy Server directives. Uncomment the following lines to
+# enable the proxy server:
+#
+#
+#ProxyRequests On
+#
+#
+# Order deny,allow
+# Deny from all
+# Allow from .example.com
+#
+
+#
+# Enable/disable the handling of HTTP/1.1 "Via:" headers.
+# ("Full" adds the server version; "Block" removes all outgoing Via: headers)
+# Set to one of: Off | On | Full | Block
+#
+#ProxyVia On
+
+#
+# To enable a cache of proxied content, uncomment the following lines.
+# See http://httpd.apache.org/docs/2.2/mod/mod_cache.html for more details.
+#
+#
+# CacheEnable disk /
+# CacheRoot "/var/cache/mod_proxy"
+#
+#
+
+#
+# End of proxy directives.
+
+### Section 3: Virtual Hosts
+#
+# VirtualHost: If you want to maintain multiple domains/hostnames on your
+# machine you can setup VirtualHost containers for them. Most configurations
+# use only name-based virtual hosts so the server doesn't need to worry about
+# IP addresses. This is indicated by the asterisks in the directives below.
+#
+# Please see the documentation at
+#
+# for further details before you try to setup virtual hosts.
+#
+# You may use the command line option '-S' to verify your virtual host
+# configuration.
+
+#
+# VirtualHost example:
+# Almost any Apache directive may go into a VirtualHost container.
+# The first VirtualHost section is used for requests without a known
+# server name.
+#
+#
+# ServerAdmin webmaster@dummy-host.example.com
+# DocumentRoot /www/docs/dummy-host.example.com
+# ServerName dummy-host.example.com
+# ErrorLog logs/dummy-host.example.com-error_log
+# CustomLog logs/dummy-host.example.com-access_log common
+#
+
diff --git a/configuration/imageupgrade.sh b/configuration/imageupgrade.sh
new file mode 100755
index 00000000000..4f60257b0ca
--- /dev/null
+++ b/configuration/imageupgrade.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# Upgrades the AWS EC2 instance that this script is assumed to be executed on.
+# The steps are as follows:
+
+REBOOT_INDICATOR=/var/run/is-rebooted
+
+run_yum_update() {
+ echo "Updating packages using yum" >>/var/log/sailing.err
+ yum -y update
+}
+
+run_git_pull() {
+ echo "Pulling git to /home/sailing/code" >>/var/log/sailing.err
+ su - sailing -c "cd code; git pull"
+}
+
+run_refresh_instance_install_release() {
+ echo "Upgrading sailing server software" >>/var/log/sailing.err
+ su - sailing -c "cd servers; for i in *; do echo \"Upgrading \$i\"; cd \$i; echo \"Updating release in \`pwd\`\"; ./refreshInstance.sh install-release; cd ..; done" >>/var/log/sailing.err
+}
+
+clean_logrotate_target() {
+ echo "Clearing logrorate-targets" >>/var/log/sailing.err
+ rm -rf /var/log/logrotate-target/*
+}
+
+clean_httpd_logs() {
+ echo "Clearing httpd logs" >>/var/log/sailing.err
+ service httpd stop
+ rm -rf /var/log/httpd/*
+ rm /etc/httpd/conf.d/001-internals.conf
+}
+
+clean_sailing_logs() {
+ echo "Clearing sailing logs" >>/var/log/sailing.err
+ rm -rf /home/sailing/servers/*/logs/*
+}
+
+clean_startup_logs() {
+ echo "Clearing bootstrap logs" >>/var/log/sailing.err
+ rm /var/log/sailing*
+ # Ensure that upon the next boot the reboot indicator is not present, indicating that it's the first boot
+ rm "${REBOOT_INDICATOR}"
+}
+
+run_yum_update
+run_git_pull
+run_refresh_instance_install_release
+clean_logrotate_target
+clean_httpd_logs
+clean_sailing_logs
+clean_startup_logs
+
+# Finally, shut down the node unless "no-shutdown" was provided in the user data, so that a new AMI can be constructed cleanly
+if /opt/aws/bin/ec2-metadata -d | grep "^no-shutdown$"; then
+ echo "Shutdown disabled by no-shutdown option in user data"
+ touch /tmp/image-upgrade-finished
+else
+ shutdown -h now &
+fi
diff --git a/configuration/installPluginsForEclipse2020-06.sh b/configuration/installPluginsForEclipse2020-06.sh
old mode 100644
new mode 100755
diff --git a/configuration/installPluginsForEclipse2020-09.sh b/configuration/installPluginsForEclipse2020-09.sh
new file mode 100755
index 00000000000..62ba0bb0bb1
--- /dev/null
+++ b/configuration/installPluginsForEclipse2020-09.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# You need an installation of the 2020-09 release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
+# https://www.eclipse.org/downloads/packages/release/2020-09/r/eclipse-ide-eclipse-committers
+
+if [[ $1 == "" ]]; then
+ echo "You need to specify the Eclipse installation directory"
+ exit 1
+fi
+
+installPath="$1"
+
+installPlugins() {
+ "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
+ echo ""
+}
+
+updatePlugins() {
+ "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
+ echo ""
+}
+
+uninstallPlugins() {
+ "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -uninstallIU $1
+ echo ""
+}
+
+# Not necessary if using Eclipse for JEE developers
+echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
+installPlugins http://download.eclipse.org/releases/2020-09/ org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
+
+echo "Installing GWT plugin..."
+installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/v3/release com.gwtplugins.eclipse.suite.v3.feature.feature.group
+
+echo "Installing Mission Control plugin..."
+installPlugins http://download.oracle.com/technology/products/missioncontrol/updatesites/base/6.0.0/eclipse com.oracle.jmc.feature.ide.feature.group,com.oracle.jmc.feature.flightrecorder.feature.group,com.oracle.jmc.feature.core.feature.group,com.oracle.jmc.feature.console.feature.group,com.oracle.jmc.feature.core.feature.group,com.oracle.jmc.feature.core.feature.group
+
+echo "Installing GWT SDM debug bridge..."
+installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
+
+echo "Installing EasyShell..."
+installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
+
+echo "Installing Memory Analyzer..."
+installPlugins http://download.eclipse.org/releases/2020-09 org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
+
+echo "Installing SAP JVM Tools (profiler) ..."
+installPlugins https://tools.hana.ondemand.com/oxygen com.sap.jvm.profiling.feature.group
+
+echo "Installing UMLet ..."
+installPlugins https://www.umlet.com/umlet_latest/repository/ umlet-eclipse-feature.feature.group
+
+echo "Installing javax.xml.bind for news feed polling (see https://stackoverflow.com/questions/52528693/eclipse-internal-error-polling-news-feeds)..."
+installPlugins http://download.eclipse.org/tools/orbit/downloads/drops/R20200224183213/repository javax.xml.bind
+
+echo "Installing Enhanced Class Decompiler"
+installPlugins https://ecd-plugin.github.io/update org.sf.feeling.decompiler.feature.group,org.sf.feeling.decompiler.cfr.feature.group,org.sf.feeling.decompiler.jad.feature.group,org.sf.feeling.decompiler.jd.feature.group,org.sf.feeling.decompiler.procyon.feature.group
+
+echo "Uninstalling Wild Web Developer"
+uninstallPlugins org.eclipse.wildwebdeveloper.feature.feature.group
+
+echo "Installation completed!"
diff --git a/configuration/launchhudsonslave b/configuration/launchhudsonslave
index 71dcf06e6e8..bb90f885ce1 100755
--- a/configuration/launchhudsonslave
+++ b/configuration/launchhudsonslave
@@ -1,8 +1,8 @@
#!/bin/bash
AWS=/root/.local/bin/aws
REGION=eu-west-1
-#HUDSON_SLAVE_AMI_ID=ami-04956fe5a5293da6f
-HUDSON_SLAVE_AMI_ID=ami-0251a199f8d4a4729
+#HUDSON_SLAVE_AMI_ID=ami-05c12a89e3371ba45
+HUDSON_SLAVE_AMI_ID=ami-0c153e4928f340fe5
echo Launching instance...
instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'`
if [ "$instanceid" = "" ]; then
diff --git a/configuration/mongo_instance_setup/add-as-replica b/configuration/mongo_instance_setup/add-as-replica
index 2556fefcc73..d1dcf59bd43 100755
--- a/configuration/mongo_instance_setup/add-as-replica
+++ b/configuration/mongo_instance_setup/add-as-replica
@@ -1,21 +1,20 @@
#!/bin/bash
-REPLICA_SET_NAME=$(ec2-metadata | grep REPLICA_SET_NAME | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_NAME=//')
+eval $( ec2-metadata -d | sed -e 's/^user-data: //' )
if [ -z "$REPLICA_SET_NAME" ]; then
REPLICA_SET_NAME=live
fi
-REPLICA_SET_PRIMARY=$(ec2-metadata | grep REPLICA_SET_PRIMARY | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_PRIMARY=//')
if [ -z "$REPLICA_SET_PRIMARY" ]; then
REPLICA_SET_PRIMARY=mongo0.internal.sapsailing.com:27017
fi
-REPLICA_SET_PRIORITY=$(ec2-metadata | grep REPLICA_SET_PRIORITY | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_PRIORITY=//')
if [ -z "$REPLICA_SET_PRIORITY" ]; then
REPLICA_SET_PRIORITY=1
fi
-REPLICA_SET_VOTES=$(ec2-metadata | grep REPLICA_SET_VOTES | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_VOTES=//')
if [ -z "$REPLICA_SET_VOTES" ]; then
REPLICA_SET_VOTES=0
fi
if [ \! -z "REPLICA_SET_PRIMARY" ]; then
IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //')
echo "rs.add({host: \"$IP:27017\", priority: $REPLICA_SET_PRIORITY, votes: $REPLICA_SET_VOTES})" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true"
+else
+ echo "rs.initiate()" | mongo
fi
diff --git a/configuration/mongo_instance_setup/mongo-replica-set.service b/configuration/mongo_instance_setup/mongo-replica-set.service
index 5cba8232875..7f705ac6e3c 100644
--- a/configuration/mongo_instance_setup/mongo-replica-set.service
+++ b/configuration/mongo_instance_setup/mongo-replica-set.service
@@ -11,4 +11,4 @@ Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/bin/add-as-replica
ExecStop=/usr/local/bin/remove-as-replica
-TimeoutStopSpec=120s
+TimeoutStopSec=120s
diff --git a/configuration/mongo_instance_setup/remove-as-replica b/configuration/mongo_instance_setup/remove-as-replica
index 0f74eb32c70..9eb6385d1b7 100755
--- a/configuration/mongo_instance_setup/remove-as-replica
+++ b/configuration/mongo_instance_setup/remove-as-replica
@@ -1,6 +1,5 @@
#!/bin/bash
-REPLICA_SET_NAME=$(ec2-metadata | grep REPLICA_SET_NAME | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_NAME=//')
-REPLICA_SET_PRIMARY=$(ec2-metadata | grep REPLICA_SET_PRIMARY | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_PRIMARY=//')
+eval $( ec2-metadata -d | sed -e 's/^user-data: //' )
if [ \! -z "REPLICA_SET_PRIMARY" ]; then
IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //')
echo "rs.remove(\"$IP:27017\")" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true"
diff --git a/configuration/motd-banner b/configuration/motd-banner
new file mode 100755
index 00000000000..071d87b8dac
--- /dev/null
+++ b/configuration/motd-banner
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+REGION=`wget -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/[a-z]$//'`
+AMI_ID=`wget -q -O - http://169.254.169.254/latest/meta-data/ami-id`
+AMI_INFO=`aws ec2 describe-images --region ${REGION} --image-ids "$AMI_ID"`
+AMI_NAME=`echo $AMI_INFO | python -c "import sys,json; print json.load(sys.stdin)['Images'][0]['Name']"`
+AMI_CREATED=`echo $AMI_INFO | python -c "import sys,json; print json.load(sys.stdin)['Images'][0]['CreationDate']"`
+
+INSTANCE_ID=`wget -q -O - http://169.254.169.254/latest/meta-data/instance-id`
+INSTANCE_NAME_INFO=`aws ec2 describe-tags --region ${REGION} --filter "Name=resource-id,Values=$INSTANCE_ID"`
+INSTANCE_AWS_NAME=`echo $INSTANCE_NAME_INFO | python -c "import sys,json; print json.load(sys.stdin)['Tags'][0]['Value']"`
+
+source /home/sailing/servers/server/env.sh
+cat << EOF
+...........................................................
+/////////////////////////////////////////////////////////,
+///////////////////////////////////////////////////////,
+///* .*////// ///// .*/////////////,
+(. /((/ (((( /((((((((,
+. ,((((/.,(((( .((( /(/* ((((((,
+. ./((((((((, , *(( /(((* /(((,
+(. ./((* ,(. (( */*. ((,
+(((* ( ((( .( /*
+((((((((/ , .,/(*
+(/ .*(((* (((((*
+, (((/((/ (##*
+##(,. ./#,,,,,/#######,,,,,,,,,(*
+#################################* .*,
+###############################* ,./ *
+.............................. .
+
+Welcome to instance running image $AMI_NAME!
+The image has been created at <$AMI_CREATED>.
+
+This instance is named <$INSTANCE_AWS_NAME> and
+is running the Analytics server <$SERVER_NAME>.
+
+It will use a maximum of <$MEMORY> memory and is configured to
+be a <$USE_ENVIRONMENT>. Please handle with love and care!
+
+EOF
diff --git a/configuration/sailing b/configuration/sailing
index 21c2d5f3cb2..d582adb7b93 100755
--- a/configuration/sailing
+++ b/configuration/sailing
@@ -22,12 +22,16 @@
RETVAL=0
-JAVA_START_INSTANCES="server"
SERVERS_DIR=/home/sailing/servers
+cd "${SERVERS_DIR}"
+JAVA_START_INSTANCES="$(find * -type d -prune)"
GIT_REPOSITORY=/home/sailing/code
APACHE_CONFIG_DIR=/etc/httpd/conf.d
+APACHE_INTERNALS_CONFIG_FILE="$APACHE_CONFIG_DIR/001-internals.conf"
+EC2_METADATA_CMD=/opt/aws/bin/ec2-metadata
+REBOOT_INDICATOR=/var/run/is-rebooted
-echo "Executing with $1" >>/var/log/sailing.out
+echo "Executing with $1 at `date`" >>/var/log/sailing.err
start_tmux() {
su - sailing -c "/home/sailing/bin/tmuxConsole.sh unattended"
@@ -35,47 +39,48 @@ start_tmux() {
}
start_servers() {
- su - sailing -c "cd $GIT_REPOSITORY && git fetch && git merge origin/master" 2>>/var/log/sailing.err >>/var/log/sailing.out
- NUMBER_OF_INSTANCES=`echo "$JAVA_START_INSTANCES" | wc -w`
- MEMORY_PER_INSTANCE_IN_MB=`cat /proc/meminfo | grep MemTotal | awk '{printf("%i\n", ($2 / 1024 * 0.75 - 1500) / '$NUMBER_OF_INSTANCES' );}'`
- echo "Using ${MEMORY_PER_INSTANCE_IN_MB}MB as default heap size per instance."
- for conf in $JAVA_START_INSTANCES; do
- echo "Checking for amazon update..." >>/var/log/sailing.out
- echo "MEMORY=\"${MEMORY_PER_INSTANCE_IN_MB}m\"" >>$SERVERS_DIR/$conf/env.sh
- su - sailing -c "cd $SERVERS_DIR/$conf && $GIT_REPOSITORY/java/target/refreshInstance.sh auto-install" 2>>/var/log/sailing.err >>/var/log/sailing.out
- (
- # execute env.sh in a subshell which now has the same set of variables that the server will see
- source $SERVERS_DIR/$conf/env.sh
- echo "Starting Java server \"$conf\"" >>/var/log/sailing.out
- su - sailing -c "cd $SERVERS_DIR/$conf && ./start" 2>>/var/log/sailing.err >>/var/log/sailing.out
- server_name="`echo $SERVER_NAME | tr [A-Z] [a-z]`"
- echo "Appending macro invocation to $APACHE_CONFIG_DIR/001-events.conf to map internal IP $INSTANCE_INTERNAL_IP4 to plain server running $SERVER_PORT..." >>/var/log/sailing.out
- echo "Use Plain-SSL ${INSTANCE_INTERNAL_IP4} 127.0.0.1 $SERVER_PORT" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Appending macro invocation to $APACHE_CONFIG_DIR/001-events.conf to map ${server_name}.sapsailing.com to event with ID $EVENT_ID with server running on local port $SERVER_PORT..." >>/var/log/sailing.out
- echo "## EVENT ${server_name}" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Use Event-SSL ${server_name}.sapsailing.com \"$EVENT_ID\" 127.0.0.1 $SERVER_PORT" >>$APACHE_CONFIG_DIR/001-events.conf
- # check if an secondary event shall be pasted to 001-events.conf
- if [[ ${HTTP_SEC_EVENT} && ${HTTP_SEC_EVENT_ID} ]]; then
- http_sec_event="`echo $HTTP_SEC_EVENT | tr [A-Z] [a-z]`"
- echo "Appending macro invocation to $APACHE_CONFIG_DIR/001-events.conf to map ${http_sec_event}.sapsailing.com to event with ID ${HTTP_SEC_EVENT_ID} with server running on local port $SERVER_PORT..." >>/var/log/sailing.out
- echo "## EVENT ${http_sec_event}" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Use Event ${http_sec_event}.sapsailing.com \"$HTTP_SEC_EVENT_ID\" 127.0.0.1 $SERVER_PORT" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Use Event-SSL ${http_sec_event}.sapsailing.com \"$HTTP_SEC_EVENT_ID\" 127.0.0.1 $SERVER_PORT" >>$APACHE_CONFIG_DIR/001-events.conf
- fi
- HTTP_LOGROTATE=/etc/logrotate.d/httpd
- echo "Patching $HTTP_LOGROTATE so that old logs go to /var/log/old/$SERVER_NAME/$INSTANCE_IP4" >>/var/log/sailing.out
- sed -i -e "s/\/var\/log\/old\/\([^/]*\)\/\([^/ ]*\)/\/var\/log\/old\/$SERVER_NAME\/$INSTANCE_IP4/" $HTTP_LOGROTATE
- # terminating the subshell will forget all the variable assignments brought in by env.sh, clearing for the next server instance
- )
- RETVAL=$?
- [ $RETVAL -eq 0 ] && success || failure
- done
- launch_httpd
+ if which $EC2_METADATA_CMD && $EC2_METADATA_CMD -d | sed "s/user-data\: //g" | grep "^image-upgrade$"; then
+ echo "Found image-upgrade in EC2 user data; upgrading image, then probably shutting down for AMI creation depending on the no-shutdown user data string..." >>/var/log/sailing.err
+ $GIT_REPOSITORY/configuration/imageupgrade.sh
+ else
+ echo "No image-upgrade request found in EC2 user data $($EC2_METADATA_CMD -d); proceeding with regular server launch..." >>/var/log/sailing.err
+ echo "Servers to launch: ${JAVA_START_INSTANCES}" >>/var/log/sailing.err
+ NUMBER_OF_INSTANCES=`echo "$JAVA_START_INSTANCES" | wc -w`
+ FIRST_SERVER=`echo $JAVA_START_INSTANCES | awk '{print $1;}'`
+ if [ -f "${REBOOT_INDICATOR}" ]; then
+ echo "This is a re-boot. No EC2 user data is evaluated for server configuration; no server configuration is performed. Only configured applications are launched." >>/var/log/sailing.err
+ for conf in ${JAVA_START_INSTANCES}; do
+ su - sailing -c "cd ${SERVERS_DIR}/${conf} && ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err
+ done
+ else
+ echo "This is a first-time boot. EC2 user data is evaluated for potential application deployment and configuration; reverse proxy entries may be created, and applications are launched." >>/var/log/sailing.err
+ if [ ${NUMBER_OF_INSTANCES} -gt 1 ]; then
+ echo "More than one server cannot be configured from user data; only configuring ${FIRST_SERVER}" >>/var/log/sailing.err
+ fi
+ configure_and_start_server "${FIRST_SERVER}"
+ create_basic_httpd_config
+ reload_httpd
+ echo 1 >"${REBOOT_INDICATOR}"
+ fi
+ fi
+}
+
+# Call with the server directory name (not the full path, just a single element from ${JAVA_START_INSTANCE}) as parameter
+# Example: configure_and_start_server server
+# This is expected to be called only in case there is only one server to configure; otherwise, the same EC2 user data
+# would get applied to all application configurations which would not be a good idea.
+configure_and_start_server() {
+ conf="$1"
+ mkdir -p "${SERVERS_DIR}/${conf}" >/dev/null 2>/dev/null
+ local ENV_SH="${SERVERS_DIR}/$conf/env.sh"
+ su - sailing -c "cd ${SERVERS_DIR}/${conf} && ${GIT_REPOSITORY}/java/target/refreshInstance.sh auto-install; ./start; ./defineReverseProxyMappings.sh" 2>>/var/log/sailing.err >>/var/log/sailing.err
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && success || failure
}
stop_servers() {
for conf in $JAVA_START_INSTANCES; do
- echo "Stopping Java server $conf" >> /var/log/sailing.out
+ echo "Stopping Java server $conf" >> /var/log/sailing.err
su - sailing -c "cd $SERVERS_DIR/$conf && ./stop"
RETVAL=$?
[ $RETVAL -eq 0 ] && success || failure
@@ -85,41 +90,45 @@ stop_servers() {
}
sync_logs() {
- echo "Executing logrotate followed by a sync to ensure that logs are synchronized" >> /var/log/sailing.out
+ echo "Executing logrotate followed by a sync to ensure that logs are synchronized" >> /var/log/sailing.err
logrotate -f /etc/logrotate.conf
sync
}
-# launch_httpd(server_name, port, event_id)
-launch_httpd() {
- echo "Will try to launch httpd so this replica can work with an ELB easily." >>/var/log/sailing.out
+reload_httpd() {
+ echo "Will try to launch httpd so this replica can work with an ELB easily." >>/var/log/sailing.err
if [ -x /etc/init.d/httpd ]; then
- FIRST_SERVER=`echo $JAVA_START_INSTANCES | awk '{print $1;}'`
- source $SERVERS_DIR/$FIRST_SERVER/env.sh
- # Append Apache macro invocation for /internal-server-status based on mod_status and INSTANCE_DNS to $APACHE_CONFIG_DIR/001-events.conf
- echo "Appending macro usage for $INSTANCE_DNS/internal-server-status URL for mod_status based Apache monitoring to $APACHE_CONFIG_DIR/001-events.conf" >>/var/log/sailing.out
- echo "## SERVER STATUS" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Use Status $INSTANCE_DNS internal-server-status" >>$APACHE_CONFIG_DIR/001-events.conf
- echo "Launching httpd..." >>/var/log/sailing.out
- service httpd start
+ echo "Reloading httpd configuration..." >>/var/log/sailing.err
+ service httpd reload
else
- echo "Can't launch httpd; start script doesn't seem to be installed at /etc/init.d/httpd"
+ echo "Can't launch httpd; start script doesn't seem to be installed at /etc/init.d/httpd"
fi
}
+# Adds a Plain-SSL mapping to the first server's port and a mapping for /internal-server-status, both to 001-internals.conf
+create_basic_httpd_config() {
+ FIRST_SERVER=`echo $JAVA_START_INSTANCES | awk '{print $1;}'`
+ source $SERVERS_DIR/$FIRST_SERVER/env.sh
+ echo "Writing macro invocation to ${APACHE_INTERNALS_CONFIG_FILE} to map internal IP $INSTANCE_INTERNAL_IP4 to plain server running $SERVER_PORT..." >>/var/log/sailing.err
+ echo "Use Plain-SSL ${INSTANCE_INTERNAL_IP4} 127.0.0.1 $SERVER_PORT" >"${APACHE_INTERNALS_CONFIG_FILE}"
+ # Append Apache macro invocation for /internal-server-status based on mod_status and INSTANCE_DNS to "${APACHE_INTERNALS_CONFIG_FILE}"
+ echo "Appending macro usage for $INSTANCE_DNS/internal-server-status URL for mod_status based Apache monitoring to ${APACHE_INTERNALS_CONFIG_FILE}" >>/var/log/sailing.err
+ echo "## SERVER STATUS" >>"${APACHE_INTERNALS_CONFIG_FILE}"
+ echo "Use Status $INSTANCE_DNS internal-server-status" >>"${APACHE_INTERNALS_CONFIG_FILE}"
+}
+
stop_httpd() {
if [ -x /etc/init.d/httpd ]; then
service httpd stop
- echo "Stopped httpd..." >>/var/log/sailing.out
+ echo "Stopped httpd..." >>/var/log/sailing.err
fi
}
# See how we were called.
case "$1" in
start)
- /usr/local/bin/mountnvmeswap
- /usr/sbin/update-motd
start_servers
+ /usr/sbin/update-motd
touch /var/lock/subsys/sailing
;;
stop)
diff --git a/home.md b/home.md
index be7c488b29a..a5a11b21c28 100644
--- a/home.md
+++ b/home.md
@@ -33,6 +33,7 @@ SAP is at the center of today’s technology revolution, developing innovations
* [[Amazon EC2|wiki/info/landscape/amazon-ec2]]
* [[EC2 Backup Strategy|wiki/info/landscape/amazon-ec2-backup-strategy]]
* [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]]
+ * [[Upgrading an EC2 image|wiki/info/landscape/upgrading-ec2-image]]
* [[Creating a webserver EC2 image from scratch|wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch]]
* [[EC2 mail relaying vs. Amazon Simple E-Mail Service (SES)|wiki/info/landscape/mail-relaying]]
* [[Establishing support@sapsailing.com with AWS SES, SNS, and Lambda|wiki/info/landscape/support-email]]
@@ -93,9 +94,11 @@ SAP is at the center of today’s technology revolution, developing innovations
## HowTo
* [[Onboarding|wiki/howto/onboarding]]
+* [[Managing Events with the AdminConsole|wiki/howto/adminconsoleinstructions]]
* [[Importing Sessions from Expedition|wiki/howto/expeditionimport]]
* [[Checking our DBs for a user record by e-mail|wiki/howto/privacy]]
* [[Managing ORC Performance Curve Regattas|wiki/howto/setup-orc-regatta]]
+* [[Qualtrics Surveys|wiki/howto/qualtrics/qualtrics]]
### Development
diff --git a/java/com.amazon.aws.aws-java-api.updatesite/README b/java/com.amazon.aws.aws-java-api.updatesite/README
index 2d92fc642b2..78c5b0380b0 100755
--- a/java/com.amazon.aws.aws-java-api.updatesite/README
+++ b/java/com.amazon.aws.aws-java-api.updatesite/README
@@ -6,3 +6,6 @@ and established manifest, classpath and build.properties file so the project
can be built both, in Eclipse and with Maven.
See /java/com.amazon.aws.aws-java-api/README for further instructions.
+
+See also /java/com.sap.sailing.targetplatform/scripts/uploadAwsApiRepositoryToServer.sh
+for uploading the update site to the p2 repository at p2.sapsailing.com.
\ No newline at end of file
diff --git a/java/com.amazon.aws.aws-java-api.updatesite/features/aws-sdk/feature.xml b/java/com.amazon.aws.aws-java-api.updatesite/features/aws-sdk/feature.xml
index 9a66d6904ff..1a7f58d5dde 100755
--- a/java/com.amazon.aws.aws-java-api.updatesite/features/aws-sdk/feature.xml
+++ b/java/com.amazon.aws.aws-java-api.updatesite/features/aws-sdk/feature.xml
@@ -2,21 +2,21 @@
diff --git a/java/com.amazon.aws.aws-java-api.updatesite/site.xml b/java/com.amazon.aws.aws-java-api.updatesite/site.xml
index a13455cf456..6ffe012824e 100755
--- a/java/com.amazon.aws.aws-java-api.updatesite/site.xml
+++ b/java/com.amazon.aws.aws-java-api.updatesite/site.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/java/com.amazon.aws.aws-java-api/README b/java/com.amazon.aws.aws-java-api/README
index 74bbec0ce19..f233a53a834 100755
--- a/java/com.amazon.aws.aws-java-api/README
+++ b/java/com.amazon.aws.aws-java-api/README
@@ -57,3 +57,6 @@ updates .classpath, pom.xml, build.properties and META-INF/MANIFEST.MF according
runs a Maven build to produce the wrapper bundle's JAR file. This JAR is then copied to the
update site project's plugins/aws-sdk folder, the feature.xml file for the update site is updated
to reflect the correct version number, and then the update site is built using Maven.
+
+If you like what you've got, update the p2.sapsailing.com version of the aws-sdk update site by invoking
+ /java/com.sap.sailing.targetplatform/scripts/uploadAwsApiRepositoryToServer.sh
\ No newline at end of file
diff --git a/java/com.amazon.aws.aws-java-api/build.gradle b/java/com.amazon.aws.aws-java-api/build.gradle
index 7c98d54dea3..dd140de2709 100644
--- a/java/com.amazon.aws.aws-java-api/build.gradle
+++ b/java/com.amazon.aws.aws-java-api/build.gradle
@@ -5,13 +5,13 @@ repositories {
}
dependencies {
- implementation platform('software.amazon.awssdk:bom:2.13.61')
+ implementation platform('software.amazon.awssdk:bom:2.15.16')
implementation 'software.amazon.awssdk:s3'
implementation 'software.amazon.awssdk:ec2'
implementation 'software.amazon.awssdk:route53'
implementation 'software.amazon.awssdk:cloudwatch'
implementation 'software.amazon.awssdk:elasticloadbalancingv2'
- implementation 'software.amazon.awssdk:firehose'
+ implementation 'software.amazon.awssdk:autoscaling'
implementation group: 'software.amazon.awssdk', name: 'sdk-core', classifier: 'sources'
implementation group: 'software.amazon.awssdk', name: 'arns', classifier: 'sources'
implementation group: 'software.amazon.awssdk', name: 'aws-core', classifier: 'sources'
@@ -24,7 +24,7 @@ dependencies {
implementation group: 'software.amazon.awssdk', name: 'route53', classifier: 'sources'
implementation group: 'software.amazon.awssdk', name: 'cloudwatch', classifier: 'sources'
implementation group: 'software.amazon.awssdk', name: 'elasticloadbalancingv2', classifier: 'sources'
- implementation group: 'software.amazon.awssdk', name: 'firehose', classifier: 'sources'
+ implementation group: 'software.amazon.awssdk', name: 'autoscaling', classifier: 'sources'
}
task downloadLibs(type: Copy) {
into file('lib')
diff --git a/java/com.amazon.aws.aws-java-api/pom.xml b/java/com.amazon.aws.aws-java-api/pom.xml
index ab57a6b2b75..83c4b8e5f66 100644
--- a/java/com.amazon.aws.aws-java-api/pom.xml
+++ b/java/com.amazon.aws.aws-java-api/pom.xml
@@ -8,6 +8,6 @@
1.0.0-SNAPSHOTcom.amazon.aws.aws-java-api
- 2.13.61
+ 2.15.16eclipse-plugin
diff --git a/java/com.sap.sailing.declination/NOAAImporter (No Proxy).launch b/java/com.sap.sailing.declination/NOAAImporter (No Proxy).launch
index 5fe9d48152e..edb94cd9a81 100644
--- a/java/com.sap.sailing.declination/NOAAImporter (No Proxy).launch
+++ b/java/com.sap.sailing.declination/NOAAImporter (No Proxy).launch
@@ -7,6 +7,7 @@
-
+
+
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BranchIOConstants.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BranchIOConstants.java
index c8bfdb36e06..39063b963c8 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BranchIOConstants.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BranchIOConstants.java
@@ -11,6 +11,14 @@ public class BranchIOConstants {
public static final String OPEN_REGATTA_2_APP_BRANCHIO_PATH = "checkinUrl";
public static final String OPEN_REGATTA_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/publicInvite";
public static final String OPEN_REGATTA_3_APP_BRANCHIO_PATH = "checkinUrl";
+ public static final String RACEMANAGER_APP_BRANCH_QUICK_LINK = "https://racemanager-app.sapsailing.com/invite";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_SERVER_URL = "server_url";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_DEVICE_CONFIG_IDENTIFIER = "device_config_identifier";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_DEVICE_CONFIG_UUID = "device_config_uuid";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_TOKEN = "token";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_EVENT_ID = "event_id";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_COURSE_AREA_UUID = "course_area_uuid";
+ public static final String RACEMANAGER_APP_BRANCH_PARAM_PRIORITY = "priority";
private BranchIOConstants() {
}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java
index 835bfc675f3..5f06edf0c58 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java
@@ -372,6 +372,7 @@ public enum DetailType implements Serializable {
allowed.add(RACE_IMPLIED_WIND);
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
allowed.add(RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
+ allowed.add(RACE_CURRENT_COURSE_OVER_GROUND_IN_TRUE_DEGREES);
allowed.add(RACE_CURRENT_DISTANCE_FOILED_IN_METERS);
allowed.add(RACE_CURRENT_DURATION_FOILED_IN_SECONDS);
allowed.add(RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS);
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java
index 9f3beb0590c..df29dfa6d84 100644
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java
@@ -16,20 +16,15 @@ public class BoatClassDTO extends NamedDTO implements Serializable, Comparablenull, no leg details are known yet, the race is not being tracked or the details
@@ -618,6 +621,8 @@ public class LeaderboardEntryDTO implements Serializable {
result = prime * result + ((calculatedTime == null) ? 0 : calculatedTime.hashCode());
result = prime * result + ((calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null) ? 0
: calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.hashCode());
+ result = prime * result + ((currentSpeedAndCourseOverGround == null) ? 0
+ : currentSpeedAndCourseOverGround.hashCode());
result = prime * result + (discarded ? 1231 : 1237);
result = prime * result + ((distanceToStarboardSideOfStartLineInMeters == null) ? 0
: distanceToStarboardSideOfStartLineInMeters.hashCode());
@@ -703,6 +708,12 @@ public class LeaderboardEntryDTO implements Serializable {
} else if (!calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead
.equals(other.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead))
return false;
+ if (currentSpeedAndCourseOverGround == null) {
+ if (other.currentSpeedAndCourseOverGround != null)
+ return false;
+ } else if (!currentSpeedAndCourseOverGround
+ .equals(other.currentSpeedAndCourseOverGround))
+ return false;
if (discarded != other.discarded)
return false;
if (distanceToStarboardSideOfStartLineInMeters == null) {
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DeviceConfigurationQRCodeUtils.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DeviceConfigurationQRCodeUtils.java
index 4060377e9ae..6a852f9f744 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DeviceConfigurationQRCodeUtils.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DeviceConfigurationQRCodeUtils.java
@@ -7,42 +7,64 @@ import java.util.UUID;
/**
* This class is used by our backend, in GWT-client code and by the Android app. Therefore we cannot use classes like
- * {@link URLEncoder} to help us with the encoding.
+ * {@link URLEncoder} to help us with the encoding.
*/
public class DeviceConfigurationQRCodeUtils {
- public static final String deviceIdentifierKey = "identifier";
+ private static final String BASE_INVITATION_URL = "https://racemanager-app.sapsailing.com/invite";
+ public static final String serverUrl = "server_url";
+ public static final String eventId = "event_id";
+ public static final String courseAreaId = "course_area_uuid";
+ public static final String priority = "priority";
+ public static final String deviceIdentifierKey = "device_config_identifier";
+ public static final String deviceUuidKey = "device_config_uuid";
public static final String accessTokenKey = "token";
- public static final String deviceUuidKey = "uuid";
-
+
public static class DeviceConfigurationDetails {
- private final String apkUrl;
+ private final String url;
private final String deviceIdentifier;
private final UUID uuid;
private final String accessToken;
- public DeviceConfigurationDetails(String apkUrl, UUID uuid, String deviceConfigurationName, String accessToken) {
+
+ public DeviceConfigurationDetails(String url, UUID uuid, String deviceConfigurationName, String accessToken) {
super();
- this.apkUrl = apkUrl;
+ this.url = url;
this.uuid = uuid;
this.deviceIdentifier = deviceConfigurationName;
this.accessToken = accessToken;
}
- public String getApkUrl() {
- return apkUrl;
+
+ public String getUrl() {
+ return url;
}
+
public UUID getUuid() { return uuid; }
+
public String getDeviceIdentifier() { return deviceIdentifier; }
+
public String getAccessToken() {
return accessToken;
}
}
-
+
public static interface URLDecoder {
String decode(String encodedURL);
}
- public static String composeQRContent(String urlEncodedDeviceConfigName, String apkUrl, String accessToken, String urlEncodedDeviceIdAsString) {
- return apkUrl + "#" + deviceIdentifierKey + "=" + urlEncodedDeviceConfigName + "&" + deviceUuidKey + "="
- + urlEncodedDeviceIdAsString + "&" + accessTokenKey + "=" + accessToken;
+ public static String composeQRContent(String serverUrlWithoutFinalSlash, String urlEncodedDeviceConfigName,
+ String urlEncodedDeviceIdAsString, UUID eventId, UUID courseAreaId, Integer priority,
+ String accessToken) {
+ return BASE_INVITATION_URL
+ + "?"+serverUrl+"="+serverUrlWithoutFinalSlash
+ + "&" + deviceIdentifierKey + "=" + urlEncodedDeviceConfigName
+ + "&" + deviceUuidKey + "=" + urlEncodedDeviceIdAsString
+ + (eventId != null ? ("&" + DeviceConfigurationQRCodeUtils.eventId + "=" + eventId) : "")
+ + (courseAreaId != null ? ("&" + DeviceConfigurationQRCodeUtils.courseAreaId + "=" + courseAreaId) : "")
+ + (priority != null ? ("&" + DeviceConfigurationQRCodeUtils.priority + "=" + priority) : "")
+ + (accessToken != null ? ("&" + accessTokenKey + "=" + accessToken) : "");
}
public static DeviceConfigurationDetails splitQRContent(String qrCodeContent, URLDecoder urlDecoder) {
@@ -50,7 +72,7 @@ public class DeviceConfigurationQRCodeUtils {
if (fragmentIndex == -1 || fragmentIndex == 0 || qrCodeContent.length() == fragmentIndex + 1) {
throw new IllegalArgumentException("There is no server or identifier.");
}
- String fragment = qrCodeContent.substring(fragmentIndex + 1, qrCodeContent.length());
+ String fragment = qrCodeContent.substring(fragmentIndex + 1);
final String[] params = fragment.split("&");
final Map paramMap = new HashMap<>();
for (String param : params) {
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/AuthorPriority.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/AuthorPriority.java
new file mode 100755
index 00000000000..b01c1ab8edc
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/AuthorPriority.java
@@ -0,0 +1,26 @@
+package com.sap.sailing.domain.common.racelog;
+
+/**
+ * Generally, any {@code AbstractLogEvent} has an author of type {@code AbstractLogEventAuthor} which has a numerical
+ * non-negative priority. Lesser numerical values represent higher priority/precedence. This enumeration captures
+ * the default priorities the Race Manager App is expected to handle.
+ *
+ * @author Axel Uhl (D043530)
+ *
+ */
+public enum AuthorPriority {
+ ADMIN(0),
+ OFFICER_ON_VESSEL(1),
+ SHORE_CONTROL(2),
+ DEMO_MODE(3);
+
+ private final int priority;
+
+ private AuthorPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/MarkAlreadyUsedInRaceException.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/MarkAlreadyUsedInRaceException.java
new file mode 100644
index 00000000000..34574edde19
--- /dev/null
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/MarkAlreadyUsedInRaceException.java
@@ -0,0 +1,25 @@
+package com.sap.sailing.domain.common.racelog.tracking;
+
+import java.io.Serializable;
+
+public class MarkAlreadyUsedInRaceException extends Exception implements Serializable {
+
+ private static final long serialVersionUID = 2040461905745135256L;
+
+ public MarkAlreadyUsedInRaceException() {
+ super();
+ this.raceNames = "";
+ }
+
+ public MarkAlreadyUsedInRaceException(String message, String raceNames) {
+ super(message);
+ this.raceNames = raceNames;
+ }
+
+ private String raceNames;
+
+ public String getRaceNames() {
+ return raceNames;
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java
index 2ae5d1c2842..83019609dba 100755
--- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java
+++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/windfinder/AvailableWindFinderSpotCollections.java
@@ -24,7 +24,8 @@ public enum AvailableWindFinderSpotCollections {
TEGERNSEE("tegernsee"),
TORTOLA("tortola"),
AUCKLAND("auckland"),
- AARHUS("aarhus");
+ AARHUS("aarhus"),
+ NEW_YORK("new_york");
private final String name;
diff --git a/java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF
index 91f62f7d8ca..890dcc954e1 100644
--- a/java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF
@@ -8,8 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.json.simple;bundle-version="1.1.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1",
- org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)",
- org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)",
+ org.apache.httpcomponents.httpclient;bundle-version="4.5.5",
+ org.apache.httpcomponents.httpcore;bundle-version="4.4.9",
com.sap.sailing.domain.common,
com.sap.sailing.domain.igtimiadapter.persistence,
com.sap.sailing.domain.shared.android,
diff --git a/java/com.sap.sailing.domain.igtimiadapter.gateway/build.properties b/java/com.sap.sailing.domain.igtimiadapter.gateway/build.properties
index a4af5b4a1ef..cab8cf66a3c 100644
--- a/java/com.sap.sailing.domain.igtimiadapter.gateway/build.properties
+++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/build.properties
@@ -3,4 +3,5 @@ source.. = src/,\
output.. = bin/
bin.includes = META-INF/,\
.,\
- WEB-INF/
+ WEB-INF/,\
+ redirect.html
diff --git a/java/com.sap.sailing.domain.igtimiadapter.gateway/redirect.html b/java/com.sap.sailing.domain.igtimiadapter.gateway/redirect.html
new file mode 100644
index 00000000000..2c42adca434
--- /dev/null
+++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/redirect.html
@@ -0,0 +1,29 @@
+
+
+Igtimi
+
+
+
+
+
\ No newline at end of file
diff --git a/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/oauth/AuthorizationCallback.java b/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/oauth/AuthorizationCallback.java
index c9ec0b8ad6f..a377ce1d9a2 100644
--- a/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/oauth/AuthorizationCallback.java
+++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/oauth/AuthorizationCallback.java
@@ -4,10 +4,12 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
+import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
@@ -55,7 +57,10 @@ public class AuthorizationCallback {
Response result;
if (state != null) {
// redirect to the server whose base URL is contained in the state parameter
- result = Response.temporaryRedirect(new URI(state+"/igtimi/oauth/v1"+AUTHORIZATIONCALLBACK+"?code="+URLEncoder.encode(code, "UTF-8"))).build();
+ String target = Base64.getEncoder().encodeToString(
+ (state + "/igtimi/oauth/v1" + AUTHORIZATIONCALLBACK + "?code=" + URLEncoder.encode(code, "UTF-8"))
+ .getBytes());
+ result = Response.temporaryRedirect(new URI(state + "/igtimi/redirect.html?" + target)).build();
} else {
if (code != null) {
try {
@@ -82,7 +87,15 @@ public class AuthorizationCallback {
}
return result;
}
-
+
+ @POST
+ @Produces("text/plain;charset=UTF-8")
+ @Path(AUTHORIZATIONCALLBACK)
+ public Response obtainAccessTokenPost(@QueryParam("code") String code, @QueryParam("state") String state)
+ throws ClientProtocolException, IOException, IllegalStateException, ParseException, URISyntaxException {
+ return obtainAccessToken(code, state);
+ }
+
@GET
@Produces("application/json;charset=UTF-8")
@Path("uri")
diff --git a/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF
index 18426fa0674..fbd948f9ce4 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF
@@ -8,8 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.json.simple;bundle-version="1.1.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1",
- org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)",
- org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)",
+ org.apache.httpcomponents.httpclient;bundle-version="4.5.5",
+ org.apache.httpcomponents.httpcore;bundle-version="4.4.9",
com.sap.sailing.domain.common,
com.sap.sailing.domain.igtimiadapter.persistence,
com.sap.sailing.domain.shared.android,
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ConnectivityUtils.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ConnectivityUtils.java
index 984b278e5f0..987236be597 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ConnectivityUtils.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ConnectivityUtils.java
@@ -16,7 +16,7 @@ import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@@ -48,7 +48,7 @@ public class ConnectivityUtils {
return result.toString();
}
- public static HttpResponse postForm(String baseUrl, final String action, final Map inputFieldsToSubmit, DefaultHttpClient client, String referer)
+ public static HttpResponse postForm(String baseUrl, final String action, final Map inputFieldsToSubmit, CloseableHttpClient client, String referer)
throws UnsupportedEncodingException, IOException, ClientProtocolException {
HttpPost post = new HttpPost(baseUrl+action);
List urlParameters = new ArrayList();
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/DataAccessWindowDeserializer.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/DataAccessWindowDeserializer.java
index 2678f29ae1c..205172d5b48 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/DataAccessWindowDeserializer.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/DataAccessWindowDeserializer.java
@@ -14,8 +14,8 @@ public class DataAccessWindowDeserializer extends HasPermissionsDeserializer {
public DataAccessWindow createDataAccessWindowFromJson(JSONObject resourceJson, IgtimiConnection conn) {
return new DataAccessWindowImpl((Long) resourceJson.get("id"),
- new MillisecondsTimePoint(((Double) resourceJson.get("start_time")).longValue()),
- new MillisecondsTimePoint(((Double) resourceJson.get("end_time")).longValue()),
+ new MillisecondsTimePoint(((Number) resourceJson.get("start_time")).longValue()),
+ new MillisecondsTimePoint(((Number) resourceJson.get("end_time")).longValue()),
(String) resourceJson.get("device_serial_number"),
getPermissions((JSONObject) resourceJson.get("permissions")),
getSecurityEntity((JSONObject) resourceJson.get("recipient")));
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java
index 4dc43d64ccb..9823b5404d7 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java
@@ -25,15 +25,13 @@ import org.apache.commons.lang.StringEscapeUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
-import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
@@ -115,7 +113,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
}
private Account getAccount(String creatorName, String accessToken) throws ClientProtocolException, IOException, IllegalStateException, ParseException {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
HttpGet getAccount = new HttpGet(getApiV1BaseUrl()+"account?access_token="+accessToken);
HttpResponse accountResponse = client.execute(getAccount);
JSONObject accountJson = ConnectivityUtils.getJsonFromResponse(accountResponse);
@@ -375,7 +373,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
* @throws RuntimeException in case there was an error while retrieving the token
*/
public Account obtainAccessTokenFromAuthorizationCode(String creatorName, String code) throws ClientProtocolException, IOException, IllegalStateException, ParseException {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(getOauthTokenUrl());
List urlParameters = new ArrayList();
urlParameters.add(new BasicNameValuePair("grant_type", "authorization_code"));
@@ -402,42 +400,23 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
* @return the authorization code which can then be used to obtain a permanent access token to be used by our client
* to access data owned by the user identified by e-mail and password.
*/
- public String authorizeAndReturnAuthorizedCode(String userEmail, String userPassword)
+ public String authorizeAndReturnAuthorizedCode(final String userEmail, final String userPassword)
throws ClientProtocolException, IOException, IllegalStateException, ParserConfigurationException,
SAXException, ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
logger.info("Trying to authorize application client " + getClient().getId() + " for user " + userEmail);
- DefaultHttpClient client = new SystemDefaultHttpClient();
- CookieStore cookieStore = new BasicCookieStore();
- client.setCookieStore(cookieStore);
- client.setRedirectStrategy(new LaxRedirectStrategyForAllRedirectResponseCodes());
+ final RedirectStrategyExtractingAuthorizationCode codeExtractor =
+ new RedirectStrategyExtractingAuthorizationCode(new LaxRedirectStrategyForAllRedirectResponseCodes());
+ CloseableHttpClient client = HttpClientBuilder.create()
+ .setDefaultCookieStore(new BasicCookieStore())
+ .setRedirectStrategy(codeExtractor)
+ .build();
HttpGet get = new HttpGet(getOauthAuthorizeUrl());
HttpResponse responseForAuthorize = client.execute(get);
- return signInAndReturnAuthorizationForm(client, responseForAuthorize, userEmail, userPassword);
- }
-
- @Override
- public Account createAccountToAccessUserData(String creatorName, String userEmail, String userPassword) throws ClientProtocolException,
- IOException, IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
- InstantiationException, IllegalAccessException, ClassCastException, ParseException {
- final Account result;
- String code = authorizeAndReturnAuthorizedCode(userEmail, userPassword);
- if (code != null) {
- result = obtainAccessTokenFromAuthorizationCode(creatorName, code);
- } else {
- result = null;
- }
- return result;
- }
-
- private String signInAndReturnAuthorizationForm(DefaultHttpClient client, HttpResponse response,
- final String userEmail, final String userPassword) throws ParserConfigurationException, SAXException,
- IOException, UnsupportedEncodingException, ClientProtocolException, IllegalStateException,
- ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
final String[] action = new String[1];
final Map inputFieldsToSubmit = new HashMap<>();
try {
- String pageContent = ConnectivityUtils.getContent(response).replaceAll("", "");
+ String pageContent = ConnectivityUtils.getContent(responseForAuthorize).replaceAll("", "");
parser.parse(new ByteArrayInputStream(pageContent.getBytes("UTF-8")), new DefaultHandler() {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
@@ -462,10 +441,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
// swallow; we try to grab what we can; let's hope it was enough...
logger.log(Level.FINE, "Exception trying to parse Igtimi authorization document", e);
}
- response.getEntity().getContent().close();
- final RedirectStrategy oldRedirectStrategy = client.getRedirectStrategy();
- final RedirectStrategyExtractingAuthorizationCode codeExtractor = new RedirectStrategyExtractingAuthorizationCode(oldRedirectStrategy);
- client.setRedirectStrategy(codeExtractor);
+ responseForAuthorize.getEntity().getContent().close();
logger.info("Posting sign-in form for user "+userEmail);
HttpResponse authorizationForm = ConnectivityUtils.postForm(getBaseUrl(), action[0], inputFieldsToSubmit, client, getSignInUrl());
if (codeExtractor.getCode() == null) {
@@ -477,12 +453,26 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
return codeExtractor.getCode();
}
+ @Override
+ public Account createAccountToAccessUserData(String creatorName, String userEmail, String userPassword) throws ClientProtocolException,
+ IOException, IllegalStateException, ParserConfigurationException, SAXException, ClassNotFoundException,
+ InstantiationException, IllegalAccessException, ClassCastException, ParseException {
+ final Account result;
+ String code = authorizeAndReturnAuthorizedCode(userEmail, userPassword);
+ if (code != null) {
+ result = obtainAccessTokenFromAuthorizationCode(creatorName, code);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
/**
* Parses the form in the autorizationForm response and posts it by submitting the form that contains
* the commit button with the value "Authorize". If a redirect strategy is set on the client it will
* see the redirect URL in the Location header. The redirection target is closed immediately.
*/
- private void authorizeAndGetCode(HttpResponse authorizationForm, DefaultHttpClient client)
+ private void authorizeAndGetCode(HttpResponse authorizationForm, CloseableHttpClient client)
throws IllegalStateException, SAXException, IOException, ParserConfigurationException,
ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException {
// If the user already authorized the app, an empty document will be returned
@@ -528,7 +518,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
}
public HttpClient getHttpClient() {
- HttpClient client = new SystemDefaultHttpClient();
+ HttpClient client = HttpClientBuilder.create().build();
return client;
}
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/RedirectStrategyExtractingAuthorizationCode.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/RedirectStrategyExtractingAuthorizationCode.java
index 3714d8e02bd..35a76c3b537 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/RedirectStrategyExtractingAuthorizationCode.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/RedirectStrategyExtractingAuthorizationCode.java
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.igtimiadapter.impl;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.Charset;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -66,7 +67,7 @@ public class RedirectStrategyExtractingAuthorizationCode implements RedirectStra
}
private String getCodeFromRedirect(final Header locationHeader) throws URISyntaxException {
- List params = URLEncodedUtils.parse(new URI(locationHeader.getValue()), "UTF-8");
+ List params = URLEncodedUtils.parse(new URI(locationHeader.getValue()), Charset.forName("UTF-8"));
for (NameValuePair param : params) {
if (param.getName().equals("code")) {
return param.getValue();
diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java
index 662cece766a..3c6b5a46ca3 100755
--- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java
+++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java
@@ -12,8 +12,6 @@ import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.ShutdownThread;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
@@ -297,13 +295,7 @@ public class WebSocketConnectionManager implements LiveDataConnection {
try {
if (uri.getScheme().equals("ws") || uri.getScheme().equals("wss")) {
logger.log(Level.INFO, "Trying to connect to " + uri + " for " + this);
- client = new WebSocketClient(new SslContextFactory()) {
- @Override
- public void destroy() {
- super.destroy();
- ShutdownThread.deregister(this); // this will make sure that after destroy() no reference is being held to this client
- }
- };
+ client = new WebSocketClient();
client.start();
currentSocket = new WebSocket();
igtimiServerTimepoint = null;
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
index b577352f4f6..721b992d996 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
@@ -1341,7 +1341,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
- autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, new MongoObjectFactoryImpl(database), registrationLinkSecret);
+ autoRestartTrackingUponCompetitorSetChange == null ? false : autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor,
+ new MongoObjectFactoryImpl(database), registrationLinkSecret);
} else {
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series, /* persistent */true,
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
index b0de29362ec..29ae0712349 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
@@ -24,6 +24,7 @@ import org.json.simple.JSONObject;
import com.mongodb.BasicDBList;
import com.mongodb.DuplicateKeyException;
import com.mongodb.MongoCommandException;
+import com.mongodb.ReadConcern;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
@@ -328,7 +329,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
@Override
public void storeLeaderboard(Leaderboard leaderboard) {
- MongoCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name());
+ MongoCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name()).withWriteConcern(WriteConcern.MAJORITY);
try {
leaderboardCollection.createIndex(new Document(FieldNames.LEADERBOARD_NAME.name(), 1));
} catch (NullPointerException npe) {
@@ -501,7 +502,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
@Override
public void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup) {
MongoCollection leaderboardGroupCollection = database.getCollection(CollectionNames.LEADERBOARD_GROUPS.name());
- MongoCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name());
+ MongoCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name()).withReadConcern(ReadConcern.MAJORITY);
try {
leaderboardGroupCollection.createIndex(new Document(FieldNames.LEADERBOARD_GROUP_NAME.name(), 1));
@@ -532,6 +533,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
Document leaderboardQuery = new Document(FieldNames.LEADERBOARD_NAME.name(), leaderboard.getName());
Document dbLeaderboard = leaderboardCollection.find(leaderboardQuery).first();
if (dbLeaderboard == null) {
+ // TODO use readConcern / writeConcern "majority"
storeLeaderboard(leaderboard);
dbLeaderboard = leaderboardCollection.find(leaderboardQuery).first();
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java
index c4b31ae5f96..1c5ba98c4de 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/MongoSensorFixStoreFactory.java
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence.racelog.tracking;
import java.net.UnknownHostException;
import com.mongodb.MongoException;
+import com.mongodb.WriteConcern;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreFactoryImpl;
@@ -19,4 +20,13 @@ public interface MongoSensorFixStoreFactory {
TypeBasedServiceFinderFactory serviceFinderFactory)
throws UnknownHostException, MongoException;
+ /**
+ * Like {@link #getMongoGPSFixStore(MongoObjectFactory, DomainObjectFactory, TypeBasedServiceFinderFactory)}, but
+ * additionally the write concern for the MongoDB writes can be controlled which otherwise usually defaults
+ * to {@link WriteConcern#UNACKNOWLEDGED} for the fixes as well as the metadata.
+ */
+ MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory, DomainObjectFactory domainObjectFactory,
+ TypeBasedServiceFinderFactory serviceFinderFactory, WriteConcern writeConcern)
+ throws UnknownHostException, MongoException;
+
}
\ No newline at end of file
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java
index af43d83b83b..3d107366420 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoSensorFixStoreFactoryImpl.java
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence.racelog.tracking.impl;
import java.net.UnknownHostException;
import com.mongodb.MongoException;
+import com.mongodb.WriteConcern;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
@@ -16,4 +17,11 @@ public class MongoSensorFixStoreFactoryImpl implements MongoSensorFixStoreFactor
throws UnknownHostException, MongoException {
return new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory);
}
+
+ @Override
+ public MongoSensorFixStore getMongoGPSFixStore(MongoObjectFactory mongoObjectFactory,
+ DomainObjectFactory domainObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory,
+ WriteConcern writeConcern) throws UnknownHostException, MongoException {
+ return new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, serviceFinderFactory, writeConcern);
+ }
}
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_pt.properties b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_pt.properties
index 1f322a58c3a..5b960c77000 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_pt.properties
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_pt.properties
@@ -3,7 +3,7 @@ followBranchDeeplink=Clique no link seguinte para iniciar o app {0} e efetuar o
welcomeTo=Bem-vindo(a) a
buoyTender=navio-balizador
buoyPingerAppName=SAP Sailing Buoy Pinger
-sailInSightAppName=SAP Sail Insight suportado pela SAP
+sailInSightAppName=SAP Sail InSight suportado pela SAP
register=Registro
scanQRCodeOrVisitUrlToRegisterAs=Certifique-se de que tem o app {0} instalado. Depois, digitalize o QRCode seguinte ou clique no link utilizando o seu smartphone para se registrar como
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_zh.properties b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_zh.properties
old mode 100755
new mode 100644
index 2e83375c766..7615d7be238
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_zh.properties
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages_zh.properties
@@ -3,7 +3,7 @@ followBranchDeeplink=点击以下链接启动 {0} 应用并注册为 {1}
welcomeTo=欢迎使用
buoyTender=浮标供应船
buoyPingerAppName=SAP Sailing Buoy Pinger
-sailInSightAppName=SAP 提供支持的 Sail Insight
+sailInSightAppName=SAP 提供支持的 Sail InSight
register=注册
scanQRCodeOrVisitUrlToRegisterAs=确保已安装 {0} 应用。然后,使用智能手机扫描以下二维码或点击链接注册
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingInvitationMailBuilder.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingInvitationMailBuilder.java
index f0e373e8ef9..d9cc81e46bd 100755
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingInvitationMailBuilder.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingInvitationMailBuilder.java
@@ -109,17 +109,12 @@ abstract class RaceLogTrackingInvitationMailBuilder {
SerializableMultipartSupplier getMultipartSupplier() throws MessagingException {
final SerializableMultipartSupplier mixedSupplier = new SerializableMultipartSupplier("mixed");
-
final SerializableMultipartSupplier alternativeSupplier = new SerializableMultipartSupplier("alternative");
mixedSupplier.addBodyPart(new SerializableMultipartMimeBodyPartSupplier(alternativeSupplier));
-
- alternativeSupplier.addBodyPart(new SerializableDefaultMimeBodyPartSupplier(text.toString(), "text/plain"));
-
+ alternativeSupplier.addBodyPart(new SerializableDefaultMimeBodyPartSupplier(text.toString(), "text/plain; charset=UTF-8"));
final SerializableMultipartSupplier relatedSupplier = new SerializableMultipartSupplier("related");
alternativeSupplier.addBodyPart(new SerializableMultipartMimeBodyPartSupplier(relatedSupplier));
-
- relatedSupplier.addBodyPart(new SerializableDefaultMimeBodyPartSupplier(html.toString(), "text/html"));
-
+ relatedSupplier.addBodyPart(new SerializableDefaultMimeBodyPartSupplier(html.toString(), "text/html; charset=UTF-8"));
for (Entry imageEntry : pngAttachAndInline.entrySet()) {
final String contentType = "image/png";
final String cid = "<" + imageEntry.getKey() + ">";
@@ -128,7 +123,6 @@ abstract class RaceLogTrackingInvitationMailBuilder {
relatedSupplier.addBodyPart(new SerializableFileMimeBodyPartSupplier(img, contentType, cid, filename));
mixedSupplier.addBodyPart(new SerializableFileMimeBodyPartSupplier(img, contentType, filename));
}
-
return mixedSupplier;
}
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
index 14f6f25115f..42f962fc9ac 100755
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/FixLoaderAndTracker.java
@@ -693,7 +693,10 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
private TimeRange getTrackingTimeRange() {
final TimePoint startOfTracking = trackedRace.getStartOfTracking();
- final TimePoint endOfTracking = trackedRace.getEndOfTracking();
+ // in case (erroneously) startOfTracking is *after* endOfTracking, return an empty interval starting and ending
+ // at startOfTracking (see also bug 5354).
+ final TimePoint endOfTracking = startOfTracking != null && trackedRace.getEndOfTracking() != null &&
+ startOfTracking.after(trackedRace.getEndOfTracking()) ? startOfTracking : trackedRace.getEndOfTracking();
return new TimeRangeImpl(startOfTracking == null ? TimePoint.BeginningOfTime : startOfTracking,
endOfTracking == null ? TimePoint.EndOfTime : endOfTracking);
}
diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java
index 1e9d9a598a8..3d59e1c1c90 100644
--- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java
+++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java
@@ -65,7 +65,7 @@ public class CurrentRaceFilterImplTest {
_505RaceRows.add(_505RaceRow);
_505Series.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, _505RaceRows));
final RaceGroup _505 = new RaceGroupImpl("505", /* displayName */ null,
- new BoatClassImpl(BoatClassMasterdata._5O5.getDisplayName(), BoatClassMasterdata._5O5), /* canBoatsOfCompetitorsChangePerRace */ false,
+ new BoatClassImpl(BoatClassMasterdata._5O5), /* canBoatsOfCompetitorsChangePerRace */ false,
_505Series, /* regattaConfiguration */ null);
return _505;
}
@@ -75,7 +75,7 @@ public class CurrentRaceFilterImplTest {
final List yesRaceRows = Arrays.asList(createRaceRow(1, 9, "R", "Yellow"), createRaceRow(1, 9, "R", "Blue"));
yesSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, yesRaceRows));
final RaceGroup yes = new RaceGroupImpl("YES", /* displayName */ null,
- new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
+ new BoatClassImpl(BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
yesSeries, /* regattaConfiguration */ null);
return yes;
}
@@ -89,7 +89,7 @@ public class CurrentRaceFilterImplTest {
final List isafMedalRaceRows = Arrays.asList(createRaceRow(1, 1, "M", "Medal"));
isafSeries.add(new SeriesWithRowsImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, isafMedalRaceRows));
final RaceGroup isaf = new RaceGroupImpl("ISAF", /* displayName */ null,
- new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
+ new BoatClassImpl(BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
isafSeries, /* regattaConfiguration */ null);
return isaf;
}
@@ -109,7 +109,7 @@ public class CurrentRaceFilterImplTest {
final List essDay4RaceRows = Arrays.asList(createRaceRow(16, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 4", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay4RaceRows));
final RaceGroup ess = new RaceGroupImpl("ESS", /* displayName */ null,
- new BoatClassImpl(BoatClassMasterdata.EXTREME_40.getDisplayName(), BoatClassMasterdata.EXTREME_40), /* canBoatsOfCompetitorsChangePerRace */ false,
+ new BoatClassImpl(BoatClassMasterdata.EXTREME_40), /* canBoatsOfCompetitorsChangePerRace */ false,
essSeries, /* regattaConfiguration */ null);
return ess;
}
@@ -119,7 +119,7 @@ public class CurrentRaceFilterImplTest {
final List leagueRaceRows = Arrays.asList(createRaceRow(1, 15, "F", "Red"), createRaceRow(1, 15, "F", "Green"), createRaceRow(1, 15, "F", "Blue"));
leagueSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, leagueRaceRows));
final RaceGroup league = new RaceGroupImpl("League", /* displayName */ null,
- new BoatClassImpl(BoatClassMasterdata.J70.getDisplayName(), BoatClassMasterdata.J70), /* canBoatsOfCompetitorsChangePerRace */ false,
+ new BoatClassImpl(BoatClassMasterdata.J70), /* canBoatsOfCompetitorsChangePerRace */ false,
leagueSeries, /* regattaConfiguration */ null);
return league;
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLogEventAuthor.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLogEventAuthor.java
index 0e54f84218b..bb7118d6224 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLogEventAuthor.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/AbstractLogEventAuthor.java
@@ -19,10 +19,9 @@ public interface AbstractLogEventAuthor extends Named, Comparable
- *
Start Vessel
- *
Finish Vessel
+ *
Race Officer on a Vessel
*
Shore Control
- *
Compatibility priority for old events
+ *
Demo Mode
*
*/
int getPriority();
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
index d9fc807d5c0..04b7c4ad257 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
@@ -73,7 +73,6 @@ extends TrackImpl implements AbstractLog {
*/
public AbstractLogImpl(String nameForReadWriteLock, Serializable identifier, Comparator comparator) {
super(new ArrayListNavigableSet(comparator), nameForReadWriteLock);
-
this.listeners = new HashSet();
this.id = identifier;
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/MarkFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/MarkFinder.java
new file mode 100644
index 00000000000..0abecc41d8c
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/impl/MarkFinder.java
@@ -0,0 +1,40 @@
+package com.sap.sailing.domain.abstractlog.regatta.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
+import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
+import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
+import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogSetCompetitorTimeOnTimeFactorEvent;
+import com.sap.sailing.domain.base.Competitor;
+import com.sap.sailing.domain.base.Mark;
+
+
+/**
+ * BaseLogAnalyzer used for finding the last {@link RegattaLogSetCompetitorTimeOnTimeFactorEvent} event for a specific
+ * {@link Competitor} in a regatta log.
+ *
+ * If no corresponding event has been found, null is returned.
+ */
+public class MarkFinder extends BaseLogAnalyzer> {
+
+ public MarkFinder(RegattaLog log) {
+ super(log);
+ }
+
+ @Override
+ protected Set performAnalysis() {
+ Set marks = new HashSet<>();
+ for (RegattaLogEvent regattaLogEvent : log.getUnrevokedEventsDescending()) {
+ if (regattaLogEvent instanceof RegattaLogDefineMarkEvent) {
+ RegattaLogDefineMarkEvent defineMarkEvent = (RegattaLogDefineMarkEvent) regattaLogEvent;
+ marks.add(defineMarkEvent.getMark());
+ }
+ }
+ return marks;
+ }
+
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/DetailedRaceInfo.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/DetailedRaceInfo.java
index 0a9608804c5..07a1b803e7e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/DetailedRaceInfo.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/DetailedRaceInfo.java
@@ -17,20 +17,18 @@ public class DetailedRaceInfo extends SimpleRaceInfo {
private final String eventName;
private final String leaderboardDisplayName;
private final String leaderboardName;
- private final UUID eventID;
private EventType eventType;
public DetailedRaceInfo(RegattaAndRaceIdentifier identifier, String leaderboardName, String leaderboardDisplayName,
TimePoint timePoint, UUID eventId, String eventName, EventType eventType,
URL remoteUrl) {
- super(identifier, timePoint, remoteUrl);
+ super(identifier, timePoint, remoteUrl, eventId);
if (leaderboardName == null || eventId == null) {
throw new IllegalStateException("DetailedRaceInfo Data is not allowed to contain any null values!");
}
this.leaderboardName = leaderboardName;
this.leaderboardDisplayName = leaderboardDisplayName;
this.eventName = eventName;
- this.eventID = eventId;
this.eventType = eventType;
}
@@ -54,10 +52,6 @@ public class DetailedRaceInfo extends SimpleRaceInfo {
return leaderboardName;
}
- public UUID getEventID() {
- return eventID;
- }
-
@Override
public String toString() {
return "DetailedRaceInfo [identifier=" + getIdentifier() + ", leaderboardName=" + leaderboardName
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/SimpleRaceInfo.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/SimpleRaceInfo.java
index 15cf9422074..607132bbcb2 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/SimpleRaceInfo.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/anniversary/SimpleRaceInfo.java
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.anniversary;
import java.io.Serializable;
import java.net.URL;
+import java.util.UUID;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sse.common.TimePoint;
@@ -20,19 +21,21 @@ public class SimpleRaceInfo implements Serializable {
private final RegattaAndRaceIdentifier identifier;
private final TimePoint startOfRace;
private final URL remoteUrl;
+ protected final UUID eventID;
/**
* @param remoteUrl
* use {@code null} to mean "local"; a local server does not necessarily know under which URL it is being
* reached and therefore cannot provide this
*/
- public SimpleRaceInfo(RegattaAndRaceIdentifier identifier, TimePoint startOfRace, URL remoteUrl) {
+ public SimpleRaceInfo(RegattaAndRaceIdentifier identifier, TimePoint startOfRace, URL remoteUrl, UUID eventId) {
if (identifier == null || startOfRace == null) {
throw new IllegalStateException("SimpleRaceInfo Data is not allowed to contain any null values!");
}
this.identifier = identifier;
this.startOfRace = startOfRace;
this.remoteUrl = remoteUrl;
+ this.eventID = eventId;
}
public RegattaAndRaceIdentifier getIdentifier() {
@@ -51,12 +54,17 @@ public class SimpleRaceInfo implements Serializable {
return startOfRace;
}
+ public UUID getEventID() {
+ return eventID;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
result = prime * result + ((startOfRace == null) ? 0 : startOfRace.hashCode());
+ result = prime * result + ((eventID == null) ? 0 : eventID.hashCode());
return result;
}
@@ -79,6 +87,11 @@ public class SimpleRaceInfo implements Serializable {
return false;
} else if (!startOfRace.equals(other.startOfRace))
return false;
+ if (eventID == null) {
+ if (other.eventID != null)
+ return false;
+ } else if (!eventID.equals(other.eventID))
+ return false;
return true;
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatClass.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatClass.java
index 76924454a73..81709ca3e8c 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatClass.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/BoatClass.java
@@ -45,8 +45,6 @@ public interface BoatClass extends Named, IsManagedByCache extends Compet
*/
CourseArea getExistingCourseAreaById(Serializable courseAreaId);
+ Collection getAllMarks();
+
Mark getExistingMarkByIdAsString(String toStringRepresentationOfID);
Mark getExistingMarkById(Serializable id);
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/DeviceConfiguration.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/DeviceConfiguration.java
index 54c92866f63..4e3dff000dc 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/DeviceConfiguration.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/DeviceConfiguration.java
@@ -1,8 +1,12 @@
package com.sap.sailing.domain.base.configuration;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
+import com.sap.sailing.domain.base.CourseArea;
+import com.sap.sailing.domain.base.EventBase;
+import com.sap.sailing.domain.base.Venue;
import com.sap.sse.common.Named;
import com.sap.sse.common.WithID;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
@@ -38,4 +42,47 @@ public interface DeviceConfiguration extends WithID, WithQualifiedObjectIdentifi
* Copy me.
*/
DeviceConfiguration copy();
+
+ /**
+ * The optional ID of an {@link EventBase} object that can be resolved by the server and which has a
+ * {@link EventBase#getVenue() venue} with {@link Venue#getCourseAreas() course areas}. If a valid event ID is set
+ * here, any {@link #getCourseAreaId() course area} specified by ID should be part of the event's venue, and UIs as
+ * well as service methods taking in a {@link DeviceConfiguration} shall make sure to validate this constraint.
+ * Likewise, if an event ID of an existing event is specified, the {@link #getAllowedCourseAreaNames()} results are
+ * expected to match {@link CourseArea#getName() course area names} of those course areas in the event's venue.
+ */
+ Optional getEventId();
+
+ /**
+ * @param eventId if {@code null}, an {@link Optional#empty() empty} optional will result from {@link #getEventId()}.
+ */
+ void setEventId(UUID eventId);
+
+ /**
+ * If a valid {@link #getEventId() event ID} is specified, a course area ID can be specified here to default the
+ * log-in to that course area. This may come in handy if devices are to be pre-configured for hand-out to a specific
+ * course area only. Make sure that this ID references a course area whose name is part of the result of calling
+ * {@link #getAllowedCourseAreaNames()}.
+ */
+ Optional getCourseAreaId();
+
+ /**
+ * @param courseAreaId if {@code null}, an {@link Optional#empty() empty} optional will result from {@link #getCourseAreaId()}.
+ */
+ void setCourseAreaId(UUID courseAreaId);
+
+ /**
+ * During the Race Manager App's login process the user is asked to choose a "role" such as "Race Officer on the
+ * Water" or "Shore Control." These map to numeric priorities, with lesser numerical values taking higher
+ * precedence. "Race Officer on the Water" would map to the number 1, "Shore Control" to 2. An exceptional priority
+ * of 3 may be translated by the app into "Demo Mode" if that is permissible. The field may not be set, then
+ * returning an empty {@link Optional}, meaning that this device configuration leaves it up for the user to choose
+ * the priority during the login process.
+ */
+ Optional getPriority();
+
+ /**
+ * Sets the {@link #getPriority() priority}.
+ */
+ void setPriority(Integer priority);
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/DeviceConfigurationImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/DeviceConfigurationImpl.java
index e581f7dd222..80835816d93 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/DeviceConfigurationImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/DeviceConfigurationImpl.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.base.configuration.impl;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
@@ -21,6 +22,9 @@ public class DeviceConfigurationImpl extends NamedImpl implements DeviceConfigur
private String resultsMailRecipient;
private List byNameDesignerCourseNames;
private final UUID id;
+ private UUID eventId;
+ private UUID courseAreaId;
+ private Integer priority;
public DeviceConfigurationImpl(RegattaConfiguration regattaConfiguration, UUID id, String name) {
super(name);
@@ -37,11 +41,47 @@ public class DeviceConfigurationImpl extends NamedImpl implements DeviceConfigur
this.regattaConfiguration = proceduresConfiguration;
}
+ @Override
+ public Optional getEventId() {
+ return Optional.ofNullable(eventId);
+ }
+
+ @Override
+ public void setEventId(UUID eventId) {
+ this.eventId = eventId;
+ }
+
+ @Override
+ public Optional getCourseAreaId() {
+ return Optional.ofNullable(courseAreaId);
+ }
+
+ @Override
+ public void setCourseAreaId(UUID courseAreaId) {
+ this.courseAreaId = courseAreaId;
+ }
+
+ @Override
+ public Optional getPriority() {
+ return Optional.ofNullable(priority);
+ }
+
+ @Override
+ public void setPriority(Integer priority) {
+ this.priority = priority;
+ }
+
+ /**
+ * May return {@code null}
+ */
@Override
public RegattaConfiguration getRegattaConfiguration() {
return regattaConfiguration;
}
+ /**
+ * May return {@code null}
+ */
@Override
public List getAllowedCourseAreaNames() {
return allowedCourseAreaNames;
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java
index bbf614aaa42..bc2bee94cde 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java
@@ -39,7 +39,6 @@ public class BoatClassImpl extends NamedImpl implements BoatClass {
private final boolean typicallyStartsUpwind;
- private final String displayName;
private final Distance hullLength;
@@ -48,22 +47,20 @@ public class BoatClassImpl extends NamedImpl implements BoatClass {
private final BoatHullType hullType;
public BoatClassImpl(String name, boolean typicallyStartsUpwind) {
- this(name, typicallyStartsUpwind, /* displayName */ null,
- // use the typical dinghy parameters as default
- /* hull length */ new MeterDistance(5), /* hullBeam */ new MeterDistance(1.8),
- /* hullType */ BoatHullType.MONOHULL);
+ this(name, typicallyStartsUpwind, // use the typical dinghy parameters as default
+ /* hull length */ new MeterDistance(5),
+ /* hullBeam */ new MeterDistance(1.8), /* hullType */ BoatHullType.MONOHULL);
}
- public BoatClassImpl(String name, BoatClassMasterdata masterData) {
- this(name, masterData.isTypicallyStartsUpwind(), masterData.getDisplayName(), masterData.getHullLength(),
- masterData.getHullBeam(), masterData.getHullType());
+ public BoatClassImpl(BoatClassMasterdata masterData) {
+ this(masterData.getDisplayName(), masterData.isTypicallyStartsUpwind(), masterData.getHullLength(), masterData.getHullBeam(),
+ masterData.getHullType());
}
- public BoatClassImpl(String name, boolean typicallyStartsUpwind, String displayName,
- Distance hullLength, Distance hullBeam, BoatHullType hullType) {
+ public BoatClassImpl(String name, boolean typicallyStartsUpwind, Distance hullLength,
+ Distance hullBeam, BoatHullType hullType) {
super(name);
this.typicallyStartsUpwind = typicallyStartsUpwind;
- this.displayName = displayName;
this.hullLength = hullLength;
this.hullBeam = hullBeam;
this.hullType = hullType;
@@ -136,10 +133,6 @@ public class BoatClassImpl extends NamedImpl implements BoatClass {
return domainFactory.getOrCreateBoatClass(getName(), typicallyStartsUpwind());
}
- public String getDisplayName() {
- return displayName;
- }
-
public Distance getHullBeam() {
return hullBeam;
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java
index 07dd8205bf6..5e130480a31 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java
@@ -5,6 +5,7 @@ import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.net.URI;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -353,7 +354,7 @@ public class SharedDomainFactoryImpl implements Sha
result = boatClassCache.get(unifiedBoatClassName);
if (result == null) {
if (unifiedBoatClassName != null && boatClassMasterdata != null) {
- result = new BoatClassImpl(boatClassMasterdata.getDisplayName(), boatClassMasterdata);
+ result = new BoatClassImpl(boatClassMasterdata);
boatClassCache.put(unifiedBoatClassName, result);
}
}
@@ -448,6 +449,11 @@ public class SharedDomainFactoryImpl implements Sha
public Mark getExistingMarkByIdAsString(String toStringRepresentationOfID) {
return markCache.get(markIdCache.get(toStringRepresentationOfID));
}
+
+ @Override
+ public Collection getAllMarks() {
+ return markCache.values();
+ }
@Override
public RLR getRaceLogResolver() {
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorAndBoatStoreImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorAndBoatStoreImpl.java
index 6ae5b241c82..43b4841134a 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorAndBoatStoreImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorAndBoatStoreImpl.java
@@ -647,7 +647,7 @@ public class TransientCompetitorAndBoatStoreImpl implements CompetitorAndBoatSto
boatDTO = weakBoatDTOCache.get(b);
if (boatDTO == null) {
BoatClassDTO boatClassDTO = new BoatClassDTO(b.getBoatClass().getName(),
- b.getBoatClass().getDisplayName(), b.getBoatClass().getHullLength(), b.getBoatClass().getHullBeam());
+ b.getBoatClass().getHullLength(), b.getBoatClass().getHullBeam());
boatDTO = new BoatDTO(b.getId().toString(), b.getName(), boatClassDTO, b.getSailID(), b.getColor());
weakBoatDTOCache.put(b, boatDTO);
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkConfiguration.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkConfiguration.java
index 0601fcbbf0c..81d0d13bf4b 100755
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkConfiguration.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkConfiguration.java
@@ -10,7 +10,7 @@ package com.sap.sailing.domain.coursetemplate;
*
*/
public interface FreestyleMarkConfiguration
extends MarkConfiguration
{
- CommonMarkProperties getFreestyleProperties();
+ FreestyleMarkProperties getFreestyleProperties();
/**
* @return a non-{@code null} object if this mark configuration originated from a {@link MarkProperties} object
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkProperties.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkProperties.java
new file mode 100644
index 00000000000..67cdae678ce
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/FreestyleMarkProperties.java
@@ -0,0 +1,10 @@
+package com.sap.sailing.domain.coursetemplate;
+
+/**
+ * While sharing most of @{@link CommonMarkProperties}, {@link MarkProperties} do have the distinct
+ * {@link FreestyleMarkConfiguration} property tags.
+ *
+ */
+public interface FreestyleMarkProperties extends CommonMarkProperties, HasTags {
+ void setTags(Iterable tags);
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/HasTags.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/HasTags.java
index 235b914b9c5..c42c1620f4e 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/HasTags.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/HasTags.java
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.coursetemplate;
public interface HasTags {
-
- public Iterable getTags();
+ Iterable getTags();
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/MarkProperties.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/MarkProperties.java
index 93d4d2495a7..e20e861b304 100755
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/MarkProperties.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/MarkProperties.java
@@ -39,7 +39,7 @@ import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
* @author Axel Uhl (d043530)
*
*/
-public interface MarkProperties extends CommonMarkProperties, NamedWithUUID, HasTags, WithQualifiedObjectIdentifier {
+public interface MarkProperties extends FreestyleMarkProperties, NamedWithUUID, WithQualifiedObjectIdentifier {
void setColor(Color color);
void setShape(String shape);
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkConfigurationImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkConfigurationImpl.java
index cbf3b5cd056..fb02a5d441c 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkConfigurationImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkConfigurationImpl.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.coursetemplate.impl;
import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
+import com.sap.sailing.domain.coursetemplate.FreestyleMarkProperties;
import com.sap.sailing.domain.coursetemplate.FreestyleMarkConfiguration;
import com.sap.sailing.domain.coursetemplate.MarkConfigurationVisitor;
import com.sap.sailing.domain.coursetemplate.MarkProperties;
@@ -10,10 +11,10 @@ public class FreestyleMarkConfigurationImpl
extends MarkConfigurationImpl
private static final long serialVersionUID = 8167307943855869321L;
private final MarkProperties optionalMarkProperties;
- private final CommonMarkProperties freestyleProperties;
+ private final FreestyleMarkProperties freestyleProperties;
public FreestyleMarkConfigurationImpl(MarkTemplate optionalMarkTemplate, MarkProperties optionalMarkProperties,
- CommonMarkProperties freestyleProperties, P annotation) {
+ FreestyleMarkProperties freestyleProperties, P annotation) {
super(optionalMarkTemplate, annotation);
this.optionalMarkProperties = optionalMarkProperties;
this.freestyleProperties = freestyleProperties;
@@ -35,7 +36,7 @@ public class FreestyleMarkConfigurationImpl
extends MarkConfigurationImpl
}
@Override
- public CommonMarkProperties getFreestyleProperties() {
+ public FreestyleMarkProperties getFreestyleProperties() {
return freestyleProperties;
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkPropertiesImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkPropertiesImpl.java
new file mode 100644
index 00000000000..c77b77c6bc1
--- /dev/null
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FreestyleMarkPropertiesImpl.java
@@ -0,0 +1,31 @@
+package com.sap.sailing.domain.coursetemplate.impl;
+
+import com.sap.sailing.domain.common.MarkType;
+import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
+import com.sap.sailing.domain.coursetemplate.FreestyleMarkProperties;
+import com.sap.sse.common.Color;
+
+public class FreestyleMarkPropertiesImpl extends CommonMarkPropertiesImpl implements FreestyleMarkProperties {
+ private static final long serialVersionUID = -925045903410189747L;
+ private Iterable tags;
+
+ public FreestyleMarkPropertiesImpl(String name, String shortName, Color color, String shape, String pattern, MarkType type, Iterable tags) {
+ super(name, shortName, color, shape, pattern, type);
+ this.tags = tags;
+ }
+
+ public FreestyleMarkPropertiesImpl(final CommonMarkProperties commonMarkProperties, Iterable tags) {
+ this(commonMarkProperties.getName(), commonMarkProperties.getShortName(), commonMarkProperties.getColor(),
+ commonMarkProperties.getShape(), commonMarkProperties.getPattern(), commonMarkProperties.getType(), tags);
+ }
+
+ @Override
+ public Iterable getTags() {
+ return this.tags;
+ }
+
+ @Override
+ public void setTags(Iterable tags) {
+ this.tags = tags;
+ }
+}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/MarkPropertiesImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/MarkPropertiesImpl.java
index 8d4343a75e4..4847d0d8cdc 100755
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/MarkPropertiesImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/MarkPropertiesImpl.java
@@ -6,7 +6,6 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.sap.sailing.domain.common.MarkType;
-import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
import com.sap.sailing.domain.coursetemplate.MarkProperties;
import com.sap.sailing.domain.coursetemplate.MarkRole;
import com.sap.sailing.domain.coursetemplate.MarkTemplate;
@@ -14,10 +13,9 @@ import com.sap.sailing.domain.coursetemplate.Positioning;
import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint;
-public class MarkPropertiesImpl extends CommonMarkPropertiesImpl implements MarkProperties {
+public class MarkPropertiesImpl extends FreestyleMarkPropertiesImpl implements MarkProperties {
private static final long serialVersionUID = -5588202720707030502L;
private Positioning positioningInformation;
- private Iterable tags;
private UUID id;
private final ConcurrentHashMap lastUsedMarkTemplate = new ConcurrentHashMap<>();
@@ -29,12 +27,7 @@ public class MarkPropertiesImpl extends CommonMarkPropertiesImpl implements Mark
public MarkPropertiesImpl(UUID id, String name, String shortName, Color color, String shape, String pattern,
MarkType type) {
- super(name, shortName, color, shape, pattern, type);
- this.id = id;
- }
-
- public MarkPropertiesImpl(UUID id, final CommonMarkProperties commonMarkProperties) {
- super(commonMarkProperties);
+ super(name, shortName, color, shape, pattern, type, /* tags */ null);
this.id = id;
}
@@ -78,15 +71,6 @@ public class MarkPropertiesImpl extends CommonMarkPropertiesImpl implements Mark
this.positioningInformation = positioningInformation;
}
- @Override
- public Iterable getTags() {
- return this.tags;
- }
-
- public void setTags(Iterable tags) {
- this.tags = tags;
- }
-
@Override
public Map getLastUsedMarkTemplate() {
return Collections.unmodifiableMap(lastUsedMarkTemplate);
diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java
index ce1d0e0fa6d..918e6484ccd 100644
--- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java
+++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.swisstimingreplayadapter.test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -10,9 +11,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.text.ParseException;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.junit.Ignore;
import org.junit.Test;
@@ -24,23 +23,16 @@ import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplaySer
public class SwissTimingRaceConfigurationTest {
- private static final String JSON_URL = "/2012_OSG.json";
+ private static final String JSON_URL = "https://static.sapsailing.com/OSG2012/OSG2012_static.json";
- @Ignore("Takes a very long time; only used to see if we can parse all configurations")
@Test
public void testLoadConfigurations() throws IOException, ParseException, org.json.simple.parser.ParseException {
- InputStream inputStream = getClass().getResourceAsStream(JSON_URL);
+ InputStream inputStream = (InputStream) new URL(JSON_URL).getContent();
List races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).parseJSONObject(inputStream , JSON_URL);
- Map configsById = new HashMap();
- for (SwissTimingReplayRace race : races) {
- URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, race.getRaceId()));
- URLConnection connection = configFileURL.openConnection();
- InputStream configDataStream = connection.getInputStream();
- SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
- configsById.put(race.getRaceId(), raceConfig);
- }
+ assertFalse(races.isEmpty());
}
+ @Ignore("SwissTiming shut down their configuration server for good (2020-11-13), so we cannot get at the configs anymore")
@Test
public void testRaceConfig_446483_no_detail_config() throws Exception {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483"));
@@ -58,6 +50,7 @@ public class SwissTimingRaceConfigurationTest {
}
+ @Ignore("SwissTiming shut down their configuration server for good (2020-11-13), so we cannot get at the configs anymore")
@Test
public void testRaceConfig_6260_with_detail_config() throws Exception {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260"));
@@ -76,5 +69,4 @@ public class SwissTimingRaceConfigurationTest {
assertEquals("Nothe", config_446483.location);
assertEquals("-2,442963", config_446483.longitude);
}
-
}
diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java
index 0944a2c338c..7ce71219257 100644
--- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java
+++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java
@@ -15,6 +15,7 @@ import java.net.URL;
import java.util.List;
import java.util.Map.Entry;
+import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.domain.base.Boat;
@@ -76,11 +77,12 @@ public class SwissTimingReplayAdapterServiceTest {
assertEquals(321920, replayCountListener.rankingMarkCount);
}
+ @Ignore("SwissTiming has shut down the ota2.sportresult.com server around 2020-11-13")
@Test
public void testRaceData_SAW005905_20120805_EqualsOnlineVersion() throws Exception {
byte[] localCopy = read(getClass().getResourceAsStream("/SAW005905.20120805.replay"));
byte[] onlineCopy = read((InputStream) new URL(
- "http://ota2.sportresult.com/Replay?id=446495&_start=0").getContent());
+ "https://static.sapsailing.com/OSG2012/446495.rsc?id=446495").getContent());
assertArrayEquals(localCopy, onlineCopy);
}
@@ -120,7 +122,7 @@ public class SwissTimingReplayAdapterServiceTest {
SwissTimingReplayTestListener replayCountListener = new SwissTimingReplayTestListener();
// race ID is 450053, as extracted from http://live.ota.st-sportservice.com/service?cmd=unity_race_overview&id=91
byte[] onlineCopy = read((InputStream) new URL(
- "http://ota2.sportresult.com/Replay?id=446495&_start=0").getContent());
+ "https://static.sapsailing.com/OSG2012/446495.rsc?id=446495").getContent());
new SwissTimingReplayParserImpl().readData(new ByteArrayInputStream(onlineCopy), replayCountListener);
assertEquals(0, replayCountListener.keyFrameIndexSum);
assertEquals(715, replayCountListener.keyFrameIndexPositionCount);
diff --git a/java/com.sap.sailing.domain.test/TestTTCM tunneled through localhost.launch b/java/com.sap.sailing.domain.test/TestTTCM tunneled through localhost.launch
index 0c9ed8dc4ae..3fc126263c8 100644
--- a/java/com.sap.sailing.domain.test/TestTTCM tunneled through localhost.launch
+++ b/java/com.sap.sailing.domain.test/TestTTCM tunneled through localhost.launch
@@ -11,6 +11,7 @@
+
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
index c846860e42f..0eefe69193b 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java
@@ -78,7 +78,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
new ThresholdBasedResultDiscardingRuleImpl(new int[0]), new LowPoint(), courseArea);
competitorProviderFlexibleLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(flexibleLeaderboard);
regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE,
- EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl("49er", BoatClassMasterdata._49ER),
+ EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl(BoatClassMasterdata._49ER),
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* startDate */ null, /* endDate */null,
/* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(),
courseArea, /* registrationLinkSecret */ UUID.randomUUID().toString());
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java
index d38a049e397..c4a96ff76a9 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateDuringNonAtomicSerializationTest.java
@@ -77,7 +77,7 @@ public class CourseUpdateDuringNonAtomicSerializationTest implements Serializabl
removeCalls = new HashMap<>();
course = new CourseImpl("Test Course", waypoints);
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, "Test Regatta",
- new BoatClassImpl("505", BoatClassMasterdata._5O5),
+ new BoatClassImpl(BoatClassMasterdata._5O5),
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED,
/*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null,
new LowPoint(), UUID.randomUUID(), new CourseAreaImpl("Alpha", UUID.randomUUID()),
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DomainFactoryImplTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DomainFactoryImplTest.java
index 4028c4e361a..11d80655f72 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DomainFactoryImplTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DomainFactoryImplTest.java
@@ -33,7 +33,7 @@ public class DomainFactoryImplTest {
public void testGetDominantBoatClass_TestWithOneObject() {
List list = new ArrayList<>();
list.add("LSR");
- Assert.assertEquals("Laser Int.", domainFactory.getDominantBoatClass(list).getDisplayName());
+ Assert.assertEquals("Laser Int.", domainFactory.getDominantBoatClass(list).getName());
}
@Test
@@ -41,7 +41,7 @@ public class DomainFactoryImplTest {
List list = new ArrayList<>();
list.add("LSR");
list.add("18.Footer");
- String result = domainFactory.getDominantBoatClass(list).getDisplayName();
+ String result = domainFactory.getDominantBoatClass(list).getName();
if(!(result == "Laser Int." || result == "18Footer")) {
Assert.assertEquals("Laser Int.", result);
}
@@ -53,7 +53,7 @@ public class DomainFactoryImplTest {
list.add("LSR");
list.add("18.Footer");
list.add("LSR");
- String result = domainFactory.getDominantBoatClass(list).getDisplayName();
+ String result = domainFactory.getDominantBoatClass(list).getName();
Assert.assertEquals("Laser Int.", result);
}
@@ -65,7 +65,7 @@ public class DomainFactoryImplTest {
list.add("18.Footer");
list.add("Laser Int.");
list.add("Laser");
- String result = domainFactory.getDominantBoatClass(list).getDisplayName();
+ String result = domainFactory.getDominantBoatClass(list).getName();
Assert.assertEquals("Laser Int.", result);
}
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java
index 72f2100a9d6..263630d148e 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardCourseChangeTest.java
@@ -65,7 +65,6 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LeaderboardCourseChangeTest {
-
/**
* See bug 2011
*
@@ -75,19 +74,14 @@ public class LeaderboardCourseChangeTest {
*/
@Test
public void testLeaderboardDTOCreationForCourseChange() throws NoWindException, InterruptedException, ExecutionException {
-
Date date = Calendar.getInstance().getTime();
TimePoint timePoint = new MillisecondsTimePoint(date);
-
TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class);
-
Fleet fleet = new FleetImpl("TestFleet");
Series series = createSeries(trackedRegattaRegistry, fleet);
Set seriesSet = new HashSet<>();
seriesSet.add(series);
-
BoatClass boatClass = new BoatClassImpl("TestClass", true);
-
String raceColumnName = "TestRace";
RaceColumn raceColumn = series.addRaceColumn(raceColumnName, trackedRegattaRegistry);
ScoringScheme scoringScheme = new LowPoint();
@@ -119,26 +113,20 @@ public class LeaderboardCourseChangeTest {
raceColumn.setTrackedRace(fleet, mockedTrackedRace);
Set raceColumnNames = new HashSet<>();
raceColumnNames.add(raceColumnName);
-
int[] ruleRaw = { 5, 3, 2 };
ThresholdBasedResultDiscardingRule rule = new ThresholdBasedResultDiscardingRuleImpl(ruleRaw);
Leaderboard leaderboard = createRegattaLeaderboard(mockedRegatta, rule);
-
DomainFactory baseDomainFactory = new DomainFactoryImpl(DomainFactory.TEST_RACE_LOG_RESOLVER);
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false,
trackedRegattaRegistry, baseDomainFactory, /* fillTotalPointsUncorrected */ false);
-
assertEquals(6, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator()
.next().legDetails.size());
-
course.removeWaypoint(2);
course.removeWaypoint(3);
-
leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false, trackedRegattaRegistry,
baseDomainFactory, /* fillTotalPointsUncorrected */ false);
assertEquals(4, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator()
.next().legDetails.size());
-
}
protected RegattaLeaderboard createRegattaLeaderboard(Regatta mockedRegatta,
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverDetectionOnKielerWoche505Race2DataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverDetectionOnKielerWoche505Race2DataTest.java
index b76e66ffb5b..5deb440b068 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverDetectionOnKielerWoche505Race2DataTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverDetectionOnKielerWoche505Race2DataTest.java
@@ -73,7 +73,7 @@ public class ManeuverDetectionOnKielerWoche505Race2DataTest extends AbstractMane
TimePoint firstWindwardMarkPassing = hassosMarkPassingsIter.next().getTimePoint();
List firstLegFineApproximation = dp.approximate(new MeterDistance(20), startMarkPassing, firstWindwardMarkPassing);
assertNotNull(firstLegFineApproximation);
- assertEquals(11, firstLegFineApproximation.size());
+ assertEquals(9, firstLegFineApproximation.size()); // changed from 11 to 9 with bug 5425 because a few outliers are now removed
List firstLegCoarseApproximation = dp.approximate(new MeterDistance(50), startMarkPassing, firstWindwardMarkPassing);
assertNotNull(firstLegCoarseApproximation);
assertEquals(4, firstLegCoarseApproximation.size());
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java
index 082b05eb499..d15c374f718 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java
@@ -218,7 +218,7 @@ public abstract class TrackBasedTest {
}
public static RegattaImpl createTestRegatta(String regattaName, Iterable raceColumnNames) {
- final BoatClass boatClass = new BoatClassImpl("boat class name", BoatClassMasterdata._12M);
+ final BoatClass boatClass = new BoatClassImpl(BoatClassMasterdata._12M);
final TimePoint startDate = MillisecondsTimePoint.now();
final TimePoint endDate = startDate.plus(MillisecondsDurationImpl.ONE_DAY);
final boolean isMedal = false;
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java
index 6aa01edcd23..1c37e34d88e 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/AbstractMockedRaceMarkPassingTest.java
@@ -48,7 +48,7 @@ import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class AbstractMockedRaceMarkPassingTest {
- final BoatClassImpl boatClass = new BoatClassImpl("boat", true, "boat", new MeterDistance(10), new MeterDistance(5), null);
+ final BoatClassImpl boatClass = new BoatClassImpl("boat", true, new MeterDistance(10), new MeterDistance(5), null);
protected Competitor ron = new CompetitorImpl("Ron", "Ron", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
protected Competitor tom = new CompetitorImpl("Tom", "Tom", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
protected Competitor ben = new CompetitorImpl("Ben", "Ben", "KYC", null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorWithTrackersOnStartBoatTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorWithTrackersOnStartBoatTest.java
index f940443e48b..0d0b95b67eb 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorWithTrackersOnStartBoatTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/MarkPassingCalculatorWithTrackersOnStartBoatTest.java
@@ -45,7 +45,7 @@ public class MarkPassingCalculatorWithTrackersOnStartBoatTest extends AbstractEx
@Before
public void setUp() throws IOException, ParseException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
startOfSetup = MillisecondsTimePoint.now();
- trackedRace = readRace("/MoevensteinCompetitorPositions.json.gz", "/MoevensteinMarkPositions.json.gz", new BoatClassImpl("J/70", BoatClassMasterdata.J70));
+ trackedRace = readRace("/MoevensteinCompetitorPositions.json.gz", "/MoevensteinMarkPositions.json.gz", new BoatClassImpl(BoatClassMasterdata.J70));
markPassings = new HashMap<>();
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
markPassings.put(competitor, trackedRace.getMarkPassings(competitor, /* waitForLatest */ true));
diff --git a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF
index 9cfff10364e..994354d93d1 100644
--- a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF
@@ -25,11 +25,11 @@ Export-Package: com.sap.sailing.domain.tractracadapter,
com.sap.sailing.server.replication.test"
Bundle-ActivationPolicy: lazy
Import-Package: org.apache.http,
- org.apache.http.client;version="[4.2.5,4.3.0)",
- org.apache.http.client.entity;version="[4.2.5,4.3.0)",
- org.apache.http.client.methods;version="[4.2.5,4.3.0)",
- org.apache.http.impl.client;version="[4.2.5,4.3.0)",
- org.apache.http.message;version="[4.2.5,4.3.0)",
+ org.apache.http.client;version="4.5.5",
+ org.apache.http.client.entity;version="4.5.5",
+ org.apache.http.client.methods;version="4.5.5",
+ org.apache.http.impl.client;version="4.5.5",
+ org.apache.http.message;version="4.4.9",
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.7.0"
diff --git a/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderReportParserTest.java b/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderReportParserTest.java
index 6822f3d2e8b..ce746bf0bd4 100644
--- a/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderReportParserTest.java
+++ b/java/com.sap.sailing.domain.windfinderadapter.test/src/com/sap/sailing/domain/windfinderadapter/impl/WindFinderReportParserTest.java
@@ -49,7 +49,7 @@ public class WindFinderReportParserTest {
@Test
public void testOtherSpots() throws MalformedURLException, IOException, ParseException, InterruptedException, ExecutionException {
- assertEquals(4, Util.size(new ReviewedSpotsCollectionImpl("kielerfoerde").getSpots(/* cached */ false)));
+ assertEquals(3, Util.size(new ReviewedSpotsCollectionImpl("kielerfoerde").getSpots(/* cached */ false)));
assertEquals(3, Util.size(new ReviewedSpotsCollectionImpl("chiemsee").getSpots(/* cached */ false)));
assertEquals(1, Util.size(new ReviewedSpotsCollectionImpl("starnbergersee").getSpots(/* cached */ false)));
assertEquals(1, Util.size(new ReviewedSpotsCollectionImpl("wannsee").getSpots(/* cached */ false)));
diff --git a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF
index 761e8bbf3d5..6befe174cb8 100755
--- a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF
@@ -62,9 +62,9 @@ Import-Package: com.sap.sse.security,
org.apache.commons.io.input;version="2.2.0",
org.apache.commons.lang;version="2.6.0",
org.apache.http,
- org.apache.http.client;version="[4.2.5,4.3.0)",
- org.apache.http.client.entity;version="[4.2.5,4.3.0)",
- org.apache.http.client.methods;version="[4.2.5,4.3.0)",
- org.apache.http.impl.client;version="[4.2.5,4.3.0)",
- org.apache.http.message;version="[4.2.5,4.3.0)"
+ org.apache.http.client;version="4.5.5",
+ org.apache.http.client.entity;version="4.5.5",
+ org.apache.http.client.methods;version="4.5.5",
+ org.apache.http.impl.client;version="4.5.5",
+ org.apache.http.message;version="4.4.9"
Automatic-Module-Name: com.sap.sailing.domain
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java
index f4cce8b36c3..a506181bf6d 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java
@@ -394,7 +394,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
this.getScoreCorrection() == null ? null : this.getScoreCorrection().getComment(),
this.getScoringScheme() == null ? null : this.getScoringScheme().getType(), this
.getScoringScheme().isHigherBetter(), () -> UUID.randomUUID().toString(), addOverallDetails,
- boatClass==null?null:new BoatClassDTO(boatClass.getName(), boatClass.getDisplayName(), boatClass.getHullLength(), boatClass.getHullBeam()));
+ boatClass==null?null:new BoatClassDTO(boatClass.getName(), boatClass.getHullLength(), boatClass.getHullBeam()));
result.type = getLeaderboardType();
result.competitors = new ArrayList<>();
result.setName(this.getName());
@@ -689,6 +689,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
Date timePointOfLastPositionFixAtOrBeforeQueryTimePoint = getTimePointOfLastFixAtOrBefore(competitor, trackedRace, timePoint);
if (track != null) {
entryDTO.averageSamplingInterval = track.getAverageIntervalBetweenRawFixes();
+ entryDTO.currentSpeedAndCourseOverGround = track.getEstimatedSpeed(timePoint);
}
if (timePointOfLastPositionFixAtOrBeforeQueryTimePoint != null) {
long timeDifferenceInMs = timePoint.asMillis() - timePointOfLastPositionFixAtOrBeforeQueryTimePoint.getTime();
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java
index d387ce2e209..ca85336eb8b 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointMatchRacing.java
@@ -10,7 +10,11 @@ import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetche
import com.sap.sse.common.TimePoint;
/**
- * Scoring system indented to by used for match racing where the winner gets 1 points and the loser gets 0 points
+ * Scoring system indented to by used for match racing where the winner gets 1 points and the loser gets 0 points.
+ *
+ * TODO: tie breaking according to the rules in C11.1
*/
public class HighPointMatchRacing extends HighPoint {
private static final long serialVersionUID = 9124974081268072297L;
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java
index 772cfd87df4..7c395f68b2e 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveByImpliedWindRankingMetric.java
@@ -636,16 +636,19 @@ implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric {
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
final Competitor competitorFarthestAhead = getCompetitorFarthestAhead(timePoint, cache);
if (startOfRace != null) {
- final Duration actualRaceDuration = startOfRace.until(timePoint);
+ final Duration durationSinceStartOfRaceUntilTimePoint = startOfRace.until(timePoint);
final Set>> futures = new HashSet<>();
for (final Competitor competitor : getTrackedRace().getRace().getCompetitors()) {
futures.add(ForkJoinTask.adapt(()->{
final Duration correctedTime = getCorrectedTime(competitor, timePoint, cache);
- return new Pair<>(competitor, new CompetitorRankingInfoImpl(
- timePoint, competitor, getWindwardDistanceTraveled(competitor, timePoint, cache),
- actualRaceDuration, correctedTime,
- getEstimatedActualDurationToCompetitorFarthestAhead(competitor, competitorFarthestAhead, timePoint, cache),
- correctedTime));
+ return new Pair<>(competitor,
+ new CompetitorRankingInfoImpl(timePoint, competitor,
+ getWindwardDistanceTraveled(competitor, timePoint, cache),
+ durationSinceStartOfRaceUntilTimePoint,
+ getTrackedRace().getTimeSailedSinceRaceStart(competitor, timePoint), correctedTime,
+ getEstimatedActualDurationToCompetitorFarthestAhead(competitor,
+ competitorFarthestAhead, timePoint, cache),
+ correctedTime));
}).fork());
}
for (final ForkJoinTask> future : futures) {
@@ -706,7 +709,7 @@ implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric {
Duration result;
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace != null) {
- final Duration actualRaceDuration = startOfRace.until(rankingInfo.getTimePoint());
+ final Duration timeSailedSinceRaceStart = getTrackedRace().getTimeSailedSinceRaceStart(competitor, rankingInfo.getTimePoint());
final Competitor leader = rankingInfo.getLeaderByCorrectedEstimatedTimeToCompetitorFarthestAhead();
try {
final ORCPerformanceCurve competitorPerformanceCurve = cache.getPerformanceCurveForPartialCourse(
@@ -719,7 +722,7 @@ implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric {
leader, getImpliedWindSupplier(cache));
if (impliedWindOfLeader != null) {
final Duration allowanceForLeaderInCompetitorsPerformanceCurve = competitorPerformanceCurve.getAllowancePerCourse(impliedWindOfLeader);
- result = actualRaceDuration.minus(allowanceForLeaderInCompetitorsPerformanceCurve);
+ result = timeSailedSinceRaceStart.minus(allowanceForLeaderInCompetitorsPerformanceCurve);
} else {
result = null; // no implied wind for leader could be determined
}
@@ -741,35 +744,30 @@ implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric {
Duration result;
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (trackedLegOfCompetitor.hasStartedLeg(timePoint) && startOfRace != null) {
- final Duration actualRaceDurationForCompetitor = startOfRace.until(rankingInfo.getTimePoint());
- final ORCPerformanceCurveRankingInfo orcpcsRankingInfo = (ORCPerformanceCurveRankingInfo) rankingInfo;
- final Competitor legLeader = orcpcsRankingInfo.getLeaderInLegByCalculatedTime(trackedLegOfCompetitor.getLeg(), cache);
+ final ORCPerformanceCurveRankingInfo orcPcsRankingInfo = (ORCPerformanceCurveRankingInfo) rankingInfo;
+ final Competitor legLeader = orcPcsRankingInfo.getLeaderInLegByCalculatedTime(trackedLegOfCompetitor.getLeg(), cache);
final Competitor competitor = trackedLegOfCompetitor.getCompetitor();
- if (competitor == legLeader) {
- result = Duration.NULL;
- } else {
- TimePoint timeForCompetitorImpliedWindCalculation;
- try {
- timeForCompetitorImpliedWindCalculation = nowOrLegFinishTimeIfFinishedAtTimePoint(trackedLegOfCompetitor, timePoint, cache);
- final ORCPerformanceCurve competitorPerformanceCurveForLeg = getPerformanceCurveForPartialCourse(competitor, timeForCompetitorImpliedWindCalculation, cache);
- if (competitorPerformanceCurveForLeg == null) {
- result = null;
- } else {
- final TimePoint timeForLegLeaderImpliedWindCalculation = nowOrLegFinishTimeIfFinishedAtTimePoint(
- getTrackedRace().getTrackedLeg(legLeader, trackedLegOfCompetitor.getLeg()), timePoint, cache);
- final Speed legLeaderImpliedWindInOrAtEndOfLeg = cache.getImpliedWind(timeForLegLeaderImpliedWindCalculation, getTrackedRace(), competitor,
- getImpliedWindSupplier(cache));
- if (legLeaderImpliedWindInOrAtEndOfLeg != null) {
- final Duration correctedTimeOfLegLeaderInCompetitorsPerformanceCurve = competitorPerformanceCurveForLeg.getAllowancePerCourse(legLeaderImpliedWindInOrAtEndOfLeg);
- result = actualRaceDurationForCompetitor.minus(correctedTimeOfLegLeaderInCompetitorsPerformanceCurve);
- } else {
- result = null;
- }
- }
- } catch (FunctionEvaluationException e) {
- logger.log(Level.WARNING, "Problem with performance curve calculation for competitor "+competitor+" or "+legLeader, e);
+ try {
+ final TimePoint timeForCompetitorImpliedWindCalculation = timePointOrLegFinishTimeIfFinishedAtTimePoint(trackedLegOfCompetitor, timePoint, cache);
+ final ORCPerformanceCurve competitorPerformanceCurveForLeg = getPerformanceCurveForPartialCourse(competitor, timeForCompetitorImpliedWindCalculation, cache);
+ if (competitorPerformanceCurveForLeg == null) {
result = null;
+ } else {
+ final TimePoint timeForLegLeaderImpliedWindCalculation = timePointOrLegFinishTimeIfFinishedAtTimePoint(
+ getTrackedRace().getTrackedLeg(legLeader, trackedLegOfCompetitor.getLeg()), timePoint, cache);
+ final Speed legLeaderImpliedWindInOrAtEndOfLeg = cache.getImpliedWind(timeForLegLeaderImpliedWindCalculation, getTrackedRace(), legLeader,
+ getImpliedWindSupplier(cache));
+ if (legLeaderImpliedWindInOrAtEndOfLeg != null) {
+ final Duration correctedTimeOfLegLeaderInCompetitorsPerformanceCurve = competitorPerformanceCurveForLeg.getAllowancePerCourse(legLeaderImpliedWindInOrAtEndOfLeg);
+ final Duration actualRaceDurationForCompetitor = startOfRace.until(timeForCompetitorImpliedWindCalculation);
+ result = actualRaceDurationForCompetitor.minus(correctedTimeOfLegLeaderInCompetitorsPerformanceCurve);
+ } else {
+ result = null;
+ }
}
+ } catch (FunctionEvaluationException e) {
+ logger.log(Level.WARNING, "Problem with performance curve calculation for competitor "+competitor+" or "+legLeader, e);
+ result = null;
}
} else {
result = null; // competitor didn't start leg yet at timePoint
@@ -777,7 +775,7 @@ implements com.sap.sailing.domain.orc.ORCPerformanceCurveRankingMetric {
return result;
}
- private TimePoint nowOrLegFinishTimeIfFinishedAtTimePoint(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint,
+ private TimePoint timePointOrLegFinishTimeIfFinishedAtTimePoint(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws FunctionEvaluationException {
final TimePoint timeForImpliedWindCalculation;
if (trackedLegOfCompetitor.hasFinishedLeg(timePoint)) {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java
index 709b7fbae52..54456862a4a 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java
@@ -70,8 +70,8 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv
/**
* This PolynomialSplineFunction is created with the array of course specific allowances for the boat which this
* ORCPerformanceCurve belongs to. This function contains subfunctions for each interval between two given
- * calculated points. The input for the function is the value of the implied wind (speed in kts) and the output an
- * allowance in sec/nm.
+ * calculated points. The input for the function is the implied wind speed in knots and the output the expected
+ * average speed over ground in knots.
*/
private final UnivariateDifferentiableFunction functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse;
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPublicCertificateDatabaseImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPublicCertificateDatabaseImpl.java
index e476a4b4c2a..ce4d129d23f 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPublicCertificateDatabaseImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPublicCertificateDatabaseImpl.java
@@ -40,7 +40,7 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
@@ -277,7 +277,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
public Iterable search(CountryCode issuingCountry, Integer yearOfIssuance, String referenceNumber,
String yachtName, String sailNumber, String boatClassName, boolean includeInvalid) throws Exception {
final Set result = new HashSet<>();
- final HttpClient client = new SystemDefaultHttpClient();
+ final HttpClient client = HttpClientBuilder.create().build();
final List params = new ArrayList<>();
params.add(ACTION_PARAM);
params.add(XSLP_PARAM);
@@ -496,7 +496,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
private ORCCertificate getSingleCertificate(final String queryParameters)
throws IOException, org.json.simple.parser.ParseException, ClientProtocolException {
final HttpGet getRequest = new HttpGet(SINGLE_CERTIFICATE_DOWNLOAD_URL+"&"+queryParameters);
- final HttpClient client = new SystemDefaultHttpClient();
+ final HttpClient client = HttpClientBuilder.create().build();
addAuthorizationHeader(getRequest);
final Iterable certificates = new ORCCertificatesJsonImporter().read(client.execute(getRequest).getEntity().getContent())
.getCertificates();
@@ -629,7 +629,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
final Set restrictedResults = new HashSet<>();
for (final CertificateHandle handle : filterHandlesForCurrentYear(certificateHandles)) {
final BoatClassMasterdata boatClassMasterData = BoatClassMasterdata.resolveBoatClass(handle.getBoatClassName());
- if (boatClassMasterData != null && boatClassMasterData.getDisplayName().equals(boatClass.getDisplayName())) {
+ if (boatClassMasterData != null && boatClassMasterData.getDisplayName().equals(boatClass.getName())) {
restrictedResults.add(handle);
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
index b00cf1cd85c..5566d8bb53b 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
@@ -57,21 +57,29 @@ public abstract class AbstractRankingMetric implements RankingMetric {
*/
private final Distance windwardDistanceSailed;
+ private final Duration durationSinceStartOfRaceUntilTimePoint;
+
/**
* Usually the difference between {@link #timePoint} and the start of the race
+ *
+ * FIXME bug5316: there is an important difference to be made here in case {@link #timePoint} is after the {@link #competitor} has finished the race;
+ * then, timeElapsed should probably stop counting when the race is finished, but the difference betten {@link #timePoint} and the start of the race keeps counting...
*/
private final Duration timeElapsed;
/**
* The corrected time for the {@link #competitor}, assuming the race ended at {@link #timePoint}. This
* is applying the handicaps proportionately to the time and distance the competitor sailed so far.
+ *
+ * FIXME bug5316: we probably want this to reflect the {@link #timeElapsed}, so stop counting when the {@link #competitor} finished the race
*/
private final Duration correctedTime;
/**
* Based on the {@link #competitor}'s average VMG in the current leg and the windward position
* of the competitor that is farthest ahead in the race, how long would it take {@link #competitor}
- * to reach the competitor farthest ahead if that competitor stopped at {@link #timePoint}?
+ * to reach the competitor farthest ahead if that competitor stopped at {@link #timePoint}? Can be
+ * a negative number if {@link #competitor} finished the race before {@link #timePoint}.
*/
private final Duration estimatedActualDurationToCompetitorFarthestAhead;
@@ -83,12 +91,13 @@ public abstract class AbstractRankingMetric implements RankingMetric {
private final Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead;
public CompetitorRankingInfoImpl(TimePoint timePoint, Competitor competitor, Distance windwardDistanceSailed,
- Duration timeElapsed, Duration correctedTime, Duration estimatedActualDurationToCompetitorFarthestAhead,
- Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead) {
+ Duration durationSinceStartOfRaceUntilTimePoint, Duration timeElapsed, Duration correctedTime,
+ Duration estimatedActualDurationToCompetitorFarthestAhead, Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead) {
super();
this.timePoint = timePoint;
this.competitor = competitor;
this.windwardDistanceSailed = windwardDistanceSailed;
+ this.durationSinceStartOfRaceUntilTimePoint = durationSinceStartOfRaceUntilTimePoint;
this.timeElapsed = timeElapsed;
this.correctedTime = correctedTime;
this.estimatedActualDurationToCompetitorFarthestAhead = estimatedActualDurationToCompetitorFarthestAhead;
@@ -110,6 +119,11 @@ public abstract class AbstractRankingMetric implements RankingMetric {
return windwardDistanceSailed;
}
+ @Override
+ public Duration getDurationSinceStartOfRaceUntilTimePoint() {
+ return durationSinceStartOfRaceUntilTimePoint;
+ }
+
@Override
public Duration getTimeElapsed() {
return timeElapsed;
@@ -312,16 +326,16 @@ public abstract class AbstractRankingMetric implements RankingMetric {
* For the situation at timePoint, determines how long in real, uncorrected time who lags
* behind to in the leg identified by legWho. If both are still sailing in the leg at
* timePoint, this is the time who needs with constant average VMG to reach
- * to's position at timePoint. If only to has already finished the leg at
- * {@code timePoint} then who is projected to the end of the leg using her average VMG on the leg, and
- * the difference between who's projected and to's actual mark passing times is returned.
- * Note that in this latter case it doesn't matter whether {@code who} already has a mark passing for the end of the
- * leg or not; the idea is to not "rewrite history" by letting the mark passing have an impact on the rankings prior
- * to the mark passing.
+ * to's "windward" (or along course for reaching legs) position at timePoint. If only
+ * to has already finished the leg at {@code timePoint} then who's projected duration to the end
+ * of the leg using her average VMG on the leg is used. Note that in this latter case it doesn't matter whether
+ * {@code who} already has a mark passing for the end of the leg or not; the idea is to not "rewrite history" by
+ * letting the mark passing have an impact on the rankings prior to the mark passing.
*
*
* The result may be a negative duration in case who reached the position in question before
- * timePoint.
+ * timePoint. If who sailed past the waypoint without receiving a mark passing, we assume
+ * who has to spend as much time to get back to the waypoint and will return a positive duration.
*
*
* Precondition: who and to have both started sailing the leg at timePoint
@@ -339,12 +353,15 @@ public abstract class AbstractRankingMetric implements RankingMetric {
if (whosLegFinishTime != null && !whosLegFinishTime.after(timePoint)) {
// who's leg finishing time is known and is already reached at timePoint; we don't need to extrapolate;
// "who" needs no more time at timePoint to reach the end of the leg
- toEndOfLegOrTo = Duration.NULL;
+ toEndOfLegOrTo = timePoint.until(whosLegFinishTime);
} else {
// estimate who's leg finishing time by extrapolating with the average VMG (if available) or the current VMG
// (if no average VMG can currently be computed, e.g., because the time point is exactly at the leg start)
final Position positionOfEndOfLeg = getTrackedRace().getApproximatePosition(legWho.getLeg().getTo(), timePoint);
- toEndOfLegOrTo = getDurationToReach(positionOfEndOfLeg, timePoint, legWho, cache);
+ // Turn into a positive duration because a negative duration can only occur if "who" sailed past the waypoint without
+ // receiving a mark passing and hence now has to sail back, taking a positive duration to get there.
+ final Duration durationToReach = getDurationToReach(positionOfEndOfLeg, timePoint, legWho, cache);
+ toEndOfLegOrTo = durationToReach==null?null:durationToReach.abs();
}
} else {
// competitor "to" is still in same leg; project "who" to "to"'s position using VMG
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
index c1ef2a04d64..d35fd3bc602 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
@@ -37,7 +37,7 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
@Override
public Duration getActualTimeFromRaceStartToReachFarthestAheadInLeg(Competitor competitor, Leg leg, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
final Duration result;
- final TrackedLegOfCompetitor tloc = getTrackedRace().getTrackedLeg(competitor, getTimePoint());
+ final TrackedLegOfCompetitor tloc = getTrackedRace().getTrackedLeg(competitor, leg);
final Duration raceDurationAtTimePoint = getTrackedRace().getStartOfRace().until(getTimePoint());
if (tloc != null && tloc.hasStartedLeg(getTimePoint())) {
final Competitor competitorFarthestAheadInLeg = getCompetitorFarthestAheadInLeg(leg, getTimePoint(), cache);
@@ -83,7 +83,7 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
final Distance totalWindwardDistanceTraveled = getWindwardDistanceTraveled(competitorFarthestAhead, timePoint, cache);
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace != null) {
- final Duration actualRaceDuration = startOfRace.until(timePoint);
+ final Duration durationSinceStartOfRaceUntilTimePoint = startOfRace.until(timePoint);
final Waypoint finish = getTrackedRace().getRace().getCourse().getLastWaypoint();
for (Competitor competitor : getCompetitors()) {
// accommodate also for the possibility of an empty course (finish==null)
@@ -94,13 +94,13 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
// at the finish mark passing:
timeElapsed = startOfRace.until(finishMarkPassing.getTimePoint());
} else {
- timeElapsed = actualRaceDuration;
+ timeElapsed = durationSinceStartOfRaceUntilTimePoint;
}
// TODO bug5110: we cannot compute the following if at timePoint the position of either of the two competitors involved is unknown; we can, however, do this if timePoint is after the two finish mark passings, or if the competitorFarthestAhead has already finished at timePoint and the position of "competitor" is known.
final Duration predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead = getPredictedDurationToReachWindwardPositionOf(
competitor, competitorFarthestAhead, timePoint, cache);
final Duration totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead = predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead == null ? null
- : timeElapsed.plus(predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead);
+ : durationSinceStartOfRaceUntilTimePoint.plus(predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead);
final Duration calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead = totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead == null ? null
: getCalculatedTime(
competitor,
@@ -113,8 +113,9 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
() -> getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), () -> getTrackedRace()
.getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolated */true),
getTrackedRace().getTimeSailedSinceRaceStart(competitor, timePoint), totalWindwardDistanceTraveled);
- RankingMetric.CompetitorRankingInfo rankingInfo = new CompetitorRankingInfoImpl(timePoint, competitor,
- getWindwardDistanceTraveled(competitor, timePoint, cache), timeElapsed, calculatedTime,
+ RankingMetric.CompetitorRankingInfo rankingInfo = new CompetitorRankingInfoImpl(timePoint,
+ competitor, getWindwardDistanceTraveled(competitor, timePoint, cache),
+ durationSinceStartOfRaceUntilTimePoint, timeElapsed, calculatedTime,
predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead,
calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead);
result.put(competitor, rankingInfo);
@@ -191,7 +192,11 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
final Competitor who = legWho.getCompetitor();
final Competitor to = legTo.getCompetitor();
if (who == to) {
- result = Duration.NULL;
+ if (isAssumedToHaveFinishedLeg(timePoint, legWho)) {
+ result = timePoint.until(legWho.getFinishTime()); // negative time; reached end of leg some time ago
+ } else {
+ result = Duration.NULL; // still racing in the leg; reaches its own position in no time
+ }
} else {
assert getTrackedRace().getRace().getCourse().getIndexOfWaypoint(legWho.getLeg().getFrom()) <= getTrackedRace()
.getRace().getCourse().getIndexOfWaypoint(legTo.getLeg().getFrom());
@@ -199,8 +204,9 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
// when "who" has sailed (or is extrapolated) beyond the end of the leg without having received a mark passing.
// In this case we want to assume that it will take "who" approximately that long again to reach back to
// the waypoint to actually pass it; so we use the absolute duration instead.
- final Duration signedDuration = getPredictedDurationToEndOfLegOrTo(timePoint, legWho, legWho.getTrackedLeg().getTrackedLeg(to), cache);
- final Duration toEndOfLegOrTo = signedDuration == null ? null : signedDuration.abs();
+ final Duration toEndOfLegOrTo = getPredictedDurationToEndOfLegOrTo(timePoint, legWho, legWho.getTrackedLeg().getTrackedLeg(to), cache);
+ // toEndOfLegOrTo may be a negative Duration in case both have finished the leg before timePoint; it will then represent the duration
+ // between "who"'s leg finish mark passing and timePoint.
if (toEndOfLegOrTo == null) {
result = null;
} else {
@@ -208,6 +214,7 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
if (legWho.getLeg() == legTo.getLeg()) {
durationForSubsequentLegsToReachAtEqualPerformance = Duration.NULL;
} else {
+ // FIXME bug5316: isn't it strange to assume that "to" reached its current position at timePoint? It could have been way earlier than that...
durationForSubsequentLegsToReachAtEqualPerformance = getDurationToReachAtEqualPerformance(who, to,
legWho.getLeg().getTo(), timePoint, cache);
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
index dbc4c0ae8ee..76a70e73250 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
@@ -55,8 +55,6 @@ public class OneDesignRankingMetric extends NonPerformanceCurveRankingMetric {
// because otherwise the competitor would not be considered in this leg anymore. In this case, the mark passing
// times for finishing the leg are compared between competitor and leader.
final Duration result;
- final TrackedLegOfCompetitor currentLegWho = getCurrentLegOrLastLegIfAlreadyFinished(competitor,
- rankingInfo.getTimePoint());
final Competitor to = rankingInfo.getCompetitorFarthestAhead();
final TrackedLegOfCompetitor currentLegTo = getCurrentLegOrLastLegIfAlreadyFinished(to, rankingInfo.getTimePoint());
if (currentLegTo == null) {
@@ -68,45 +66,18 @@ public class OneDesignRankingMetric extends NonPerformanceCurveRankingMetric {
// not work properly because it considers both on the same leg and therefore doesn't restrict the time to when to finished
// the leg and looks at the excess time.
final Waypoint lastWaypoint = getTrackedRace().getRace().getCourse().getLastWaypoint();
- if (tosLegFinishingTime != null && !tosLegFinishingTime.after(rankingInfo.getTimePoint()) &&
- currentLegTo.getTrackedLeg().getLeg().getTo() == lastWaypoint) {
- // to has finished the race
- if (currentLegWho == null) {
- result = null;
- } else {
- final TimePoint whosLegFinishingTime = currentLegWho.getFinishTime();
- if (whosLegFinishingTime != null && !whosLegFinishingTime.after(rankingInfo.getTimePoint())) {
- // both have finished their leg; this means both have finished the race because otherwise this wouldn't be
- // who's current leg
- result = tosLegFinishingTime.until(whosLegFinishingTime);
- } else {
- final Duration etaNextMark = currentLegWho.getEstimatedTimeToNextMark(rankingInfo.getTimePoint(), WindPositionMode.EXACT);
- if (etaNextMark == null) {
- result = null;
- } else {
- final Duration timeToTookToFinishRaceStartingAtNextMark;
- final Waypoint whosNextMark = currentLegWho.getTrackedLeg().getLeg().getTo();
- if (whosNextMark == lastWaypoint) {
- // who is in the last leg; the ETA to who's next mark is the solution
- timeToTookToFinishRaceStartingAtNextMark = Duration.NULL;
- } else {
- // after passing the next mark, who still needs to travel about the same time that to
- // traveled after passing the next mark until finishing the race
- final MarkPassing tosMarkPassingForWhosNextMark = getTrackedRace().getMarkPassing(to, whosNextMark);
- timeToTookToFinishRaceStartingAtNextMark = tosMarkPassingForWhosNextMark == null ? null :
- tosMarkPassingForWhosNextMark.getTimePoint().until(tosLegFinishingTime);
- }
- if (timeToTookToFinishRaceStartingAtNextMark == null) {
- result = null;
- } else {
- final TimePoint etaAtFinish = rankingInfo.getTimePoint().plus(etaNextMark.plus(timeToTookToFinishRaceStartingAtNextMark));
- result = tosLegFinishingTime.until(etaAtFinish);
- }
- }
- }
- }
+
+ final Duration howLongWhoNedsToReachTosPosition = rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead();
+ if (howLongWhoNedsToReachTosPosition == null) {
+ result = null;
} else {
- result = rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead();
+ if (tosLegFinishingTime != null && !tosLegFinishingTime.after(rankingInfo.getTimePoint()) &&
+ currentLegTo.getTrackedLeg().getLeg().getTo() == lastWaypoint) {
+ final Duration howLongAgoToPassedTheFinishLine = tosLegFinishingTime.until(rankingInfo.getTimePoint());
+ result = howLongWhoNedsToReachTosPosition.plus(howLongAgoToPassedTheFinishLine);
+ } else {
+ result = howLongWhoNedsToReachTosPosition;
+ }
}
}
return result;
@@ -169,6 +140,12 @@ public class OneDesignRankingMetric extends NonPerformanceCurveRankingMetric {
return getWindwardDistanceTraveled(competitor, timePoint, cache);
}
+ @Override
+ public Duration getDurationSinceStartOfRaceUntilTimePoint() {
+ final TimePoint startOfRace = getTrackedRace().getStartOfRace();
+ return startOfRace==null?null:startOfRace.until(getTimePoint());
+ }
+
@Override
public Duration getTimeElapsed() {
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java
index 97800f6d80c..a2ef6819812 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java
@@ -60,10 +60,17 @@ public interface RankingMetric extends Serializable {
* until {@link #competitor} has actually started.
*/
Distance getWindwardDistanceSailed();
+
+ /**
+ * Keeps counting, even after the competitor has finished or the entire race has finished.
+ */
+ Duration getDurationSinceStartOfRaceUntilTimePoint();
/**
* Usually the difference between {@link #timePoint} and the start of the race; null if the
- * race start time is not known.
+ * race start time is not known. If the {@link #getCompetitor() competitor} has finished the race before the
+ * {@link #getTimePoint() time point}, the difference between race start time and the competitor's finish
+ * mark passing time point is used to make the time elapsed stop counting after finishing the race.
*/
Duration getTimeElapsed();
@@ -82,7 +89,7 @@ public interface RankingMetric extends Serializable {
default Duration getEstimatedActualDurationFromRaceStartToCompetitorFarthestAhead() {
final Duration estimatedActualDurationFromTimePointToCompetitorFarthestAhead = getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead();
- final Duration actualTime = getTimeElapsed();
+ final Duration actualTime = getDurationSinceStartOfRaceUntilTimePoint();
return actualTime == null ? null :
estimatedActualDurationFromTimePointToCompetitorFarthestAhead == null ? null :
actualTime.plus(estimatedActualDurationFromTimePointToCompetitorFarthestAhead);
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java
index c8ccea398c9..995b09d4ef5 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.ranking;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -24,6 +26,7 @@ import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
+import com.sap.sse.util.SerializableRunnable;
import com.sap.sse.util.WeakReferenceWithCleanerCallback;
/**
@@ -52,13 +55,13 @@ import com.sap.sse.util.WeakReferenceWithCleanerCallback;
*
*/
public class TimeOnTimeAndDistanceRankingMetric extends NonPerformanceCurveRankingMetric {
- private static final long serialVersionUID = -2321904208518686420L;
+ private static final long serialVersionUID = 2827013130741242548L;
private final TimeOnTimeFactorMapping timeOnTimeFactor;
private final TimeOnDistanceAllowancePerNauticalMileMap timeOnDistanceFactorNauticalMile;
- private final ConcurrentHashMap timeOnTimeFactorCache;
+ private ConcurrentHashMap timeOnTimeFactorCache;
/**
* For each value obtained and stored in {@link #timeOnTimeFactorCache}, an update callback is registered
@@ -68,9 +71,9 @@ public class TimeOnTimeAndDistanceRankingMetric extends NonPerformanceCurveRanki
* When used together with a {@link WeakReferenceWithCleanerCallback}, any listener / observer relations
* can be terminated at that point.
*/
- private final ConcurrentHashMap timeOnTimeFactorCacheUpdateCallbacks;
+ private ConcurrentHashMap timeOnTimeFactorCacheUpdateCallbacks;
- private final ConcurrentHashMap timeOnDistanceFactorInSecondsPerNauticalMileCache;
+ private ConcurrentHashMap timeOnDistanceFactorInSecondsPerNauticalMileCache;
/**
* For each value obtained and stored in {@link #timeOnTimeFactorCache}, an update callback is registered
@@ -80,7 +83,7 @@ public class TimeOnTimeAndDistanceRankingMetric extends NonPerformanceCurveRanki
* When used together with a {@link WeakReferenceWithCleanerCallback}, any listener / observer relations
* can be terminated at that point.
*/
- private final ConcurrentHashMap timeOnDistanceFactorInSecondsPerNauticalMileCacheUpdateCallbacks;
+ private ConcurrentHashMap timeOnDistanceFactorInSecondsPerNauticalMileCacheUpdateCallbacks;
/**
* The regular constructor that can also be used as TimeOnTimeAndDistanceRankingMetric::new
@@ -118,14 +121,26 @@ public class TimeOnTimeAndDistanceRankingMetric extends NonPerformanceCurveRanki
timeOnDistanceFactorInSecondsPerNauticalMileCacheUpdateCallbacks = new ConcurrentHashMap<>();
}
+ /**
+ * Re-establishes empty caches for the time-on-time factors (TMF) and the time-on-distance allowances as well as the
+ * callbacks connected to those cache entries.
+ */
+ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ timeOnTimeFactorCache = new ConcurrentHashMap<>();
+ timeOnDistanceFactorInSecondsPerNauticalMileCache = new ConcurrentHashMap<>();
+ timeOnTimeFactorCacheUpdateCallbacks = new ConcurrentHashMap<>();
+ timeOnDistanceFactorInSecondsPerNauticalMileCacheUpdateCallbacks = new ConcurrentHashMap<>();
+ }
+
private Runnable getTimeOnTimeFactorCacheUpdateCallback(Competitor competitor) {
- final Runnable result = ()->timeOnTimeFactorCache.remove(competitor);
+ final SerializableRunnable result = ()->timeOnTimeFactorCache.remove(competitor);
timeOnTimeFactorCacheUpdateCallbacks.put(competitor, result);
return result;
}
private Runnable getTimeOnDistanceAllowanceCacheUpdateCallback(Competitor competitor) {
- final Runnable result = ()->timeOnDistanceFactorInSecondsPerNauticalMileCache.remove(competitor);
+ final SerializableRunnable result = ()->timeOnDistanceFactorInSecondsPerNauticalMileCache.remove(competitor);
timeOnDistanceFactorInSecondsPerNauticalMileCacheUpdateCallbacks.put(competitor, result);
return result;
}
@@ -184,7 +199,7 @@ public class TimeOnTimeAndDistanceRankingMetric extends NonPerformanceCurveRanki
for (TrackedLeg tl : getTrackedRace().getTrackedLegs()) {
totalWindwardDistanceIncludingCompleteLeg = totalWindwardDistanceIncludingCompleteLeg.add(tl
.getWindwardDistance(cache));
- if (tl == trackedLeg) {
+ if (tl == trackedLeg || totalWindwardDistanceIncludingCompleteLeg == null) {
break;
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicGPSFixMovingTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicGPSFixMovingTrackImpl.java
index 9b209ce49a2..5eec9f82271 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicGPSFixMovingTrackImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicGPSFixMovingTrackImpl.java
@@ -31,12 +31,17 @@ import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Speed;
import com.sap.sse.common.TimePoint;
+import com.sap.sse.common.impl.DegreeBearingImpl;
public class DynamicGPSFixMovingTrackImpl extends GPSFixTrackImpl implements DynamicGPSFixTrack {
private static final Logger logger = Logger.getLogger(DynamicGPSFixMovingTrackImpl.class.getName());
private static final long serialVersionUID = 9111448573301259784L;
private static final double MAX_SPEED_FACTOR_COMPARED_TO_MEASURED_SPEED_FOR_FILTERING = 2;
+ private static final Bearing MAX_COURSE_DIFFERENCE_BETWEEN_MEASURED_AND_COMPUTED_IN_DEGREES = new DegreeBearingImpl(90);
+
+ private long numberOfOutliersDueToCOGDiff;
+ private long numberOfFixesCheckedForOutlier;
/**
* Uses lossy compaction of fixes. See {@link CompactPositionHelper}. Use {@link #DynamicGPSFixMovingTrackImpl(Object, long, boolean)}
@@ -223,17 +228,11 @@ public class DynamicGPSFixMovingTrackImpl extends GPSFixTrackImpl extends GPSFixTrackImpl extends GPSFixTrackImpl 0)));
+ if (failedDueToCOGDiff) {
+ numberOfOutliersDueToCOGDiff++;
+ logger.finer(()->""+DynamicGPSFixMovingTrackImpl.this+": computed COG "+speedToNeighbor.getBearing()+" differs significantly (by "+
+ speedToNeighbor.getBearing().getDifferenceTo(takeCogAndSogFrom.getSpeed().getBearing()).abs()+
+ ") from reported COG "+takeCogAndSogFrom.getSpeed().getBearing());
+ }
+ numberOfFixesCheckedForOutlier++;
+ return foundValidPreviousFixInRange;
+ }
+
@Override
public void setMillisecondsOverWhichToAverage(long millisecondsOverWhichToAverage) {
super.setMillisecondsOverWhichToAverage(millisecondsOverWhichToAverage);
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/FinishTimeUpdateHandler.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/FinishTimeUpdateHandler.java
index e5c027ec562..62a4f388d03 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/FinishTimeUpdateHandler.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/FinishTimeUpdateHandler.java
@@ -13,8 +13,8 @@ import java.util.logging.Logger;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.AbstractHttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.parser.ParseException;
@@ -59,8 +59,9 @@ public class FinishTimeUpdateHandler extends UpdateHandler {
params.add(new BasicNameValuePair(FIELD_TRACKING_END_TIME, String.valueOf(newFinishedTime.plus(
TrackedRace.STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH).asMillis())));
request.setEntity(new UrlEncodedFormEntity(params));
- final AbstractHttpClient client = new SystemDefaultHttpClient();
- client.setRedirectStrategy(new LaxRedirectStrategyForAllRedirectResponseCodes());
+ final CloseableHttpClient client = HttpClientBuilder.create()
+ .setRedirectStrategy(new LaxRedirectStrategyForAllRedirectResponseCodes())
+ .build();
logger.info("Using " + stopTrackingURI.toString() + " to stop tracking");
final HttpResponse response = client.execute(request);
try {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/StartTimeUpdateHandler.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/StartTimeUpdateHandler.java
index b33650f08d4..32748350efd 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/StartTimeUpdateHandler.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/StartTimeUpdateHandler.java
@@ -15,10 +15,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.AbstractHttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.parser.ParseException;
@@ -92,8 +92,9 @@ public class StartTimeUpdateHandler extends UpdateHandler implements StartTimeCh
params.add(new BasicNameValuePair(FIELD_TRACKING_START_TIME, String.valueOf(newStartTime.minus(
TrackedRace.START_TRACKING_THIS_MUCH_BEFORE_RACE_START).asMillis())));
request.setEntity(new UrlEncodedFormEntity(params));
- final AbstractHttpClient client = new SystemDefaultHttpClient();
- client.setRedirectStrategy(new LaxRedirectStrategyForAllRedirectResponseCodes());
+ final HttpClient client = HttpClientBuilder.create()
+ .setRedirectStrategy(new LaxRedirectStrategyForAllRedirectResponseCodes())
+ .build();
logger.info("Using " + startTrackingURI.toString() + " to start tracking");
final HttpResponse response = client.execute(request);
try {
diff --git a/java/com.sap.sailing.feature.p2build/scripts/README b/java/com.sap.sailing.feature.p2build/scripts/README
index cf419621e87..57c4321dbab 100644
--- a/java/com.sap.sailing.feature.p2build/scripts/README
+++ b/java/com.sap.sailing.feature.p2build/scripts/README
@@ -6,5 +6,5 @@ what is defined in the .product file.
Example usage:
- analyzeProduct.sh ../raceanalysis.product ../../com.sap.sailing.server/SailingServer\ \(No\ Proxy\,\ GENERATED\).launch
+ updateEclipseLaunchers.sh ../raceanalysis.product ../../com.sap.sailing.server/SailingServer\ \(No\ Proxy\,\ GENERATED\).launch
diff --git a/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh b/java/com.sap.sailing.feature.p2build/scripts/updateEclipseLaunchers.sh
similarity index 97%
rename from java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh
rename to java/com.sap.sailing.feature.p2build/scripts/updateEclipseLaunchers.sh
index 0d570e4ec2e..fcf1cd16727 100755
--- a/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh
+++ b/java/com.sap.sailing.feature.p2build/scripts/updateEclipseLaunchers.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Call like this: analyzeProduct.sh [ ...]
+# Call like this: updateEclipseLaunchers.sh [ ...]
# Updates Eclipse .launch files with the bundles needed for starting an SAP Sailing server based on the current feature.xml.
# The .launch files must be in the workspace format that was introduced with Eclipse 2019-12. The script prints an error when no matching
# replacement section could be found.
@@ -82,7 +82,7 @@ while [ "$2" != "" ]; do
else
echo "Patching $2 ..."
if echo "$2" | grep -q winddbTest; then
- patched_workspace_bundles="${workspace_bundles}${NL}${NL}"
+ patched_workspace_bundles="${workspace_bundles}${NL}${NL}"
else
patched_workspace_bundles="${workspace_bundles}"
fi
diff --git a/java/com.sap.sailing.feature.p2build/scripts/updateSeleniumPom.sh b/java/com.sap.sailing.feature.p2build/scripts/updateSeleniumPom.sh
index 20f960569a8..5b367c13244 100755
--- a/java/com.sap.sailing.feature.p2build/scripts/updateSeleniumPom.sh
+++ b/java/com.sap.sailing.feature.p2build/scripts/updateSeleniumPom.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Call like this: analyzeProduct.sh
+# Call like this: updateSeleniumPom.sh
# Will patch the bundleStartLevel section in the pom.xml file so that all auto-start bundles
# configured in the product specification are also launched properly and with the correct start level
# for the Selenium tests.
diff --git a/java/com.sap.sailing.feature.runtime/feature.xml b/java/com.sap.sailing.feature.runtime/feature.xml
index 6c196d28d02..c300bbc664a 100644
--- a/java/com.sap.sailing.feature.runtime/feature.xml
+++ b/java/com.sap.sailing.feature.runtime/feature.xml
@@ -32,7 +32,7 @@
id="org.apache.commons.codec"
download-size="0"
install-size="0"
- version="1.6.0.v201305230611"
+ version="1.10.0.v20180409-1845"
unpack="false"/>
-
+
-
+
-
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
-
-
+
+
-
+
+
-
+
-
-
+
+
diff --git a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
index ca29a193fce..12e736bb138 100755
--- a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
+++ b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml
@@ -147,6 +147,15 @@
UserManagementService/service/usermanagement
+
+ Chargebee Subscription Service
+ ChargebeeSubscriptionService
+ com.sap.sailing.gwt.ui.server.subscription.chargebee.ChargebeeSubscriptionWriteServiceImpl
+
+
+ ChargebeeSubscriptionService
+ /service/subscription/chargebee
+
diff --git a/java/com.sap.sailing.gwt.ui/imprint.json b/java/com.sap.sailing.gwt.ui/imprint.json
index 97ff0810937..3f9b67610b3 100644
--- a/java/com.sap.sailing.gwt.ui/imprint.json
+++ b/java/com.sap.sailing.gwt.ui/imprint.json
@@ -753,7 +753,7 @@
"acknowledgements": [
"Copyright 2016 yanwsh@gmail.com"
],
- "key": "Apache License 2.0"
+ "key": "ASL2.0"
}
],
"licenses": [
@@ -781,5 +781,27 @@
"key": "UNIDATA",
"file": "unidata.txt"
}
+ ],
+ "disclaimers":[
+ {
+ "title":"SAP Legal Disclosure",
+ "link": "https://www.sap.com/corporate/en/legal/impressum.html",
+ "content": "Please visit ${link} to view the general SAP legal disclosure."
+ },
+ {
+ "title":"YouTube",
+ "link": "https://www.youtube.com/t/terms",
+ "content": "The application utilizes the YouTube video streaming service. Therefore, by using the application, users implicitly agree to the terms of service provided by YouTube, found under ${link}."
+ },
+ {
+ "title":"Google Privacy",
+ "link": "https://policies.google.com/privacy",
+ "content": "The application utilizes services of Google. Therefore, by using the application, users implicitly agree to the privacy policies of Google, as stated under ${link}."
+ },
+ {
+ "title":"Google Maps",
+ "link": "http://www.google.com/intl/en/policies/terms/",
+ "content": "Google Maps Service (\"GM Service\"). If the Cloud Service accesses the GM Service through a Google Maps API, customer's use of the GM Service is subject to Google's Terms of Service, which are set forth at ${link}. If customer does not accept the Google Terms of Service, including, but not limited to, all limitations and restrictions therein, customer may not use the GM Service in the Cloud Service. Use of the GM Service in or through the Cloud Service will constitute customer's acceptance of Google's Terms of Service. Customer's usage of the GM Service in or through the Cloud Service can be terminated by SAP without reason at any time and SAP will not be required to provide an equivalent service via another provider."
+ }
]
}
\ No newline at end of file
diff --git a/java/com.sap.sailing.gwt.ui/js/Vimeo.js b/java/com.sap.sailing.gwt.ui/js/Vimeo.js
deleted file mode 100644
index c30cf4ef9c2..00000000000
--- a/java/com.sap.sailing.gwt.ui/js/Vimeo.js
+++ /dev/null
@@ -1,657 +0,0 @@
-/* The MIT License (MIT)
-Copyright (c) 2014-2015 Benoit Tremblay
-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. */
-(function() {
- 'use strict';
-
- var VimeoState = {
- UNSTARTED: -1,
- ENDED: 0,
- PLAYING: 1,
- PAUSED: 2,
- BUFFERING: 3
- };
-
- var Tech = videojs.getComponent('Tech');
-
- var Vimeo = videojs.extend(Tech, {
- constructor: function(options, ready) {
- Tech.call(this, options, ready);
- if(options.poster != "") {this.setPoster(options.poster);}
- this.setSrc(this.options_.source.src, true);
-
- // Set the vjs-vimeo class to the player
- // Parent is not set yet so we have to wait a tick
- setTimeout(function() {
- this.el_.parentNode.className += ' vjs-vimeo';
- }.bind(this));
-
- },
-
- dispose: function() {
- this.el_.parentNode.className = this.el_.parentNode.className.replace(' vjs-vimeo', '');
- },
-
- createEl: function() {
- this.vimeo = {};
- this.vimeoInfo = {};
- this.baseUrl = 'https://player.vimeo.com/video/';
- this.baseApiUrl = 'https://www.vimeo.com/api/v2/video/';
- this.videoId = Vimeo.parseUrl(this.options_.source.src).videoId;
-
- this.iframe = document.createElement('iframe');
- this.iframe.setAttribute('id', this.options_.techId);
- this.iframe.setAttribute('title', 'Vimeo Video Player');
- this.iframe.setAttribute('class', 'vimeoplayer');
- this.iframe.setAttribute('src', this.baseUrl + this.videoId + '?api=1&player_id=' + this.options_.techId);
- this.iframe.setAttribute('frameborder', '0');
- this.iframe.setAttribute('scrolling', 'no');
- this.iframe.setAttribute('marginWidth', '0');
- this.iframe.setAttribute('marginHeight', '0');
- this.iframe.setAttribute('webkitAllowFullScreen', '0');
- this.iframe.setAttribute('mozallowfullscreen', '0');
- this.iframe.setAttribute('allowFullScreen', '0');
-
- var divWrapper = document.createElement('div');
- divWrapper.setAttribute('style', 'margin:0 auto;padding-bottom:56.25%;width:100%;height:0;position:relative;overflow:hidden;');
- divWrapper.setAttribute('class', 'vimeoFrame');
- divWrapper.appendChild(this.iframe);
-
- if (!_isOnMobile && !this.options_.ytControls) {
- var divBlocker = document.createElement('div');
- divBlocker.setAttribute('class', 'vjs-iframe-blocker');
- divBlocker.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%');
-
- // In case the blocker is still there and we want to pause
- divBlocker.onclick = function() {
- this.onPause();
- }.bind(this);
-
- divWrapper.appendChild(divBlocker);
- }
-
- if (Vimeo.isApiReady) {
- this.initPlayer();
- } else {
- Vimeo.apiReadyQueue.push(this);
- }
-
- if(this.options_.poster == "") {
- $.getJSON(this.baseApiUrl + this.videoId + '.json?callback=?', {format: "json"}, (function(_this){
- return function(data) {
- // Set the low resolution first
- _this.setPoster(data[0].thumbnail_large);
- };
- })(this));
- }
-
- return divWrapper;
- },
-
- initPlayer: function() {
- var self = this;
- var vimeoVideoID = Vimeo.parseUrl(this.options_.source.src).videoId;
- //load vimeo
- if (this.vimeo && this.vimeo.api) {
- this.vimeo.api('unload');
- delete this.vimeo;
- }
-
- self.vimeo = $f(self.iframe);
-
- self.vimeoInfo = {
- state: VimeoState.UNSTARTED,
- volume: 1,
- muted: false,
- muteVolume: 1,
- time: 0,
- duration: 0,
- buffered: 0,
- url: self.baseUrl + self.videoId,
- error: null
- };
-
- this.vimeo.addEvent('ready', function(id){
- self.onReady();
-
- self.vimeo.addEvent('loadProgress', function(data, id){ self.onLoadProgress(data); });
- self.vimeo.addEvent('playProgress', function(data, id){ self.onPlayProgress(data); });
- self.vimeo.addEvent('play', function(id){ self.onPlay(); });
- self.vimeo.addEvent('pause', function(id){ self.onPause(); });
- self.vimeo.addEvent('finish', function(id){ self.onFinish(); });
- self.vimeo.addEvent('seek', function(data, id){ self.onSeek(data); });
-
- });
-
- },
-
- onReady: function(){
- this.isReady_ = true;
- this.triggerReady();
- this.trigger('loadedmetadata');
- if (this.startMuted) {
- this.setMuted(true);
- this.startMuted = false;
- }
- },
-
- onLoadProgress: function(data) {
- var durationUpdate = !this.vimeoInfo.duration;
- this.vimeoInfo.duration = data.duration;
- this.vimeoInfo.buffered = data.percent;
- this.trigger('progress');
- if (durationUpdate) this.trigger('durationchange');
- },
- onPlayProgress: function(data) {
- this.vimeoInfo.time = data.seconds;
- this.trigger('timeupdate');
- },
- onPlay: function() {
- this.vimeoInfo.state = VimeoState.PLAYING;
- this.trigger('play');
- },
- onPause: function() {
- this.vimeoInfo.state = VimeoState.PAUSED;
- this.trigger('pause');
- },
- onFinish: function() {
- this.vimeoInfo.state = VimeoState.ENDED;
- this.trigger('ended');
- },
- onSeek: function(data) {
- this.trigger('seeking');
- this.vimeoInfo.time = data.seconds;
- this.trigger('timeupdate');
- this.trigger('seeked');
- },
- onError: function(error){
- this.error = error;
- this.trigger('error');
- },
-
- error: function() {
- switch (this.errorNumber) {
- case 2:
- return { code: 'Unable to find the video' };
-
- case 5:
- return { code: 'Error while trying to play the video' };
-
- case 100:
- return { code: 'Unable to find the video' };
-
- case 101:
- case 150:
- return { code: 'Playback on other Websites has been disabled by the video owner.' };
- }
-
- return { code: 'Vimeo unknown error (' + this.errorNumber + ')' };
- },
-
- src: function() {
- return this.source;
- },
-
- poster: function() {
- return this.poster_;
- },
-
- setPoster: function(poster) {
- this.poster_ = poster;
- },
-
- setSrc: function(source) {
- if (!source || !source.src) {
- return;
- }
-
- this.source = source;
- this.url = Vimeo.parseUrl(source.src);
-
- if (!this.options_.poster) {
- if (this.url.videoId) {
- $.getJSON(this.baseApiUrl + this.videoId + '.json?callback=?', {format: "json"}, (function(_this){
- return function(data) {
- // Set the low resolution first
- _this.poster_ = data[0].thumbnail_small;
- };
- })(this));
-
- // Check if their is a high res
- this.checkHighResPoster();
- }
- }
-
- if (this.options_.autoplay && !_isOnMobile) {
- if (this.isReady_) {
- this.play();
- } else {
- this.playOnReady = true;
- }
- }
- },
-
- supportsFullScreen: function() {
- return true;
- },
-
- //TRIGGER
- load : function(){},
- play : function(){ this.vimeo.api('play'); },
- pause : function(){ this.vimeo.api('pause'); },
- paused : function(){
- return this.vimeoInfo.state !== VimeoState.PLAYING &&
- this.vimeoInfo.state !== VimeoState.BUFFERING;
- },
-
- currentTime : function(){ return this.vimeoInfo.time || 0; },
-
- setCurrentTime :function(seconds){
- this.vimeo.api('seekTo', seconds);
- this.player_.trigger('timeupdate');
- },
-
- duration :function(){ return this.vimeoInfo.duration || 0; },
- buffered :function(){ return videojs.createTimeRange(0, (this.vimeoInfo.buffered*this.vimeoInfo.duration) || 0); },
-
- volume :function() { return (this.vimeoInfo.muted)? this.vimeoInfo.muteVolume : this.vimeoInfo.volume; },
- setVolume :function(percentAsDecimal){
- this.vimeo.api('setvolume', percentAsDecimal);
- this.vimeoInfo.volume = percentAsDecimal;
- this.player_.trigger('volumechange');
- },
- currentSrc :function() {
- return this.el_.src;
- },
- muted :function() { return this.vimeoInfo.muted || false; },
- setMuted :function(muted) {
- if (muted) {
- this.vimeoInfo.muteVolume = this.vimeoInfo.volume;
- this.setVolume(0);
- } else {
- this.setVolume(this.vimeoInfo.muteVolume);
- }
-
- this.vimeoInfo.muted = muted;
- this.player_.trigger('volumechange');
- },
-
- // Tries to get the highest resolution thumbnail available for the video
- checkHighResPoster: function(){
- var uri = '';
-
- try {
-
- $.getJSON(this.baseApiUrl + this.videoId + '.json?callback=?', {format: "json"}, (function(_uri){
- return function(data) {
- // Set the low resolution first
- _uri = data[0].thumbnail_large;
- };
- })(uri));
-
- var image = new Image();
- image.onload = function(){
- // Onload thumbnail
- if('naturalHeight' in this){
- if(this.naturalHeight <= 90 || this.naturalWidth <= 120) {
- this.onerror();
- return;
- }
- } else if(this.height <= 90 || this.width <= 120) {
- this.onerror();
- return;
- }
-
- this.poster_ = uri;
- this.trigger('posterchange');
- }.bind(this);
- image.onerror = function(){};
- image.src = uri;
- }
- catch(e){}
- }
- });
-
- Vimeo.isSupported = function() {
- return true;
- };
-
- Vimeo.canPlaySource = function(e) {
- return (e.type === 'video/vimeo');
- };
-
- var _isOnMobile = /(iPad|iPhone|iPod|Android)/g.test(navigator.userAgent);
-
- Vimeo.parseUrl = function(url) {
- var result = {
- videoId: null
- };
-
- var regex = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/;
- var match = url.match(regex);
-
- if (match) {
- result.videoId = match[5];
- }
-
- return result;
- };
-
- function injectCss() {
- var css = // iframe blocker to catch mouse events
- '.vjs-vimeo .vjs-iframe-blocker { display: none; }' +
- '.vjs-vimeo.vjs-user-inactive .vjs-iframe-blocker { display: block; }' +
- '.vjs-vimeo .vjs-poster { background-size: cover; }' +
- '.vjs-vimeo { height:100%; }' +
- '.vimeoplayer { width:100%; height:180%; position:absolute; left:0; top:-40%; }';
-
- var head = document.head || document.getElementsByTagName('head')[0];
-
- var style = document.createElement('style');
- style.type = 'text/css';
-
- if (style.styleSheet){
- style.styleSheet.cssText = css;
- } else {
- style.appendChild(document.createTextNode(css));
- }
-
- head.appendChild(style);
- }
-
- Vimeo.apiReadyQueue = [];
-
- var vimeoIframeAPIReady = function() {
- Vimeo.isApiReady = true;
- injectCss();
-
- for (var i = 0; i < Vimeo.apiReadyQueue.length; ++i) {
- Vimeo.apiReadyQueue[i].initPlayer();
- }
- };
-
- vimeoIframeAPIReady();
-
- videojs.registerTech('Vimeo', Vimeo);
-
-
-
- // Froogaloop API -------------------------------------------------------------
-
- // From https://github.com/vimeo/player-api/blob/master/javascript/froogaloop.js
- // Init style shamelessly stolen from jQuery http://jquery.com
- var Froogaloop = (function(){
- // Define a local copy of Froogaloop
- function Froogaloop(iframe) {
- // The Froogaloop object is actually just the init constructor
- return new Froogaloop.fn.init(iframe);
- }
-
- var eventCallbacks = {},
- hasWindowEvent = false,
- isReady = false,
- slice = Array.prototype.slice,
- playerOrigin = '*';
-
- Froogaloop.fn = Froogaloop.prototype = {
- element: null,
-
- init: function(iframe) {
- if (typeof iframe === "string") {
- iframe = document.getElementById(iframe);
- }
-
- this.element = iframe;
-
- return this;
- },
-
- /*
- * Calls a function to act upon the player.
- *
- * @param {string} method The name of the Javascript API method to call. Eg: 'play'.
- * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
- * or callback function when the method returns a value.
- */
- api: function(method, valueOrCallback) {
- if (!this.element || !method) {
- return false;
- }
-
- var self = this,
- element = self.element,
- target_id = element.id !== '' ? element.id : null,
- params = !isFunction(valueOrCallback) ? valueOrCallback : null,
- callback = isFunction(valueOrCallback) ? valueOrCallback : null;
-
- // Store the callback for get functions
- if (callback) {
- storeCallback(method, callback, target_id);
- }
-
- postMessage(method, params, element);
- return self;
- },
-
- /*
- * Registers an event listener and a callback function that gets called when the event fires.
- *
- * @param eventName (String): Name of the event to listen for.
- * @param callback (Function): Function that should be called when the event fires.
- */
- addEvent: function(eventName, callback) {
- if (!this.element) {
- return false;
- }
-
- var self = this,
- element = self.element,
- target_id = element.id !== '' ? element.id : null;
-
-
- storeCallback(eventName, callback, target_id);
-
- // The ready event is not registered via postMessage. It fires regardless.
- if (eventName != 'ready') {
- postMessage('addEventListener', eventName, element);
- }
- else if (eventName == 'ready' && isReady) {
- callback.call(null, target_id);
- }
-
- return self;
- },
-
- /*
- * Unregisters an event listener that gets called when the event fires.
- *
- * @param eventName (String): Name of the event to stop listening for.
- */
- removeEvent: function(eventName) {
- if (!this.element) {
- return false;
- }
-
- var self = this,
- element = self.element,
- target_id = element.id !== '' ? element.id : null,
- removed = removeCallback(eventName, target_id);
-
- // The ready event is not registered
- if (eventName != 'ready' && removed) {
- postMessage('removeEventListener', eventName, element);
- }
- }
- };
-
- /**
- * Handles posting a message to the parent window.
- *
- * @param method (String): name of the method to call inside the player. For api calls
- * this is the name of the api method (api_play or api_pause) while for events this method
- * is api_addEventListener.
- * @param params (Object or Array): List of parameters to submit to the method. Can be either
- * a single param or an array list of parameters.
- * @param target (HTMLElement): Target iframe to post the message to.
- */
- function postMessage(method, params, target) {
- if (!target.contentWindow.postMessage) {
- return false;
- }
-
- var data = JSON.stringify({
- method: method,
- value: params
- });
-
- target.contentWindow.postMessage(data, playerOrigin);
- }
-
- /**
- * Event that fires whenever the window receives a message from its parent
- * via window.postMessage.
- */
- function onMessageReceived(event) {
- var data, method;
-
- try {
- data = JSON.parse(event.data);
- method = data.event || data.method;
- }
- catch(e) {
- //fail silently... like a ninja!
- }
-
- if (method == 'ready' && !isReady) {
- isReady = true;
- }
-
- // Handles messages from the vimeo player only
- if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
- return false;
- }
-
- if (playerOrigin === '*') {
- playerOrigin = event.origin;
- }
-
- var value = data.value,
- eventData = data.data,
- target_id = target_id === '' ? null : data.player_id,
-
- callback = getCallback(method, target_id),
- params = [];
-
- if (!callback) {
- return false;
- }
-
- if (value !== undefined) {
- params.push(value);
- }
-
- if (eventData) {
- params.push(eventData);
- }
-
- if (target_id) {
- params.push(target_id);
- }
-
- return params.length > 0 ? callback.apply(null, params) : callback.call();
- }
-
-
- /**
- * Stores submitted callbacks for each iframe being tracked and each
- * event for that iframe.
- *
- * @param eventName (String): Name of the event. Eg. api_onPlay
- * @param callback (Function): Function that should get executed when the
- * event is fired.
- * @param target_id (String) [Optional]: If handling more than one iframe then
- * it stores the different callbacks for different iframes based on the iframe's
- * id.
- */
- function storeCallback(eventName, callback, target_id) {
- if (target_id) {
- if (!eventCallbacks[target_id]) {
- eventCallbacks[target_id] = {};
- }
- eventCallbacks[target_id][eventName] = callback;
- }
- else {
- eventCallbacks[eventName] = callback;
- }
- }
-
- /**
- * Retrieves stored callbacks.
- */
- function getCallback(eventName, target_id) {
- if (target_id && eventCallbacks[target_id]) {
- return eventCallbacks[target_id][eventName];
- }
- else if (eventCallbacks[eventName]) {
- return eventCallbacks[eventName];
- }
- }
-
- function removeCallback(eventName, target_id) {
- if (target_id && eventCallbacks[target_id]) {
- if (!eventCallbacks[target_id][eventName]) {
- return false;
- }
- eventCallbacks[target_id][eventName] = null;
- }
- else {
- if (!eventCallbacks[eventName]) {
- return false;
- }
- eventCallbacks[eventName] = null;
- }
-
- return true;
- }
-
- function isFunction(obj) {
- return !!(obj && obj.constructor && obj.call && obj.apply);
- }
-
- function isArray(obj) {
- return toString.call(obj) === '[object Array]';
- }
-
- // Give the init function the Froogaloop prototype for later instantiation
- Froogaloop.fn.init.prototype = Froogaloop.fn;
-
- // Listens for the message event.
- // W3C
- if (window.addEventListener) {
- window.addEventListener('message', onMessageReceived, false);
- }
- // IE
- else {
- window.attachEvent('onmessage', onMessageReceived);
- }
-
- // Expose froogaloop to the global object
- return (window.Froogaloop = window.$f = Froogaloop);
-
- })();
-})();
-
diff --git a/java/com.sap.sailing.gwt.ui/js/Youtube.js b/java/com.sap.sailing.gwt.ui/js/Youtube.js
new file mode 100644
index 00000000000..cbc813cfa85
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/js/Youtube.js
@@ -0,0 +1,798 @@
+/* The MIT License (MIT)
+
+Copyright (c) 2014-2015 Benoit Tremblay
+
+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. */
+/*global define, YT*/
+(function (root, factory) {
+ if(typeof exports==='object' && typeof module!=='undefined') {
+ var videojs = require('video.js');
+ module.exports = factory(videojs.default || videojs);
+ } else if(typeof define === 'function' && define.amd) {
+ define(['videojs'], function(videojs){
+ return (root.Youtube = factory(videojs));
+ });
+ } else {
+ root.Youtube = factory(root.videojs);
+ }
+}(this, function(videojs) {
+ 'use strict';
+
+ var _isOnMobile = videojs.browser.IS_IOS || videojs.browser.IS_NATIVE_ANDROID;
+ var Tech = videojs.getTech('Tech');
+
+ var Youtube = videojs.extend(Tech, {
+
+ constructor: function(options, ready) {
+ Tech.call(this, options, ready);
+
+ this.setPoster(options.poster);
+ this.setSrc(this.options_.source, true);
+
+ // Set the vjs-youtube class to the player
+ // Parent is not set yet so we have to wait a tick
+ this.setTimeout(function() {
+ if (this.el_) {
+ this.el_.parentNode.className += ' vjs-youtube';
+
+ if (_isOnMobile) {
+ this.el_.parentNode.className += ' vjs-youtube-mobile';
+ }
+
+ if (Youtube.isApiReady) {
+ this.initYTPlayer();
+ } else {
+ Youtube.apiReadyQueue.push(this);
+ }
+ }
+ }.bind(this));
+ },
+
+ dispose: function() {
+ if (this.ytPlayer) {
+ //Dispose of the YouTube Player
+ if (this.ytPlayer.stopVideo) {
+ this.ytPlayer.stopVideo();
+ }
+ if (this.ytPlayer.destroy) {
+ this.ytPlayer.destroy();
+ }
+ } else {
+ //YouTube API hasn't finished loading or the player is already disposed
+ var index = Youtube.apiReadyQueue.indexOf(this);
+ if (index !== -1) {
+ Youtube.apiReadyQueue.splice(index, 1);
+ }
+ }
+ this.ytPlayer = null;
+
+ this.el_.parentNode.className = this.el_.parentNode.className
+ .replace(' vjs-youtube', '')
+ .replace(' vjs-youtube-mobile', '');
+ this.el_.parentNode.removeChild(this.el_);
+
+ //Needs to be called after the YouTube player is destroyed, otherwise there will be a null reference exception
+ Tech.prototype.dispose.call(this);
+ },
+
+ createEl: function() {
+ var div = document.createElement('div');
+ div.setAttribute('id', this.options_.techId);
+ div.setAttribute('style', 'width:100%;height:100%;top:0;left:0;position:absolute');
+ div.setAttribute('class', 'vjs-tech');
+
+ var divWrapper = document.createElement('div');
+ divWrapper.appendChild(div);
+
+ if (!_isOnMobile && !this.options_.ytControls) {
+ var divBlocker = document.createElement('div');
+ divBlocker.setAttribute('class', 'vjs-iframe-blocker');
+ divBlocker.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%');
+
+ // In case the blocker is still there and we want to pause
+ divBlocker.onclick = function() {
+ this.pause();
+ }.bind(this);
+
+ divWrapper.appendChild(divBlocker);
+ }
+
+ return divWrapper;
+ },
+
+ initYTPlayer: function() {
+ var playerVars = {
+ controls: 0,
+ modestbranding: 1,
+ rel: 0,
+ showinfo: 0,
+ loop: this.options_.loop ? 1 : 0
+ };
+
+ // Let the user set any YouTube parameter
+ // https://developers.google.com/youtube/player_parameters?playerVersion=HTML5#Parameters
+ // To use YouTube controls, you must use ytControls instead
+ // To use the loop or autoplay, use the video.js settings
+
+ if (typeof this.options_.autohide !== 'undefined') {
+ playerVars.autohide = this.options_.autohide;
+ }
+
+ if (typeof this.options_['cc_load_policy'] !== 'undefined') {
+ playerVars['cc_load_policy'] = this.options_['cc_load_policy'];
+ }
+
+ if (typeof this.options_.ytControls !== 'undefined') {
+ playerVars.controls = this.options_.ytControls;
+ }
+
+ if (typeof this.options_.disablekb !== 'undefined') {
+ playerVars.disablekb = this.options_.disablekb;
+ }
+
+ if (typeof this.options_.color !== 'undefined') {
+ playerVars.color = this.options_.color;
+ }
+
+ if (!playerVars.controls) {
+ // Let video.js handle the fullscreen unless it is the YouTube native controls
+ playerVars.fs = 0;
+ } else if (typeof this.options_.fs !== 'undefined') {
+ playerVars.fs = this.options_.fs;
+ }
+
+ if (this.options_.source.src.indexOf('end=') !== -1) {
+ var srcEndTime = this.options_.source.src.match(/end=([0-9]*)/);
+ this.options_.end = parseInt(srcEndTime[1]);
+ }
+
+ if (typeof this.options_.end !== 'undefined') {
+ playerVars.end = this.options_.end;
+ }
+
+ if (typeof this.options_.hl !== 'undefined') {
+ playerVars.hl = this.options_.hl;
+ } else if (typeof this.options_.language !== 'undefined') {
+ // Set the YouTube player on the same language than video.js
+ playerVars.hl = this.options_.language.substr(0, 2);
+ }
+
+ if (typeof this.options_['iv_load_policy'] !== 'undefined') {
+ playerVars['iv_load_policy'] = this.options_['iv_load_policy'];
+ }
+
+ if (typeof this.options_.list !== 'undefined') {
+ playerVars.list = this.options_.list;
+ } else if (this.url && typeof this.url.listId !== 'undefined') {
+ playerVars.list = this.url.listId;
+ }
+
+ if (typeof this.options_.listType !== 'undefined') {
+ playerVars.listType = this.options_.listType;
+ }
+
+ if (typeof this.options_.modestbranding !== 'undefined') {
+ playerVars.modestbranding = this.options_.modestbranding;
+ }
+
+ if (typeof this.options_.playlist !== 'undefined') {
+ playerVars.playlist = this.options_.playlist;
+ }
+
+ if (typeof this.options_.playsinline !== 'undefined') {
+ playerVars.playsinline = this.options_.playsinline;
+ }
+
+ if (typeof this.options_.rel !== 'undefined') {
+ playerVars.rel = this.options_.rel;
+ }
+
+ if (typeof this.options_.showinfo !== 'undefined') {
+ playerVars.showinfo = this.options_.showinfo;
+ }
+
+ if (this.options_.source.src.indexOf('start=') !== -1) {
+ var srcStartTime = this.options_.source.src.match(/start=([0-9]*)/);
+ this.options_.start = parseInt(srcStartTime[1]);
+ }
+
+ if (typeof this.options_.start !== 'undefined') {
+ playerVars.start = this.options_.start;
+ }
+
+ if (typeof this.options_.theme !== 'undefined') {
+ playerVars.theme = this.options_.theme;
+ }
+
+ // Allow undocumented options to be passed along via customVars
+ if (typeof this.options_.customVars !== 'undefined') {
+ var customVars = this.options_.customVars;
+ Object.keys(customVars).forEach(function(key) {
+ playerVars[key] = customVars[key];
+ });
+ }
+
+ this.activeVideoId = this.url ? this.url.videoId : null;
+ this.activeList = playerVars.list;
+
+ var playerConfig = {
+ videoId: this.activeVideoId,
+ playerVars: playerVars,
+ events: {
+ onReady: this.onPlayerReady.bind(this),
+ onPlaybackQualityChange: this.onPlayerPlaybackQualityChange.bind(this),
+ onPlaybackRateChange: this.onPlayerPlaybackRateChange.bind(this),
+ onStateChange: this.onPlayerStateChange.bind(this),
+ onVolumeChange: this.onPlayerVolumeChange.bind(this),
+ onError: this.onPlayerError.bind(this)
+ }
+ };
+
+ if (typeof this.options_.enablePrivacyEnhancedMode !== 'undefined' && this.options_.enablePrivacyEnhancedMode) {
+ playerConfig.host = 'https://www.youtube-nocookie.com';
+ }
+
+ this.ytPlayer = new YT.Player(this.options_.techId, playerConfig);
+ },
+
+ onPlayerReady: function() {
+ if (this.options_.muted) {
+ this.ytPlayer.mute();
+ }
+
+ var playbackRates = this.ytPlayer.getAvailablePlaybackRates();
+ if (playbackRates.length > 1) {
+ this.featuresPlaybackRate = true;
+ }
+
+ this.playerReady_ = true;
+ this.triggerReady();
+
+ if (this.playOnReady) {
+ this.play();
+ } else if (this.cueOnReady) {
+ this.cueVideoById_(this.url.videoId);
+ this.activeVideoId = this.url.videoId;
+ }
+ },
+
+ onPlayerPlaybackQualityChange: function() {
+
+ },
+
+ onPlayerPlaybackRateChange: function() {
+ this.trigger('ratechange');
+ },
+
+ onPlayerStateChange: function(e) {
+ var state = e.data;
+
+ if (state === this.lastState || this.errorNumber) {
+ return;
+ }
+
+ this.lastState = state;
+
+ switch (state) {
+ case -1:
+ this.trigger('loadstart');
+ this.trigger('loadedmetadata');
+ this.trigger('durationchange');
+ this.trigger('ratechange');
+ break;
+
+ case YT.PlayerState.ENDED:
+ this.trigger('ended');
+ break;
+
+ case YT.PlayerState.PLAYING:
+ this.trigger('timeupdate');
+ this.trigger('durationchange');
+ this.trigger('playing');
+ this.trigger('play');
+
+ if (this.isSeeking) {
+ this.onSeeked();
+ }
+ break;
+
+ case YT.PlayerState.PAUSED:
+ this.trigger('canplay');
+ if (this.isSeeking) {
+ this.onSeeked();
+ } else {
+ this.trigger('pause');
+ }
+ break;
+
+ case YT.PlayerState.BUFFERING:
+ this.player_.trigger('timeupdate');
+ this.player_.trigger('waiting');
+ break;
+ }
+ },
+
+ onPlayerVolumeChange: function() {
+ this.trigger('volumechange');
+ },
+
+ onPlayerError: function(e) {
+ this.errorNumber = e.data;
+ this.trigger('pause');
+ this.trigger('error');
+ },
+
+ error: function() {
+ var code = 1000 + this.errorNumber; // as smaller codes are reserved
+ switch (this.errorNumber) {
+ case 5:
+ return { code: code, message: 'Error while trying to play the video' };
+
+ case 2:
+ case 100:
+ return { code: code, message: 'Unable to find the video' };
+
+ case 101:
+ case 150:
+ return {
+ code: code,
+ message: 'Playback on other Websites has been disabled by the video owner.'
+ };
+ }
+
+ return { code: code, message: 'YouTube unknown error (' + this.errorNumber + ')' };
+ },
+
+ loadVideoById_: function(id) {
+ var options = {
+ videoId: id
+ };
+ if (this.options_.start) {
+ options.startSeconds = this.options_.start;
+ }
+ if (this.options_.end) {
+ options.endSeconds = this.options_.end;
+ }
+ this.ytPlayer.loadVideoById(options);
+ },
+
+ cueVideoById_: function(id) {
+ var options = {
+ videoId: id
+ };
+ if (this.options_.start) {
+ options.startSeconds = this.options_.start;
+ }
+ if (this.options_.end) {
+ options.endSeconds = this.options_.end;
+ }
+ this.ytPlayer.cueVideoById(options);
+ },
+
+ src: function(src) {
+ if (src) {
+ this.setSrc({ src: src });
+ }
+
+ return this.source;
+ },
+
+ poster: function() {
+ // You can't start programmaticlly a video with a mobile
+ // through the iframe so we hide the poster and the play button (with CSS)
+ if (_isOnMobile) {
+ return null;
+ }
+
+ return this.poster_;
+ },
+
+ setPoster: function(poster) {
+ this.poster_ = poster;
+ },
+
+ setSrc: function(source) {
+ if (!source || !source.src) {
+ return;
+ }
+
+ delete this.errorNumber;
+ this.source = source;
+ this.url = Youtube.parseUrl(source.src);
+
+ if (!this.options_.poster) {
+ if (this.url.videoId) {
+ // Set the low resolution first
+ this.poster_ = 'https://img.youtube.com/vi/' + this.url.videoId + '/0.jpg';
+ this.trigger('posterchange');
+
+ // Check if their is a high res
+ this.checkHighResPoster();
+ }
+ }
+
+ if (this.options_.autoplay && !_isOnMobile) {
+ if (this.isReady_) {
+ this.play();
+ } else {
+ this.playOnReady = true;
+ }
+ } else if (this.activeVideoId !== this.url.videoId) {
+ if (this.isReady_) {
+ this.cueVideoById_(this.url.videoId);
+ this.activeVideoId = this.url.videoId;
+ } else {
+ this.cueOnReady = true;
+ }
+ }
+ },
+
+ autoplay: function() {
+ return this.options_.autoplay;
+ },
+
+ setAutoplay: function(val) {
+ this.options_.autoplay = val;
+ },
+
+ loop: function() {
+ return this.options_.loop;
+ },
+
+ setLoop: function(val) {
+ this.options_.loop = val;
+ },
+
+ play: function() {
+ if (!this.url || !this.url.videoId) {
+ return;
+ }
+
+ this.wasPausedBeforeSeek = false;
+
+ if (this.isReady_) {
+ if (this.url.listId) {
+ if (this.activeList === this.url.listId) {
+ this.ytPlayer.playVideo();
+ } else {
+ this.ytPlayer.loadPlaylist(this.url.listId);
+ this.activeList = this.url.listId;
+ }
+ }
+
+ if (this.activeVideoId === this.url.videoId) {
+ this.ytPlayer.playVideo();
+ } else {
+ this.loadVideoById_(this.url.videoId);
+ this.activeVideoId = this.url.videoId;
+ }
+ } else {
+ this.trigger('waiting');
+ this.playOnReady = true;
+ }
+ },
+
+ pause: function() {
+ if (this.ytPlayer) {
+ this.ytPlayer.pauseVideo();
+ }
+ },
+
+ paused: function() {
+ return (this.ytPlayer) ?
+ (this.lastState !== YT.PlayerState.PLAYING && this.lastState !== YT.PlayerState.BUFFERING)
+ : true;
+ },
+
+ currentTime: function() {
+ return this.ytPlayer ? this.ytPlayer.getCurrentTime() : 0;
+ },
+
+ setCurrentTime: function(seconds) {
+ if (this.lastState === YT.PlayerState.PAUSED) {
+ this.timeBeforeSeek = this.currentTime();
+ }
+
+ if (!this.isSeeking) {
+ this.wasPausedBeforeSeek = this.paused();
+ }
+
+ this.ytPlayer.seekTo(seconds, true);
+ this.trigger('timeupdate');
+ this.trigger('seeking');
+ this.isSeeking = true;
+
+ // A seek event during pause does not return an event to trigger a seeked event,
+ // so run an interval timer to look for the currentTime to change
+ if (this.lastState === YT.PlayerState.PAUSED && this.timeBeforeSeek !== seconds) {
+ clearInterval(this.checkSeekedInPauseInterval);
+ this.checkSeekedInPauseInterval = setInterval(function() {
+ if (this.lastState !== YT.PlayerState.PAUSED || !this.isSeeking) {
+ // If something changed while we were waiting for the currentTime to change,
+ // clear the interval timer
+ clearInterval(this.checkSeekedInPauseInterval);
+ } else if (this.currentTime() !== this.timeBeforeSeek) {
+ this.trigger('timeupdate');
+ this.onSeeked();
+ }
+ }.bind(this), 250);
+ }
+ },
+
+ seeking: function () {
+ return this.isSeeking;
+ },
+
+ seekable: function () {
+ if(!this.ytPlayer) {
+ return videojs.createTimeRange();
+ }
+
+ return videojs.createTimeRange(0, this.ytPlayer.getDuration());
+ },
+
+ onSeeked: function() {
+ clearInterval(this.checkSeekedInPauseInterval);
+ this.isSeeking = false;
+
+ if (this.wasPausedBeforeSeek) {
+ this.pause();
+ }
+
+ this.trigger('seeked');
+ },
+
+ playbackRate: function() {
+ return this.ytPlayer ? this.ytPlayer.getPlaybackRate() : 1;
+ },
+
+ setPlaybackRate: function(suggestedRate) {
+ if (!this.ytPlayer) {
+ return;
+ }
+
+ this.ytPlayer.setPlaybackRate(suggestedRate);
+ },
+
+ duration: function() {
+ return this.ytPlayer ? this.ytPlayer.getDuration() : 0;
+ },
+
+ currentSrc: function() {
+ return this.source && this.source.src;
+ },
+
+ ended: function() {
+ return this.ytPlayer ? (this.lastState === YT.PlayerState.ENDED) : false;
+ },
+
+ volume: function() {
+ return this.ytPlayer ? this.ytPlayer.getVolume() / 100.0 : 1;
+ },
+
+ setVolume: function(percentAsDecimal) {
+ if (!this.ytPlayer) {
+ return;
+ }
+
+ this.ytPlayer.setVolume(percentAsDecimal * 100.0);
+ },
+
+ muted: function() {
+ return this.ytPlayer ? this.ytPlayer.isMuted() : false;
+ },
+
+ setMuted: function(mute) {
+ if (!this.ytPlayer) {
+ return;
+ }
+ else{
+ this.muted(true);
+ }
+
+ if (mute) {
+ this.ytPlayer.mute();
+ } else {
+ this.ytPlayer.unMute();
+ }
+ this.setTimeout( function(){
+ this.trigger('volumechange');
+ }, 50);
+ },
+
+ buffered: function() {
+ if(!this.ytPlayer || !this.ytPlayer.getVideoLoadedFraction) {
+ return videojs.createTimeRange();
+ }
+
+ var bufferedEnd = this.ytPlayer.getVideoLoadedFraction() * this.ytPlayer.getDuration();
+
+ return videojs.createTimeRange(0, bufferedEnd);
+ },
+
+ // TODO: Can we really do something with this on YouTUbe?
+ preload: function() {},
+ load: function() {},
+ reset: function() {},
+ networkState: function () {
+ if (!this.ytPlayer) {
+ return 0; //NETWORK_EMPTY
+ }
+ switch (this.ytPlayer.getPlayerState()) {
+ case -1: //unstarted
+ return 0; //NETWORK_EMPTY
+ case 3: //buffering
+ return 2; //NETWORK_LOADING
+ default:
+ return 1; //NETWORK_IDLE
+ }
+ },
+ readyState: function () {
+ if (!this.ytPlayer) {
+ return 0; //HAVE_NOTHING
+ }
+ switch (this.ytPlayer.getPlayerState()) {
+ case -1: //unstarted
+ return 0; //HAVE_NOTHING
+ case 5: //video cued
+ return 1; //HAVE_METADATA
+ case 3: //buffering
+ return 2; //HAVE_CURRENT_DATA
+ default:
+ return 4; //HAVE_ENOUGH_DATA
+ }
+ },
+
+ supportsFullScreen: function() {
+ return document.fullscreenEnabled ||
+ document.webkitFullscreenEnabled ||
+ document.mozFullScreenEnabled ||
+ document.msFullscreenEnabled;
+ },
+
+ // Tries to get the highest resolution thumbnail available for the video
+ checkHighResPoster: function(){
+ var uri = 'https://img.youtube.com/vi/' + this.url.videoId + '/maxresdefault.jpg';
+
+ try {
+ var image = new Image();
+ image.onload = function(){
+ // Onload may still be called if YouTube returns the 120x90 error thumbnail
+ if('naturalHeight' in image){
+ if (image.naturalHeight <= 90 || image.naturalWidth <= 120) {
+ return;
+ }
+ } else if(image.height <= 90 || image.width <= 120) {
+ return;
+ }
+
+ this.poster_ = uri;
+ this.trigger('posterchange');
+ }.bind(this);
+ image.onerror = function(){};
+ image.src = uri;
+ }
+ catch(e){}
+ }
+ });
+
+ Youtube.isSupported = function() {
+ return true;
+ };
+
+ Youtube.canPlaySource = function(e) {
+ return Youtube.canPlayType(e.type);
+ };
+
+ Youtube.canPlayType = function(e) {
+ return (e === 'video/youtube');
+ };
+
+ Youtube.parseUrl = function(url) {
+ var result = {
+ videoId: null
+ };
+
+ var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
+ var match = url.match(regex);
+
+ if (match && match[2].length === 11) {
+ result.videoId = match[2];
+ }
+
+ var regPlaylist = /[?&]list=([^#\&\?]+)/;
+ match = url.match(regPlaylist);
+
+ if(match && match[1]) {
+ result.listId = match[1];
+ }
+
+ return result;
+ };
+
+ function apiLoaded() {
+ YT.ready(function() {
+ Youtube.isApiReady = true;
+
+ for (var i = 0; i < Youtube.apiReadyQueue.length; ++i) {
+ Youtube.apiReadyQueue[i].initYTPlayer();
+ }
+ });
+ }
+
+ function loadScript(src, callback) {
+ var loaded = false;
+ var tag = document.createElement('script');
+ var firstScriptTag = document.getElementsByTagName('script')[0];
+ if (!firstScriptTag) {
+ // when loaded in jest without jsdom setup it doesn't get any element.
+ // In jest it doesn't really make sense to do anything, because no one is watching youtube in jest
+ return;
+ }
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
+ tag.onload = function () {
+ if (!loaded) {
+ loaded = true;
+ callback();
+ }
+ };
+ tag.onreadystatechange = function () {
+ if (!loaded && (this.readyState === 'complete' || this.readyState === 'loaded')) {
+ loaded = true;
+ callback();
+ }
+ };
+ tag.src = src;
+ }
+
+ function injectCss() {
+ var css = // iframe blocker to catch mouse events
+ '.vjs-youtube .vjs-iframe-blocker { display: none; }' +
+ '.vjs-youtube.vjs-user-inactive .vjs-iframe-blocker { display: block; }' +
+ '.vjs-youtube .vjs-poster { background-size: cover; }' +
+ '.vjs-youtube-mobile .vjs-big-play-button { display: none; }';
+
+ var head = document.head || document.getElementsByTagName('head')[0];
+
+ var style = document.createElement('style');
+ style.type = 'text/css';
+
+ if (style.styleSheet){
+ style.styleSheet.cssText = css;
+ } else {
+ style.appendChild(document.createTextNode(css));
+ }
+
+ head.appendChild(style);
+ }
+
+ Youtube.apiReadyQueue = [];
+
+ if (typeof document !== 'undefined'){
+ loadScript('https://www.youtube.com/iframe_api', apiLoaded);
+ injectCss();
+ }
+
+ // Older versions of VJS5 doesn't have the registerTech function
+ if (typeof videojs.registerTech !== 'undefined') {
+ videojs.registerTech('Youtube', Youtube);
+ } else {
+ videojs.registerComponent('Youtube', Youtube);
+ }
+}));
diff --git a/java/com.sap.sailing.gwt.ui/js/Youtube.min.js b/java/com.sap.sailing.gwt.ui/js/Youtube.min.js
index 9c499229c02..a9ecd9e0cd5 100644
--- a/java/com.sap.sailing.gwt.ui/js/Youtube.min.js
+++ b/java/com.sap.sailing.gwt.ui/js/Youtube.min.js
@@ -1 +1 @@
-(function(root,factory){if(typeof define==="function"&&define.amd){define(["video.js"],function(videojs){return root.Youtube=factory(videojs)})}else if(typeof module==="object"&&module.exports){module.exports=root.Youtube=factory(require("video.js"))}else{root.Youtube=factory(root.videojs)}})(this,function(videojs){"use strict";var Tech=videojs.getComponent("Tech");var Youtube=videojs.extend(Tech,{constructor:function(options,ready){Tech.call(this,options,ready);this.setPoster(options.poster);this.setSrc(this.options_.source,true);setTimeout(function(){this.el_.parentNode.className+=" vjs-youtube";if(_isOnMobile){this.el_.parentNode.className+=" vjs-youtube-mobile"}if(Youtube.isApiReady){this.initYTPlayer()}else{Youtube.apiReadyQueue.push(this)}}.bind(this))},dispose:function(){this.el_.parentNode.className=this.el_.parentNode.className.replace(" vjs-youtube","").replace(" vjs-youtube-mobile","")},createEl:function(){var div=document.createElement("div");div.setAttribute("id",this.options_.techId);div.setAttribute("style","width:100%;height:100%;top:0;left:0;position:absolute");var divWrapper=document.createElement("div");divWrapper.appendChild(div);if(!_isOnMobile&&!this.options_.ytControls){var divBlocker=document.createElement("div");divBlocker.setAttribute("class","vjs-iframe-blocker");divBlocker.setAttribute("style","position:absolute;top:0;left:0;width:100%;height:100%");divBlocker.onclick=function(){this.pause()}.bind(this);divWrapper.appendChild(divBlocker)}return divWrapper},initYTPlayer:function(){var playerVars={controls:0,modestbranding:1,rel:0,showinfo:0,loop:this.options_.loop?1:0};if(typeof this.options_.autohide!=="undefined"){playerVars.autohide=this.options_.autohide}if(typeof this.options_["cc_load_policy"]!=="undefined"){playerVars["cc_load_policy"]=this.options_["cc_load_policy"]}if(typeof this.options_.ytControls!=="undefined"){playerVars.controls=this.options_.ytControls}if(typeof this.options_.disablekb!=="undefined"){playerVars.disablekb=this.options_.disablekb}if(typeof this.options_.end!=="undefined"){playerVars.end=this.options_.end}if(typeof this.options_.color!=="undefined"){playerVars.color=this.options_.color}if(!playerVars.controls){playerVars.fs=0}else if(typeof this.options_.fs!=="undefined"){playerVars.fs=this.options_.fs}if(typeof this.options_.end!=="undefined"){playerVars.end=this.options_.end}if(typeof this.options_.hl!=="undefined"){playerVars.hl=this.options_.hl}else if(typeof this.options_.language!=="undefined"){playerVars.hl=this.options_.language.substr(0,2)}if(typeof this.options_["iv_load_policy"]!=="undefined"){playerVars["iv_load_policy"]=this.options_["iv_load_policy"]}if(typeof this.options_.list!=="undefined"){playerVars.list=this.options_.list}else if(this.url&&typeof this.url.listId!=="undefined"){playerVars.list=this.url.listId}if(typeof this.options_.listType!=="undefined"){playerVars.listType=this.options_.listType}if(typeof this.options_.modestbranding!=="undefined"){playerVars.modestbranding=this.options_.modestbranding}if(typeof this.options_.playlist!=="undefined"){playerVars.playlist=this.options_.playlist}if(typeof this.options_.playsinline!=="undefined"){playerVars.playsinline=this.options_.playsinline}if(typeof this.options_.rel!=="undefined"){playerVars.rel=this.options_.rel}if(typeof this.options_.showinfo!=="undefined"){playerVars.showinfo=this.options_.showinfo}if(typeof this.options_.start!=="undefined"){playerVars.start=this.options_.start}if(typeof this.options_.theme!=="undefined"){playerVars.theme=this.options_.theme}this.activeVideoId=this.url?this.url.videoId:null;this.activeList=playerVars.list;this.ytPlayer=new YT.Player(this.options_.techId,{videoId:this.activeVideoId,playerVars:playerVars,events:{onReady:this.onPlayerReady.bind(this),onPlaybackQualityChange:this.onPlayerPlaybackQualityChange.bind(this),onStateChange:this.onPlayerStateChange.bind(this),onError:this.onPlayerError.bind(this)}})},onPlayerReady:function(){this.playerReady_=true;this.triggerReady();if(this.playOnReady){this.play()}},onPlayerPlaybackQualityChange:function(){},onPlayerStateChange:function(e){var state=e.data;if(state===this.lastState||this.errorNumber){return}switch(state){case-1:this.trigger("loadedmetadata");this.trigger("durationchange");break;case YT.PlayerState.ENDED:this.trigger("ended");break;case YT.PlayerState.PLAYING:this.trigger("timeupdate");this.trigger("durationchange");this.trigger("playing");this.trigger("play");if(this.isSeeking){this.onSeeked()}break;case YT.PlayerState.PAUSED:this.trigger("canplay");if(this.isSeeking){this.onSeeked()}else{this.trigger("pause")}break;case YT.PlayerState.BUFFERING:this.player_.trigger("timeupdate");this.player_.trigger("waiting");break}this.lastState=state},onPlayerError:function(e){this.errorNumber=e.data;this.trigger("error");this.ytPlayer.stopVideo();this.ytPlayer.destroy();this.ytPlayer=null},error:function(){switch(this.errorNumber){case 5:return{code:"Error while trying to play the video"};case 2:case 100:case 150:return{code:"Unable to find the video"};case 101:return{code:"Playback on other Websites has been disabled by the video owner."}}return{code:"YouTube unknown error ("+this.errorNumber+")"}},src:function(src){if(src){this.setSrc({src:src});if(this.options_.autoplay&&!_isOnMobile){this.play()}}return this.source},poster:function(){if(_isOnMobile){return null}return this.poster_},setPoster:function(poster){this.poster_=poster},setSrc:function(source){if(!source||!source.src){return}delete this.errorNumber;this.source=source;this.url=Youtube.parseUrl(source.src);if(!this.options_.poster){if(this.url.videoId){this.poster_="https://img.youtube.com/vi/"+this.url.videoId+"/0.jpg";this.checkHighResPoster()}}if(this.options_.autoplay&&!_isOnMobile){if(this.isReady_){this.play()}else{this.playOnReady=true}}},play:function(){if(!this.url||!this.url.videoId){return}this.wasPausedBeforeSeek=false;if(this.isReady_){if(this.url.listId){if(this.activeList===this.url.listId){this.ytPlayer.playVideo()}else{this.ytPlayer.loadPlaylist(this.url.listId);this.activeList=this.url.listId}}if(this.activeVideoId===this.url.videoId){this.ytPlayer.playVideo()}else{this.ytPlayer.loadVideoById(this.url.videoId);this.activeVideoId=this.url.videoId}}else{this.trigger("waiting");this.playOnReady=true}},pause:function(){if(this.ytPlayer){this.ytPlayer.pauseVideo()}},paused:function(){return this.ytPlayer?this.lastState!==YT.PlayerState.PLAYING&&this.lastState!==YT.PlayerState.BUFFERING:true},currentTime:function(){return this.ytPlayer?this.ytPlayer.getCurrentTime():0},setCurrentTime:function(seconds){if(this.lastState===YT.PlayerState.PAUSED){this.timeBeforeSeek=this.currentTime()}if(!this.isSeeking){this.wasPausedBeforeSeek=this.paused()}this.ytPlayer.seekTo(seconds,true);this.trigger("timeupdate");this.trigger("seeking");this.isSeeking=true;if(this.lastState===YT.PlayerState.PAUSED&&this.timeBeforeSeek!==seconds){clearInterval(this.checkSeekedInPauseInterval);this.checkSeekedInPauseInterval=setInterval(function(){if(this.lastState!==YT.PlayerState.PAUSED||!this.isSeeking){clearInterval(this.checkSeekedInPauseInterval)}else if(this.currentTime()!==this.timeBeforeSeek){this.trigger("timeupdate");this.onSeeked()}}.bind(this),250)}},onSeeked:function(){clearInterval(this.checkSeekedInPauseInterval);this.isSeeking=false;if(this.wasPausedBeforeSeek){this.pause()}this.trigger("seeked")},playbackRate:function(){return this.ytPlayer?this.ytPlayer.getPlaybackRate():1},setPlaybackRate:function(suggestedRate){if(!this.ytPlayer){return}this.ytPlayer.setPlaybackRate(suggestedRate);this.trigger("ratechange")},duration:function(){return this.ytPlayer?this.ytPlayer.getDuration():0},currentSrc:function(){return this.source},ended:function(){return this.ytPlayer?this.lastState===YT.PlayerState.ENDED:false},volume:function(){return this.ytPlayer?this.ytPlayer.getVolume()/100:1},setVolume:function(percentAsDecimal){if(!this.ytPlayer){return}this.ytPlayer.setVolume(percentAsDecimal*100);this.setTimeout(function(){this.trigger("volumechange")},50)},muted:function(){return this.ytPlayer?this.ytPlayer.isMuted():false},setMuted:function(mute){if(!this.ytPlayer){return}else{this.muted(true)}if(mute){this.ytPlayer.mute()}else{this.ytPlayer.unMute()}this.setTimeout(function(){this.trigger("volumechange")},50)},buffered:function(){if(!this.ytPlayer||!this.ytPlayer.getVideoLoadedFraction){return{length:0,start:function(){throw new Error("This TimeRanges object is empty")},end:function(){throw new Error("This TimeRanges object is empty")}}}var end=this.ytPlayer.getVideoLoadedFraction()*this.ytPlayer.getDuration();return{length:this.ytPlayer.getDuration(),start:function(){return 0},end:function(){return end}}},load:function(){},reset:function(){},supportsFullScreen:function(){return true},checkHighResPoster:function(){var uri="https://img.youtube.com/vi/"+this.url.videoId+"/maxresdefault.jpg";try{var image=new Image;image.onload=function(){if("naturalHeight"in image){if(image.naturalHeight<=90||image.naturalWidth<=120){return}}else if(image.height<=90||image.width<=120){return}this.poster_=uri;this.trigger("posterchange")}.bind(this);image.onerror=function(){};image.src=uri}catch(e){}}});Youtube.isSupported=function(){return true};Youtube.canPlaySource=function(e){return e.type==="video/youtube"};var _isOnMobile=/(iPad|iPhone|iPod|Android)/g.test(navigator.userAgent);Youtube.parseUrl=function(url){var result={videoId:null};var regex=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;var match=url.match(regex);if(match&&match[2].length===11){result.videoId=match[2]}var regPlaylist=/[?&]list=([^#\&\?]+)/;match=url.match(regPlaylist);if(match&&match[1]){result.listId=match[1]}return result};function loadApi(){var tag=document.createElement("script");tag.src="https://www.youtube.com/iframe_api";var firstScriptTag=document.getElementsByTagName("script")[0];firstScriptTag.parentNode.insertBefore(tag,firstScriptTag)}function injectCss(){var css=".vjs-youtube .vjs-iframe-blocker { display: none; }"+".vjs-youtube.vjs-user-inactive .vjs-iframe-blocker { display: block; }"+".vjs-youtube .vjs-poster { background-size: cover; }"+".vjs-youtube-mobile .vjs-big-play-button { display: none; }";var head=document.head||document.getElementsByTagName("head")[0];var style=document.createElement("style");style.type="text/css";if(style.styleSheet){style.styleSheet.cssText=css}else{style.appendChild(document.createTextNode(css))}head.appendChild(style)}Youtube.apiReadyQueue=[];window.onYouTubeIframeAPIReady=function(){Youtube.isApiReady=true;for(var i=0;i1){this.featuresPlaybackRate=true}this.playerReady_=true;this.triggerReady();if(this.playOnReady){this.play()}else if(this.cueOnReady){this.cueVideoById_(this.url.videoId);this.activeVideoId=this.url.videoId}},onPlayerPlaybackQualityChange:function(){},onPlayerPlaybackRateChange:function(){this.trigger("ratechange")},onPlayerStateChange:function(e){var state=e.data;if(state===this.lastState||this.errorNumber){return}this.lastState=state;switch(state){case-1:this.trigger("loadstart");this.trigger("loadedmetadata");this.trigger("durationchange");this.trigger("ratechange");break;case YT.PlayerState.ENDED:this.trigger("ended");break;case YT.PlayerState.PLAYING:this.trigger("timeupdate");this.trigger("durationchange");this.trigger("playing");this.trigger("play");if(this.isSeeking){this.onSeeked()}break;case YT.PlayerState.PAUSED:this.trigger("canplay");if(this.isSeeking){this.onSeeked()}else{this.trigger("pause")}break;case YT.PlayerState.BUFFERING:this.player_.trigger("timeupdate");this.player_.trigger("waiting");break}},onPlayerVolumeChange:function(){this.trigger("volumechange")},onPlayerError:function(e){this.errorNumber=e.data;this.trigger("pause");this.trigger("error")},error:function(){var code=1e3+this.errorNumber;switch(this.errorNumber){case 5:return{code:code,message:"Error while trying to play the video"};case 2:case 100:return{code:code,message:"Unable to find the video"};case 101:case 150:return{code:code,message:"Playback on other Websites has been disabled by the video owner."}}return{code:code,message:"YouTube unknown error ("+this.errorNumber+")"}},loadVideoById_:function(id){var options={videoId:id};if(this.options_.start){options.startSeconds=this.options_.start}if(this.options_.end){options.endSeconds=this.options_.end}this.ytPlayer.loadVideoById(options)},cueVideoById_:function(id){var options={videoId:id};if(this.options_.start){options.startSeconds=this.options_.start}if(this.options_.end){options.endSeconds=this.options_.end}this.ytPlayer.cueVideoById(options)},src:function(src){if(src){this.setSrc({src:src})}return this.source},poster:function(){if(_isOnMobile){return null}return this.poster_},setPoster:function(poster){this.poster_=poster},setSrc:function(source){if(!source||!source.src){return}delete this.errorNumber;this.source=source;this.url=Youtube.parseUrl(source.src);if(!this.options_.poster){if(this.url.videoId){this.poster_="https://img.youtube.com/vi/"+this.url.videoId+"/0.jpg";this.trigger("posterchange");this.checkHighResPoster()}}if(this.options_.autoplay&&!_isOnMobile){if(this.isReady_){this.play()}else{this.playOnReady=true}}else if(this.activeVideoId!==this.url.videoId){if(this.isReady_){this.cueVideoById_(this.url.videoId);this.activeVideoId=this.url.videoId}else{this.cueOnReady=true}}},autoplay:function(){return this.options_.autoplay},setAutoplay:function(val){this.options_.autoplay=val},loop:function(){return this.options_.loop},setLoop:function(val){this.options_.loop=val},play:function(){if(!this.url||!this.url.videoId){return}this.wasPausedBeforeSeek=false;if(this.isReady_){if(this.url.listId){if(this.activeList===this.url.listId){this.ytPlayer.playVideo()}else{this.ytPlayer.loadPlaylist(this.url.listId);this.activeList=this.url.listId}}if(this.activeVideoId===this.url.videoId){this.ytPlayer.playVideo()}else{this.loadVideoById_(this.url.videoId);this.activeVideoId=this.url.videoId}}else{this.trigger("waiting");this.playOnReady=true}},pause:function(){if(this.ytPlayer){this.ytPlayer.pauseVideo()}},paused:function(){return this.ytPlayer?this.lastState!==YT.PlayerState.PLAYING&&this.lastState!==YT.PlayerState.BUFFERING:true},currentTime:function(){return this.ytPlayer?this.ytPlayer.getCurrentTime():0},setCurrentTime:function(seconds){if(this.lastState===YT.PlayerState.PAUSED){this.timeBeforeSeek=this.currentTime()}if(!this.isSeeking){this.wasPausedBeforeSeek=this.paused()}this.ytPlayer.seekTo(seconds,true);this.trigger("timeupdate");this.trigger("seeking");this.isSeeking=true;if(this.lastState===YT.PlayerState.PAUSED&&this.timeBeforeSeek!==seconds){clearInterval(this.checkSeekedInPauseInterval);this.checkSeekedInPauseInterval=setInterval(function(){if(this.lastState!==YT.PlayerState.PAUSED||!this.isSeeking){clearInterval(this.checkSeekedInPauseInterval)}else if(this.currentTime()!==this.timeBeforeSeek){this.trigger("timeupdate");this.onSeeked()}}.bind(this),250)}},seeking:function(){return this.isSeeking},seekable:function(){if(!this.ytPlayer){return videojs.createTimeRange()}return videojs.createTimeRange(0,this.ytPlayer.getDuration())},onSeeked:function(){clearInterval(this.checkSeekedInPauseInterval);this.isSeeking=false;if(this.wasPausedBeforeSeek){this.pause()}this.trigger("seeked")},playbackRate:function(){return this.ytPlayer?this.ytPlayer.getPlaybackRate():1},setPlaybackRate:function(suggestedRate){if(!this.ytPlayer){return}this.ytPlayer.setPlaybackRate(suggestedRate)},duration:function(){return this.ytPlayer?this.ytPlayer.getDuration():0},currentSrc:function(){return this.source&&this.source.src},ended:function(){return this.ytPlayer?this.lastState===YT.PlayerState.ENDED:false},volume:function(){return this.ytPlayer?this.ytPlayer.getVolume()/100:1},setVolume:function(percentAsDecimal){if(!this.ytPlayer){return}this.ytPlayer.setVolume(percentAsDecimal*100)},muted:function(){return this.ytPlayer?this.ytPlayer.isMuted():false},setMuted:function(mute){if(!this.ytPlayer){return}else{this.muted(true)}if(mute){this.ytPlayer.mute()}else{this.ytPlayer.unMute()}this.setTimeout(function(){this.trigger("volumechange")},50)},buffered:function(){if(!this.ytPlayer||!this.ytPlayer.getVideoLoadedFraction){return videojs.createTimeRange()}var bufferedEnd=this.ytPlayer.getVideoLoadedFraction()*this.ytPlayer.getDuration();return videojs.createTimeRange(0,bufferedEnd)},preload:function(){},load:function(){},reset:function(){},networkState:function(){if(!this.ytPlayer){return 0}switch(this.ytPlayer.getPlayerState()){case-1:return 0;case 3:return 2;default:return 1}},readyState:function(){if(!this.ytPlayer){return 0}switch(this.ytPlayer.getPlayerState()){case-1:return 0;case 5:return 1;case 3:return 2;default:return 4}},supportsFullScreen:function(){return document.fullscreenEnabled||document.webkitFullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled},checkHighResPoster:function(){var uri="https://img.youtube.com/vi/"+this.url.videoId+"/maxresdefault.jpg";try{var image=new Image;image.onload=function(){if("naturalHeight"in image){if(image.naturalHeight<=90||image.naturalWidth<=120){return}}else if(image.height<=90||image.width<=120){return}this.poster_=uri;this.trigger("posterchange")}.bind(this);image.onerror=function(){};image.src=uri}catch(e){}}});Youtube.isSupported=function(){return true};Youtube.canPlaySource=function(e){return Youtube.canPlayType(e.type)};Youtube.canPlayType=function(e){return e==="video/youtube"};Youtube.parseUrl=function(url){var result={videoId:null};var regex=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;var match=url.match(regex);if(match&&match[2].length===11){result.videoId=match[2]}var regPlaylist=/[?&]list=([^#\&\?]+)/;match=url.match(regPlaylist);if(match&&match[1]){result.listId=match[1]}return result};function apiLoaded(){YT.ready(function(){Youtube.isApiReady=true;for(var i=0;i 0 ) ? 1 : + x;
-
- };
-
-}
-
-if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) {
-
- // Missing in IE9-11.
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
-
- Object.defineProperty( Function.prototype, 'name', {
-
- get: function () {
-
- return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ];
-
- }
-
- } );
-
-}
-
-if ( Object.assign === undefined ) {
-
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
-
- Object.defineProperty( Object, 'assign', {
-
- writable: true,
- configurable: true,
-
- value: function ( target ) {
-
- 'use strict';
-
- if ( target === undefined || target === null ) {
-
- throw new TypeError( "Cannot convert first argument to object" );
-
- }
-
- var to = Object( target );
-
- for ( var i = 1, n = arguments.length; i !== n; ++ i ) {
-
- var nextSource = arguments[ i ];
-
- if ( nextSource === undefined || nextSource === null ) continue;
-
- nextSource = Object( nextSource );
-
- var keysArray = Object.keys( nextSource );
-
- for ( var nextIndex = 0, len = keysArray.length; nextIndex !== len; ++ nextIndex ) {
-
- var nextKey = keysArray[ nextIndex ];
- var desc = Object.getOwnPropertyDescriptor( nextSource, nextKey );
-
- if ( desc !== undefined && desc.enumerable ) {
-
- to[ nextKey ] = nextSource[ nextKey ];
-
- }
-
- }
-
- }
-
- return to;
-
- }
-
- } );
-
-}
-
-// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button
-
-THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
-
-// GL STATE CONSTANTS
-
-THREE.CullFaceNone = 0;
-THREE.CullFaceBack = 1;
-THREE.CullFaceFront = 2;
-THREE.CullFaceFrontBack = 3;
-
-THREE.FrontFaceDirectionCW = 0;
-THREE.FrontFaceDirectionCCW = 1;
-
-// SHADOWING TYPES
-
-THREE.BasicShadowMap = 0;
-THREE.PCFShadowMap = 1;
-THREE.PCFSoftShadowMap = 2;
-
-// MATERIAL CONSTANTS
-
-// side
-
-THREE.FrontSide = 0;
-THREE.BackSide = 1;
-THREE.DoubleSide = 2;
-
-// shading
-
-THREE.FlatShading = 1;
-THREE.SmoothShading = 2;
-
-// colors
-
-THREE.NoColors = 0;
-THREE.FaceColors = 1;
-THREE.VertexColors = 2;
-
-// blending modes
-
-THREE.NoBlending = 0;
-THREE.NormalBlending = 1;
-THREE.AdditiveBlending = 2;
-THREE.SubtractiveBlending = 3;
-THREE.MultiplyBlending = 4;
-THREE.CustomBlending = 5;
-
-// custom blending equations
-// (numbers start from 100 not to clash with other
-// mappings to OpenGL constants defined in Texture.js)
-
-THREE.AddEquation = 100;
-THREE.SubtractEquation = 101;
-THREE.ReverseSubtractEquation = 102;
-THREE.MinEquation = 103;
-THREE.MaxEquation = 104;
-
-// custom blending destination factors
-
-THREE.ZeroFactor = 200;
-THREE.OneFactor = 201;
-THREE.SrcColorFactor = 202;
-THREE.OneMinusSrcColorFactor = 203;
-THREE.SrcAlphaFactor = 204;
-THREE.OneMinusSrcAlphaFactor = 205;
-THREE.DstAlphaFactor = 206;
-THREE.OneMinusDstAlphaFactor = 207;
-
-// custom blending source factors
-
-//THREE.ZeroFactor = 200;
-//THREE.OneFactor = 201;
-//THREE.SrcAlphaFactor = 204;
-//THREE.OneMinusSrcAlphaFactor = 205;
-//THREE.DstAlphaFactor = 206;
-//THREE.OneMinusDstAlphaFactor = 207;
-THREE.DstColorFactor = 208;
-THREE.OneMinusDstColorFactor = 209;
-THREE.SrcAlphaSaturateFactor = 210;
-
-// depth modes
-
-THREE.NeverDepth = 0;
-THREE.AlwaysDepth = 1;
-THREE.LessDepth = 2;
-THREE.LessEqualDepth = 3;
-THREE.EqualDepth = 4;
-THREE.GreaterEqualDepth = 5;
-THREE.GreaterDepth = 6;
-THREE.NotEqualDepth = 7;
-
-
-// TEXTURE CONSTANTS
-
-THREE.MultiplyOperation = 0;
-THREE.MixOperation = 1;
-THREE.AddOperation = 2;
-
-// Tone Mapping modes
-
-THREE.NoToneMapping = 0; // do not do any tone mapping, not even exposure (required for special purpose passes.)
-THREE.LinearToneMapping = 1; // only apply exposure.
-THREE.ReinhardToneMapping = 2;
-THREE.Uncharted2ToneMapping = 3; // John Hable
-THREE.CineonToneMapping = 4; // optimized filmic operator by Jim Hejl and Richard Burgess-Dawson
-
-// Mapping modes
-
-THREE.UVMapping = 300;
-
-THREE.CubeReflectionMapping = 301;
-THREE.CubeRefractionMapping = 302;
-
-THREE.EquirectangularReflectionMapping = 303;
-THREE.EquirectangularRefractionMapping = 304;
-
-THREE.SphericalReflectionMapping = 305;
-THREE.CubeUVReflectionMapping = 306;
-THREE.CubeUVRefractionMapping = 307;
-
-// Wrapping modes
-
-THREE.RepeatWrapping = 1000;
-THREE.ClampToEdgeWrapping = 1001;
-THREE.MirroredRepeatWrapping = 1002;
-
-// Filters
-
-THREE.NearestFilter = 1003;
-THREE.NearestMipMapNearestFilter = 1004;
-THREE.NearestMipMapLinearFilter = 1005;
-THREE.LinearFilter = 1006;
-THREE.LinearMipMapNearestFilter = 1007;
-THREE.LinearMipMapLinearFilter = 1008;
-
-// Data types
-
-THREE.UnsignedByteType = 1009;
-THREE.ByteType = 1010;
-THREE.ShortType = 1011;
-THREE.UnsignedShortType = 1012;
-THREE.IntType = 1013;
-THREE.UnsignedIntType = 1014;
-THREE.FloatType = 1015;
-THREE.HalfFloatType = 1025;
-
-// Pixel types
-
-//THREE.UnsignedByteType = 1009;
-THREE.UnsignedShort4444Type = 1016;
-THREE.UnsignedShort5551Type = 1017;
-THREE.UnsignedShort565Type = 1018;
-
-// Pixel formats
-
-THREE.AlphaFormat = 1019;
-THREE.RGBFormat = 1020;
-THREE.RGBAFormat = 1021;
-THREE.LuminanceFormat = 1022;
-THREE.LuminanceAlphaFormat = 1023;
-// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
-THREE.RGBEFormat = THREE.RGBAFormat; //1024;
-THREE.DepthFormat = 1026;
-
-// DDS / ST3C Compressed texture formats
-
-THREE.RGB_S3TC_DXT1_Format = 2001;
-THREE.RGBA_S3TC_DXT1_Format = 2002;
-THREE.RGBA_S3TC_DXT3_Format = 2003;
-THREE.RGBA_S3TC_DXT5_Format = 2004;
-
-
-// PVRTC compressed texture formats
-
-THREE.RGB_PVRTC_4BPPV1_Format = 2100;
-THREE.RGB_PVRTC_2BPPV1_Format = 2101;
-THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
-THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
-
-// ETC compressed texture formats
-
-THREE.RGB_ETC1_Format = 2151;
-
-// Loop styles for AnimationAction
-
-THREE.LoopOnce = 2200;
-THREE.LoopRepeat = 2201;
-THREE.LoopPingPong = 2202;
-
-// Interpolation
-
-THREE.InterpolateDiscrete = 2300;
-THREE.InterpolateLinear = 2301;
-THREE.InterpolateSmooth = 2302;
-
-// Interpolant ending modes
-
-THREE.ZeroCurvatureEnding = 2400;
-THREE.ZeroSlopeEnding = 2401;
-THREE.WrapAroundEnding = 2402;
-
-// Triangle Draw modes
-
-THREE.TrianglesDrawMode = 0;
-THREE.TriangleStripDrawMode = 1;
-THREE.TriangleFanDrawMode = 2;
-
-// Texture Encodings
-
-THREE.LinearEncoding = 3000; // No encoding at all.
-THREE.sRGBEncoding = 3001;
-THREE.GammaEncoding = 3007; // uses GAMMA_FACTOR, for backwards compatibility with WebGLRenderer.gammaInput/gammaOutput
-
-// The following Texture Encodings are for RGB-only (no alpha) HDR light emission sources.
-// These encodings should not specified as output encodings except in rare situations.
-THREE.RGBEEncoding = 3002; // AKA Radiance.
-THREE.LogLuvEncoding = 3003;
-THREE.RGBM7Encoding = 3004;
-THREE.RGBM16Encoding = 3005;
-THREE.RGBDEncoding = 3006; // MaxRange is 256.
-
-// Depth packing strategies
-
-THREE.BasicDepthPacking = 3200; // for writing to float textures for high precision or for visualizing results in RGB buffers
-THREE.RGBADepthPacking = 3201; // for packing into RGBA buffers.
-
-// File:src/math/Color.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- */
-
-THREE.Color = function ( color ) {
-
- if ( arguments.length === 3 ) {
-
- return this.fromArray( arguments );
+ Number.EPSILON = Math.pow( 2, - 52 );
}
- return this.set( color );
+ if ( Number.isInteger === undefined ) {
-};
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
-THREE.Color.prototype = {
+ Number.isInteger = function ( value ) {
- constructor: THREE.Color,
-
- r: 1, g: 1, b: 1,
-
- set: function ( value ) {
-
- if ( value instanceof THREE.Color ) {
-
- this.copy( value );
-
- } else if ( typeof value === 'number' ) {
-
- this.setHex( value );
-
- } else if ( typeof value === 'string' ) {
-
- this.setStyle( value );
-
- }
-
- return this;
-
- },
-
- setScalar: function ( scalar ) {
-
- this.r = scalar;
- this.g = scalar;
- this.b = scalar;
-
- },
-
- setHex: function ( hex ) {
-
- hex = Math.floor( hex );
-
- this.r = ( hex >> 16 & 255 ) / 255;
- this.g = ( hex >> 8 & 255 ) / 255;
- this.b = ( hex & 255 ) / 255;
-
- return this;
-
- },
-
- setRGB: function ( r, g, b ) {
-
- this.r = r;
- this.g = g;
- this.b = b;
-
- return this;
-
- },
-
- setHSL: function () {
-
- 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 ) * 6 * ( 2 / 3 - t );
- return p;
-
- }
-
- return function ( h, s, l ) {
-
- // h,s,l ranges are in 0.0 - 1.0
- h = THREE.Math.euclideanModulo( h, 1 );
- s = THREE.Math.clamp( s, 0, 1 );
- l = THREE.Math.clamp( l, 0, 1 );
-
- if ( s === 0 ) {
-
- this.r = this.g = this.b = l;
-
- } else {
-
- var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
- var q = ( 2 * l ) - p;
-
- this.r = hue2rgb( q, p, h + 1 / 3 );
- this.g = hue2rgb( q, p, h );
- this.b = hue2rgb( q, p, h - 1 / 3 );
-
- }
-
- return this;
+ return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value;
};
- }(),
-
- setStyle: function ( style ) {
-
- function handleAlpha( string ) {
-
- if ( string === undefined ) return;
-
- if ( parseFloat( string ) < 1 ) {
-
- console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
-
- }
-
- }
-
-
- var m;
-
- if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
-
- // rgb / hsl
-
- var color;
- var name = m[ 1 ];
- var components = m[ 2 ];
-
- switch ( name ) {
-
- case 'rgb':
- case 'rgba':
-
- if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
-
- // rgb(255,0,0) rgba(255,0,0,0.5)
- this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
- this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
- this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
-
- handleAlpha( color[ 5 ] );
-
- return this;
-
- }
-
- if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
-
- // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
- this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
- this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
- this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
-
- handleAlpha( color[ 5 ] );
-
- return this;
-
- }
-
- break;
-
- case 'hsl':
- case 'hsla':
-
- if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
-
- // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
- var h = parseFloat( color[ 1 ] ) / 360;
- var s = parseInt( color[ 2 ], 10 ) / 100;
- var l = parseInt( color[ 3 ], 10 ) / 100;
-
- handleAlpha( color[ 5 ] );
-
- return this.setHSL( h, s, l );
-
- }
-
- break;
-
- }
-
- } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
-
- // hex color
-
- var hex = m[ 1 ];
- var size = hex.length;
-
- if ( size === 3 ) {
-
- // #ff0
- this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
- this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
- this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
-
- return this;
-
- } else if ( size === 6 ) {
-
- // #ff0000
- this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
- this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
- this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
-
- return this;
-
- }
-
- }
-
- if ( style && style.length > 0 ) {
-
- // color keywords
- var hex = THREE.ColorKeywords[ style ];
-
- if ( hex !== undefined ) {
-
- // red
- this.setHex( hex );
-
- } else {
-
- // unknown color
- console.warn( 'THREE.Color: Unknown color ' + style );
-
- }
-
- }
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor( this.r, this.g, this.b );
-
- },
-
- copy: function ( color ) {
-
- this.r = color.r;
- this.g = color.g;
- this.b = color.b;
-
- return this;
-
- },
-
- copyGammaToLinear: function ( color, gammaFactor ) {
-
- if ( gammaFactor === undefined ) gammaFactor = 2.0;
-
- this.r = Math.pow( color.r, gammaFactor );
- this.g = Math.pow( color.g, gammaFactor );
- this.b = Math.pow( color.b, gammaFactor );
-
- return this;
-
- },
-
- copyLinearToGamma: function ( color, gammaFactor ) {
-
- if ( gammaFactor === undefined ) gammaFactor = 2.0;
-
- var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
-
- this.r = Math.pow( color.r, safeInverse );
- this.g = Math.pow( color.g, safeInverse );
- this.b = Math.pow( color.b, safeInverse );
-
- return this;
-
- },
-
- convertGammaToLinear: function () {
-
- var r = this.r, g = this.g, b = this.b;
-
- this.r = r * r;
- this.g = g * g;
- this.b = b * b;
-
- return this;
-
- },
-
- convertLinearToGamma: function () {
-
- this.r = Math.sqrt( this.r );
- this.g = Math.sqrt( this.g );
- this.b = Math.sqrt( this.b );
-
- return this;
-
- },
-
- getHex: function () {
-
- return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
-
- },
-
- getHexString: function () {
-
- return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
-
- },
-
- getHSL: function ( optionalTarget ) {
-
- // h,s,l ranges are in 0.0 - 1.0
-
- var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
-
- var r = this.r, g = this.g, b = this.b;
-
- var max = Math.max( r, g, b );
- var min = Math.min( r, g, b );
-
- var hue, saturation;
- var lightness = ( min + max ) / 2.0;
-
- if ( min === max ) {
-
- hue = 0;
- saturation = 0;
-
- } else {
-
- var delta = max - min;
-
- saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
-
- switch ( max ) {
-
- case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
- case g: hue = ( b - r ) / delta + 2; break;
- case b: hue = ( r - g ) / delta + 4; break;
-
- }
-
- hue /= 6;
-
- }
-
- hsl.h = hue;
- hsl.s = saturation;
- hsl.l = lightness;
-
- return hsl;
-
- },
-
- getStyle: function () {
-
- return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
-
- },
-
- offsetHSL: function ( h, s, l ) {
-
- var hsl = this.getHSL();
-
- hsl.h += h; hsl.s += s; hsl.l += l;
-
- this.setHSL( hsl.h, hsl.s, hsl.l );
-
- return this;
-
- },
-
- add: function ( color ) {
-
- this.r += color.r;
- this.g += color.g;
- this.b += color.b;
-
- return this;
-
- },
-
- addColors: function ( color1, color2 ) {
-
- this.r = color1.r + color2.r;
- this.g = color1.g + color2.g;
- this.b = color1.b + color2.b;
-
- return this;
-
- },
-
- addScalar: function ( s ) {
-
- this.r += s;
- this.g += s;
- this.b += s;
-
- return this;
-
- },
-
- multiply: function ( color ) {
-
- this.r *= color.r;
- this.g *= color.g;
- this.b *= color.b;
-
- return this;
-
- },
-
- multiplyScalar: function ( s ) {
-
- this.r *= s;
- this.g *= s;
- this.b *= s;
-
- return this;
-
- },
-
- lerp: function ( color, alpha ) {
-
- this.r += ( color.r - this.r ) * alpha;
- this.g += ( color.g - this.g ) * alpha;
- this.b += ( color.b - this.b ) * alpha;
-
- return this;
-
- },
-
- equals: function ( c ) {
-
- return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
-
- },
-
- fromArray: function ( array, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- this.r = array[ offset ];
- this.g = array[ offset + 1 ];
- this.b = array[ offset + 2 ];
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this.r;
- array[ offset + 1 ] = this.g;
- array[ offset + 2 ] = this.b;
-
- return array;
-
}
-};
-
-THREE.ColorKeywords = { '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, '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 };
-
-// File:src/math/Quaternion.js
-
-/**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author bhouston / http://clara.io
- */
-
-THREE.Quaternion = function ( x, y, z, w ) {
-
- this._x = x || 0;
- this._y = y || 0;
- this._z = z || 0;
- this._w = ( w !== undefined ) ? w : 1;
-
-};
-
-THREE.Quaternion.prototype = {
-
- constructor: THREE.Quaternion,
-
- get x () {
-
- return this._x;
-
- },
-
- set x ( value ) {
-
- this._x = value;
- this.onChangeCallback();
-
- },
-
- get y () {
-
- return this._y;
-
- },
-
- set y ( value ) {
-
- this._y = value;
- this.onChangeCallback();
-
- },
-
- get z () {
-
- return this._z;
-
- },
-
- set z ( value ) {
-
- this._z = value;
- this.onChangeCallback();
-
- },
-
- get w () {
-
- return this._w;
-
- },
-
- set w ( value ) {
-
- this._w = value;
- this.onChangeCallback();
-
- },
-
- set: function ( x, y, z, w ) {
-
- this._x = x;
- this._y = y;
- this._z = z;
- this._w = w;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor( this._x, this._y, this._z, this._w );
-
- },
-
- copy: function ( quaternion ) {
-
- this._x = quaternion.x;
- this._y = quaternion.y;
- this._z = quaternion.z;
- this._w = quaternion.w;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- setFromEuler: function ( euler, update ) {
-
- if ( euler instanceof THREE.Euler === false ) {
-
- throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
-
- }
-
- // http://www.mathworks.com/matlabcentral/fileexchange/
- // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
- // content/SpinCalc.m
-
- var c1 = Math.cos( euler._x / 2 );
- var c2 = Math.cos( euler._y / 2 );
- var c3 = Math.cos( euler._z / 2 );
- var s1 = Math.sin( euler._x / 2 );
- var s2 = Math.sin( euler._y / 2 );
- var s3 = Math.sin( euler._z / 2 );
-
- var order = euler.order;
-
- if ( order === 'XYZ' ) {
-
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
-
- } else if ( order === 'YXZ' ) {
-
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
-
- } else if ( order === 'ZXY' ) {
-
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
-
- } else if ( order === 'ZYX' ) {
-
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
-
- } else if ( order === 'YZX' ) {
-
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
-
- } else if ( order === 'XZY' ) {
-
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
-
- }
-
- if ( update !== false ) this.onChangeCallback();
-
- return this;
-
- },
-
- setFromAxisAngle: function ( axis, angle ) {
-
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
-
- // assumes axis is normalized
-
- var halfAngle = angle / 2, s = Math.sin( halfAngle );
-
- this._x = axis.x * s;
- this._y = axis.y * s;
- this._z = axis.z * s;
- this._w = Math.cos( halfAngle );
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- setFromRotationMatrix: function ( m ) {
-
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
-
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
- var te = m.elements,
-
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
-
- trace = m11 + m22 + m33,
- s;
-
- if ( trace > 0 ) {
-
- s = 0.5 / Math.sqrt( trace + 1.0 );
-
- this._w = 0.25 / s;
- this._x = ( m32 - m23 ) * s;
- this._y = ( m13 - m31 ) * s;
- this._z = ( m21 - m12 ) * s;
-
- } else if ( m11 > m22 && m11 > m33 ) {
-
- s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
-
- this._w = ( m32 - m23 ) / s;
- this._x = 0.25 * s;
- this._y = ( m12 + m21 ) / s;
- this._z = ( m13 + m31 ) / s;
-
- } else if ( m22 > m33 ) {
-
- s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
-
- this._w = ( m13 - m31 ) / s;
- this._x = ( m12 + m21 ) / s;
- this._y = 0.25 * s;
- this._z = ( m23 + m32 ) / s;
-
- } else {
-
- s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
-
- this._w = ( m21 - m12 ) / s;
- this._x = ( m13 + m31 ) / s;
- this._y = ( m23 + m32 ) / s;
- this._z = 0.25 * s;
-
- }
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- setFromUnitVectors: function () {
-
- // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
-
- // assumes direction vectors vFrom and vTo are normalized
-
- var v1, r;
-
- var EPS = 0.000001;
-
- return function ( vFrom, vTo ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
-
- r = vFrom.dot( vTo ) + 1;
-
- if ( r < EPS ) {
-
- r = 0;
-
- if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
-
- v1.set( - vFrom.y, vFrom.x, 0 );
-
- } else {
-
- v1.set( 0, - vFrom.z, vFrom.y );
-
- }
-
- } else {
-
- v1.crossVectors( vFrom, vTo );
-
- }
-
- this._x = v1.x;
- this._y = v1.y;
- this._z = v1.z;
- this._w = r;
-
- this.normalize();
-
- return this;
-
- };
-
- }(),
-
- inverse: function () {
-
- this.conjugate().normalize();
-
- return this;
-
- },
-
- conjugate: function () {
-
- this._x *= - 1;
- this._y *= - 1;
- this._z *= - 1;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- dot: function ( v ) {
-
- return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
-
- },
-
- lengthSq: function () {
-
- return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
-
- },
-
- length: function () {
-
- return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
-
- },
-
- normalize: function () {
-
- var l = this.length();
-
- if ( l === 0 ) {
-
- this._x = 0;
- this._y = 0;
- this._z = 0;
- this._w = 1;
-
- } else {
-
- l = 1 / l;
-
- this._x = this._x * l;
- this._y = this._y * l;
- this._z = this._z * l;
- this._w = this._w * l;
-
- }
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- multiply: function ( q, p ) {
-
- if ( p !== undefined ) {
-
- console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
- return this.multiplyQuaternions( q, p );
-
- }
-
- return this.multiplyQuaternions( this, q );
-
- },
-
- multiplyQuaternions: function ( a, b ) {
-
- // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
-
- var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
- var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
-
- this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
- this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
- this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
- this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- slerp: function ( qb, t ) {
-
- if ( t === 0 ) return this;
- if ( t === 1 ) return this.copy( qb );
-
- var x = this._x, y = this._y, z = this._z, w = this._w;
-
- // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
-
- var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
-
- if ( cosHalfTheta < 0 ) {
-
- this._w = - qb._w;
- this._x = - qb._x;
- this._y = - qb._y;
- this._z = - qb._z;
-
- cosHalfTheta = - cosHalfTheta;
-
- } else {
-
- this.copy( qb );
-
- }
-
- if ( cosHalfTheta >= 1.0 ) {
-
- this._w = w;
- this._x = x;
- this._y = y;
- this._z = z;
-
- return this;
-
- }
-
- var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
-
- if ( Math.abs( sinHalfTheta ) < 0.001 ) {
-
- this._w = 0.5 * ( w + this._w );
- this._x = 0.5 * ( x + this._x );
- this._y = 0.5 * ( y + this._y );
- this._z = 0.5 * ( z + this._z );
-
- return this;
-
- }
-
- var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
- var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
- ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
-
- this._w = ( w * ratioA + this._w * ratioB );
- this._x = ( x * ratioA + this._x * ratioB );
- this._y = ( y * ratioA + this._y * ratioB );
- this._z = ( z * ratioA + this._z * ratioB );
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- equals: function ( quaternion ) {
-
- return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
-
- },
-
- fromArray: function ( array, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- this._x = array[ offset ];
- this._y = array[ offset + 1 ];
- this._z = array[ offset + 2 ];
- this._w = array[ offset + 3 ];
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this._x;
- array[ offset + 1 ] = this._y;
- array[ offset + 2 ] = this._z;
- array[ offset + 3 ] = this._w;
-
- return array;
-
- },
-
- onChange: function ( callback ) {
-
- this.onChangeCallback = callback;
-
- return this;
-
- },
-
- onChangeCallback: function () {}
-
-};
-
-Object.assign( THREE.Quaternion, {
-
- slerp: function( qa, qb, qm, t ) {
-
- return qm.copy( qa ).slerp( qb, t );
-
- },
-
- slerpFlat: function(
- dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
-
- // fuzz-free, array-based Quaternion SLERP operation
-
- var x0 = src0[ srcOffset0 + 0 ],
- y0 = src0[ srcOffset0 + 1 ],
- z0 = src0[ srcOffset0 + 2 ],
- w0 = src0[ srcOffset0 + 3 ],
-
- x1 = src1[ srcOffset1 + 0 ],
- y1 = src1[ srcOffset1 + 1 ],
- z1 = src1[ srcOffset1 + 2 ],
- w1 = src1[ srcOffset1 + 3 ];
-
- if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
-
- var s = 1 - t,
-
- cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
-
- dir = ( cos >= 0 ? 1 : - 1 ),
- sqrSin = 1 - cos * cos;
-
- // Skip the Slerp for tiny steps to avoid numeric problems:
- if ( sqrSin > Number.EPSILON ) {
-
- var sin = Math.sqrt( sqrSin ),
- len = Math.atan2( sin, cos * dir );
-
- s = Math.sin( s * len ) / sin;
- t = Math.sin( t * len ) / sin;
-
- }
-
- var tDir = t * dir;
-
- x0 = x0 * s + x1 * tDir;
- y0 = y0 * s + y1 * tDir;
- z0 = z0 * s + z1 * tDir;
- w0 = w0 * s + w1 * tDir;
-
- // Normalize in case we just did a lerp:
- if ( s === 1 - t ) {
-
- var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
-
- x0 *= f;
- y0 *= f;
- z0 *= f;
- w0 *= f;
-
- }
-
- }
-
- dst[ dstOffset ] = x0;
- dst[ dstOffset + 1 ] = y0;
- dst[ dstOffset + 2 ] = z0;
- dst[ dstOffset + 3 ] = w0;
-
- }
-
-} );
-
-// File:src/math/Vector2.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author philogb / http://blog.thejit.org/
- * @author egraether / http://egraether.com/
- * @author zz85 / http://www.lab4games.net/zz85/blog
- */
-
-THREE.Vector2 = function ( x, y ) {
-
- this.x = x || 0;
- this.y = y || 0;
-
-};
-
-THREE.Vector2.prototype = {
-
- constructor: THREE.Vector2,
-
- get width() {
-
- return this.x;
-
- },
-
- set width( value ) {
-
- this.x = value;
-
- },
-
- get height() {
-
- return this.y;
-
- },
-
- set height( value ) {
-
- this.y = value;
-
- },
-
//
- set: function ( x, y ) {
+ if ( Math.sign === undefined ) {
- this.x = x;
- this.y = y;
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
- return this;
+ Math.sign = function ( x ) {
- },
+ return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;
- setScalar: function ( scalar ) {
+ };
- this.x = scalar;
- this.y = scalar;
+ }
- return this;
+ if ( Function.prototype.name === undefined ) {
- },
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
- setX: function ( x ) {
+ Object.defineProperty( Function.prototype, 'name', {
- this.x = x;
+ get: function () {
- return this;
+ return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ];
- },
+ }
- setY: function ( y ) {
+ } );
- this.y = y;
+ }
- return this;
+ if ( Object.assign === undefined ) {
- },
+ // Missing in IE
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
- setComponent: function ( index, value ) {
+ ( function () {
- switch ( index ) {
+ Object.assign = function ( target ) {
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- default: throw new Error( 'index is out of range: ' + index );
+ 'use strict';
+
+ if ( target === undefined || target === null ) {
+
+ throw new TypeError( 'Cannot convert undefined or null to object' );
+
+ }
+
+ var output = Object( target );
+
+ for ( var index = 1; index < arguments.length; index ++ ) {
+
+ var source = arguments[ index ];
+
+ if ( source !== undefined && source !== null ) {
+
+ for ( var nextKey in source ) {
+
+ if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {
+
+ output[ nextKey ] = source[ nextKey ];
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return output;
+
+ };
+
+ } )();
+
+ }
+
+ /**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+ function EventDispatcher() {}
+
+ Object.assign( EventDispatcher.prototype, {
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = listenerArray.slice( 0 );
+
+ for ( var i = 0, l = array.length; i < l; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
}
- },
+ } );
- getComponent: function ( index ) {
+ var REVISION = '86';
+ var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
+ var CullFaceNone = 0;
+ var CullFaceBack = 1;
+ var CullFaceFront = 2;
+ var CullFaceFrontBack = 3;
+ var FrontFaceDirectionCW = 0;
+ var FrontFaceDirectionCCW = 1;
+ var BasicShadowMap = 0;
+ var PCFShadowMap = 1;
+ var PCFSoftShadowMap = 2;
+ var FrontSide = 0;
+ var BackSide = 1;
+ var DoubleSide = 2;
+ var FlatShading = 1;
+ var SmoothShading = 2;
+ var NoColors = 0;
+ var FaceColors = 1;
+ var VertexColors = 2;
+ var NoBlending = 0;
+ var NormalBlending = 1;
+ var AdditiveBlending = 2;
+ var SubtractiveBlending = 3;
+ var MultiplyBlending = 4;
+ var CustomBlending = 5;
+ var AddEquation = 100;
+ var SubtractEquation = 101;
+ var ReverseSubtractEquation = 102;
+ var MinEquation = 103;
+ var MaxEquation = 104;
+ var ZeroFactor = 200;
+ var OneFactor = 201;
+ var SrcColorFactor = 202;
+ var OneMinusSrcColorFactor = 203;
+ var SrcAlphaFactor = 204;
+ var OneMinusSrcAlphaFactor = 205;
+ var DstAlphaFactor = 206;
+ var OneMinusDstAlphaFactor = 207;
+ var DstColorFactor = 208;
+ var OneMinusDstColorFactor = 209;
+ var SrcAlphaSaturateFactor = 210;
+ var NeverDepth = 0;
+ var AlwaysDepth = 1;
+ var LessDepth = 2;
+ var LessEqualDepth = 3;
+ var EqualDepth = 4;
+ var GreaterEqualDepth = 5;
+ var GreaterDepth = 6;
+ var NotEqualDepth = 7;
+ var MultiplyOperation = 0;
+ var MixOperation = 1;
+ var AddOperation = 2;
+ var NoToneMapping = 0;
+ var LinearToneMapping = 1;
+ var ReinhardToneMapping = 2;
+ var Uncharted2ToneMapping = 3;
+ var CineonToneMapping = 4;
+ var UVMapping = 300;
+ var CubeReflectionMapping = 301;
+ var CubeRefractionMapping = 302;
+ var EquirectangularReflectionMapping = 303;
+ var EquirectangularRefractionMapping = 304;
+ var SphericalReflectionMapping = 305;
+ var CubeUVReflectionMapping = 306;
+ var CubeUVRefractionMapping = 307;
+ var RepeatWrapping = 1000;
+ var ClampToEdgeWrapping = 1001;
+ var MirroredRepeatWrapping = 1002;
+ var NearestFilter = 1003;
+ var NearestMipMapNearestFilter = 1004;
+ var NearestMipMapLinearFilter = 1005;
+ var LinearFilter = 1006;
+ var LinearMipMapNearestFilter = 1007;
+ var LinearMipMapLinearFilter = 1008;
+ var UnsignedByteType = 1009;
+ var ByteType = 1010;
+ var ShortType = 1011;
+ var UnsignedShortType = 1012;
+ var IntType = 1013;
+ var UnsignedIntType = 1014;
+ var FloatType = 1015;
+ var HalfFloatType = 1016;
+ var UnsignedShort4444Type = 1017;
+ var UnsignedShort5551Type = 1018;
+ var UnsignedShort565Type = 1019;
+ var UnsignedInt248Type = 1020;
+ var AlphaFormat = 1021;
+ var RGBFormat = 1022;
+ var RGBAFormat = 1023;
+ var LuminanceFormat = 1024;
+ var LuminanceAlphaFormat = 1025;
+ var RGBEFormat = RGBAFormat;
+ var DepthFormat = 1026;
+ var DepthStencilFormat = 1027;
+ var RGB_S3TC_DXT1_Format = 2001;
+ var RGBA_S3TC_DXT1_Format = 2002;
+ var RGBA_S3TC_DXT3_Format = 2003;
+ var RGBA_S3TC_DXT5_Format = 2004;
+ var RGB_PVRTC_4BPPV1_Format = 2100;
+ var RGB_PVRTC_2BPPV1_Format = 2101;
+ var RGBA_PVRTC_4BPPV1_Format = 2102;
+ var RGBA_PVRTC_2BPPV1_Format = 2103;
+ var RGB_ETC1_Format = 2151;
+ var LoopOnce = 2200;
+ var LoopRepeat = 2201;
+ var LoopPingPong = 2202;
+ var InterpolateDiscrete = 2300;
+ var InterpolateLinear = 2301;
+ var InterpolateSmooth = 2302;
+ var ZeroCurvatureEnding = 2400;
+ var ZeroSlopeEnding = 2401;
+ var WrapAroundEnding = 2402;
+ var TrianglesDrawMode = 0;
+ var TriangleStripDrawMode = 1;
+ var TriangleFanDrawMode = 2;
+ var LinearEncoding = 3000;
+ var sRGBEncoding = 3001;
+ var GammaEncoding = 3007;
+ var RGBEEncoding = 3002;
+ var LogLuvEncoding = 3003;
+ var RGBM7Encoding = 3004;
+ var RGBM16Encoding = 3005;
+ var RGBDEncoding = 3006;
+ var BasicDepthPacking = 3200;
+ var RGBADepthPacking = 3201;
- switch ( index ) {
+ /**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
- case 0: return this.x;
- case 1: return this.y;
- default: throw new Error( 'index is out of range: ' + index );
+ var _Math = {
+
+ DEG2RAD: Math.PI / 180,
+ RAD2DEG: 180 / Math.PI,
+
+ generateUUID: function () {
+
+ // http://www.broofa.com/Tools/Math.uuid.htm
+
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
+ var uuid = new Array( 36 );
+ var rnd = 0, r;
+
+ return function generateUUID() {
+
+ for ( var i = 0; i < 36; i ++ ) {
+
+ if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
+
+ uuid[ i ] = '-';
+
+ } else if ( i === 14 ) {
+
+ uuid[ i ] = '4';
+
+ } else {
+
+ if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
+ r = rnd & 0xf;
+ rnd = rnd >> 4;
+ uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+
+ }
+
+ }
+
+ return uuid.join( '' );
+
+ };
+
+ }(),
+
+ clamp: function ( value, min, max ) {
+
+ return Math.max( min, Math.min( max, value ) );
+
+ },
+
+ // compute euclidian modulo of m % n
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
+ euclideanModulo: function ( n, m ) {
+
+ return ( ( n % m ) + m ) % m;
+
+ },
+
+ // Linear mapping from range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // https://en.wikipedia.org/wiki/Linear_interpolation
+
+ lerp: function ( x, y, t ) {
+
+ return ( 1 - t ) * x + t * y;
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ degToRad: function ( degrees ) {
+
+ return degrees * _Math.DEG2RAD;
+
+ },
+
+ radToDeg: function ( radians ) {
+
+ return radians * _Math.RAD2DEG;
+
+ },
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ nearestPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ nextPowerOfTwo: function ( value ) {
+
+ value --;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value ++;
+
+ return value;
}
- },
+ };
- clone: function () {
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author philogb / http://blog.thejit.org/
+ * @author egraether / http://egraether.com/
+ * @author zz85 / http://www.lab4games.net/zz85/blog
+ */
- return new this.constructor( this.x, this.y );
+ function Vector2( x, y ) {
- },
+ this.x = x || 0;
+ this.y = y || 0;
- copy: function ( v ) {
+ }
- this.x = v.x;
- this.y = v.y;
+ Object.defineProperties( Vector2.prototype, {
- return this;
+ "width" : {
- },
+ get: function () {
- add: function ( v, w ) {
+ return this.x;
- if ( w !== undefined ) {
+ },
- console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
+ set: function ( value ) {
+
+ this.x = value;
+
+ }
+
+ },
+
+ "height" : {
+
+ get: function () {
+
+ return this.y;
+
+ },
+
+ set: function ( value ) {
+
+ this.y = value;
+
+ }
}
- this.x += v.x;
- this.y += v.y;
+ } );
- return this;
+ Object.assign( Vector2.prototype, {
- },
+ isVector2: true,
- addScalar: function ( s ) {
+ set: function ( x, y ) {
- this.x += s;
- this.y += s;
+ this.x = x;
+ this.y = y;
- return this;
+ return this;
- },
+ },
- addVectors: function ( a, b ) {
+ setScalar: function ( scalar ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
+ this.x = scalar;
+ this.y = scalar;
- return this;
+ return this;
- },
+ },
- addScaledVector: function ( v, s ) {
+ setX: function ( x ) {
- this.x += v.x * s;
- this.y += v.y * s;
+ this.x = x;
- return this;
+ return this;
- },
+ },
- sub: function ( v, w ) {
+ setY: function ( y ) {
- if ( w !== undefined ) {
+ this.y = y;
- console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
+ return this;
- }
+ },
- this.x -= v.x;
- this.y -= v.y;
+ setComponent: function ( index, value ) {
- return this;
+ switch ( index ) {
- },
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
- subScalar: function ( s ) {
+ }
- this.x -= s;
- this.y -= s;
+ return this;
- return this;
+ },
- },
+ getComponent: function ( index ) {
- subVectors: function ( a, b ) {
+ switch ( index ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
- return this;
+ }
- },
+ },
- multiply: function ( v ) {
+ clone: function () {
- this.x *= v.x;
- this.y *= v.y;
+ return new this.constructor( this.x, this.y );
- return this;
+ },
- },
+ copy: function ( v ) {
- multiplyScalar: function ( scalar ) {
+ this.x = v.x;
+ this.y = v.y;
- if ( isFinite( scalar ) ) {
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ },
+
+ multiply: function ( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
this.x *= scalar;
this.y *= scalar;
- } else {
+ return this;
- this.x = 0;
- this.y = 0;
+ },
- }
+ divide: function ( v ) {
- return this;
-
- },
-
- divide: function ( v ) {
-
- this.x /= v.x;
- this.y /= v.y;
-
- return this;
-
- },
-
- divideScalar: function ( scalar ) {
-
- return this.multiplyScalar( 1 / scalar );
-
- },
-
- min: function ( v ) {
-
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
-
- return this;
-
- },
-
- max: function ( v ) {
-
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
-
- return this;
-
- },
-
- clamp: function ( min, max ) {
-
- // This function assumes min < max, if this assumption isn't true it will not operate correctly
-
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
-
- return this;
-
- },
-
- clampScalar: function () {
-
- var min, max;
-
- return function clampScalar( minVal, maxVal ) {
-
- if ( min === undefined ) {
-
- min = new THREE.Vector2();
- max = new THREE.Vector2();
-
- }
-
- min.set( minVal, minVal );
- max.set( maxVal, maxVal );
-
- return this.clamp( min, max );
-
- };
-
- }(),
-
- clampLength: function ( min, max ) {
-
- var length = this.length();
-
- this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
-
- return this;
-
- },
-
- floor: function () {
-
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
-
- return this;
-
- },
-
- ceil: function () {
-
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
-
- return this;
-
- },
-
- round: function () {
-
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
-
- return this;
-
- },
-
- roundToZero: function () {
-
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
-
- return this;
-
- },
-
- negate: function () {
-
- this.x = - this.x;
- this.y = - this.y;
-
- return this;
-
- },
-
- dot: function ( v ) {
-
- return this.x * v.x + this.y * v.y;
-
- },
-
- lengthSq: function () {
-
- return this.x * this.x + this.y * this.y;
-
- },
-
- length: function () {
-
- return Math.sqrt( this.x * this.x + this.y * this.y );
-
- },
-
- lengthManhattan: function() {
-
- return Math.abs( this.x ) + Math.abs( this.y );
-
- },
-
- normalize: function () {
-
- return this.divideScalar( this.length() );
-
- },
-
- angle: function () {
-
- // computes the angle in radians with respect to the positive x-axis
-
- var angle = Math.atan2( this.y, this.x );
-
- if ( angle < 0 ) angle += 2 * Math.PI;
-
- return angle;
-
- },
-
- distanceTo: function ( v ) {
-
- return Math.sqrt( this.distanceToSquared( v ) );
-
- },
-
- distanceToSquared: function ( v ) {
-
- var dx = this.x - v.x, dy = this.y - v.y;
- return dx * dx + dy * dy;
-
- },
-
- setLength: function ( length ) {
-
- return this.multiplyScalar( length / this.length() );
-
- },
-
- lerp: function ( v, alpha ) {
-
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
-
- return this;
-
- },
-
- lerpVectors: function ( v1, v2, alpha ) {
-
- this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
-
- return this;
-
- },
-
- equals: function ( v ) {
-
- return ( ( v.x === this.x ) && ( v.y === this.y ) );
-
- },
-
- fromArray: function ( array, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
-
- return array;
-
- },
-
- fromAttribute: function ( attribute, index, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- index = index * attribute.itemSize + offset;
-
- this.x = attribute.array[ index ];
- this.y = attribute.array[ index + 1 ];
-
- return this;
-
- },
-
- rotateAround: function ( center, angle ) {
-
- var c = Math.cos( angle ), s = Math.sin( angle );
-
- var x = this.x - center.x;
- var y = this.y - center.y;
-
- this.x = x * c - y * s + center.x;
- this.y = x * s + y * c + center.y;
-
- return this;
-
- }
-
-};
-
-// File:src/math/Vector3.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author *kile / http://kile.stravaganza.org/
- * @author philogb / http://blog.thejit.org/
- * @author mikael emtinger / http://gomo.se/
- * @author egraether / http://egraether.com/
- * @author WestLangley / http://github.com/WestLangley
- */
-
-THREE.Vector3 = function ( x, y, z ) {
-
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
-
-};
-
-THREE.Vector3.prototype = {
-
- constructor: THREE.Vector3,
-
- set: function ( x, y, z ) {
-
- this.x = x;
- this.y = y;
- this.z = z;
-
- return this;
-
- },
-
- setScalar: function ( scalar ) {
-
- this.x = scalar;
- this.y = scalar;
- this.z = scalar;
-
- return this;
-
- },
-
- setX: function ( x ) {
-
- this.x = x;
-
- return this;
-
- },
-
- setY: function ( y ) {
-
- this.y = y;
-
- return this;
-
- },
-
- setZ: function ( z ) {
-
- this.z = z;
-
- return this;
-
- },
-
- setComponent: function ( index, value ) {
-
- switch ( index ) {
-
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- case 2: this.z = value; break;
- default: throw new Error( 'index is out of range: ' + index );
-
- }
-
- },
-
- getComponent: function ( index ) {
-
- switch ( index ) {
-
- case 0: return this.x;
- case 1: return this.y;
- case 2: return this.z;
- default: throw new Error( 'index is out of range: ' + index );
-
- }
-
- },
-
- clone: function () {
-
- return new this.constructor( this.x, this.y, this.z );
-
- },
-
- copy: function ( v ) {
-
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
-
- return this;
-
- },
-
- add: function ( v, w ) {
-
- if ( w !== undefined ) {
-
- console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
-
- }
-
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
-
- return this;
-
- },
-
- addScalar: function ( s ) {
-
- this.x += s;
- this.y += s;
- this.z += s;
-
- return this;
-
- },
-
- addVectors: function ( a, b ) {
-
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- this.z = a.z + b.z;
-
- return this;
-
- },
-
- addScaledVector: function ( v, s ) {
-
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
-
- return this;
-
- },
-
- sub: function ( v, w ) {
-
- if ( w !== undefined ) {
-
- console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
-
- }
-
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
-
- return this;
-
- },
-
- subScalar: function ( s ) {
-
- this.x -= s;
- this.y -= s;
- this.z -= s;
-
- return this;
-
- },
-
- subVectors: function ( a, b ) {
-
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- this.z = a.z - b.z;
-
- return this;
-
- },
-
- multiply: function ( v, w ) {
-
- if ( w !== undefined ) {
-
- console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
- return this.multiplyVectors( v, w );
-
- }
-
- this.x *= v.x;
- this.y *= v.y;
- this.z *= v.z;
-
- return this;
-
- },
-
- multiplyScalar: function ( scalar ) {
-
- if ( isFinite( scalar ) ) {
-
- this.x *= scalar;
- this.y *= scalar;
- this.z *= scalar;
-
- } else {
-
- this.x = 0;
- this.y = 0;
- this.z = 0;
-
- }
-
- return this;
-
- },
-
- multiplyVectors: function ( a, b ) {
-
- this.x = a.x * b.x;
- this.y = a.y * b.y;
- this.z = a.z * b.z;
-
- return this;
-
- },
-
- applyEuler: function () {
-
- var quaternion;
-
- return function applyEuler( euler ) {
-
- if ( euler instanceof THREE.Euler === false ) {
-
- console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
-
- }
-
- if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
-
- this.applyQuaternion( quaternion.setFromEuler( euler ) );
+ this.x /= v.x;
+ this.y /= v.y;
return this;
- };
+ },
- }(),
+ divideScalar: function ( scalar ) {
- applyAxisAngle: function () {
+ return this.multiplyScalar( 1 / scalar );
- var quaternion;
+ },
- return function applyAxisAngle( axis, angle ) {
+ min: function ( v ) {
- if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
-
- this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
return this;
- };
+ },
- }(),
+ max: function ( v ) {
- applyMatrix3: function ( m ) {
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
- var x = this.x;
- var y = this.y;
- var z = this.z;
+ return this;
- var e = m.elements;
+ },
- this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
- this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
- this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+ clamp: function ( min, max ) {
- return this;
+ // assumes min < max, componentwise
- },
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- applyMatrix4: function ( m ) {
+ return this;
- // input: THREE.Matrix4 affine matrix
+ },
- var x = this.x, y = this.y, z = this.z;
+ clampScalar: function () {
- var e = m.elements;
+ var min = new Vector2();
+ var max = new Vector2();
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
+ return function clampScalar( minVal, maxVal ) {
- return this;
+ min.set( minVal, minVal );
+ max.set( maxVal, maxVal );
- },
+ return this.clamp( min, max );
- applyProjection: function ( m ) {
+ };
- // input: THREE.Matrix4 projection matrix
+ }(),
- var x = this.x, y = this.y, z = this.z;
+ clampLength: function ( min, max ) {
- var e = m.elements;
- var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
+ var length = this.length();
- this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
- this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
- this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
- return this;
+ },
- },
+ floor: function () {
- applyQuaternion: function ( q ) {
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
- var x = this.x;
- var y = this.y;
- var z = this.z;
+ return this;
- var qx = q.x;
- var qy = q.y;
- var qz = q.z;
- var qw = q.w;
+ },
- // calculate quat * vector
+ ceil: function () {
- var ix = qw * x + qy * z - qz * y;
- var iy = qw * y + qz * x - qx * z;
- var iz = qw * z + qx * y - qy * x;
- var iw = - qx * x - qy * y - qz * z;
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
- // calculate result * inverse quat
+ return this;
- this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
- this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
- this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+ },
- return this;
+ round: function () {
- },
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
- project: function () {
+ return this;
- var matrix;
+ },
- return function project( camera ) {
+ roundToZero: function () {
- if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
- return this.applyProjection( matrix );
+ return this;
- };
+ },
- }(),
+ negate: function () {
- unproject: function () {
+ this.x = - this.x;
+ this.y = - this.y;
- var matrix;
+ return this;
- return function unproject( camera ) {
+ },
- if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ dot: function ( v ) {
- matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
- return this.applyProjection( matrix );
+ return this.x * v.x + this.y * v.y;
- };
+ },
- }(),
+ lengthSq: function () {
- transformDirection: function ( m ) {
+ return this.x * this.x + this.y * this.y;
- // input: THREE.Matrix4 affine matrix
- // vector interpreted as a direction
+ },
- var x = this.x, y = this.y, z = this.z;
+ length: function () {
- var e = m.elements;
+ return Math.sqrt( this.x * this.x + this.y * this.y );
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+ },
- this.normalize();
+ lengthManhattan: function() {
- return this;
+ return Math.abs( this.x ) + Math.abs( this.y );
- },
+ },
- divide: function ( v ) {
+ normalize: function () {
- this.x /= v.x;
- this.y /= v.y;
- this.z /= v.z;
+ return this.divideScalar( this.length() || 1 );
- return this;
+ },
- },
+ angle: function () {
- divideScalar: function ( scalar ) {
+ // computes the angle in radians with respect to the positive x-axis
- return this.multiplyScalar( 1 / scalar );
+ var angle = Math.atan2( this.y, this.x );
- },
+ if ( angle < 0 ) angle += 2 * Math.PI;
- min: function ( v ) {
+ return angle;
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- this.z = Math.min( this.z, v.z );
+ },
- return this;
+ distanceTo: function ( v ) {
- },
+ return Math.sqrt( this.distanceToSquared( v ) );
- max: function ( v ) {
+ },
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- this.z = Math.max( this.z, v.z );
+ distanceToSquared: function ( v ) {
- return this;
+ var dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
- },
+ },
- clamp: function ( min, max ) {
+ distanceToManhattan: function ( v ) {
- // This function assumes min < max, if this assumption isn't true it will not operate correctly
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ },
- return this;
+ setLength: function ( length ) {
- },
+ return this.normalize().multiplyScalar( length );
- clampScalar: function () {
+ },
- var min, max;
+ lerp: function ( v, alpha ) {
- return function clampScalar( minVal, maxVal ) {
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
- if ( min === undefined ) {
+ return this;
- min = new THREE.Vector3();
- max = new THREE.Vector3();
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ },
+
+ fromBufferAttribute: function ( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );
}
- min.set( minVal, minVal, minVal );
- max.set( maxVal, maxVal, maxVal );
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
- return this.clamp( min, max );
+ return this;
- };
+ },
- }(),
+ rotateAround: function ( center, angle ) {
- clampLength: function ( min, max ) {
+ var c = Math.cos( angle ), s = Math.sin( angle );
- var length = this.length();
+ var x = this.x - center.x;
+ var y = this.y - center.y;
- this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );
+ this.x = x * c - y * s + center.x;
+ this.y = x * s + y * c + center.y;
- return this;
-
- },
-
- floor: function () {
-
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- this.z = Math.floor( this.z );
-
- return this;
-
- },
-
- ceil: function () {
-
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- this.z = Math.ceil( this.z );
-
- return this;
-
- },
-
- round: function () {
-
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- this.z = Math.round( this.z );
-
- return this;
-
- },
-
- roundToZero: function () {
-
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
-
- return this;
-
- },
-
- negate: function () {
-
- this.x = - this.x;
- this.y = - this.y;
- this.z = - this.z;
-
- return this;
-
- },
-
- dot: function ( v ) {
-
- return this.x * v.x + this.y * v.y + this.z * v.z;
-
- },
-
- lengthSq: function () {
-
- return this.x * this.x + this.y * this.y + this.z * this.z;
-
- },
-
- length: function () {
-
- return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
-
- },
-
- lengthManhattan: function () {
-
- return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
-
- },
-
- normalize: function () {
-
- return this.divideScalar( this.length() );
-
- },
-
- setLength: function ( length ) {
-
- return this.multiplyScalar( length / this.length() );
-
- },
-
- lerp: function ( v, alpha ) {
-
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- this.z += ( v.z - this.z ) * alpha;
-
- return this;
-
- },
-
- lerpVectors: function ( v1, v2, alpha ) {
-
- this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
-
- return this;
-
- },
-
- cross: function ( v, w ) {
-
- if ( w !== undefined ) {
-
- console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
- return this.crossVectors( v, w );
+ return this;
}
- var x = this.x, y = this.y, z = this.z;
+ } );
- this.x = y * v.z - z * v.y;
- this.y = z * v.x - x * v.z;
- this.z = x * v.y - y * v.x;
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ */
- return this;
+ var textureId = 0;
- },
+ function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
- crossVectors: function ( a, b ) {
+ Object.defineProperty( this, 'id', { value: textureId ++ } );
- var ax = a.x, ay = a.y, az = a.z;
- var bx = b.x, by = b.y, bz = b.z;
+ this.uuid = _Math.generateUUID();
- this.x = ay * bz - az * by;
- this.y = az * bx - ax * bz;
- this.z = ax * by - ay * bx;
+ this.name = '';
- return this;
+ this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;
+ this.mipmaps = [];
- },
+ this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;
- projectOnVector: function () {
+ this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;
+ this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;
- var v1, dot;
+ this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;
- return function projectOnVector( vector ) {
+ this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
- if ( v1 === undefined ) v1 = new THREE.Vector3();
+ this.format = format !== undefined ? format : RGBAFormat;
+ this.type = type !== undefined ? type : UnsignedByteType;
- v1.copy( vector ).normalize();
+ this.offset = new Vector2( 0, 0 );
+ this.repeat = new Vector2( 1, 1 );
- dot = this.dot( v1 );
+ this.generateMipmaps = true;
+ this.premultiplyAlpha = false;
+ this.flipY = true;
+ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
- return this.copy( v1 ).multiplyScalar( dot );
+ // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
+ //
+ // Also changing the encoding after already used by a Material will not automatically make the Material
+ // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.
+ this.encoding = encoding !== undefined ? encoding : LinearEncoding;
- };
-
- }(),
-
- projectOnPlane: function () {
-
- var v1;
-
- return function projectOnPlane( planeNormal ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
-
- v1.copy( this ).projectOnVector( planeNormal );
-
- return this.sub( v1 );
-
- };
-
- }(),
-
- reflect: function () {
-
- // reflect incident vector off plane orthogonal to normal
- // normal is assumed to have unit length
-
- var v1;
-
- return function reflect( normal ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
-
- return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
-
- };
-
- }(),
-
- angleTo: function ( v ) {
-
- var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
-
- // clamp, to handle numerical problems
-
- return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
-
- },
-
- distanceTo: function ( v ) {
-
- return Math.sqrt( this.distanceToSquared( v ) );
-
- },
-
- distanceToSquared: function ( v ) {
-
- var dx = this.x - v.x;
- var dy = this.y - v.y;
- var dz = this.z - v.z;
-
- return dx * dx + dy * dy + dz * dz;
-
- },
-
- setFromSpherical: function( s ) {
-
- var sinPhiRadius = Math.sin( s.phi ) * s.radius;
-
- this.x = sinPhiRadius * Math.sin( s.theta );
- this.y = Math.cos( s.phi ) * s.radius;
- this.z = sinPhiRadius * Math.cos( s.theta );
-
- return this;
-
- },
-
- setFromMatrixPosition: function ( m ) {
-
- return this.setFromMatrixColumn( m, 3 );
-
- },
-
- setFromMatrixScale: function ( m ) {
-
- var sx = this.setFromMatrixColumn( m, 0 ).length();
- var sy = this.setFromMatrixColumn( m, 1 ).length();
- var sz = this.setFromMatrixColumn( m, 2 ).length();
-
- this.x = sx;
- this.y = sy;
- this.z = sz;
-
- return this;
-
- },
-
- setFromMatrixColumn: function ( m, index ) {
-
- if ( typeof m === 'number' ) {
-
- console.warn( 'THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).' );
-
- m = arguments[ 1 ];
- index = arguments[ 0 ];
-
- }
-
- return this.fromArray( m.elements, index * 4 );
-
- },
-
- equals: function ( v ) {
-
- return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
-
- },
-
- fromArray: function ( array, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- this.z = array[ offset + 2 ];
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- array[ offset + 2 ] = this.z;
-
- return array;
-
- },
-
- fromAttribute: function ( attribute, index, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- index = index * attribute.itemSize + offset;
-
- this.x = attribute.array[ index ];
- this.y = attribute.array[ index + 1 ];
- this.z = attribute.array[ index + 2 ];
-
- return this;
+ this.version = 0;
+ this.onUpdate = null;
}
-};
+ Texture.DEFAULT_IMAGE = undefined;
+ Texture.DEFAULT_MAPPING = UVMapping;
-// File:src/math/Vector4.js
+ Object.defineProperty( Texture.prototype, "needsUpdate", {
-/**
- * @author supereggbert / http://www.paulbrunt.co.uk/
- * @author philogb / http://blog.thejit.org/
- * @author mikael emtinger / http://gomo.se/
- * @author egraether / http://egraether.com/
- * @author WestLangley / http://github.com/WestLangley
- */
+ set: function ( value ) {
-THREE.Vector4 = function ( x, y, z, w ) {
-
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
- this.w = ( w !== undefined ) ? w : 1;
-
-};
-
-THREE.Vector4.prototype = {
-
- constructor: THREE.Vector4,
-
- set: function ( x, y, z, w ) {
-
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
-
- return this;
-
- },
-
- setScalar: function ( scalar ) {
-
- this.x = scalar;
- this.y = scalar;
- this.z = scalar;
- this.w = scalar;
-
- return this;
-
- },
-
- setX: function ( x ) {
-
- this.x = x;
-
- return this;
-
- },
-
- setY: function ( y ) {
-
- this.y = y;
-
- return this;
-
- },
-
- setZ: function ( z ) {
-
- this.z = z;
-
- return this;
-
- },
-
- setW: function ( w ) {
-
- this.w = w;
-
- return this;
-
- },
-
- setComponent: function ( index, value ) {
-
- switch ( index ) {
-
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- case 2: this.z = value; break;
- case 3: this.w = value; break;
- default: throw new Error( 'index is out of range: ' + index );
+ if ( value === true ) this.version ++;
}
- },
+ } );
- getComponent: function ( index ) {
+ Object.assign( Texture.prototype, EventDispatcher.prototype, {
- switch ( index ) {
+ constructor: Texture,
- case 0: return this.x;
- case 1: return this.y;
- case 2: return this.z;
- case 3: return this.w;
- default: throw new Error( 'index is out of range: ' + index );
+ isTexture: true,
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.image = source.image;
+ this.mipmaps = source.mipmaps.slice( 0 );
+
+ this.mapping = source.mapping;
+
+ this.wrapS = source.wrapS;
+ this.wrapT = source.wrapT;
+
+ this.magFilter = source.magFilter;
+ this.minFilter = source.minFilter;
+
+ this.anisotropy = source.anisotropy;
+
+ this.format = source.format;
+ this.type = source.type;
+
+ this.offset.copy( source.offset );
+ this.repeat.copy( source.repeat );
+
+ this.generateMipmaps = source.generateMipmaps;
+ this.premultiplyAlpha = source.premultiplyAlpha;
+ this.flipY = source.flipY;
+ this.unpackAlignment = source.unpackAlignment;
+ this.encoding = source.encoding;
+
+ return this;
+
+ },
+
+ toJSON: function ( meta ) {
+
+ if ( meta.textures[ this.uuid ] !== undefined ) {
+
+ return meta.textures[ this.uuid ];
+
+ }
+
+ function getDataURL( image ) {
+
+ var canvas;
+
+ if ( image.toDataURL !== undefined ) {
+
+ canvas = image;
+
+ } else {
+
+ canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );
+
+ }
+
+ if ( canvas.width > 2048 || canvas.height > 2048 ) {
+
+ return canvas.toDataURL( 'image/jpeg', 0.6 );
+
+ } else {
+
+ return canvas.toDataURL( 'image/png' );
+
+ }
+
+ }
+
+ var output = {
+ metadata: {
+ version: 4.5,
+ type: 'Texture',
+ generator: 'Texture.toJSON'
+ },
+
+ uuid: this.uuid,
+ name: this.name,
+
+ mapping: this.mapping,
+
+ repeat: [ this.repeat.x, this.repeat.y ],
+ offset: [ this.offset.x, this.offset.y ],
+ wrap: [ this.wrapS, this.wrapT ],
+
+ minFilter: this.minFilter,
+ magFilter: this.magFilter,
+ anisotropy: this.anisotropy,
+
+ flipY: this.flipY
+ };
+
+ if ( this.image !== undefined ) {
+
+ // TODO: Move to THREE.Image
+
+ var image = this.image;
+
+ if ( image.uuid === undefined ) {
+
+ image.uuid = _Math.generateUUID(); // UGH
+
+ }
+
+ if ( meta.images[ image.uuid ] === undefined ) {
+
+ meta.images[ image.uuid ] = {
+ uuid: image.uuid,
+ url: getDataURL( image )
+ };
+
+ }
+
+ output.image = image.uuid;
+
+ }
+
+ meta.textures[ this.uuid ] = output;
+
+ return output;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ transformUv: function ( uv ) {
+
+ if ( this.mapping !== UVMapping ) return;
+
+ uv.multiply( this.repeat );
+ uv.add( this.offset );
+
+ if ( uv.x < 0 || uv.x > 1 ) {
+
+ switch ( this.wrapS ) {
+
+ case RepeatWrapping:
+
+ uv.x = uv.x - Math.floor( uv.x );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.x = uv.x < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+ uv.x = Math.ceil( uv.x ) - uv.x;
+
+ } else {
+
+ uv.x = uv.x - Math.floor( uv.x );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( uv.y < 0 || uv.y > 1 ) {
+
+ switch ( this.wrapT ) {
+
+ case RepeatWrapping:
+
+ uv.y = uv.y - Math.floor( uv.y );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.y = uv.y < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+ uv.y = Math.ceil( uv.y ) - uv.y;
+
+ } else {
+
+ uv.y = uv.y - Math.floor( uv.y );
+
+ }
+ break;
+
+ }
+
+ }
+
+ if ( this.flipY ) {
+
+ uv.y = 1 - uv.y;
+
+ }
}
- },
+ } );
- clone: function () {
+ /**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
- return new this.constructor( this.x, this.y, this.z, this.w );
+ function Vector4( x, y, z, w ) {
- },
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ this.w = ( w !== undefined ) ? w : 1;
- copy: function ( v ) {
+ }
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
- this.w = ( v.w !== undefined ) ? v.w : 1;
+ Object.assign( Vector4.prototype, {
- return this;
+ isVector4: true,
- },
+ set: function ( x, y, z, w ) {
- add: function ( v, w ) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
- if ( w !== undefined ) {
+ return this;
- console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
+ },
- }
+ setScalar: function ( scalar ) {
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
- this.w += v.w;
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+ this.w = scalar;
- return this;
+ return this;
- },
+ },
- addScalar: function ( s ) {
+ setX: function ( x ) {
- this.x += s;
- this.y += s;
- this.z += s;
- this.w += s;
+ this.x = x;
- return this;
+ return this;
- },
+ },
- addVectors: function ( a, b ) {
+ setY: function ( y ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- this.z = a.z + b.z;
- this.w = a.w + b.w;
+ this.y = y;
- return this;
+ return this;
- },
+ },
- addScaledVector: function ( v, s ) {
+ setZ: function ( z ) {
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
- this.w += v.w * s;
+ this.z = z;
- return this;
+ return this;
- },
+ },
- sub: function ( v, w ) {
+ setW: function ( w ) {
- if ( w !== undefined ) {
+ this.w = w;
- console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
+ return this;
- }
+ },
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
- this.w -= v.w;
+ setComponent: function ( index, value ) {
- return this;
+ switch ( index ) {
- },
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ case 3: this.w = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
- subScalar: function ( s ) {
+ }
- this.x -= s;
- this.y -= s;
- this.z -= s;
- this.w -= s;
+ return this;
- return this;
+ },
- },
+ getComponent: function ( index ) {
- subVectors: function ( a, b ) {
+ switch ( index ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- this.z = a.z - b.z;
- this.w = a.w - b.w;
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ case 3: return this.w;
+ default: throw new Error( 'index is out of range: ' + index );
- return this;
+ }
- },
+ },
- multiplyScalar: function ( scalar ) {
+ clone: function () {
- if ( isFinite( scalar ) ) {
+ return new this.constructor( this.x, this.y, this.z, this.w );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ this.w = ( v.w !== undefined ) ? v.w : 1;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ this.w += v.w;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+ this.w += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+ this.w = a.w + b.w;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+ this.w += v.w * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ this.w -= v.w;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+ this.w -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+ this.w = a.w - b.w;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
this.w *= scalar;
- } else {
+ return this;
- this.x = 0;
- this.y = 0;
- this.z = 0;
- this.w = 0;
+ },
- }
+ applyMatrix4: function ( m ) {
- return this;
+ var x = this.x, y = this.y, z = this.z, w = this.w;
+ var e = m.elements;
- },
-
- applyMatrix4: function ( m ) {
-
- var x = this.x;
- var y = this.y;
- var z = this.z;
- var w = this.w;
-
- var e = m.elements;
-
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
- this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
-
- return this;
-
- },
-
- divideScalar: function ( scalar ) {
-
- return this.multiplyScalar( 1 / scalar );
-
- },
-
- setAxisAngleFromQuaternion: function ( q ) {
-
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
-
- // q is assumed to be normalized
-
- this.w = 2 * Math.acos( q.w );
-
- var s = Math.sqrt( 1 - q.w * q.w );
-
- if ( s < 0.0001 ) {
-
- this.x = 1;
- this.y = 0;
- this.z = 0;
-
- } else {
-
- this.x = q.x / s;
- this.y = q.y / s;
- this.z = q.z / s;
-
- }
-
- return this;
-
- },
-
- setAxisAngleFromRotationMatrix: function ( m ) {
-
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
-
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
- var angle, x, y, z, // variables for result
- epsilon = 0.01, // margin to allow for rounding errors
- epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
-
- te = m.elements,
-
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
-
- if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
- ( Math.abs( m13 - m31 ) < epsilon ) &&
- ( Math.abs( m23 - m32 ) < epsilon ) ) {
-
- // singularity found
- // first check for identity matrix which must have +1 for all terms
- // in leading diagonal and zero in other terms
-
- if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
- ( Math.abs( m13 + m31 ) < epsilon2 ) &&
- ( Math.abs( m23 + m32 ) < epsilon2 ) &&
- ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
-
- // this singularity is identity matrix so angle = 0
-
- this.set( 1, 0, 0, 0 );
-
- return this; // zero angle, arbitrary axis
-
- }
-
- // otherwise this singularity is angle = 180
-
- angle = Math.PI;
-
- var xx = ( m11 + 1 ) / 2;
- var yy = ( m22 + 1 ) / 2;
- var zz = ( m33 + 1 ) / 2;
- var xy = ( m12 + m21 ) / 4;
- var xz = ( m13 + m31 ) / 4;
- var yz = ( m23 + m32 ) / 4;
-
- if ( ( xx > yy ) && ( xx > zz ) ) {
-
- // m11 is the largest diagonal term
-
- if ( xx < epsilon ) {
-
- x = 0;
- y = 0.707106781;
- z = 0.707106781;
-
- } else {
-
- x = Math.sqrt( xx );
- y = xy / x;
- z = xz / x;
-
- }
-
- } else if ( yy > zz ) {
-
- // m22 is the largest diagonal term
-
- if ( yy < epsilon ) {
-
- x = 0.707106781;
- y = 0;
- z = 0.707106781;
-
- } else {
-
- y = Math.sqrt( yy );
- x = xy / y;
- z = yz / y;
-
- }
-
- } else {
-
- // m33 is the largest diagonal term so base result on this
-
- if ( zz < epsilon ) {
-
- x = 0.707106781;
- y = 0.707106781;
- z = 0;
-
- } else {
-
- z = Math.sqrt( zz );
- x = xz / z;
- y = yz / z;
-
- }
-
- }
-
- this.set( x, y, z, angle );
-
- return this; // return 180 deg rotation
-
- }
-
- // as we have reached here there are no singularities so we can handle normally
-
- var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
- ( m13 - m31 ) * ( m13 - m31 ) +
- ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
-
- if ( Math.abs( s ) < 0.001 ) s = 1;
-
- // prevent divide by zero, should not happen if matrix is orthogonal and should be
- // caught by singularity test above, but I've left it in just in case
-
- this.x = ( m32 - m23 ) / s;
- this.y = ( m13 - m31 ) / s;
- this.z = ( m21 - m12 ) / s;
- this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
-
- return this;
-
- },
-
- min: function ( v ) {
-
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- this.z = Math.min( this.z, v.z );
- this.w = Math.min( this.w, v.w );
-
- return this;
-
- },
-
- max: function ( v ) {
-
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- this.z = Math.max( this.z, v.z );
- this.w = Math.max( this.w, v.w );
-
- return this;
-
- },
-
- clamp: function ( min, max ) {
-
- // This function assumes min < max, if this assumption isn't true it will not operate correctly
-
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- this.z = Math.max( min.z, Math.min( max.z, this.z ) );
- this.w = Math.max( min.w, Math.min( max.w, this.w ) );
-
- return this;
-
- },
-
- clampScalar: function () {
-
- var min, max;
-
- return function clampScalar( minVal, maxVal ) {
-
- if ( min === undefined ) {
-
- min = new THREE.Vector4();
- max = new THREE.Vector4();
-
- }
-
- min.set( minVal, minVal, minVal, minVal );
- max.set( maxVal, maxVal, maxVal, maxVal );
-
- return this.clamp( min, max );
-
- };
-
- }(),
-
- floor: function () {
-
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- this.z = Math.floor( this.z );
- this.w = Math.floor( this.w );
-
- return this;
-
- },
-
- ceil: function () {
-
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- this.z = Math.ceil( this.z );
- this.w = Math.ceil( this.w );
-
- return this;
-
- },
-
- round: function () {
-
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- this.z = Math.round( this.z );
- this.w = Math.round( this.w );
-
- return this;
-
- },
-
- roundToZero: function () {
-
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
- this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
-
- return this;
-
- },
-
- negate: function () {
-
- this.x = - this.x;
- this.y = - this.y;
- this.z = - this.z;
- this.w = - this.w;
-
- return this;
-
- },
-
- dot: function ( v ) {
-
- return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
-
- },
-
- lengthSq: function () {
-
- return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
-
- },
-
- length: function () {
-
- return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
-
- },
-
- lengthManhattan: function () {
-
- return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
-
- },
-
- normalize: function () {
-
- return this.divideScalar( this.length() );
-
- },
-
- setLength: function ( length ) {
-
- return this.multiplyScalar( length / this.length() );
-
- },
-
- lerp: function ( v, alpha ) {
-
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- this.z += ( v.z - this.z ) * alpha;
- this.w += ( v.w - this.w ) * alpha;
-
- return this;
-
- },
-
- lerpVectors: function ( v1, v2, alpha ) {
-
- this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
-
- return this;
-
- },
-
- equals: function ( v ) {
-
- return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
-
- },
-
- fromArray: function ( array, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- this.z = array[ offset + 2 ];
- this.w = array[ offset + 3 ];
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- array[ offset + 2 ] = this.z;
- array[ offset + 3 ] = this.w;
-
- return array;
-
- },
-
- fromAttribute: function ( attribute, index, offset ) {
-
- if ( offset === undefined ) offset = 0;
-
- index = index * attribute.itemSize + offset;
-
- this.x = attribute.array[ index ];
- this.y = attribute.array[ index + 1 ];
- this.z = attribute.array[ index + 2 ];
- this.w = attribute.array[ index + 3 ];
-
- return this;
-
- }
-
-};
-
-// File:src/math/Euler.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author bhouston / http://clara.io
- */
-
-THREE.Euler = function ( x, y, z, order ) {
-
- this._x = x || 0;
- this._y = y || 0;
- this._z = z || 0;
- this._order = order || THREE.Euler.DefaultOrder;
-
-};
-
-THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
-
-THREE.Euler.DefaultOrder = 'XYZ';
-
-THREE.Euler.prototype = {
-
- constructor: THREE.Euler,
-
- get x () {
-
- return this._x;
-
- },
-
- set x ( value ) {
-
- this._x = value;
- this.onChangeCallback();
-
- },
-
- get y () {
-
- return this._y;
-
- },
-
- set y ( value ) {
-
- this._y = value;
- this.onChangeCallback();
-
- },
-
- get z () {
-
- return this._z;
-
- },
-
- set z ( value ) {
-
- this._z = value;
- this.onChangeCallback();
-
- },
-
- get order () {
-
- return this._order;
-
- },
-
- set order ( value ) {
-
- this._order = value;
- this.onChangeCallback();
-
- },
-
- set: function ( x, y, z, order ) {
-
- this._x = x;
- this._y = y;
- this._z = z;
- this._order = order || this._order;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor( this._x, this._y, this._z, this._order );
-
- },
-
- copy: function ( euler ) {
-
- this._x = euler._x;
- this._y = euler._y;
- this._z = euler._z;
- this._order = euler._order;
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- setFromRotationMatrix: function ( m, order, update ) {
-
- var clamp = THREE.Math.clamp;
-
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
- var te = m.elements;
- var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
- var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
- var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
-
- order = order || this._order;
-
- if ( order === 'XYZ' ) {
-
- this._y = Math.asin( clamp( m13, - 1, 1 ) );
-
- if ( Math.abs( m13 ) < 0.99999 ) {
-
- this._x = Math.atan2( - m23, m33 );
- this._z = Math.atan2( - m12, m11 );
-
- } else {
-
- this._x = Math.atan2( m32, m22 );
- this._z = 0;
-
- }
-
- } else if ( order === 'YXZ' ) {
-
- this._x = Math.asin( - clamp( m23, - 1, 1 ) );
-
- if ( Math.abs( m23 ) < 0.99999 ) {
-
- this._y = Math.atan2( m13, m33 );
- this._z = Math.atan2( m21, m22 );
-
- } else {
-
- this._y = Math.atan2( - m31, m11 );
- this._z = 0;
-
- }
-
- } else if ( order === 'ZXY' ) {
-
- this._x = Math.asin( clamp( m32, - 1, 1 ) );
-
- if ( Math.abs( m32 ) < 0.99999 ) {
-
- this._y = Math.atan2( - m31, m33 );
- this._z = Math.atan2( - m12, m22 );
-
- } else {
-
- this._y = 0;
- this._z = Math.atan2( m21, m11 );
-
- }
-
- } else if ( order === 'ZYX' ) {
-
- this._y = Math.asin( - clamp( m31, - 1, 1 ) );
-
- if ( Math.abs( m31 ) < 0.99999 ) {
-
- this._x = Math.atan2( m32, m33 );
- this._z = Math.atan2( m21, m11 );
-
- } else {
-
- this._x = 0;
- this._z = Math.atan2( - m12, m22 );
-
- }
-
- } else if ( order === 'YZX' ) {
-
- this._z = Math.asin( clamp( m21, - 1, 1 ) );
-
- if ( Math.abs( m21 ) < 0.99999 ) {
-
- this._x = Math.atan2( - m23, m22 );
- this._y = Math.atan2( - m31, m11 );
-
- } else {
-
- this._x = 0;
- this._y = Math.atan2( m13, m33 );
-
- }
-
- } else if ( order === 'XZY' ) {
-
- this._z = Math.asin( - clamp( m12, - 1, 1 ) );
-
- if ( Math.abs( m12 ) < 0.99999 ) {
-
- this._x = Math.atan2( m32, m22 );
- this._y = Math.atan2( m13, m11 );
-
- } else {
-
- this._x = Math.atan2( - m23, m33 );
- this._y = 0;
-
- }
-
- } else {
-
- console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order );
-
- }
-
- this._order = order;
-
- if ( update !== false ) this.onChangeCallback();
-
- return this;
-
- },
-
- setFromQuaternion: function () {
-
- var matrix;
-
- return function ( q, order, update ) {
-
- if ( matrix === undefined ) matrix = new THREE.Matrix4();
- matrix.makeRotationFromQuaternion( q );
- this.setFromRotationMatrix( matrix, order, update );
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+ this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
return this;
- };
+ },
- }(),
+ divideScalar: function ( scalar ) {
- setFromVector3: function ( v, order ) {
+ return this.multiplyScalar( 1 / scalar );
- return this.set( v.x, v.y, v.z, order || this._order );
+ },
- },
+ setAxisAngleFromQuaternion: function ( q ) {
- reorder: function () {
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
- // WARNING: this discards revolution information -bhouston
+ // q is assumed to be normalized
- var q = new THREE.Quaternion();
+ this.w = 2 * Math.acos( q.w );
- return function ( newOrder ) {
+ var s = Math.sqrt( 1 - q.w * q.w );
- q.setFromEuler( this );
- this.setFromQuaternion( q, newOrder );
+ if ( s < 0.0001 ) {
- };
+ this.x = 1;
+ this.y = 0;
+ this.z = 0;
- }(),
+ } else {
- equals: function ( euler ) {
-
- return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
-
- },
-
- fromArray: function ( array ) {
-
- this._x = array[ 0 ];
- this._y = array[ 1 ];
- this._z = array[ 2 ];
- if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
-
- this.onChangeCallback();
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- array[ offset ] = this._x;
- array[ offset + 1 ] = this._y;
- array[ offset + 2 ] = this._z;
- array[ offset + 3 ] = this._order;
-
- return array;
-
- },
-
- toVector3: function ( optionalResult ) {
-
- if ( optionalResult ) {
-
- return optionalResult.set( this._x, this._y, this._z );
-
- } else {
-
- return new THREE.Vector3( this._x, this._y, this._z );
-
- }
-
- },
-
- onChange: function ( callback ) {
-
- this.onChangeCallback = callback;
-
- return this;
-
- },
-
- onChangeCallback: function () {}
-
-};
-
-// File:src/math/Line3.js
-
-/**
- * @author bhouston / http://clara.io
- */
-
-THREE.Line3 = function ( start, end ) {
-
- this.start = ( start !== undefined ) ? start : new THREE.Vector3();
- this.end = ( end !== undefined ) ? end : new THREE.Vector3();
-
-};
-
-THREE.Line3.prototype = {
-
- constructor: THREE.Line3,
-
- set: function ( start, end ) {
-
- this.start.copy( start );
- this.end.copy( end );
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor().copy( this );
-
- },
-
- copy: function ( line ) {
-
- this.start.copy( line.start );
- this.end.copy( line.end );
-
- return this;
-
- },
-
- center: function ( optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 );
-
- },
-
- delta: function ( optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- return result.subVectors( this.end, this.start );
-
- },
-
- distanceSq: function () {
-
- return this.start.distanceToSquared( this.end );
-
- },
-
- distance: function () {
-
- return this.start.distanceTo( this.end );
-
- },
-
- at: function ( t, optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
-
- return this.delta( result ).multiplyScalar( t ).add( this.start );
-
- },
-
- closestPointToPointParameter: function () {
-
- var startP = new THREE.Vector3();
- var startEnd = new THREE.Vector3();
-
- return function ( point, clampToLine ) {
-
- startP.subVectors( point, this.start );
- startEnd.subVectors( this.end, this.start );
-
- var startEnd2 = startEnd.dot( startEnd );
- var startEnd_startP = startEnd.dot( startP );
-
- var t = startEnd_startP / startEnd2;
-
- if ( clampToLine ) {
-
- t = THREE.Math.clamp( t, 0, 1 );
+ this.x = q.x / s;
+ this.y = q.y / s;
+ this.z = q.z / s;
}
- return t;
-
- };
-
- }(),
-
- closestPointToPoint: function ( point, clampToLine, optionalTarget ) {
-
- var t = this.closestPointToPointParameter( point, clampToLine );
-
- var result = optionalTarget || new THREE.Vector3();
-
- return this.delta( result ).multiplyScalar( t ).add( this.start );
-
- },
-
- applyMatrix4: function ( matrix ) {
-
- this.start.applyMatrix4( matrix );
- this.end.applyMatrix4( matrix );
-
- return this;
-
- },
-
- equals: function ( line ) {
-
- return line.start.equals( this.start ) && line.end.equals( this.end );
-
- }
-
-};
-
-// File:src/math/Box2.js
-
-/**
- * @author bhouston / http://clara.io
- */
-
-THREE.Box2 = function ( min, max ) {
-
- this.min = ( min !== undefined ) ? min : new THREE.Vector2( + Infinity, + Infinity );
- this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity );
-
-};
-
-THREE.Box2.prototype = {
-
- constructor: THREE.Box2,
-
- set: function ( min, max ) {
-
- this.min.copy( min );
- this.max.copy( max );
-
- return this;
-
- },
-
- setFromPoints: function ( points ) {
-
- this.makeEmpty();
-
- for ( var i = 0, il = points.length; i < il; i ++ ) {
-
- this.expandByPoint( points[ i ] );
-
- }
-
- return this;
-
- },
-
- setFromCenterAndSize: function () {
-
- var v1 = new THREE.Vector2();
-
- return function ( center, size ) {
-
- var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
- this.min.copy( center ).sub( halfSize );
- this.max.copy( center ).add( halfSize );
-
return this;
- };
+ },
- }(),
+ setAxisAngleFromRotationMatrix: function ( m ) {
- clone: function () {
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
- return new this.constructor().copy( this );
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- },
+ var angle, x, y, z, // variables for result
+ epsilon = 0.01, // margin to allow for rounding errors
+ epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
- copy: function ( box ) {
+ te = m.elements,
- this.min.copy( box.min );
- this.max.copy( box.max );
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
- return this;
+ if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
+ ( Math.abs( m13 - m31 ) < epsilon ) &&
+ ( Math.abs( m23 - m32 ) < epsilon ) ) {
- },
+ // singularity found
+ // first check for identity matrix which must have +1 for all terms
+ // in leading diagonal and zero in other terms
- makeEmpty: function () {
+ if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
+ ( Math.abs( m13 + m31 ) < epsilon2 ) &&
+ ( Math.abs( m23 + m32 ) < epsilon2 ) &&
+ ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
- this.min.x = this.min.y = + Infinity;
- this.max.x = this.max.y = - Infinity;
+ // this singularity is identity matrix so angle = 0
- return this;
+ this.set( 1, 0, 0, 0 );
- },
+ return this; // zero angle, arbitrary axis
- isEmpty: function () {
+ }
- // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+ // otherwise this singularity is angle = 180
- return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
+ angle = Math.PI;
- },
+ var xx = ( m11 + 1 ) / 2;
+ var yy = ( m22 + 1 ) / 2;
+ var zz = ( m33 + 1 ) / 2;
+ var xy = ( m12 + m21 ) / 4;
+ var xz = ( m13 + m31 ) / 4;
+ var yz = ( m23 + m32 ) / 4;
- center: function ( optionalTarget ) {
+ if ( ( xx > yy ) && ( xx > zz ) ) {
- var result = optionalTarget || new THREE.Vector2();
- return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+ // m11 is the largest diagonal term
- },
+ if ( xx < epsilon ) {
- size: function ( optionalTarget ) {
+ x = 0;
+ y = 0.707106781;
+ z = 0.707106781;
- var result = optionalTarget || new THREE.Vector2();
- return result.subVectors( this.max, this.min );
+ } else {
- },
+ x = Math.sqrt( xx );
+ y = xy / x;
+ z = xz / x;
- expandByPoint: function ( point ) {
+ }
- this.min.min( point );
- this.max.max( point );
+ } else if ( yy > zz ) {
- return this;
+ // m22 is the largest diagonal term
- },
+ if ( yy < epsilon ) {
- expandByVector: function ( vector ) {
+ x = 0.707106781;
+ y = 0;
+ z = 0.707106781;
- this.min.sub( vector );
- this.max.add( vector );
+ } else {
- return this;
+ y = Math.sqrt( yy );
+ x = xy / y;
+ z = yz / y;
- },
+ }
- expandByScalar: function ( scalar ) {
+ } else {
- this.min.addScalar( - scalar );
- this.max.addScalar( scalar );
+ // m33 is the largest diagonal term so base result on this
- return this;
+ if ( zz < epsilon ) {
- },
+ x = 0.707106781;
+ y = 0.707106781;
+ z = 0;
- containsPoint: function ( point ) {
+ } else {
- if ( point.x < this.min.x || point.x > this.max.x ||
- point.y < this.min.y || point.y > this.max.y ) {
-
- return false;
-
- }
-
- return true;
-
- },
-
- containsBox: function ( box ) {
-
- if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
- ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {
-
- return true;
-
- }
-
- return false;
-
- },
-
- getParameter: function ( point, optionalTarget ) {
-
- // This can potentially have a divide by zero if the box
- // has a size dimension of 0.
-
- var result = optionalTarget || new THREE.Vector2();
-
- return result.set(
- ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
- ( point.y - this.min.y ) / ( this.max.y - this.min.y )
- );
-
- },
-
- intersectsBox: function ( box ) {
-
- // using 6 splitting planes to rule out intersections.
-
- if ( box.max.x < this.min.x || box.min.x > this.max.x ||
- box.max.y < this.min.y || box.min.y > this.max.y ) {
-
- return false;
-
- }
-
- return true;
-
- },
-
- clampPoint: function ( point, optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector2();
- return result.copy( point ).clamp( this.min, this.max );
-
- },
-
- distanceToPoint: function () {
-
- var v1 = new THREE.Vector2();
-
- return function ( point ) {
-
- var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
- return clampedPoint.sub( point ).length();
-
- };
-
- }(),
-
- intersect: function ( box ) {
-
- this.min.max( box.min );
- this.max.min( box.max );
-
- return this;
-
- },
-
- union: function ( box ) {
-
- this.min.min( box.min );
- this.max.max( box.max );
-
- return this;
-
- },
-
- translate: function ( offset ) {
-
- this.min.add( offset );
- this.max.add( offset );
-
- return this;
-
- },
-
- equals: function ( box ) {
-
- return box.min.equals( this.min ) && box.max.equals( this.max );
-
- }
-
-};
-
-// File:src/math/Box3.js
-
-/**
- * @author bhouston / http://clara.io
- * @author WestLangley / http://github.com/WestLangley
- */
-
-THREE.Box3 = function ( min, max ) {
-
- this.min = ( min !== undefined ) ? min : new THREE.Vector3( + Infinity, + Infinity, + Infinity );
- this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity );
-
-};
-
-THREE.Box3.prototype = {
-
- constructor: THREE.Box3,
-
- set: function ( min, max ) {
-
- this.min.copy( min );
- this.max.copy( max );
-
- return this;
-
- },
-
- setFromArray: function ( array ) {
-
- var minX = + Infinity;
- var minY = + Infinity;
- var minZ = + Infinity;
-
- var maxX = - Infinity;
- var maxY = - Infinity;
- var maxZ = - Infinity;
-
- for ( var i = 0, l = array.length; i < l; i += 3 ) {
-
- var x = array[ i ];
- var y = array[ i + 1 ];
- var z = array[ i + 2 ];
-
- if ( x < minX ) minX = x;
- if ( y < minY ) minY = y;
- if ( z < minZ ) minZ = z;
-
- if ( x > maxX ) maxX = x;
- if ( y > maxY ) maxY = y;
- if ( z > maxZ ) maxZ = z;
-
- }
-
- this.min.set( minX, minY, minZ );
- this.max.set( maxX, maxY, maxZ );
-
- },
-
- setFromPoints: function ( points ) {
-
- this.makeEmpty();
-
- for ( var i = 0, il = points.length; i < il; i ++ ) {
-
- this.expandByPoint( points[ i ] );
-
- }
-
- return this;
-
- },
-
- setFromCenterAndSize: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( center, size ) {
-
- var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
-
- this.min.copy( center ).sub( halfSize );
- this.max.copy( center ).add( halfSize );
-
- return this;
-
- };
-
- }(),
-
- setFromObject: function () {
-
- // Computes the world-axis-aligned bounding box of an object (including its children),
- // accounting for both the object's, and children's, world transforms
-
- var v1 = new THREE.Vector3();
-
- return function ( object ) {
-
- var scope = this;
-
- object.updateMatrixWorld( true );
-
- this.makeEmpty();
-
- object.traverse( function ( node ) {
-
- var geometry = node.geometry;
-
- if ( geometry !== undefined ) {
-
- if ( geometry instanceof THREE.Geometry ) {
-
- var vertices = geometry.vertices;
-
- for ( var i = 0, il = vertices.length; i < il; i ++ ) {
-
- v1.copy( vertices[ i ] );
- v1.applyMatrix4( node.matrixWorld );
-
- scope.expandByPoint( v1 );
-
- }
-
- } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {
-
- var positions = geometry.attributes[ 'position' ].array;
-
- for ( var i = 0, il = positions.length; i < il; i += 3 ) {
-
- v1.fromArray( positions, i );
- v1.applyMatrix4( node.matrixWorld );
-
- scope.expandByPoint( v1 );
-
- }
+ z = Math.sqrt( zz );
+ x = xz / z;
+ y = yz / z;
}
}
- } );
+ this.set( x, y, z, angle );
- return this;
-
- };
-
- }(),
-
- clone: function () {
-
- return new this.constructor().copy( this );
-
- },
-
- copy: function ( box ) {
-
- this.min.copy( box.min );
- this.max.copy( box.max );
-
- return this;
-
- },
-
- makeEmpty: function () {
-
- this.min.x = this.min.y = this.min.z = + Infinity;
- this.max.x = this.max.y = this.max.z = - Infinity;
-
- return this;
-
- },
-
- isEmpty: function () {
-
- // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
-
- return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
-
- },
-
- center: function ( optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
-
- },
-
- size: function ( optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- return result.subVectors( this.max, this.min );
-
- },
-
- expandByPoint: function ( point ) {
-
- this.min.min( point );
- this.max.max( point );
-
- return this;
-
- },
-
- expandByVector: function ( vector ) {
-
- this.min.sub( vector );
- this.max.add( vector );
-
- return this;
-
- },
-
- expandByScalar: function ( scalar ) {
-
- this.min.addScalar( - scalar );
- this.max.addScalar( scalar );
-
- return this;
-
- },
-
- containsPoint: function ( point ) {
-
- if ( point.x < this.min.x || point.x > this.max.x ||
- point.y < this.min.y || point.y > this.max.y ||
- point.z < this.min.z || point.z > this.max.z ) {
-
- return false;
-
- }
-
- return true;
-
- },
-
- containsBox: function ( box ) {
-
- if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
- ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
- ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {
-
- return true;
-
- }
-
- return false;
-
- },
-
- getParameter: function ( point, optionalTarget ) {
-
- // This can potentially have a divide by zero if the box
- // has a size dimension of 0.
-
- var result = optionalTarget || new THREE.Vector3();
-
- return result.set(
- ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
- ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
- ( point.z - this.min.z ) / ( this.max.z - this.min.z )
- );
-
- },
-
- intersectsBox: function ( box ) {
-
- // using 6 splitting planes to rule out intersections.
-
- if ( box.max.x < this.min.x || box.min.x > this.max.x ||
- box.max.y < this.min.y || box.min.y > this.max.y ||
- box.max.z < this.min.z || box.min.z > this.max.z ) {
-
- return false;
-
- }
-
- return true;
-
- },
-
- intersectsSphere: ( function () {
-
- var closestPoint;
-
- return function intersectsSphere( sphere ) {
-
- if ( closestPoint === undefined ) closestPoint = new THREE.Vector3();
-
- // Find the point on the AABB closest to the sphere center.
- this.clampPoint( sphere.center, closestPoint );
-
- // If that point is inside the sphere, the AABB and sphere intersect.
- return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
-
- };
-
- } )(),
-
- intersectsPlane: function ( plane ) {
-
- // We compute the minimum and maximum dot product values. If those values
- // are on the same side (back or front) of the plane, then there is no intersection.
-
- var min, max;
-
- if ( plane.normal.x > 0 ) {
-
- min = plane.normal.x * this.min.x;
- max = plane.normal.x * this.max.x;
-
- } else {
-
- min = plane.normal.x * this.max.x;
- max = plane.normal.x * this.min.x;
-
- }
-
- if ( plane.normal.y > 0 ) {
-
- min += plane.normal.y * this.min.y;
- max += plane.normal.y * this.max.y;
-
- } else {
-
- min += plane.normal.y * this.max.y;
- max += plane.normal.y * this.min.y;
-
- }
-
- if ( plane.normal.z > 0 ) {
-
- min += plane.normal.z * this.min.z;
- max += plane.normal.z * this.max.z;
-
- } else {
-
- min += plane.normal.z * this.max.z;
- max += plane.normal.z * this.min.z;
-
- }
-
- return ( min <= plane.constant && max >= plane.constant );
-
- },
-
- clampPoint: function ( point, optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- return result.copy( point ).clamp( this.min, this.max );
-
- },
-
- distanceToPoint: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( point ) {
-
- var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
- return clampedPoint.sub( point ).length();
-
- };
-
- }(),
-
- getBoundingSphere: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( optionalTarget ) {
-
- var result = optionalTarget || new THREE.Sphere();
-
- result.center = this.center();
- result.radius = this.size( v1 ).length() * 0.5;
-
- return result;
-
- };
-
- }(),
-
- intersect: function ( box ) {
-
- this.min.max( box.min );
- this.max.min( box.max );
-
- // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
- if( this.isEmpty() ) this.makeEmpty();
-
- return this;
-
- },
-
- union: function ( box ) {
-
- this.min.min( box.min );
- this.max.max( box.max );
-
- return this;
-
- },
-
- applyMatrix4: function () {
-
- var points = [
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3(),
- new THREE.Vector3()
- ];
-
- return function ( matrix ) {
-
- // transform of empty box is an empty box.
- if( this.isEmpty() ) return this;
-
- // NOTE: I am using a binary pattern to specify all 2^3 combinations below
- points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
- points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
- points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
- points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
- points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
- points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
- points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
- points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
-
- this.setFromPoints( points );
-
- return this;
-
- };
-
- }(),
-
- translate: function ( offset ) {
-
- this.min.add( offset );
- this.max.add( offset );
-
- return this;
-
- },
-
- equals: function ( box ) {
-
- return box.min.equals( this.min ) && box.max.equals( this.max );
-
- }
-
-};
-
-// File:src/math/Matrix3.js
-
-/**
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author bhouston / http://clara.io
- * @author tschw
- */
-
-THREE.Matrix3 = function () {
-
- this.elements = new Float32Array( [
-
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
-
- ] );
-
- if ( arguments.length > 0 ) {
-
- console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
-
- }
-
-};
-
-THREE.Matrix3.prototype = {
-
- constructor: THREE.Matrix3,
-
- set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
-
- var te = this.elements;
-
- te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
- te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
- te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
-
- return this;
-
- },
-
- identity: function () {
-
- this.set(
-
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
-
- );
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor().fromArray( this.elements );
-
- },
-
- copy: function ( m ) {
-
- var me = m.elements;
-
- this.set(
-
- me[ 0 ], me[ 3 ], me[ 6 ],
- me[ 1 ], me[ 4 ], me[ 7 ],
- me[ 2 ], me[ 5 ], me[ 8 ]
-
- );
-
- return this;
-
- },
-
- setFromMatrix4: function( m ) {
-
- var me = m.elements;
-
- this.set(
-
- me[ 0 ], me[ 4 ], me[ 8 ],
- me[ 1 ], me[ 5 ], me[ 9 ],
- me[ 2 ], me[ 6 ], me[ 10 ]
-
- );
-
- return this;
-
- },
-
- applyToVector3Array: function () {
-
- var v1;
-
- return function ( array, offset, length ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
- if ( offset === undefined ) offset = 0;
- if ( length === undefined ) length = array.length;
-
- for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
-
- v1.fromArray( array, j );
- v1.applyMatrix3( this );
- v1.toArray( array, j );
+ return this; // return 180 deg rotation
}
+ // as we have reached here there are no singularities so we can handle normally
+
+ var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
+ ( m13 - m31 ) * ( m13 - m31 ) +
+ ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+
+ if ( Math.abs( s ) < 0.001 ) s = 1;
+
+ // prevent divide by zero, should not happen if matrix is orthogonal and should be
+ // caught by singularity test above, but I've left it in just in case
+
+ this.x = ( m32 - m23 ) / s;
+ this.y = ( m13 - m31 ) / s;
+ this.z = ( m21 - m12 ) / s;
+ this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+ return this;
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+ this.w = Math.min( this.w, v.w );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+ this.w = Math.max( this.w, v.w );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min, max;
+
+ return function clampScalar( minVal, maxVal ) {
+
+ if ( min === undefined ) {
+
+ min = new Vector4();
+ max = new Vector4();
+
+ }
+
+ min.set( minVal, minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+ this.w = Math.floor( this.w );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+ this.w = Math.ceil( this.w );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+ this.w = Math.round( this.w );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+ this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+ this.w = - this.w;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+
+ },
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() || 1 );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+ this.w += ( v.w - this.w ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+ this.w = array[ offset + 3 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+ array[ offset + 3 ] = this.w;
+
return array;
- };
+ },
- }(),
+ fromBufferAttribute: function ( attribute, index, offset ) {
- applyToBuffer: function () {
+ if ( offset !== undefined ) {
- var v1;
-
- return function applyToBuffer( buffer, offset, length ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
- if ( offset === undefined ) offset = 0;
- if ( length === undefined ) length = buffer.length / buffer.itemSize;
-
- for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
-
- v1.x = buffer.getX( j );
- v1.y = buffer.getY( j );
- v1.z = buffer.getZ( j );
-
- v1.applyMatrix3( this );
-
- buffer.setXYZ( v1.x, v1.y, v1.z );
+ console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );
}
- return buffer;
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+ this.w = attribute.getW( index );
- };
-
- }(),
-
- multiplyScalar: function ( s ) {
-
- var te = this.elements;
-
- te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
- te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
- te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
-
- return this;
-
- },
-
- determinant: function () {
-
- var te = this.elements;
-
- var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
- d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
- g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
-
- return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
-
- },
-
- getInverse: function ( matrix, throwOnDegenerate ) {
-
- if ( matrix instanceof THREE.Matrix4 ) {
-
- console.error( "THREE.Matrix3.getInverse no longer takes a Matrix4 argument." );
+ return this;
}
- var me = matrix.elements,
- te = this.elements,
+ } );
- n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
- n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
- n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
+ /**
+ * @author szimek / https://github.com/szimek/
+ * @author alteredq / http://alteredqualia.com/
+ * @author Marius Kintel / https://github.com/kintel
+ */
- t11 = n33 * n22 - n32 * n23,
- t12 = n32 * n13 - n33 * n12,
- t13 = n23 * n12 - n22 * n13,
+ /*
+ In options, we can specify:
+ * Texture parameters for an auto-generated target texture
+ * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
+ */
+ function WebGLRenderTarget( width, height, options ) {
- det = n11 * t11 + n21 * t12 + n31 * t13;
+ this.uuid = _Math.generateUUID();
- if ( det === 0 ) {
+ this.width = width;
+ this.height = height;
- var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0";
+ this.scissor = new Vector4( 0, 0, width, height );
+ this.scissorTest = false;
- if ( throwOnDegenerate || false ) {
+ this.viewport = new Vector4( 0, 0, width, height );
- throw new Error( msg );
+ options = options || {};
+
+ if ( options.minFilter === undefined ) options.minFilter = LinearFilter;
+
+ this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
+
+ this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+ this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
+ this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
+
+ }
+
+ Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, {
+
+ isWebGLRenderTarget: true,
+
+ setSize: function ( width, height ) {
+
+ if ( this.width !== width || this.height !== height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.dispose();
+
+ }
+
+ this.viewport.set( 0, 0, width, height );
+ this.scissor.set( 0, 0, width, height );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.width = source.width;
+ this.height = source.height;
+
+ this.viewport.copy( source.viewport );
+
+ this.texture = source.texture.clone();
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+ this.depthTexture = source.depthTexture;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+ } );
+
+ /**
+ * @author alteredq / http://alteredqualia.com
+ */
+
+ function WebGLRenderTargetCube( width, height, options ) {
+
+ WebGLRenderTarget.call( this, width, height, options );
+
+ this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5
+ this.activeMipMapLevel = 0;
+
+ }
+
+ WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );
+ WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;
+
+ WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
+
+ /**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://clara.io
+ */
+
+ function Quaternion( x, y, z, w ) {
+
+ this._x = x || 0;
+ this._y = y || 0;
+ this._z = z || 0;
+ this._w = ( w !== undefined ) ? w : 1;
+
+ }
+
+ Object.assign( Quaternion, {
+
+ slerp: function ( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+ },
+
+ slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
+
+ // fuzz-free, array-based Quaternion SLERP operation
+
+ var x0 = src0[ srcOffset0 + 0 ],
+ y0 = src0[ srcOffset0 + 1 ],
+ z0 = src0[ srcOffset0 + 2 ],
+ w0 = src0[ srcOffset0 + 3 ],
+
+ x1 = src1[ srcOffset1 + 0 ],
+ y1 = src1[ srcOffset1 + 1 ],
+ z1 = src1[ srcOffset1 + 2 ],
+ w1 = src1[ srcOffset1 + 3 ];
+
+ if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
+
+ var s = 1 - t,
+
+ cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
+
+ dir = ( cos >= 0 ? 1 : - 1 ),
+ sqrSin = 1 - cos * cos;
+
+ // Skip the Slerp for tiny steps to avoid numeric problems:
+ if ( sqrSin > Number.EPSILON ) {
+
+ var sin = Math.sqrt( sqrSin ),
+ len = Math.atan2( sin, cos * dir );
+
+ s = Math.sin( s * len ) / sin;
+ t = Math.sin( t * len ) / sin;
+
+ }
+
+ var tDir = t * dir;
+
+ x0 = x0 * s + x1 * tDir;
+ y0 = y0 * s + y1 * tDir;
+ z0 = z0 * s + z1 * tDir;
+ w0 = w0 * s + w1 * tDir;
+
+ // Normalize in case we just did a lerp:
+ if ( s === 1 - t ) {
+
+ var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
+
+ x0 *= f;
+ y0 *= f;
+ z0 *= f;
+ w0 *= f;
+
+ }
+
+ }
+
+ dst[ dstOffset ] = x0;
+ dst[ dstOffset + 1 ] = y0;
+ dst[ dstOffset + 2 ] = z0;
+ dst[ dstOffset + 3 ] = w0;
+
+ }
+
+ } );
+
+ Object.defineProperties( Quaternion.prototype, {
+
+ x: {
+
+ get: function () {
+
+ return this._x;
+
+ },
+
+ set: function ( value ) {
+
+ this._x = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ y: {
+
+ get: function () {
+
+ return this._y;
+
+ },
+
+ set: function ( value ) {
+
+ this._y = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ z: {
+
+ get: function () {
+
+ return this._z;
+
+ },
+
+ set: function ( value ) {
+
+ this._z = value;
+ this.onChangeCallback();
+
+ }
+
+ },
+
+ w: {
+
+ get: function () {
+
+ return this._w;
+
+ },
+
+ set: function ( value ) {
+
+ this._w = value;
+ this.onChangeCallback();
+
+ }
+
+ }
+
+ } );
+
+ Object.assign( Quaternion.prototype, {
+
+ set: function ( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this._x, this._y, this._z, this._w );
+
+ },
+
+ copy: function ( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromEuler: function ( euler, update ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ var x = euler._x, y = euler._y, z = euler._z, order = euler.order;
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ var cos = Math.cos;
+ var sin = Math.sin;
+
+ var c1 = cos( x / 2 );
+ var c2 = cos( y / 2 );
+ var c3 = cos( z / 2 );
+
+ var s1 = sin( x / 2 );
+ var s2 = sin( y / 2 );
+ var s3 = sin( z / 2 );
+
+ if ( order === 'XYZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'YXZ' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'ZXY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'ZYX' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ } else if ( order === 'YZX' ) {
+
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+
+ } else if ( order === 'XZY' ) {
+
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+
+ }
+
+ if ( update !== false ) this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromAxisAngle: function ( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ var halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromRotationMatrix: function ( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ var te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33,
+ s;
+
+ if ( trace > 0 ) {
+
+ s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
} else {
- console.warn( msg );
+ s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
}
- return this.identity();
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ setFromUnitVectors: function () {
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ var v1 = new Vector3();
+ var r;
+
+ var EPS = 0.000001;
+
+ return function setFromUnitVectors( vFrom, vTo ) {
+
+ if ( v1 === undefined ) v1 = new Vector3();
+
+ r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ v1.set( - vFrom.y, vFrom.x, 0 );
+
+ } else {
+
+ v1.set( 0, - vFrom.z, vFrom.y );
+
+ }
+
+ } else {
+
+ v1.crossVectors( vFrom, vTo );
+
+ }
+
+ this._x = v1.x;
+ this._y = v1.y;
+ this._z = v1.z;
+ this._w = r;
+
+ return this.normalize();
+
+ };
+
+ }(),
+
+ inverse: function () {
+
+ return this.conjugate().normalize();
+
+ },
+
+ conjugate: function () {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ },
+
+ lengthSq: function () {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ },
+
+ normalize: function () {
+
+ var l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ multiply: function ( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ },
+
+ premultiply: function ( q ) {
+
+ return this.multiplyQuaternions( q, this );
+
+ },
+
+ multiplyQuaternions: function ( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ slerp: function ( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ var x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
+
+ if ( Math.abs( sinHalfTheta ) < 0.001 ) {
+
+ this._w = 0.5 * ( w + this._w );
+ this._x = 0.5 * ( x + this._x );
+ this._y = 0.5 * ( y + this._y );
+ this._z = 0.5 * ( z + this._z );
+
+ return this;
+
+ }
+
+ var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
+ var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ equals: function ( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this.onChangeCallback();
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ },
+
+ onChange: function ( callback ) {
+
+ this.onChangeCallback = callback;
+
+ return this;
+
+ },
+
+ onChangeCallback: function () {}
+
+ } );
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author kile / http://kile.stravaganza.org/
+ * @author philogb / http://blog.thejit.org/
+ * @author mikael emtinger / http://gomo.se/
+ * @author egraether / http://egraether.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+ function Vector3( x, y, z ) {
+
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+
+ }
+
+ Object.assign( Vector3.prototype, {
+
+ isVector3: true,
+
+ set: function ( x, y, z ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ },
+
+ setScalar: function ( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+
+ return this;
+
+ },
+
+ setX: function ( x ) {
+
+ this.x = x;
+
+ return this;
+
+ },
+
+ setY: function ( y ) {
+
+ this.y = y;
+
+ return this;
+
+ },
+
+ setZ: function ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ },
+
+ setComponent: function ( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ },
+
+ getComponent: function ( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.x, this.y, this.z );
+
+ },
+
+ copy: function ( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ },
+
+ add: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ },
+
+ addVectors: function ( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ },
+
+ addScaledVector: function ( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+
+ return this;
+
+ },
+
+ sub: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ },
+
+ subScalar: function ( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+
+ return this;
+
+ },
+
+ subVectors: function ( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ },
+
+ multiply: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+
+ return this;
+
+ },
+
+ multiplyVectors: function ( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ },
+
+ applyEuler: function () {
+
+ var quaternion = new Quaternion();
+
+ return function applyEuler( euler ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ return this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+ };
+
+ }(),
+
+ applyAxisAngle: function () {
+
+ var quaternion = new Quaternion();
+
+ return function applyAxisAngle( axis, angle ) {
+
+ return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+ };
+
+ }(),
+
+ applyMatrix3: function ( m ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
+
+ return this;
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ var x = this.x, y = this.y, z = this.z;
+ var qx = q.x, qy = q.y, qz = q.z, qw = q.w;
+
+ // calculate quat * vector
+
+ var ix = qw * x + qy * z - qz * y;
+ var iy = qw * y + qz * x - qx * z;
+ var iz = qw * z + qx * y - qy * x;
+ var iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ },
+
+ project: function () {
+
+ var matrix = new Matrix4();
+
+ return function project( camera ) {
+
+ matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
+ return this.applyMatrix4( matrix );
+
+ };
+
+ }(),
+
+ unproject: function () {
+
+ var matrix = new Matrix4();
+
+ return function unproject( camera ) {
+
+ matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
+ return this.applyMatrix4( matrix );
+
+ };
+
+ }(),
+
+ transformDirection: function ( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ var x = this.x, y = this.y, z = this.z;
+ var e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ return this.normalize();
+
+ },
+
+ divide: function ( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ },
+
+ divideScalar: function ( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ },
+
+ min: function ( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+
+ return this;
+
+ },
+
+ max: function ( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
+
+ },
+
+ clamp: function ( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+
+ return this;
+
+ },
+
+ clampScalar: function () {
+
+ var min = new Vector3();
+ var max = new Vector3();
+
+ return function clampScalar( minVal, maxVal ) {
+
+ min.set( minVal, minVal, minVal );
+ max.set( maxVal, maxVal, maxVal );
+
+ return this.clamp( min, max );
+
+ };
+
+ }(),
+
+ clampLength: function ( min, max ) {
+
+ var length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ },
+
+ floor: function () {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ },
+
+ ceil: function () {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ },
+
+ round: function () {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ },
+
+ roundToZero: function () {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ },
+
+ negate: function () {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ },
+
+ dot: function ( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ },
+
+ // TODO lengthSquared?
+
+ lengthSq: function () {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z;
+
+ },
+
+ length: function () {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
+
+ },
+
+ lengthManhattan: function () {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ },
+
+ normalize: function () {
+
+ return this.divideScalar( this.length() || 1 );
+
+ },
+
+ setLength: function ( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ },
+
+ lerp: function ( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ },
+
+ lerpVectors: function ( v1, v2, alpha ) {
+
+ return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );
+
+ },
+
+ cross: function ( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ var x = this.x, y = this.y, z = this.z;
+
+ this.x = y * v.z - z * v.y;
+ this.y = z * v.x - x * v.z;
+ this.z = x * v.y - y * v.x;
+
+ return this;
+
+ },
+
+ crossVectors: function ( a, b ) {
+
+ var ax = a.x, ay = a.y, az = a.z;
+ var bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ },
+
+ projectOnVector: function ( vector ) {
+
+ var scalar = vector.dot( this ) / vector.lengthSq();
+
+ return this.copy( vector ).multiplyScalar( scalar );
+
+ },
+
+ projectOnPlane: function () {
+
+ var v1 = new Vector3();
+
+ return function projectOnPlane( planeNormal ) {
+
+ v1.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( v1 );
+
+ };
+
+ }(),
+
+ reflect: function () {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ var v1 = new Vector3();
+
+ return function reflect( normal ) {
+
+ return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ };
+
+ }(),
+
+ angleTo: function ( v ) {
+
+ var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( _Math.clamp( theta, - 1, 1 ) );
+
+ },
+
+ distanceTo: function ( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ },
+
+ distanceToSquared: function ( v ) {
+
+ var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ },
+
+ distanceToManhattan: function ( v ) {
+
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
+
+ },
+
+ setFromSpherical: function ( s ) {
+
+ var sinPhiRadius = Math.sin( s.phi ) * s.radius;
+
+ this.x = sinPhiRadius * Math.sin( s.theta );
+ this.y = Math.cos( s.phi ) * s.radius;
+ this.z = sinPhiRadius * Math.cos( s.theta );
+
+ return this;
+
+ },
+
+ setFromCylindrical: function ( c ) {
+
+ this.x = c.radius * Math.sin( c.theta );
+ this.y = c.y;
+ this.z = c.radius * Math.cos( c.theta );
+
+ return this;
+
+ },
+
+ setFromMatrixPosition: function ( m ) {
+
+ var e = m.elements;
+
+ this.x = e[ 12 ];
+ this.y = e[ 13 ];
+ this.z = e[ 14 ];
+
+ return this;
+
+ },
+
+ setFromMatrixScale: function ( m ) {
+
+ var sx = this.setFromMatrixColumn( m, 0 ).length();
+ var sy = this.setFromMatrixColumn( m, 1 ).length();
+ var sz = this.setFromMatrixColumn( m, 2 ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+
+ },
+
+ setFromMatrixColumn: function ( m, index ) {
+
+ return this.fromArray( m.elements, index * 4 );
+
+ },
+
+ equals: function ( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ },
+
+ fromBufferAttribute: function ( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+
+ return this;
+
}
- te[ 0 ] = t11;
- te[ 1 ] = n31 * n23 - n33 * n21;
- te[ 2 ] = n32 * n21 - n31 * n22;
+ } );
- te[ 3 ] = t12;
- te[ 4 ] = n33 * n11 - n31 * n13;
- te[ 5 ] = n31 * n12 - n32 * n11;
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author philogb / http://blog.thejit.org/
+ * @author jordi_ros / http://plattsoft.com
+ * @author D1plo1d / http://github.com/D1plo1d
+ * @author alteredq / http://alteredqualia.com/
+ * @author mikael emtinger / http://gomo.se/
+ * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://clara.io
+ * @author WestLangley / http://github.com/WestLangley
+ */
- te[ 6 ] = t13;
- te[ 7 ] = n21 * n13 - n23 * n11;
- te[ 8 ] = n22 * n11 - n21 * n12;
+ function Matrix4() {
- return this.multiplyScalar( 1 / det );
-
- },
-
- transpose: function () {
-
- var tmp, m = this.elements;
-
- tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
- tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
- tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
-
- return this;
-
- },
-
- flattenToArrayOffset: function ( array, offset ) {
-
- console.warn( "THREE.Matrix3: .flattenToArrayOffset is deprecated " +
- "- just use .toArray instead." );
-
- return this.toArray( array, offset );
-
- },
-
- getNormalMatrix: function ( matrix4 ) {
-
- return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
-
- },
-
- transposeIntoArray: function ( r ) {
-
- var m = this.elements;
-
- r[ 0 ] = m[ 0 ];
- r[ 1 ] = m[ 3 ];
- r[ 2 ] = m[ 6 ];
- r[ 3 ] = m[ 1 ];
- r[ 4 ] = m[ 4 ];
- r[ 5 ] = m[ 7 ];
- r[ 6 ] = m[ 2 ];
- r[ 7 ] = m[ 5 ];
- r[ 8 ] = m[ 8 ];
-
- return this;
-
- },
-
- fromArray: function ( array ) {
-
- this.elements.set( array );
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- var te = this.elements;
-
- array[ offset ] = te[ 0 ];
- array[ offset + 1 ] = te[ 1 ];
- array[ offset + 2 ] = te[ 2 ];
-
- array[ offset + 3 ] = te[ 3 ];
- array[ offset + 4 ] = te[ 4 ];
- array[ offset + 5 ] = te[ 5 ];
-
- array[ offset + 6 ] = te[ 6 ];
- array[ offset + 7 ] = te[ 7 ];
- array[ offset + 8 ] = te[ 8 ];
-
- return array;
-
- }
-
-};
-
-// File:src/math/Matrix4.js
-
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author supereggbert / http://www.paulbrunt.co.uk/
- * @author philogb / http://blog.thejit.org/
- * @author jordi_ros / http://plattsoft.com
- * @author D1plo1d / http://github.com/D1plo1d
- * @author alteredq / http://alteredqualia.com/
- * @author mikael emtinger / http://gomo.se/
- * @author timknip / http://www.floorplanner.com/
- * @author bhouston / http://clara.io
- * @author WestLangley / http://github.com/WestLangley
- */
-
-THREE.Matrix4 = function () {
-
- this.elements = new Float32Array( [
-
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
-
- ] );
-
- if ( arguments.length > 0 ) {
-
- console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
-
- }
-
-};
-
-THREE.Matrix4.prototype = {
-
- constructor: THREE.Matrix4,
-
- set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
-
- var te = this.elements;
-
- te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
- te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
- te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
- te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
-
- return this;
-
- },
-
- identity: function () {
-
- this.set(
+ this.elements = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
- );
+ ];
- return this;
+ if ( arguments.length > 0 ) {
- },
+ console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
- clone: function () {
+ }
- return new THREE.Matrix4().fromArray( this.elements );
+ }
- },
+ Object.assign( Matrix4.prototype, {
- copy: function ( m ) {
+ isMatrix4: true,
- this.elements.set( m.elements );
+ set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
- return this;
+ var te = this.elements;
- },
+ te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+ te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+ te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+ te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
- copyPosition: function ( m ) {
+ return this;
- var te = this.elements;
- var me = m.elements;
+ },
- te[ 12 ] = me[ 12 ];
- te[ 13 ] = me[ 13 ];
- te[ 14 ] = me[ 14 ];
+ identity: function () {
- return this;
+ this.set(
- },
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
- extractBasis: function ( xAxis, yAxis, zAxis ) {
+ );
- xAxis.setFromMatrixColumn( this, 0 );
- yAxis.setFromMatrixColumn( this, 1 );
- zAxis.setFromMatrixColumn( this, 2 );
+ return this;
- return this;
+ },
- },
+ clone: function () {
- makeBasis: function ( xAxis, yAxis, zAxis ) {
+ return new Matrix4().fromArray( this.elements );
- this.set(
- xAxis.x, yAxis.x, zAxis.x, 0,
- xAxis.y, yAxis.y, zAxis.y, 0,
- xAxis.z, yAxis.z, zAxis.z, 0,
- 0, 0, 0, 1
- );
+ },
- return this;
-
- },
-
- extractRotation: function () {
-
- var v1;
-
- return function ( m ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
+ copy: function ( m ) {
var te = this.elements;
var me = m.elements;
- var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
- var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
- var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
-
- te[ 0 ] = me[ 0 ] * scaleX;
- te[ 1 ] = me[ 1 ] * scaleX;
- te[ 2 ] = me[ 2 ] * scaleX;
-
- te[ 4 ] = me[ 4 ] * scaleY;
- te[ 5 ] = me[ 5 ] * scaleY;
- te[ 6 ] = me[ 6 ] * scaleY;
-
- te[ 8 ] = me[ 8 ] * scaleZ;
- te[ 9 ] = me[ 9 ] * scaleZ;
- te[ 10 ] = me[ 10 ] * scaleZ;
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
+ te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
+ te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
+ te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
return this;
- };
+ },
- }(),
+ copyPosition: function ( m ) {
- makeRotationFromEuler: function ( euler ) {
+ var te = this.elements, me = m.elements;
- if ( euler instanceof THREE.Euler === false ) {
+ te[ 12 ] = me[ 12 ];
+ te[ 13 ] = me[ 13 ];
+ te[ 14 ] = me[ 14 ];
- console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+ return this;
- }
+ },
- var te = this.elements;
+ extractBasis: function ( xAxis, yAxis, zAxis ) {
- var x = euler.x, y = euler.y, z = euler.z;
- var a = Math.cos( x ), b = Math.sin( x );
- var c = Math.cos( y ), d = Math.sin( y );
- var e = Math.cos( z ), f = Math.sin( z );
+ xAxis.setFromMatrixColumn( this, 0 );
+ yAxis.setFromMatrixColumn( this, 1 );
+ zAxis.setFromMatrixColumn( this, 2 );
- if ( euler.order === 'XYZ' ) {
+ return this;
- var ae = a * e, af = a * f, be = b * e, bf = b * f;
+ },
- te[ 0 ] = c * e;
- te[ 4 ] = - c * f;
- te[ 8 ] = d;
+ makeBasis: function ( xAxis, yAxis, zAxis ) {
- te[ 1 ] = af + be * d;
- te[ 5 ] = ae - bf * d;
- te[ 9 ] = - b * c;
+ this.set(
+ xAxis.x, yAxis.x, zAxis.x, 0,
+ xAxis.y, yAxis.y, zAxis.y, 0,
+ xAxis.z, yAxis.z, zAxis.z, 0,
+ 0, 0, 0, 1
+ );
- te[ 2 ] = bf - ae * d;
- te[ 6 ] = be + af * d;
- te[ 10 ] = a * c;
+ return this;
- } else if ( euler.order === 'YXZ' ) {
+ },
- var ce = c * e, cf = c * f, de = d * e, df = d * f;
+ extractRotation: function () {
- te[ 0 ] = ce + df * b;
- te[ 4 ] = de * b - cf;
- te[ 8 ] = a * d;
+ var v1 = new Vector3();
- te[ 1 ] = a * f;
- te[ 5 ] = a * e;
- te[ 9 ] = - b;
+ return function extractRotation( m ) {
- te[ 2 ] = cf * b - de;
- te[ 6 ] = df + ce * b;
- te[ 10 ] = a * c;
+ var te = this.elements;
+ var me = m.elements;
- } else if ( euler.order === 'ZXY' ) {
+ var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();
+ var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();
+ var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();
- var ce = c * e, cf = c * f, de = d * e, df = d * f;
+ te[ 0 ] = me[ 0 ] * scaleX;
+ te[ 1 ] = me[ 1 ] * scaleX;
+ te[ 2 ] = me[ 2 ] * scaleX;
- te[ 0 ] = ce - df * b;
- te[ 4 ] = - a * f;
- te[ 8 ] = de + cf * b;
+ te[ 4 ] = me[ 4 ] * scaleY;
+ te[ 5 ] = me[ 5 ] * scaleY;
+ te[ 6 ] = me[ 6 ] * scaleY;
- te[ 1 ] = cf + de * b;
- te[ 5 ] = a * e;
- te[ 9 ] = df - ce * b;
+ te[ 8 ] = me[ 8 ] * scaleZ;
+ te[ 9 ] = me[ 9 ] * scaleZ;
+ te[ 10 ] = me[ 10 ] * scaleZ;
- te[ 2 ] = - a * d;
- te[ 6 ] = b;
- te[ 10 ] = a * c;
+ return this;
- } else if ( euler.order === 'ZYX' ) {
+ };
- var ae = a * e, af = a * f, be = b * e, bf = b * f;
+ }(),
- te[ 0 ] = c * e;
- te[ 4 ] = be * d - af;
- te[ 8 ] = ae * d + bf;
+ makeRotationFromEuler: function ( euler ) {
- te[ 1 ] = c * f;
- te[ 5 ] = bf * d + ae;
- te[ 9 ] = af * d - be;
+ if ( ! ( euler && euler.isEuler ) ) {
- te[ 2 ] = - d;
- te[ 6 ] = b * c;
- te[ 10 ] = a * c;
+ console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
- } else if ( euler.order === 'YZX' ) {
-
- var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
-
- te[ 0 ] = c * e;
- te[ 4 ] = bd - ac * f;
- te[ 8 ] = bc * f + ad;
-
- te[ 1 ] = f;
- te[ 5 ] = a * e;
- te[ 9 ] = - b * e;
-
- te[ 2 ] = - d * e;
- te[ 6 ] = ad * f + bc;
- te[ 10 ] = ac - bd * f;
-
- } else if ( euler.order === 'XZY' ) {
-
- var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
-
- te[ 0 ] = c * e;
- te[ 4 ] = - f;
- te[ 8 ] = d * e;
-
- te[ 1 ] = ac * f + bd;
- te[ 5 ] = a * e;
- te[ 9 ] = ad * f - bc;
-
- te[ 2 ] = bc * f - ad;
- te[ 6 ] = b * e;
- te[ 10 ] = bd * f + ac;
-
- }
-
- // last column
- te[ 3 ] = 0;
- te[ 7 ] = 0;
- te[ 11 ] = 0;
-
- // bottom row
- te[ 12 ] = 0;
- te[ 13 ] = 0;
- te[ 14 ] = 0;
- te[ 15 ] = 1;
-
- return this;
-
- },
-
- makeRotationFromQuaternion: function ( q ) {
-
- var te = this.elements;
-
- var x = q.x, y = q.y, z = q.z, w = q.w;
- var x2 = x + x, y2 = y + y, z2 = z + z;
- var xx = x * x2, xy = x * y2, xz = x * z2;
- var yy = y * y2, yz = y * z2, zz = z * z2;
- var wx = w * x2, wy = w * y2, wz = w * z2;
-
- te[ 0 ] = 1 - ( yy + zz );
- te[ 4 ] = xy - wz;
- te[ 8 ] = xz + wy;
-
- te[ 1 ] = xy + wz;
- te[ 5 ] = 1 - ( xx + zz );
- te[ 9 ] = yz - wx;
-
- te[ 2 ] = xz - wy;
- te[ 6 ] = yz + wx;
- te[ 10 ] = 1 - ( xx + yy );
-
- // last column
- te[ 3 ] = 0;
- te[ 7 ] = 0;
- te[ 11 ] = 0;
-
- // bottom row
- te[ 12 ] = 0;
- te[ 13 ] = 0;
- te[ 14 ] = 0;
- te[ 15 ] = 1;
-
- return this;
-
- },
-
- lookAt: function () {
-
- var x, y, z;
-
- return function ( eye, target, up ) {
-
- if ( x === undefined ) x = new THREE.Vector3();
- if ( y === undefined ) y = new THREE.Vector3();
- if ( z === undefined ) z = new THREE.Vector3();
+ }
var te = this.elements;
- z.subVectors( eye, target ).normalize();
+ var x = euler.x, y = euler.y, z = euler.z;
+ var a = Math.cos( x ), b = Math.sin( x );
+ var c = Math.cos( y ), d = Math.sin( y );
+ var e = Math.cos( z ), f = Math.sin( z );
- if ( z.lengthSq() === 0 ) {
+ if ( euler.order === 'XYZ' ) {
- z.z = 1;
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - c * f;
+ te[ 8 ] = d;
+
+ te[ 1 ] = af + be * d;
+ te[ 5 ] = ae - bf * d;
+ te[ 9 ] = - b * c;
+
+ te[ 2 ] = bf - ae * d;
+ te[ 6 ] = be + af * d;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YXZ' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce + df * b;
+ te[ 4 ] = de * b - cf;
+ te[ 8 ] = a * d;
+
+ te[ 1 ] = a * f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b;
+
+ te[ 2 ] = cf * b - de;
+ te[ 6 ] = df + ce * b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZXY' ) {
+
+ var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce - df * b;
+ te[ 4 ] = - a * f;
+ te[ 8 ] = de + cf * b;
+
+ te[ 1 ] = cf + de * b;
+ te[ 5 ] = a * e;
+ te[ 9 ] = df - ce * b;
+
+ te[ 2 ] = - a * d;
+ te[ 6 ] = b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZYX' ) {
+
+ var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = be * d - af;
+ te[ 8 ] = ae * d + bf;
+
+ te[ 1 ] = c * f;
+ te[ 5 ] = bf * d + ae;
+ te[ 9 ] = af * d - be;
+
+ te[ 2 ] = - d;
+ te[ 6 ] = b * c;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YZX' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = bd - ac * f;
+ te[ 8 ] = bc * f + ad;
+
+ te[ 1 ] = f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b * e;
+
+ te[ 2 ] = - d * e;
+ te[ 6 ] = ad * f + bc;
+ te[ 10 ] = ac - bd * f;
+
+ } else if ( euler.order === 'XZY' ) {
+
+ var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - f;
+ te[ 8 ] = d * e;
+
+ te[ 1 ] = ac * f + bd;
+ te[ 5 ] = a * e;
+ te[ 9 ] = ad * f - bc;
+
+ te[ 2 ] = bc * f - ad;
+ te[ 6 ] = b * e;
+ te[ 10 ] = bd * f + ac;
}
- x.crossVectors( up, z ).normalize();
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
- if ( x.lengthSq() === 0 ) {
-
- z.x += 0.0001;
- x.crossVectors( up, z ).normalize();
-
- }
-
- y.crossVectors( z, x );
-
-
- te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
- te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
- te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
return this;
- };
+ },
- }(),
-
- multiply: function ( m, n ) {
-
- if ( n !== undefined ) {
-
- console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
- return this.multiplyMatrices( m, n );
-
- }
-
- return this.multiplyMatrices( this, m );
-
- },
-
- premultiply: function ( m ) {
-
- return this.multiplyMatrices( m, this );
-
- },
-
- multiplyMatrices: function ( a, b ) {
-
- var ae = a.elements;
- var be = b.elements;
- var te = this.elements;
-
- var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
- var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
- var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
- var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
-
- var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
- var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
- var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
- var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
-
- te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
- te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
- te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
- te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
-
- te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
- te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
- te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
- te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
-
- te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
- te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
- te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
- te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
-
- te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
- te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
- te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
- te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
-
- return this;
-
- },
-
- multiplyToArray: function ( a, b, r ) {
-
- var te = this.elements;
-
- this.multiplyMatrices( a, b );
-
- r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];
- r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];
- r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];
- r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];
-
- return this;
-
- },
-
- multiplyScalar: function ( s ) {
-
- var te = this.elements;
-
- te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
- te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
- te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
- te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
-
- return this;
-
- },
-
- applyToVector3Array: function () {
-
- var v1;
-
- return function ( array, offset, length ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
- if ( offset === undefined ) offset = 0;
- if ( length === undefined ) length = array.length;
-
- for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {
-
- v1.fromArray( array, j );
- v1.applyMatrix4( this );
- v1.toArray( array, j );
-
- }
-
- return array;
-
- };
-
- }(),
-
- applyToBuffer: function () {
-
- var v1;
-
- return function applyToBuffer( buffer, offset, length ) {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
- if ( offset === undefined ) offset = 0;
- if ( length === undefined ) length = buffer.length / buffer.itemSize;
-
- for ( var i = 0, j = offset; i < length; i ++, j ++ ) {
-
- v1.x = buffer.getX( j );
- v1.y = buffer.getY( j );
- v1.z = buffer.getZ( j );
-
- v1.applyMatrix4( this );
-
- buffer.setXYZ( v1.x, v1.y, v1.z );
-
- }
-
- return buffer;
-
- };
-
- }(),
-
- determinant: function () {
-
- var te = this.elements;
-
- var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
- var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
- var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
- var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
-
- //TODO: make this more efficient
- //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
-
- return (
- n41 * (
- + n14 * n23 * n32
- - n13 * n24 * n32
- - n14 * n22 * n33
- + n12 * n24 * n33
- + n13 * n22 * n34
- - n12 * n23 * n34
- ) +
- n42 * (
- + n11 * n23 * n34
- - n11 * n24 * n33
- + n14 * n21 * n33
- - n13 * n21 * n34
- + n13 * n24 * n31
- - n14 * n23 * n31
- ) +
- n43 * (
- + n11 * n24 * n32
- - n11 * n22 * n34
- - n14 * n21 * n32
- + n12 * n21 * n34
- + n14 * n22 * n31
- - n12 * n24 * n31
- ) +
- n44 * (
- - n13 * n22 * n31
- - n11 * n23 * n32
- + n11 * n22 * n33
- + n13 * n21 * n32
- - n12 * n21 * n33
- + n12 * n23 * n31
- )
-
- );
-
- },
-
- transpose: function () {
-
- var te = this.elements;
- var tmp;
-
- tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
- tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
- tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
-
- tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
- tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
- tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
-
- return this;
-
- },
-
- flattenToArrayOffset: function ( array, offset ) {
-
- console.warn( "THREE.Matrix3: .flattenToArrayOffset is deprecated " +
- "- just use .toArray instead." );
-
- return this.toArray( array, offset );
-
- },
-
- getPosition: function () {
-
- var v1;
-
- return function () {
-
- if ( v1 === undefined ) v1 = new THREE.Vector3();
- console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
-
- return v1.setFromMatrixColumn( this, 3 );
-
- };
-
- }(),
-
- setPosition: function ( v ) {
-
- var te = this.elements;
-
- te[ 12 ] = v.x;
- te[ 13 ] = v.y;
- te[ 14 ] = v.z;
-
- return this;
-
- },
-
- getInverse: function ( m, throwOnDegenerate ) {
-
- // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
- var te = this.elements,
- me = m.elements,
-
- n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
- n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
- n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
- n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
-
- t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
- t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
- t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
- t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
-
- var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
-
- if ( det === 0 ) {
-
- var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";
-
- if ( throwOnDegenerate || false ) {
-
- throw new Error( msg );
-
- } else {
-
- console.warn( msg );
-
- }
-
- return this.identity();
-
- }
-
- te[ 0 ] = t11;
- te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44;
- te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44;
- te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43;
-
- te[ 4 ] = t12;
- te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44;
- te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44;
- te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43;
-
- te[ 8 ] = t13;
- te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44;
- te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44;
- te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43;
-
- te[ 12 ] = t14;
- te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34;
- te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34;
- te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;
-
- return this.multiplyScalar( 1 / det );
-
- },
-
- scale: function ( v ) {
-
- var te = this.elements;
- var x = v.x, y = v.y, z = v.z;
-
- te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
- te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
- te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
- te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
-
- return this;
-
- },
-
- getMaxScaleOnAxis: function () {
-
- var te = this.elements;
-
- var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
- var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
- var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
-
- return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
-
- },
-
- makeTranslation: function ( x, y, z ) {
-
- this.set(
-
- 1, 0, 0, x,
- 0, 1, 0, y,
- 0, 0, 1, z,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- makeRotationX: function ( theta ) {
-
- var c = Math.cos( theta ), s = Math.sin( theta );
-
- this.set(
-
- 1, 0, 0, 0,
- 0, c, - s, 0,
- 0, s, c, 0,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- makeRotationY: function ( theta ) {
-
- var c = Math.cos( theta ), s = Math.sin( theta );
-
- this.set(
-
- c, 0, s, 0,
- 0, 1, 0, 0,
- - s, 0, c, 0,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- makeRotationZ: function ( theta ) {
-
- var c = Math.cos( theta ), s = Math.sin( theta );
-
- this.set(
-
- c, - s, 0, 0,
- s, c, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- makeRotationAxis: function ( axis, angle ) {
-
- // Based on http://www.gamedev.net/reference/articles/article1199.asp
-
- var c = Math.cos( angle );
- var s = Math.sin( angle );
- var t = 1 - c;
- var x = axis.x, y = axis.y, z = axis.z;
- var tx = t * x, ty = t * y;
-
- this.set(
-
- tx * x + c, tx * y - s * z, tx * z + s * y, 0,
- tx * y + s * z, ty * y + c, ty * z - s * x, 0,
- tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- makeScale: function ( x, y, z ) {
-
- this.set(
-
- x, 0, 0, 0,
- 0, y, 0, 0,
- 0, 0, z, 0,
- 0, 0, 0, 1
-
- );
-
- return this;
-
- },
-
- compose: function ( position, quaternion, scale ) {
-
- this.makeRotationFromQuaternion( quaternion );
- this.scale( scale );
- this.setPosition( position );
-
- return this;
-
- },
-
- decompose: function () {
-
- var vector, matrix;
-
- return function ( position, quaternion, scale ) {
-
- if ( vector === undefined ) vector = new THREE.Vector3();
- if ( matrix === undefined ) matrix = new THREE.Matrix4();
+ makeRotationFromQuaternion: function ( q ) {
var te = this.elements;
- var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
- var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
- var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+ var x = q._x, y = q._y, z = q._z, w = q._w;
+ var x2 = x + x, y2 = y + y, z2 = z + z;
+ var xx = x * x2, xy = x * y2, xz = x * z2;
+ var yy = y * y2, yz = y * z2, zz = z * z2;
+ var wx = w * x2, wy = w * y2, wz = w * z2;
- // if determine is negative, we need to invert one scale
- var det = this.determinant();
- if ( det < 0 ) {
+ te[ 0 ] = 1 - ( yy + zz );
+ te[ 4 ] = xy - wz;
+ te[ 8 ] = xz + wy;
- sx = - sx;
+ te[ 1 ] = xy + wz;
+ te[ 5 ] = 1 - ( xx + zz );
+ te[ 9 ] = yz - wx;
- }
+ te[ 2 ] = xz - wy;
+ te[ 6 ] = yz + wx;
+ te[ 10 ] = 1 - ( xx + yy );
- position.x = te[ 12 ];
- position.y = te[ 13 ];
- position.z = te[ 14 ];
+ // last column
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
- // scale the rotation part
-
- matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy()
-
- var invSX = 1 / sx;
- var invSY = 1 / sy;
- var invSZ = 1 / sz;
-
- matrix.elements[ 0 ] *= invSX;
- matrix.elements[ 1 ] *= invSX;
- matrix.elements[ 2 ] *= invSX;
-
- matrix.elements[ 4 ] *= invSY;
- matrix.elements[ 5 ] *= invSY;
- matrix.elements[ 6 ] *= invSY;
-
- matrix.elements[ 8 ] *= invSZ;
- matrix.elements[ 9 ] *= invSZ;
- matrix.elements[ 10 ] *= invSZ;
-
- quaternion.setFromRotationMatrix( matrix );
-
- scale.x = sx;
- scale.y = sy;
- scale.z = sz;
+ // bottom row
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
return this;
- };
+ },
- }(),
+ lookAt: function () {
- makeFrustum: function ( left, right, bottom, top, near, far ) {
+ var x = new Vector3();
+ var y = new Vector3();
+ var z = new Vector3();
- var te = this.elements;
- var x = 2 * near / ( right - left );
- var y = 2 * near / ( top - bottom );
+ return function lookAt( eye, target, up ) {
- var a = ( right + left ) / ( right - left );
- var b = ( top + bottom ) / ( top - bottom );
- var c = - ( far + near ) / ( far - near );
- var d = - 2 * far * near / ( far - near );
+ var te = this.elements;
- te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
- te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
- te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
- te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+ z.subVectors( eye, target );
- return this;
+ if ( z.lengthSq() === 0 ) {
- },
+ // eye and target are in the same position
- makePerspective: function ( fov, aspect, near, far ) {
+ z.z = 1;
- var ymax = near * Math.tan( THREE.Math.DEG2RAD * fov * 0.5 );
- var ymin = - ymax;
- var xmin = ymin * aspect;
- var xmax = ymax * aspect;
+ }
- return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );
+ z.normalize();
+ x.crossVectors( up, z );
- },
+ if ( x.lengthSq() === 0 ) {
- makeOrthographic: function ( left, right, top, bottom, near, far ) {
+ // up and z are parallel
- var te = this.elements;
- var w = 1.0 / ( right - left );
- var h = 1.0 / ( top - bottom );
- var p = 1.0 / ( far - near );
+ if ( Math.abs( up.z ) === 1 ) {
- var x = ( right + left ) * w;
- var y = ( top + bottom ) * h;
- var z = ( far + near ) * p;
-
- te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
- te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
- te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
- te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
-
- return this;
-
- },
-
- equals: function ( matrix ) {
-
- var te = this.elements;
- var me = matrix.elements;
-
- for ( var i = 0; i < 16; i ++ ) {
-
- if ( te[ i ] !== me[ i ] ) return false;
-
- }
-
- return true;
-
- },
-
- fromArray: function ( array ) {
-
- this.elements.set( array );
-
- return this;
-
- },
-
- toArray: function ( array, offset ) {
-
- if ( array === undefined ) array = [];
- if ( offset === undefined ) offset = 0;
-
- var te = this.elements;
-
- array[ offset ] = te[ 0 ];
- array[ offset + 1 ] = te[ 1 ];
- array[ offset + 2 ] = te[ 2 ];
- array[ offset + 3 ] = te[ 3 ];
-
- array[ offset + 4 ] = te[ 4 ];
- array[ offset + 5 ] = te[ 5 ];
- array[ offset + 6 ] = te[ 6 ];
- array[ offset + 7 ] = te[ 7 ];
-
- array[ offset + 8 ] = te[ 8 ];
- array[ offset + 9 ] = te[ 9 ];
- array[ offset + 10 ] = te[ 10 ];
- array[ offset + 11 ] = te[ 11 ];
-
- array[ offset + 12 ] = te[ 12 ];
- array[ offset + 13 ] = te[ 13 ];
- array[ offset + 14 ] = te[ 14 ];
- array[ offset + 15 ] = te[ 15 ];
-
- return array;
-
- }
-
-};
-
-// File:src/math/Ray.js
-
-/**
- * @author bhouston / http://clara.io
- */
-
-THREE.Ray = function ( origin, direction ) {
-
- this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
- this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();
-
-};
-
-THREE.Ray.prototype = {
-
- constructor: THREE.Ray,
-
- set: function ( origin, direction ) {
-
- this.origin.copy( origin );
- this.direction.copy( direction );
-
- return this;
-
- },
-
- clone: function () {
-
- return new this.constructor().copy( this );
-
- },
-
- copy: function ( ray ) {
-
- this.origin.copy( ray.origin );
- this.direction.copy( ray.direction );
-
- return this;
-
- },
-
- at: function ( t, optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
-
- return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );
-
- },
-
- lookAt: function ( v ) {
-
- this.direction.copy( v ).sub( this.origin ).normalize();
-
- },
-
- recast: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( t ) {
-
- this.origin.copy( this.at( t, v1 ) );
-
- return this;
-
- };
-
- }(),
-
- closestPointToPoint: function ( point, optionalTarget ) {
-
- var result = optionalTarget || new THREE.Vector3();
- result.subVectors( point, this.origin );
- var directionDistance = result.dot( this.direction );
-
- if ( directionDistance < 0 ) {
-
- return result.copy( this.origin );
-
- }
-
- return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
-
- },
-
- distanceToPoint: function ( point ) {
-
- return Math.sqrt( this.distanceSqToPoint( point ) );
-
- },
-
- distanceSqToPoint: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( point ) {
-
- var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );
-
- // point behind the ray
-
- if ( directionDistance < 0 ) {
-
- return this.origin.distanceToSquared( point );
-
- }
-
- v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
-
- return v1.distanceToSquared( point );
-
- };
-
- }(),
-
- distanceSqToSegment: function () {
-
- var segCenter = new THREE.Vector3();
- var segDir = new THREE.Vector3();
- var diff = new THREE.Vector3();
-
- return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
-
- // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
- // It returns the min distance between the ray and the segment
- // defined by v0 and v1
- // It can also set two optional targets :
- // - The closest point on the ray
- // - The closest point on the segment
-
- segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
- segDir.copy( v1 ).sub( v0 ).normalize();
- diff.copy( this.origin ).sub( segCenter );
-
- var segExtent = v0.distanceTo( v1 ) * 0.5;
- var a01 = - this.direction.dot( segDir );
- var b0 = diff.dot( this.direction );
- var b1 = - diff.dot( segDir );
- var c = diff.lengthSq();
- var det = Math.abs( 1 - a01 * a01 );
- var s0, s1, sqrDist, extDet;
-
- if ( det > 0 ) {
-
- // The ray and segment are not parallel.
-
- s0 = a01 * b1 - b0;
- s1 = a01 * b0 - b1;
- extDet = segExtent * det;
-
- if ( s0 >= 0 ) {
-
- if ( s1 >= - extDet ) {
-
- if ( s1 <= extDet ) {
-
- // region 0
- // Minimum at interior points of ray and segment.
-
- var invDet = 1 / det;
- s0 *= invDet;
- s1 *= invDet;
- sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
-
- } else {
-
- // region 1
-
- s1 = segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
-
- }
+ z.x += 0.0001;
} else {
- // region 5
-
- s1 = - segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+ z.z += 0.0001;
}
+ z.normalize();
+ x.crossVectors( up, z );
+
+ }
+
+ x.normalize();
+ y.crossVectors( z, x );
+
+ te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
+ te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
+ te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
+
+ return this;
+
+ };
+
+ }(),
+
+ multiply: function ( m, n ) {
+
+ if ( n !== undefined ) {
+
+ console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+ return this.multiplyMatrices( m, n );
+
+ }
+
+ return this.multiplyMatrices( this, m );
+
+ },
+
+ premultiply: function ( m ) {
+
+ return this.multiplyMatrices( m, this );
+
+ },
+
+ multiplyMatrices: function ( a, b ) {
+
+ var ae = a.elements;
+ var be = b.elements;
+ var te = this.elements;
+
+ var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+ var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+ var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+ var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
+
+ var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+ var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+ var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+ var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+ te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+ te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+ te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+ te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+ te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+ te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+ te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+ te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+ te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+
+ te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+ te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+ te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+ te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ var te = this.elements;
+
+ te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+ te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+ te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+ te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
+
+ return this;
+
+ },
+
+ applyToBufferAttribute: function () {
+
+ var v1 = new Vector3();
+
+ return function applyToBufferAttribute( attribute ) {
+
+ for ( var i = 0, l = attribute.count; i < l; i ++ ) {
+
+ v1.x = attribute.getX( i );
+ v1.y = attribute.getY( i );
+ v1.z = attribute.getZ( i );
+
+ v1.applyMatrix4( this );
+
+ attribute.setXYZ( i, v1.x, v1.y, v1.z );
+
+ }
+
+ return attribute;
+
+ };
+
+ }(),
+
+ determinant: function () {
+
+ var te = this.elements;
+
+ var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+ var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+ var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+ var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
+
+ //TODO: make this more efficient
+ //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
+
+ return (
+ n41 * (
+ + n14 * n23 * n32
+ - n13 * n24 * n32
+ - n14 * n22 * n33
+ + n12 * n24 * n33
+ + n13 * n22 * n34
+ - n12 * n23 * n34
+ ) +
+ n42 * (
+ + n11 * n23 * n34
+ - n11 * n24 * n33
+ + n14 * n21 * n33
+ - n13 * n21 * n34
+ + n13 * n24 * n31
+ - n14 * n23 * n31
+ ) +
+ n43 * (
+ + n11 * n24 * n32
+ - n11 * n22 * n34
+ - n14 * n21 * n32
+ + n12 * n21 * n34
+ + n14 * n22 * n31
+ - n12 * n24 * n31
+ ) +
+ n44 * (
+ - n13 * n22 * n31
+ - n11 * n23 * n32
+ + n11 * n22 * n33
+ + n13 * n21 * n32
+ - n12 * n21 * n33
+ + n12 * n23 * n31
+ )
+
+ );
+
+ },
+
+ transpose: function () {
+
+ var te = this.elements;
+ var tmp;
+
+ tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+ tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+ tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
+
+ tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+ tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+ tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
+
+ return this;
+
+ },
+
+ setPosition: function ( v ) {
+
+ var te = this.elements;
+
+ te[ 12 ] = v.x;
+ te[ 13 ] = v.y;
+ te[ 14 ] = v.z;
+
+ return this;
+
+ },
+
+ getInverse: function ( m, throwOnDegenerate ) {
+
+ // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ var te = this.elements,
+ me = m.elements,
+
+ n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
+ n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
+ n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
+ n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
+
+ t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
+ t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
+ t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
+ t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+
+ var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
+
+ if ( det === 0 ) {
+
+ var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";
+
+ if ( throwOnDegenerate === true ) {
+
+ throw new Error( msg );
+
} else {
- if ( s1 <= - extDet ) {
+ console.warn( msg );
- // region 4
+ }
- s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
- s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+ return this.identity();
- } else if ( s1 <= extDet ) {
+ }
- // region 3
+ var detInv = 1 / det;
- s0 = 0;
- s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = s1 * ( s1 + 2 * b1 ) + c;
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
+ te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
+ te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
+
+ te[ 4 ] = t12 * detInv;
+ te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
+ te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
+ te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
+
+ te[ 8 ] = t13 * detInv;
+ te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
+ te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
+ te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
+
+ te[ 12 ] = t14 * detInv;
+ te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
+ te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
+ te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
+
+ return this;
+
+ },
+
+ scale: function ( v ) {
+
+ var te = this.elements;
+ var x = v.x, y = v.y, z = v.z;
+
+ te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+ te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+ te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+ te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
+
+ return this;
+
+ },
+
+ getMaxScaleOnAxis: function () {
+
+ var te = this.elements;
+
+ var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+ var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+ var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
+
+ return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
+
+ },
+
+ makeTranslation: function ( x, y, z ) {
+
+ this.set(
+
+ 1, 0, 0, x,
+ 0, 1, 0, y,
+ 0, 0, 1, z,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationX: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, c, - s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationY: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ - s, 0, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationZ: function ( theta ) {
+
+ var c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, - s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeRotationAxis: function ( axis, angle ) {
+
+ // Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+ var c = Math.cos( angle );
+ var s = Math.sin( angle );
+ var t = 1 - c;
+ var x = axis.x, y = axis.y, z = axis.z;
+ var tx = t * x, ty = t * y;
+
+ this.set(
+
+ tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+ tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+ tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeScale: function ( x, y, z ) {
+
+ this.set(
+
+ x, 0, 0, 0,
+ 0, y, 0, 0,
+ 0, 0, z, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ makeShear: function ( x, y, z ) {
+
+ this.set(
+
+ 1, y, z, 0,
+ x, 1, z, 0,
+ x, y, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ },
+
+ compose: function ( position, quaternion, scale ) {
+
+ this.makeRotationFromQuaternion( quaternion );
+ this.scale( scale );
+ this.setPosition( position );
+
+ return this;
+
+ },
+
+ decompose: function () {
+
+ var vector = new Vector3();
+ var matrix = new Matrix4();
+
+ return function decompose( position, quaternion, scale ) {
+
+ var te = this.elements;
+
+ var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+ var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+ var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+ // if determine is negative, we need to invert one scale
+ var det = this.determinant();
+ if ( det < 0 ) sx = - sx;
+
+ position.x = te[ 12 ];
+ position.y = te[ 13 ];
+ position.z = te[ 14 ];
+
+ // scale the rotation part
+ matrix.copy( this );
+
+ var invSX = 1 / sx;
+ var invSY = 1 / sy;
+ var invSZ = 1 / sz;
+
+ matrix.elements[ 0 ] *= invSX;
+ matrix.elements[ 1 ] *= invSX;
+ matrix.elements[ 2 ] *= invSX;
+
+ matrix.elements[ 4 ] *= invSY;
+ matrix.elements[ 5 ] *= invSY;
+ matrix.elements[ 6 ] *= invSY;
+
+ matrix.elements[ 8 ] *= invSZ;
+ matrix.elements[ 9 ] *= invSZ;
+ matrix.elements[ 10 ] *= invSZ;
+
+ quaternion.setFromRotationMatrix( matrix );
+
+ scale.x = sx;
+ scale.y = sy;
+ scale.z = sz;
+
+ return this;
+
+ };
+
+ }(),
+
+ makePerspective: function ( left, right, top, bottom, near, far ) {
+
+ if ( far === undefined ) {
+
+ console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
+
+ }
+
+ var te = this.elements;
+ var x = 2 * near / ( right - left );
+ var y = 2 * near / ( top - bottom );
+
+ var a = ( right + left ) / ( right - left );
+ var b = ( top + bottom ) / ( top - bottom );
+ var c = - ( far + near ) / ( far - near );
+ var d = - 2 * far * near / ( far - near );
+
+ te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
+ te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+
+ return this;
+
+ },
+
+ makeOrthographic: function ( left, right, top, bottom, near, far ) {
+
+ var te = this.elements;
+ var w = 1.0 / ( right - left );
+ var h = 1.0 / ( top - bottom );
+ var p = 1.0 / ( far - near );
+
+ var x = ( right + left ) * w;
+ var y = ( top + bottom ) * h;
+ var z = ( far + near ) * p;
+
+ te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
+ te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
+
+ return this;
+
+ },
+
+ equals: function ( matrix ) {
+
+ var te = this.elements;
+ var me = matrix.elements;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ for ( var i = 0; i < 16; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ var te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+ array[ offset + 3 ] = te[ 3 ];
+
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+
+ array[ offset + 8 ] = te[ 8 ];
+ array[ offset + 9 ] = te[ 9 ];
+ array[ offset + 10 ] = te[ 10 ];
+ array[ offset + 11 ] = te[ 11 ];
+
+ array[ offset + 12 ] = te[ 12 ];
+ array[ offset + 13 ] = te[ 13 ];
+ array[ offset + 14 ] = te[ 14 ];
+ array[ offset + 15 ] = te[ 15 ];
+
+ return array;
+
+ }
+
+ } );
+
+ /**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
+
+ Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
+
+ this.image = { data: data, width: width, height: height };
+
+ this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
+
+ this.generateMipmaps = false;
+ this.flipY = false;
+ this.unpackAlignment = 1;
+
+ }
+
+ DataTexture.prototype = Object.create( Texture.prototype );
+ DataTexture.prototype.constructor = DataTexture;
+
+ DataTexture.prototype.isDataTexture = true;
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+ function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
+
+ images = images !== undefined ? images : [];
+ mapping = mapping !== undefined ? mapping : CubeReflectionMapping;
+
+ Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
+
+ this.flipY = false;
+
+ }
+
+ CubeTexture.prototype = Object.create( Texture.prototype );
+ CubeTexture.prototype.constructor = CubeTexture;
+
+ CubeTexture.prototype.isCubeTexture = true;
+
+ Object.defineProperty( CubeTexture.prototype, 'images', {
+
+ get: function () {
+
+ return this.image;
+
+ },
+
+ set: function ( value ) {
+
+ this.image = value;
+
+ }
+
+ } );
+
+ /**
+ * @author tschw
+ *
+ * Uniforms of a program.
+ * Those form a tree structure with a special top-level container for the root,
+ * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.
+ *
+ *
+ * Properties of inner nodes including the top-level container:
+ *
+ * .seq - array of nested uniforms
+ * .map - nested uniforms by name
+ *
+ *
+ * Methods of all nodes except the top-level container:
+ *
+ * .setValue( gl, value, [renderer] )
+ *
+ * uploads a uniform value(s)
+ * the 'renderer' parameter is needed for sampler uniforms
+ *
+ *
+ * Static methods of the top-level container (renderer factorizations):
+ *
+ * .upload( gl, seq, values, renderer )
+ *
+ * sets uniforms in 'seq' to 'values[id].value'
+ *
+ * .seqWithValue( seq, values ) : filteredSeq
+ *
+ * filters 'seq' entries with corresponding entry in values
+ *
+ *
+ * Methods of the top-level container (renderer factorizations):
+ *
+ * .setValue( gl, name, value )
+ *
+ * sets uniform with name 'name' to 'value'
+ *
+ * .set( gl, obj, prop )
+ *
+ * sets uniform from object and property with same name than uniform
+ *
+ * .setOptional( gl, obj, prop )
+ *
+ * like .set for an optional property of the object
+ *
+ */
+
+ var emptyTexture = new Texture();
+ var emptyCubeTexture = new CubeTexture();
+
+ // --- Base for inner nodes (including the root) ---
+
+ function UniformContainer() {
+
+ this.seq = [];
+ this.map = {};
+
+ }
+
+ // --- Utilities ---
+
+ // Array Caches (provide typed arrays for temporary by size)
+
+ var arrayCacheF32 = [];
+ var arrayCacheI32 = [];
+
+ // Float32Array caches used for uploading Matrix uniforms
+
+ var mat4array = new Float32Array( 16 );
+ var mat3array = new Float32Array( 9 );
+
+ // Flattening for arrays of vectors and matrices
+
+ function flatten( array, nBlocks, blockSize ) {
+
+ var firstElem = array[ 0 ];
+
+ if ( firstElem <= 0 || firstElem > 0 ) return array;
+ // unoptimized: ! isNaN( firstElem )
+ // see http://jacksondunstan.com/articles/983
+
+ var n = nBlocks * blockSize,
+ r = arrayCacheF32[ n ];
+
+ if ( r === undefined ) {
+
+ r = new Float32Array( n );
+ arrayCacheF32[ n ] = r;
+
+ }
+
+ if ( nBlocks !== 0 ) {
+
+ firstElem.toArray( r, 0 );
+
+ for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
+
+ offset += blockSize;
+ array[ i ].toArray( r, offset );
+
+ }
+
+ }
+
+ return r;
+
+ }
+
+ // Texture unit allocation
+
+ function allocTexUnits( renderer, n ) {
+
+ var r = arrayCacheI32[ n ];
+
+ if ( r === undefined ) {
+
+ r = new Int32Array( n );
+ arrayCacheI32[ n ] = r;
+
+ }
+
+ for ( var i = 0; i !== n; ++ i )
+ r[ i ] = renderer.allocTextureUnit();
+
+ return r;
+
+ }
+
+ // --- Setters ---
+
+ // Note: Defining these methods externally, because they come in a bunch
+ // and this way their names minify.
+
+ // Single scalar
+
+ function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); }
+ function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); }
+
+ // Single float vector (from flat array or THREE.VectorN)
+
+ function setValue2fv( gl, v ) {
+
+ if ( v.x === undefined ) gl.uniform2fv( this.addr, v );
+ else gl.uniform2f( this.addr, v.x, v.y );
+
+ }
+
+ function setValue3fv( gl, v ) {
+
+ if ( v.x !== undefined )
+ gl.uniform3f( this.addr, v.x, v.y, v.z );
+ else if ( v.r !== undefined )
+ gl.uniform3f( this.addr, v.r, v.g, v.b );
+ else
+ gl.uniform3fv( this.addr, v );
+
+ }
+
+ function setValue4fv( gl, v ) {
+
+ if ( v.x === undefined ) gl.uniform4fv( this.addr, v );
+ else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+
+ }
+
+ // Single matrix (from flat array or MatrixN)
+
+ function setValue2fm( gl, v ) {
+
+ gl.uniformMatrix2fv( this.addr, false, v.elements || v );
+
+ }
+
+ function setValue3fm( gl, v ) {
+
+ if ( v.elements === undefined ) {
+
+ gl.uniformMatrix3fv( this.addr, false, v );
+
+ } else {
+
+ mat3array.set( v.elements );
+ gl.uniformMatrix3fv( this.addr, false, mat3array );
+
+ }
+
+ }
+
+ function setValue4fm( gl, v ) {
+
+ if ( v.elements === undefined ) {
+
+ gl.uniformMatrix4fv( this.addr, false, v );
+
+ } else {
+
+ mat4array.set( v.elements );
+ gl.uniformMatrix4fv( this.addr, false, mat4array );
+
+ }
+
+ }
+
+ // Single texture (2D / Cube)
+
+ function setValueT1( gl, v, renderer ) {
+
+ var unit = renderer.allocTextureUnit();
+ gl.uniform1i( this.addr, unit );
+ renderer.setTexture2D( v || emptyTexture, unit );
+
+ }
+
+ function setValueT6( gl, v, renderer ) {
+
+ var unit = renderer.allocTextureUnit();
+ gl.uniform1i( this.addr, unit );
+ renderer.setTextureCube( v || emptyCubeTexture, unit );
+
+ }
+
+ // Integer / Boolean vectors or arrays thereof (always flat arrays)
+
+ function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ); }
+ function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ); }
+ function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ); }
+
+ // Helper to pick the right setter for the singular case
+
+ function getSingularSetter( type ) {
+
+ switch ( type ) {
+
+ case 0x1406: return setValue1f; // FLOAT
+ case 0x8b50: return setValue2fv; // _VEC2
+ case 0x8b51: return setValue3fv; // _VEC3
+ case 0x8b52: return setValue4fv; // _VEC4
+
+ case 0x8b5a: return setValue2fm; // _MAT2
+ case 0x8b5b: return setValue3fm; // _MAT3
+ case 0x8b5c: return setValue4fm; // _MAT4
+
+ case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
+ case 0x8b60: return setValueT6; // SAMPLER_CUBE
+
+ case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL
+ case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
+ case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
+ case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
+
+ }
+
+ }
+
+ // Array of scalars
+
+ function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ); }
+ function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ); }
+
+ // Array of vectors (flat or from THREE classes)
+
+ function setValueV2a( gl, v ) {
+
+ gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );
+
+ }
+
+ function setValueV3a( gl, v ) {
+
+ gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );
+
+ }
+
+ function setValueV4a( gl, v ) {
+
+ gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );
+
+ }
+
+ // Array of matrices (flat or from THREE clases)
+
+ function setValueM2a( gl, v ) {
+
+ gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );
+
+ }
+
+ function setValueM3a( gl, v ) {
+
+ gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );
+
+ }
+
+ function setValueM4a( gl, v ) {
+
+ gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );
+
+ }
+
+ // Array of textures (2D / Cube)
+
+ function setValueT1a( gl, v, renderer ) {
+
+ var n = v.length,
+ units = allocTexUnits( renderer, n );
+
+ gl.uniform1iv( this.addr, units );
+
+ for ( var i = 0; i !== n; ++ i ) {
+
+ renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
+
+ }
+
+ }
+
+ function setValueT6a( gl, v, renderer ) {
+
+ var n = v.length,
+ units = allocTexUnits( renderer, n );
+
+ gl.uniform1iv( this.addr, units );
+
+ for ( var i = 0; i !== n; ++ i ) {
+
+ renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
+
+ }
+
+ }
+
+ // Helper to pick the right setter for a pure (bottom-level) array
+
+ function getPureArraySetter( type ) {
+
+ switch ( type ) {
+
+ case 0x1406: return setValue1fv; // FLOAT
+ case 0x8b50: return setValueV2a; // _VEC2
+ case 0x8b51: return setValueV3a; // _VEC3
+ case 0x8b52: return setValueV4a; // _VEC4
+
+ case 0x8b5a: return setValueM2a; // _MAT2
+ case 0x8b5b: return setValueM3a; // _MAT3
+ case 0x8b5c: return setValueM4a; // _MAT4
+
+ case 0x8b5e: return setValueT1a; // SAMPLER_2D
+ case 0x8b60: return setValueT6a; // SAMPLER_CUBE
+
+ case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL
+ case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
+ case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
+ case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
+
+ }
+
+ }
+
+ // --- Uniform Classes ---
+
+ function SingleUniform( id, activeInfo, addr ) {
+
+ this.id = id;
+ this.addr = addr;
+ this.setValue = getSingularSetter( activeInfo.type );
+
+ // this.path = activeInfo.name; // DEBUG
+
+ }
+
+ function PureArrayUniform( id, activeInfo, addr ) {
+
+ this.id = id;
+ this.addr = addr;
+ this.size = activeInfo.size;
+ this.setValue = getPureArraySetter( activeInfo.type );
+
+ // this.path = activeInfo.name; // DEBUG
+
+ }
+
+ function StructuredUniform( id ) {
+
+ this.id = id;
+
+ UniformContainer.call( this ); // mix-in
+
+ }
+
+ StructuredUniform.prototype.setValue = function ( gl, value ) {
+
+ // Note: Don't need an extra 'renderer' parameter, since samplers
+ // are not allowed in structured uniforms.
+
+ var seq = this.seq;
+
+ for ( var i = 0, n = seq.length; i !== n; ++ i ) {
+
+ var u = seq[ i ];
+ u.setValue( gl, value[ u.id ] );
+
+ }
+
+ };
+
+ // --- Top-level ---
+
+ // Parser - builds up the property tree from the path strings
+
+ var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
+
+ // extracts
+ // - the identifier (member name or array index)
+ // - followed by an optional right bracket (found when array index)
+ // - followed by an optional left bracket or dot (type of subscript)
+ //
+ // Note: These portions can be read in a non-overlapping fashion and
+ // allow straightforward parsing of the hierarchy that WebGL encodes
+ // in the uniform names.
+
+ function addUniform( container, uniformObject ) {
+
+ container.seq.push( uniformObject );
+ container.map[ uniformObject.id ] = uniformObject;
+
+ }
+
+ function parseUniform( activeInfo, addr, container ) {
+
+ var path = activeInfo.name,
+ pathLength = path.length;
+
+ // reset RegExp object, because of the early exit of a previous run
+ RePathPart.lastIndex = 0;
+
+ for ( ; ; ) {
+
+ var match = RePathPart.exec( path ),
+ matchEnd = RePathPart.lastIndex,
+
+ id = match[ 1 ],
+ idIsIndex = match[ 2 ] === ']',
+ subscript = match[ 3 ];
+
+ if ( idIsIndex ) id = id | 0; // convert to integer
+
+ if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
+
+ // bare name or "pure" bottom-level array "[0]" suffix
+
+ addUniform( container, subscript === undefined ?
+ new SingleUniform( id, activeInfo, addr ) :
+ new PureArrayUniform( id, activeInfo, addr ) );
+
+ break;
+
+ } else {
+
+ // step into inner node / create it in case it doesn't exist
+
+ var map = container.map, next = map[ id ];
+
+ if ( next === undefined ) {
+
+ next = new StructuredUniform( id );
+ addUniform( container, next );
+
+ }
+
+ container = next;
+
+ }
+
+ }
+
+ }
+
+ // Root Container
+
+ function WebGLUniforms( gl, program, renderer ) {
+
+ UniformContainer.call( this );
+
+ this.renderer = renderer;
+
+ var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
+
+ for ( var i = 0; i < n; ++ i ) {
+
+ var info = gl.getActiveUniform( program, i ),
+ path = info.name,
+ addr = gl.getUniformLocation( program, path );
+
+ parseUniform( info, addr, this );
+
+ }
+
+ }
+
+ WebGLUniforms.prototype.setValue = function ( gl, name, value ) {
+
+ var u = this.map[ name ];
+
+ if ( u !== undefined ) u.setValue( gl, value, this.renderer );
+
+ };
+
+ WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
+
+ var v = object[ name ];
+
+ if ( v !== undefined ) this.setValue( gl, name, v );
+
+ };
+
+
+ // Static interface
+
+ WebGLUniforms.upload = function ( gl, seq, values, renderer ) {
+
+ for ( var i = 0, n = seq.length; i !== n; ++ i ) {
+
+ var u = seq[ i ],
+ v = values[ u.id ];
+
+ if ( v.needsUpdate !== false ) {
+
+ // note: always updating when .needsUpdate is undefined
+ u.setValue( gl, v.value, renderer );
+
+ }
+
+ }
+
+ };
+
+ WebGLUniforms.seqWithValue = function ( seq, values ) {
+
+ var r = [];
+
+ for ( var i = 0, n = seq.length; i !== n; ++ i ) {
+
+ var u = seq[ i ];
+ if ( u.id in values ) r.push( u );
+
+ }
+
+ return r;
+
+ };
+
+ /**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+ var ColorKeywords = { '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, '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 };
+
+ function Color( r, g, b ) {
+
+ if ( g === undefined && b === undefined ) {
+
+ // r is THREE.Color, hex or string
+ return this.set( r );
+
+ }
+
+ return this.setRGB( r, g, b );
+
+ }
+
+ Object.assign( Color.prototype, {
+
+ isColor: true,
+
+ r: 1, g: 1, b: 1,
+
+ set: function ( value ) {
+
+ if ( value && value.isColor ) {
+
+ this.copy( value );
+
+ } else if ( typeof value === 'number' ) {
+
+ this.setHex( value );
+
+ } else if ( typeof value === 'string' ) {
+
+ this.setStyle( value );
+
+ }
+
+ return this;
+
+ },
+
+ setScalar: function ( scalar ) {
+
+ this.r = scalar;
+ this.g = scalar;
+ this.b = scalar;
+
+ return this;
+
+ },
+
+ setHex: function ( hex ) {
+
+ hex = Math.floor( hex );
+
+ this.r = ( hex >> 16 & 255 ) / 255;
+ this.g = ( hex >> 8 & 255 ) / 255;
+ this.b = ( hex & 255 ) / 255;
+
+ return this;
+
+ },
+
+ setRGB: function ( r, g, b ) {
+
+ this.r = r;
+ this.g = g;
+ this.b = b;
+
+ return this;
+
+ },
+
+ setHSL: function () {
+
+ 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 ) * 6 * ( 2 / 3 - t );
+ return p;
+
+ }
+
+ return function setHSL( h, s, l ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+ h = _Math.euclideanModulo( h, 1 );
+ s = _Math.clamp( s, 0, 1 );
+ l = _Math.clamp( l, 0, 1 );
+
+ if ( s === 0 ) {
+
+ this.r = this.g = this.b = l;
+
+ } else {
+
+ var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
+ var q = ( 2 * l ) - p;
+
+ this.r = hue2rgb( q, p, h + 1 / 3 );
+ this.g = hue2rgb( q, p, h );
+ this.b = hue2rgb( q, p, h - 1 / 3 );
+
+ }
+
+ return this;
+
+ };
+
+ }(),
+
+ setStyle: function ( style ) {
+
+ function handleAlpha( string ) {
+
+ if ( string === undefined ) return;
+
+ if ( parseFloat( string ) < 1 ) {
+
+ console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
+
+ }
+
+ }
+
+
+ var m;
+
+ if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
+
+ // rgb / hsl
+
+ var color;
+ var name = m[ 1 ];
+ var components = m[ 2 ];
+
+ switch ( name ) {
+
+ case 'rgb':
+ case 'rgba':
+
+ if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(255,0,0) rgba(255,0,0,0.5)
+ this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
+ this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
+ this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
+ this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
+ this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
+ this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ break;
+
+ case 'hsl':
+ case 'hsla':
+
+ if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
+ var h = parseFloat( color[ 1 ] ) / 360;
+ var s = parseInt( color[ 2 ], 10 ) / 100;
+ var l = parseInt( color[ 3 ], 10 ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this.setHSL( h, s, l );
+
+ }
+
+ break;
+
+ }
+
+ } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
+
+ // hex color
+
+ var hex = m[ 1 ];
+ var size = hex.length;
+
+ if ( size === 3 ) {
+
+ // #ff0
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
+
+ return this;
+
+ } else if ( size === 6 ) {
+
+ // #ff0000
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
+
+ return this;
+
+ }
+
+ }
+
+ if ( style && style.length > 0 ) {
+
+ // color keywords
+ var hex = ColorKeywords[ style ];
+
+ if ( hex !== undefined ) {
+
+ // red
+ this.setHex( hex );
+
+ } else {
+
+ // unknown color
+ console.warn( 'THREE.Color: Unknown color ' + style );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.r, this.g, this.b );
+
+ },
+
+ copy: function ( color ) {
+
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+
+ return this;
+
+ },
+
+ copyGammaToLinear: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ this.r = Math.pow( color.r, gammaFactor );
+ this.g = Math.pow( color.g, gammaFactor );
+ this.b = Math.pow( color.b, gammaFactor );
+
+ return this;
+
+ },
+
+ copyLinearToGamma: function ( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
+
+ this.r = Math.pow( color.r, safeInverse );
+ this.g = Math.pow( color.g, safeInverse );
+ this.b = Math.pow( color.b, safeInverse );
+
+ return this;
+
+ },
+
+ convertGammaToLinear: function () {
+
+ var r = this.r, g = this.g, b = this.b;
+
+ this.r = r * r;
+ this.g = g * g;
+ this.b = b * b;
+
+ return this;
+
+ },
+
+ convertLinearToGamma: function () {
+
+ this.r = Math.sqrt( this.r );
+ this.g = Math.sqrt( this.g );
+ this.b = Math.sqrt( this.b );
+
+ return this;
+
+ },
+
+ getHex: function () {
+
+ return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
+
+ },
+
+ getHexString: function () {
+
+ return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+
+ },
+
+ getHSL: function ( optionalTarget ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+
+ var hsl = optionalTarget || { h: 0, s: 0, l: 0 };
+
+ var r = this.r, g = this.g, b = this.b;
+
+ var max = Math.max( r, g, b );
+ var min = Math.min( r, g, b );
+
+ var hue, saturation;
+ var lightness = ( min + max ) / 2.0;
+
+ if ( min === max ) {
+
+ hue = 0;
+ saturation = 0;
+
+ } else {
+
+ var delta = max - min;
+
+ saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+
+ switch ( max ) {
+
+ case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
+ case g: hue = ( b - r ) / delta + 2; break;
+ case b: hue = ( r - g ) / delta + 4; break;
+
+ }
+
+ hue /= 6;
+
+ }
+
+ hsl.h = hue;
+ hsl.s = saturation;
+ hsl.l = lightness;
+
+ return hsl;
+
+ },
+
+ getStyle: function () {
+
+ return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+
+ },
+
+ offsetHSL: function ( h, s, l ) {
+
+ var hsl = this.getHSL();
+
+ hsl.h += h; hsl.s += s; hsl.l += l;
+
+ this.setHSL( hsl.h, hsl.s, hsl.l );
+
+ return this;
+
+ },
+
+ add: function ( color ) {
+
+ this.r += color.r;
+ this.g += color.g;
+ this.b += color.b;
+
+ return this;
+
+ },
+
+ addColors: function ( color1, color2 ) {
+
+ this.r = color1.r + color2.r;
+ this.g = color1.g + color2.g;
+ this.b = color1.b + color2.b;
+
+ return this;
+
+ },
+
+ addScalar: function ( s ) {
+
+ this.r += s;
+ this.g += s;
+ this.b += s;
+
+ return this;
+
+ },
+
+ sub: function( color ) {
+
+ this.r = Math.max( 0, this.r - color.r );
+ this.g = Math.max( 0, this.g - color.g );
+ this.b = Math.max( 0, this.b - color.b );
+
+ return this;
+
+ },
+
+ multiply: function ( color ) {
+
+ this.r *= color.r;
+ this.g *= color.g;
+ this.b *= color.b;
+
+ return this;
+
+ },
+
+ multiplyScalar: function ( s ) {
+
+ this.r *= s;
+ this.g *= s;
+ this.b *= s;
+
+ return this;
+
+ },
+
+ lerp: function ( color, alpha ) {
+
+ this.r += ( color.r - this.r ) * alpha;
+ this.g += ( color.g - this.g ) * alpha;
+ this.b += ( color.b - this.b ) * alpha;
+
+ return this;
+
+ },
+
+ equals: function ( c ) {
+
+ return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
+
+ },
+
+ fromArray: function ( array, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.r = array[ offset ];
+ this.g = array[ offset + 1 ];
+ this.b = array[ offset + 2 ];
+
+ return this;
+
+ },
+
+ toArray: function ( array, offset ) {
+
+ if ( array === undefined ) array = [];
+ if ( offset === undefined ) offset = 0;
+
+ array[ offset ] = this.r;
+ array[ offset + 1 ] = this.g;
+ array[ offset + 2 ] = this.b;
+
+ return array;
+
+ },
+
+ toJSON: function () {
+
+ return this.getHex();
+
+ }
+
+ } );
+
+ /**
+ * Uniforms library for shared webgl shaders
+ */
+
+ var UniformsLib = {
+
+ common: {
+
+ diffuse: { value: new Color( 0xeeeeee ) },
+ opacity: { value: 1.0 },
+
+ map: { value: null },
+ offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) },
+
+ specularMap: { value: null },
+ alphaMap: { value: null },
+
+ envMap: { value: null },
+ flipEnvMap: { value: - 1 },
+ reflectivity: { value: 1.0 },
+ refractionRatio: { value: 0.98 }
+
+ },
+
+ aomap: {
+
+ aoMap: { value: null },
+ aoMapIntensity: { value: 1 }
+
+ },
+
+ lightmap: {
+
+ lightMap: { value: null },
+ lightMapIntensity: { value: 1 }
+
+ },
+
+ emissivemap: {
+
+ emissiveMap: { value: null }
+
+ },
+
+ bumpmap: {
+
+ bumpMap: { value: null },
+ bumpScale: { value: 1 }
+
+ },
+
+ normalmap: {
+
+ normalMap: { value: null },
+ normalScale: { value: new Vector2( 1, 1 ) }
+
+ },
+
+ displacementmap: {
+
+ displacementMap: { value: null },
+ displacementScale: { value: 1 },
+ displacementBias: { value: 0 }
+
+ },
+
+ roughnessmap: {
+
+ roughnessMap: { value: null }
+
+ },
+
+ metalnessmap: {
+
+ metalnessMap: { value: null }
+
+ },
+
+ gradientmap: {
+
+ gradientMap: { value: null }
+
+ },
+
+ fog: {
+
+ fogDensity: { value: 0.00025 },
+ fogNear: { value: 1 },
+ fogFar: { value: 2000 },
+ fogColor: { value: new Color( 0xffffff ) }
+
+ },
+
+ lights: {
+
+ ambientLightColor: { value: [] },
+
+ directionalLights: { value: [], properties: {
+ direction: {},
+ color: {},
+
+ shadow: {},
+ shadowBias: {},
+ shadowRadius: {},
+ shadowMapSize: {}
+ } },
+
+ directionalShadowMap: { value: [] },
+ directionalShadowMatrix: { value: [] },
+
+ spotLights: { value: [], properties: {
+ color: {},
+ position: {},
+ direction: {},
+ distance: {},
+ coneCos: {},
+ penumbraCos: {},
+ decay: {},
+
+ shadow: {},
+ shadowBias: {},
+ shadowRadius: {},
+ shadowMapSize: {}
+ } },
+
+ spotShadowMap: { value: [] },
+ spotShadowMatrix: { value: [] },
+
+ pointLights: { value: [], properties: {
+ color: {},
+ position: {},
+ decay: {},
+ distance: {},
+
+ shadow: {},
+ shadowBias: {},
+ shadowRadius: {},
+ shadowMapSize: {}
+ } },
+
+ pointShadowMap: { value: [] },
+ pointShadowMatrix: { value: [] },
+
+ hemisphereLights: { value: [], properties: {
+ direction: {},
+ skyColor: {},
+ groundColor: {}
+ } },
+
+ // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src
+ rectAreaLights: { value: [], properties: {
+ color: {},
+ position: {},
+ width: {},
+ height: {}
+ } }
+
+ },
+
+ points: {
+
+ diffuse: { value: new Color( 0xeeeeee ) },
+ opacity: { value: 1.0 },
+ size: { value: 1.0 },
+ scale: { value: 1.0 },
+ map: { value: null },
+ offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) }
+
+ }
+
+ };
+
+ /**
+ * Uniform Utilities
+ */
+
+ var UniformsUtils = {
+
+ merge: function ( uniforms ) {
+
+ var merged = {};
+
+ for ( var u = 0; u < uniforms.length; u ++ ) {
+
+ var tmp = this.clone( uniforms[ u ] );
+
+ for ( var p in tmp ) {
+
+ merged[ p ] = tmp[ p ];
+
+ }
+
+ }
+
+ return merged;
+
+ },
+
+ clone: function ( uniforms_src ) {
+
+ var uniforms_dst = {};
+
+ for ( var u in uniforms_src ) {
+
+ uniforms_dst[ u ] = {};
+
+ for ( var p in uniforms_src[ u ] ) {
+
+ var parameter_src = uniforms_src[ u ][ p ];
+
+ if ( parameter_src && ( parameter_src.isColor ||
+ parameter_src.isMatrix3 || parameter_src.isMatrix4 ||
+ parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||
+ parameter_src.isTexture ) ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.clone();
+
+ } else if ( Array.isArray( parameter_src ) ) {
+
+ uniforms_dst[ u ][ p ] = parameter_src.slice();
} else {
- // region 2
-
- s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
- s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+ uniforms_dst[ u ][ p ] = parameter_src;
}
}
- } else {
-
- // Ray and segment are parallel.
-
- s1 = ( a01 > 0 ) ? - segExtent : segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
-
}
- if ( optionalPointOnRay ) {
-
- optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
-
- }
-
- if ( optionalPointOnSegment ) {
-
- optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );
-
- }
-
- return sqrDist;
-
- };
-
- }(),
-
- intersectSphere: function () {
-
- var v1 = new THREE.Vector3();
-
- return function ( sphere, optionalTarget ) {
-
- v1.subVectors( sphere.center, this.origin );
- var tca = v1.dot( this.direction );
- var d2 = v1.dot( v1 ) - tca * tca;
- var radius2 = sphere.radius * sphere.radius;
-
- if ( d2 > radius2 ) return null;
-
- var thc = Math.sqrt( radius2 - d2 );
-
- // t0 = first intersect point - entrance on front of sphere
- var t0 = tca - thc;
-
- // t1 = second intersect point - exit point on back of sphere
- var t1 = tca + thc;
-
- // test to see if both t0 and t1 are behind the ray - if so, return null
- if ( t0 < 0 && t1 < 0 ) return null;
-
- // test to see if t0 is behind the ray:
- // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
- // in order to always return an intersect point that is in front of the ray.
- if ( t0 < 0 ) return this.at( t1, optionalTarget );
-
- // else t0 is in front of the ray, so return the first collision point scaled by t0
- return this.at( t0, optionalTarget );
-
- };
-
- }(),
-
- intersectsSphere: function ( sphere ) {
-
- return this.distanceToPoint( sphere.center ) <= sphere.radius;
-
- },
-
- distanceToPlane: function ( plane ) {
-
- var denominator = plane.normal.dot( this.direction );
-
- if ( denominator === 0 ) {
-
- // line is coplanar, return origin
- if ( plane.distanceToPoint( this.origin ) === 0 ) {
-
- return 0;
-
- }
-
- // Null is preferable to undefined since undefined means.... it is undefined
-
- return null;
+ return uniforms_dst;
}
- var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
+ };
- // Return if the ray never intersects the plane
+ var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n";
- return t >= 0 ? t : null;
+ var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n";
- },
+ var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n";
- intersectPlane: function ( plane, optionalTarget ) {
+ var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n";
- var t = this.distanceToPlane( plane );
+ var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
- if ( t === null ) {
+ var begin_vertex = "\nvec3 transformed = vec3( position );\n";
- return null;
+ var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n";
+
+ var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat theta = acos( dot( N, V ) );\n\tvec2 uv = vec2(\n\t\tsqrt( saturate( roughness ) ),\n\t\tsaturate( theta / ( 0.5 * PI ) ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.86267 + (0.49788 + 0.01436 * y ) * y;\n\tfloat b = 3.45068 + (4.18814 + y) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt( 1.0 - x * x ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transpose( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tvec3 result = vec3( LTC_ClippedSphereFormFactor( vectorFormFactor ) );\n\treturn result;\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n";
+
+ var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n";
+
+ var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) {\n\t\tvec4 plane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t\t\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) {\n\t\t\tvec4 plane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t\n\t#endif\n#endif\n";
+
+ var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n";
+
+ var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n";
+
+ var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n";
+
+ var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
+
+ var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n";
+
+ var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
+
+ var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif";
+
+ var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transpose( const in mat3 v ) {\n\tmat3 tmp;\n\ttmp[0] = vec3(v[0].x, v[1].x, v[2].x);\n\ttmp[1] = vec3(v[0].y, v[1].y, v[2].y);\n\ttmp[2] = vec3(v[0].z, v[1].z, v[2].z);\n\treturn tmp;\n}\n";
+
+ var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n";
+
+ var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n";
+
+ var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n";
+
+ var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n";
+
+ var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n";
+
+ var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n";
+
+ var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n";
+
+ var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n";
+
+ var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\tsampleUV.y = asin( flipNormal * reflectVec.y ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n";
+
+ var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n";
+
+ var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n";
+
+ var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n";
+
+ var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif";
+
+ var fog_pars_vertex = "#ifdef USE_FOG\n varying float fogDepth;\n#endif\n";
+
+ var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n";
+
+ var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif\n";
+
+ var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n";
+
+ var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n";
+
+ var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
+
+ var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n";
+
+ var lights_pars = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltcMat;\tuniform sampler2D ltcMag;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = saturate( reflectVec.y * 0.5 + 0.5 );\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n";
+
+ var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n";
+
+ var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n";
+
+ var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n";
+
+ var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tfloat norm = texture2D( ltcMag, uv ).a;\n\t\tvec4 t = texture2D( ltcMat, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( 1, 0, t.y ),\n\t\t\tvec3( 0, t.z, 0 ),\n\t\t\tvec3( t.w, 0, t.x )\n\t\t);\n\t\treflectedLight.directSpecular += lightColor * material.specularColor * norm * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n";
+
+ var lights_template = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n\t#endif\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tvec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n\t#ifndef STANDARD\n\t\tvec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n\t#else\n\t\tvec3 clearCoatRadiance = vec3( 0.0 );\n\t#endif\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n";
+
+ var logdepthbuf_fragment = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif";
+
+ var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n";
+
+ var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif";
+
+ var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\t#endif\n#endif\n";
+
+ var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n";
+
+ var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n";
+
+ var map_particle_fragment = "#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n";
+
+ var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n";
+
+ var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
+
+ var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
+
+ var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n";
+
+ var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif";
+
+ var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n";
+
+ var normal_flip = "#ifdef DOUBLE_SIDED\n\tfloat flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n#else\n\tfloat flipNormal = 1.0;\n#endif\n";
+
+ var normal_fragment = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal ) * flipNormal;\n#endif\n#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n";
+
+ var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n";
+
+ var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 1.0 - 2.0 * rgb.xyz;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n";
+
+ var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n";
+
+ var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n";
+
+ var dithering_fragment = "#if defined( DITHERING )\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n";
+
+ var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n";
+
+ var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
+
+ var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
+
+ var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\tfloat dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n";
+
+ var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif\n";
+
+ var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif\n";
+
+ var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}\n";
+
+ var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
+
+ var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n";
+
+ var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n";
+
+ var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n";
+
+ var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif";
+
+ var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
+
+ var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
+
+ var tonemapping_pars_fragment = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
+
+ var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif";
+
+ var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n#endif\n";
+
+ var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif";
+
+ var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
+
+ var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif";
+
+ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif";
+
+ var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
+
+ var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
+
+ var cube_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n";
+
+ var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n";
+
+ var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var distanceRGBA_frag = "uniform vec3 lightPos;\nvarying vec4 vWorldPosition;\n#include \n#include \n#include \nvoid main () {\n\t#include \n\tgl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\n}\n";
+
+ var distanceRGBA_vert = "varying vec4 vWorldPosition;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition;\n}\n";
+
+ var equirect_frag = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
+
+ var equirect_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n";
+
+ var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n";
+
+ var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include