diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 407290ab62..f9c11c962d 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include diff --git a/data/scene/atmosphereearth/atmosphereearth.mod b/data/scene/atmosphereearth/atmosphereearth.mod index f54904ac1f..656ae228b0 100644 --- a/data/scene/atmosphereearth/atmosphereearth.mod +++ b/data/scene/atmosphereearth/atmosphereearth.mod @@ -4,12 +4,14 @@ return { Name = "EarthBarycenter", Parent = "SolarSystemBarycenter", Static = true, - Ephemeris = { - Type = "Spice", - Body = "EARTH BARYCENTER", - Observer = "SUN", - Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" - }, + Transform = { + Translation = { + Type = "Spice", + Body = "EARTH BARYCENTER", + Observer = "SUN", + Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" + }, + } }, -- Earth module { diff --git a/data/scene/earth/earth.mod b/data/scene/earth/earth.mod index cda7130a76..2a20e2d275 100644 --- a/data/scene/earth/earth.mod +++ b/data/scene/earth/earth.mod @@ -31,7 +31,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "EARTH", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/jupiter/callisto/callisto.mod b/data/scene/jupiter/callisto/callisto.mod index 1af5b70f73..6e4a850cc8 100644 --- a/data/scene/jupiter/callisto/callisto.mod +++ b/data/scene/jupiter/callisto/callisto.mod @@ -24,7 +24,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "CALLISTO", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/jup260.bsp" diff --git a/data/scene/jupiter/europa/europa.mod b/data/scene/jupiter/europa/europa.mod index 44c2ded0a5..e4dc06dcbc 100644 --- a/data/scene/jupiter/europa/europa.mod +++ b/data/scene/jupiter/europa/europa.mod @@ -24,7 +24,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "EUROPA", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/jup260.bsp" diff --git a/data/scene/jupiter/ganymede/ganymede.mod b/data/scene/jupiter/ganymede/ganymede.mod index c31809f2be..9f699d122a 100644 --- a/data/scene/jupiter/ganymede/ganymede.mod +++ b/data/scene/jupiter/ganymede/ganymede.mod @@ -24,7 +24,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "GANYMEDE", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/jup260.bsp" diff --git a/data/scene/jupiter/io/io.mod b/data/scene/jupiter/io/io.mod index ba04eb8c6d..97d00d47b3 100644 --- a/data/scene/jupiter/io/io.mod +++ b/data/scene/jupiter/io/io.mod @@ -24,7 +24,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "IO", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/jup260.bsp" diff --git a/data/scene/jupiter/jupiter/jupiter.mod b/data/scene/jupiter/jupiter/jupiter.mod index 6d51c47314..70a4d65078 100644 --- a/data/scene/jupiter/jupiter/jupiter.mod +++ b/data/scene/jupiter/jupiter/jupiter.mod @@ -5,7 +5,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "JUPITER BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -37,7 +37,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, 0, 0}, -- jupiter is at its barycenter }, Rotation = { diff --git a/data/scene/lodearth/lodearth.mod b/data/scene/lodearth/lodearth.mod index 548df4a796..039ad48c39 100644 --- a/data/scene/lodearth/lodearth.mod +++ b/data/scene/lodearth/lodearth.mod @@ -6,7 +6,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "EARTH", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/lodmars/lodmars.mod b/data/scene/lodmars/lodmars.mod index 5eb5a66454..cdea432ffc 100644 --- a/data/scene/lodmars/lodmars.mod +++ b/data/scene/lodmars/lodmars.mod @@ -6,7 +6,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MARS BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/lodmercury/lodmercury.mod b/data/scene/lodmercury/lodmercury.mod index 056ca676a3..5874525f16 100644 --- a/data/scene/lodmercury/lodmercury.mod +++ b/data/scene/lodmercury/lodmercury.mod @@ -5,7 +5,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MERCURY", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/lodmoon/lodmoon.mod b/data/scene/lodmoon/lodmoon.mod index 1e5e22cc65..bc348c91ed 100644 --- a/data/scene/lodmoon/lodmoon.mod +++ b/data/scene/lodmoon/lodmoon.mod @@ -5,7 +5,7 @@ return { Parent = "EarthBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MOON", Observer = "EARTH BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/mars/mars.mod b/data/scene/mars/mars.mod index 20dc40c5ea..e51dba4807 100644 --- a/data/scene/mars/mars.mod +++ b/data/scene/mars/mars.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MARS BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/mercury/mercury.mod b/data/scene/mercury/mercury.mod index caacb59af8..ba64f42565 100644 --- a/data/scene/mercury/mercury.mod +++ b/data/scene/mercury/mercury.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MERCURY", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/moon/moon.mod b/data/scene/moon/moon.mod index 586cdc1081..d1683df018 100644 --- a/data/scene/moon/moon.mod +++ b/data/scene/moon/moon.mod @@ -30,7 +30,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "MOON", Observer = "EARTH BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/newhorizons/jupiter/callisto/callisto.mod b/data/scene/newhorizons/jupiter/callisto/callisto.mod index c00cd996e1..83e4e58c14 100644 --- a/data/scene/newhorizons/jupiter/callisto/callisto.mod +++ b/data/scene/newhorizons/jupiter/callisto/callisto.mod @@ -39,7 +39,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "CALLISTO", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -70,7 +70,7 @@ return { ]] Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -10000000, 0} }, }, diff --git a/data/scene/newhorizons/jupiter/europa/europa.mod b/data/scene/newhorizons/jupiter/europa/europa.mod index fc7c032a2b..f09899f0f3 100644 --- a/data/scene/newhorizons/jupiter/europa/europa.mod +++ b/data/scene/newhorizons/jupiter/europa/europa.mod @@ -39,7 +39,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "EUROPA", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -70,7 +70,7 @@ return { ]] Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -10000000, 0} }, }, diff --git a/data/scene/newhorizons/jupiter/ganymede/ganymede.mod b/data/scene/newhorizons/jupiter/ganymede/ganymede.mod index 3f14d6495f..eb6d0aa01b 100644 --- a/data/scene/newhorizons/jupiter/ganymede/ganymede.mod +++ b/data/scene/newhorizons/jupiter/ganymede/ganymede.mod @@ -39,7 +39,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "GANYMEDE", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -64,7 +64,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -10000000, 0} }, }, diff --git a/data/scene/newhorizons/jupiter/io/io.mod b/data/scene/newhorizons/jupiter/io/io.mod index 7fde7e5650..a5fe7540b3 100644 --- a/data/scene/newhorizons/jupiter/io/io.mod +++ b/data/scene/newhorizons/jupiter/io/io.mod @@ -39,7 +39,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "IO", Observer = "JUPITER BARYCENTER", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -80,7 +80,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -10000000, 0} }, }, diff --git a/data/scene/newhorizons/jupiter/jupiter/jupiter.mod b/data/scene/newhorizons/jupiter/jupiter/jupiter.mod index ccb6649f29..04d07a3a1e 100644 --- a/data/scene/newhorizons/jupiter/jupiter/jupiter.mod +++ b/data/scene/newhorizons/jupiter/jupiter/jupiter.mod @@ -5,7 +5,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "JUPITER BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" @@ -108,7 +108,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -100000000, 0} }, }, diff --git a/data/scene/newhorizons/newhorizons/newhorizons.mod b/data/scene/newhorizons/newhorizons/newhorizons.mod index a4ba9124f4..af08d5f56b 100644 --- a/data/scene/newhorizons/newhorizons/newhorizons.mod +++ b/data/scene/newhorizons/newhorizons/newhorizons.mod @@ -104,7 +104,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "NEW HORIZONS", Observer = "SUN", Kernels = NewHorizonsKernels diff --git a/data/scene/newhorizons/pluto/charon/charon.mod b/data/scene/newhorizons/pluto/charon/charon.mod index f7a2f5e35a..d800cce9c9 100644 --- a/data/scene/newhorizons/pluto/charon/charon.mod +++ b/data/scene/newhorizons/pluto/charon/charon.mod @@ -58,7 +58,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "CHARON", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels @@ -83,7 +83,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -1000000, 0} }, }, diff --git a/data/scene/newhorizons/pluto/hydra/hydra.mod b/data/scene/newhorizons/pluto/hydra/hydra.mod index 89246a82f6..fe07fe078c 100644 --- a/data/scene/newhorizons/pluto/hydra/hydra.mod +++ b/data/scene/newhorizons/pluto/hydra/hydra.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "HYDRA", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels @@ -53,7 +53,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {1000000, 0, 1000000}, }, }, diff --git a/data/scene/newhorizons/pluto/kerberos/kerberos.mod b/data/scene/newhorizons/pluto/kerberos/kerberos.mod index ba541c4de2..8690a6317a 100644 --- a/data/scene/newhorizons/pluto/kerberos/kerberos.mod +++ b/data/scene/newhorizons/pluto/kerberos/kerberos.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "KERBEROS", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels @@ -53,7 +53,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {1000000, 0, 1000000}, }, }, diff --git a/data/scene/newhorizons/pluto/nix/nix.mod b/data/scene/newhorizons/pluto/nix/nix.mod index 10145a29ae..84d923c36d 100644 --- a/data/scene/newhorizons/pluto/nix/nix.mod +++ b/data/scene/newhorizons/pluto/nix/nix.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "NIX", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels diff --git a/data/scene/newhorizons/pluto/pluto/pluto.mod b/data/scene/newhorizons/pluto/pluto/pluto.mod index c6bae149dd..e6c4537046 100644 --- a/data/scene/newhorizons/pluto/pluto/pluto.mod +++ b/data/scene/newhorizons/pluto/pluto/pluto.mod @@ -23,7 +23,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "PLUTO BARYCENTER", Observer = "SUN", Kernels = NewHorizonsKernels @@ -157,7 +157,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "PLUTO", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels @@ -197,7 +197,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -2000000, 0} }, }, @@ -215,7 +215,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, -4000000, 0} }, }, diff --git a/data/scene/newhorizons/pluto/styx/styx.mod b/data/scene/newhorizons/pluto/styx/styx.mod index 52918a7d26..2c2cf774b1 100644 --- a/data/scene/newhorizons/pluto/styx/styx.mod +++ b/data/scene/newhorizons/pluto/styx/styx.mod @@ -29,7 +29,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "STYX", Observer = "PLUTO BARYCENTER", Kernels = NewHorizonsKernels @@ -54,7 +54,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {1000000, 0, 1000000} }, }, diff --git a/data/scene/osirisrex-noglobebrowsing.scene b/data/scene/osirisrex-noglobebrowsing.scene new file mode 100644 index 0000000000..e6a20d9bfa --- /dev/null +++ b/data/scene/osirisrex-noglobebrowsing.scene @@ -0,0 +1,233 @@ +-- TextureResolution = "low" +TextureResolution = "med" +-- TextureResolution = "high" + +local startTime = "2016 SEP 8 23:05:00.50" + +function preInitialization() + --[[ + The scripts in this function are executed after the scene is loaded but before the + scene elements have been initialized, thus they should be used to set the time at + which the scene should start and other settings that might determine initialization + critical objects. + ]]-- + + -- Load Spice Kernels + openspace.spice.loadKernel("${OPENSPACE_DATA}/spice/de430_1850-2150.bsp") + + -- background + -- SCLK kernels needs to be loaded before CK kernels (and generally first) + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/sclk/ORX_SCLKSCET.00000.tsc") + + -- This cannot be loaded correctly for some reason! + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/dsk/RQ36mod.oct12_CCv0001.bds") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/fk/orx_v04.tf") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_lidar_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_ocams_v03.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_otes_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_rexis_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_struct_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_navcam_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_ola_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_ovirs_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/ik/orx_stowcam_v00.ti") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/lsk/naif0011.tls") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/pck/bennu_SPH250m.tpc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/pck/bennu_v10.tpc") + + -- Low res SPK + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/orx_160917_231024_pgaa3_day15m60_v1.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/orx_160914_231024_pgaa3_day12m60_v1.bsp") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/orx_160908_231024_pgaa3_day06m60_v1.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/spk_orx_160908_231024_pgaa2_day06m60_v3.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/orx_160908_231024_pgaa2_day06m60.bsp") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/OREX_20160908_M60_complete.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/OREX_20160904_M45_complete.bsp") + + -- SPK + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/de421.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/background/spk/sb-101955-76.bsp") + + -- Nominal_Profile_LowRes + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/Approach_600s_20180816T230000_20181119T010000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/Approach_NominalProfile_600s_20180816T230000_20181119T010000.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/DetailedSurvey_600s_20190108T000000_20190317T000000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/DetailedSurvey_NominalProfile_600s_20190108T000000_20190317T000000.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/OrbitalA_600s_20181203T230000_20190109T000000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/OrbitalA_NominalProfile_600s_20181203T230000_20190109T000000.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/OrbitalB_600s_20190316T000000_20190521T000000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/OrbitalB_NominalProfile600s_20190316T000000_20190521T000000.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/PrelimSurvey_600s_20181119T230000_20181204T010000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/PrelimSurvey_NominalProfile_600s_20181119T230000_20181204T010000.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/Recon_600s_20190519T000000_20190830T000000.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Profile_LowRes/Recon_NominalProfile_600s_20190519T000000_20190830T000000.bc") + + -- Nominal_Observations_Science + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/DustSearch_v1/Phase03_AP_DustSearch_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/LightCurve_v1/Phase03_AP_LightCurve_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/LightCurve_v1/Phase03_AP_LightCurve_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/NatSatSearch_v1/Phase03_AP_SatSearch_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/NatSatSearch_v1/Phase03_AP_SatSearch_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/PhaseFunction_v1/Phase03_AP_PhaseFunction_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_3.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_4.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_5.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_6.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_7.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_8.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/ShapeModel_v1/Phase03_AP_ShapeModel_9_Forced4x4.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/03_Approach/SpectraMap_v1/Phase03_AP_SpectraMap_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/MapCamOLA_v1/Phase04_PS_MC_1_v1_1a.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/MapCamOLA_v1/Phase04_PS_MC_2_v1_1a.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/OLA_v1/Phase04_PS_OLA_Nominal_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/OLA_v1/Phase04_PS_OLA_Nominal_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/OLA_v1/Phase04_PS_OLA_Nominal_3.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/OLA_v1/Phase04_PS_OLA_Nominal_4.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_3.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_4.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_5.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/04_PrelimSurvey/PolyCam_v1/Phase04_PS_PolyCam_6.bc") + + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19013_18_BBD1_info.TXT") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19014_16_BBD2_info.TXT") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19020_18_BBD3_info.TXT") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19021_19_BBD4_info.TXT") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/README.txt") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19013_18_BBD1_v2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19014_16_BBD2_v2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19020_18_BBD3_v2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/BaseballDiamond_v2/atl_19021_19_BBD4_v2.bc") + + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_3.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_4.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_5.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_6.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/EquatorialStations_v1/Phase06_DS_Equatorial_Stations_7.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/PlumeSearch_v1/Phase06_DS_Plume_Search_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/06_DetailedSurvey/PlumeSearch_v1/Phase06_DS_Plume_Search_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v1/Phase07_OB_CSS_Mapping_1.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v1/Phase07_OB_CSS_Mapping_2.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v1/Phase07_OB_CSS_Mapping_3.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v2/CSS_Mapping_1.a") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v2/CSS_Mapping_2.a") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/07_OrbitalB/CandidateSampleSite_v2/CSS_Mapping_3.a") + + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Case02_0Latitude.wmv") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Case05_20negLatitude.wmv") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Case08_40negLatitude.wmv") + --openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Case11_60negLatitude.wmv") + + local case = 2 -- Right now we only have the image times for case 2 + + if case == 2 then + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/ORX_Recon_525mSortie_Case02.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case02_0Latitude.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case02_atl_19145_04.atf") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/ORX_Recon_225mSortie_Case02.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Recon_225mSortie_Case02_0Latitude.bc") + elseif case == 5 then + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/ORX_Recon_525mSortie_Case05.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case05_20negLatitude.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case05_atl_19145_04.atf") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case05_NominalProfile.bc") + + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/ORX_Recon_225mSortie_Case05.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Recon_225mSortie_Case05_20negLatitude.bc") + elseif case == 8 then + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/525m_Sortie_v2/Recon_525mSortie_Case08_NominalProfile.bc") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/ORX_Recon_225mSortie_Case08.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Recon_225mSortie_Case08_40negLatitude.bc") + elseif case == 11 then + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/ORX_Recon_225mSortie_Case11.bsp") + openspace.spice.loadKernel("${SPICE}/OsirisRexKernels/Nominal_Observations_Science/08_Recon/225m_Sortie_v2/Recon_225mSortie_Case11_60negLatitude.bc") + end + + -- Load planetary constants + openspace.spice.loadKernel("${SPICE}/pck00010.tpc") + + dofile(openspace.absPath('${SCRIPTS}/bind_keys.lua')) + dofile(openspace.absPath('${SCRIPTS}/bind_keys_osirisrex.lua')) + + --local startTime = "2019 APR 16 12:03:00.00" + + openspace.scriptScheduler.load("${OPENSPACE_DATA}/scene/osirisrex/scheduled_scripts.lua") + + -- Removing the line below will cause all scripts prior to to be executed during initialization + -- openspace.scriptScheduler.skipTo(startTime); + +end + +function postInitialization() + --[[ + The scripts in this function are executed after all objects in the scene have been + created and initialized, but before the first render call. This is the place to set + graphical settings for the renderables. + ]]-- + openspace.printInfo("Setting default values") + openspace.setPropertyValue("Sun.renderable.enabled", false) + openspace.setPropertyValue("SunMarker.renderable.enabled", false) + openspace.setPropertyValue("EarthMarker.renderable.enabled", false) + --openspace.setPropertyValue("Constellation Bounds.renderable.enabled", false) + openspace.setPropertyValue("PlutoTrail.renderable.enabled", true) + openspace.setPropertyValue("PlutoTexture.renderable.enabled", false) + + openspace.setPropertyValue("MilkyWay.renderable.transparency", 0.55) + openspace.setPropertyValue("MilkyWay.renderable.segments", 50) + + -- Activate night textures and water masks + openspace.setPropertyValue("Earth.RenderableGlobe.WaterMasks", {0, 1}); + openspace.setPropertyValue("Earth.RenderableGlobe.NightTextures", {0, 1}); + openspace.setPropertyValue("Earth.RenderableGlobe.Atmosphere", true); + + openspace.printInfo("Done setting default values") + openspace.loadMission("${OPENSPACE_DATA}/scene/osirisrex/osirisrex/osirisrex.mission") + + openspace.time.setTime(startTime) + openspace.time.setDeltaTime(0) + + openspace.resetCameraDirection() +end + +return { + ScenePath = ".", + CommonFolder = "common", + Camera = { + Focus = "OsirisRex", + Position = {26974590199.661884, 76314608558.908020, -127086452897.101791}, + Rotation = {0.729548, -0.126024, 0.416827, 0.527382}, + }, + Modules = { + "sun", + "mercury", + -- "lodmercury", + "venus", + "earth", + "mars", + -- "lodearth", + -- "lodmoon", + -- "lodmars", + "jupiter", + "saturn", + "uranus", + "neptune", + "stars", + -- "stars-denver", + "milkyway", + -- "milkyway-eso", + --"imageplane", + "osirisrex", + } +} + diff --git a/data/scene/osirisrex/bennu/bennu.mod b/data/scene/osirisrex/bennu/bennu.mod index 6ab9afaa94..eeabed0e09 100644 --- a/data/scene/osirisrex/bennu/bennu.mod +++ b/data/scene/osirisrex/bennu/bennu.mod @@ -9,7 +9,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = BENNU_BODY, Observer = "SUN", }, diff --git a/data/scene/osirisrex/osirisrex/osirisrex.mission b/data/scene/osirisrex/osirisrex/osirisrex.mission index 5152f5fda4..3e35cffd28 100644 --- a/data/scene/osirisrex/osirisrex/osirisrex.mission +++ b/data/scene/osirisrex/osirisrex/osirisrex.mission @@ -24,125 +24,363 @@ References: --]] -return -{ Name = "OSIRIS-REx", Phases = { - - -- All 1-level phases based on [1] - { Name = "Planning and Fabrication", TimeRange = { Start = "2012 JAN 01 00:00:00", End = "2016 SEP 08 23:05:00" } }, - { Name = "Outbound Cruise", TimeRange = { Start = "2016 SEP 03 00:00:00", End = "2018 SEP 01 01:00:00" }, Phases = { - - -- Phases from [4] - { Name = "Pre Launch", TimeRange = { Start = "2016 SEP 03 01:00:00", End = "2016 SEP 08 23:05:05" } }, - { Name = "Launch", TimeRange = { Start = "2016 SEP 08 23:05:05", End = "2016 SEP 08 23:09:00" } }, - { Name = "Earth Orbit", TimeRange = { Start = "2016 SEP 08 23:09:00", End = "2016 SEP 08 23:45:00" } }, - { Name = "Solar Orbit", TimeRange = { Start = "2016 SEP 08 23:45:00", End = "2018 SEP 01 00:00:00" } }, - { Name = "Upcoming Gravity Assist", TimeRange = { Start = "2017 JAN 22 15:00:00", End = "2017 SEP 22 15:00:00" } }, - { Name = "Gravity Assist", TimeRange = { Start = "2017 SEP 22 15:00:00", End = "2017 SEP 22 21:00:00" } }, - - } }, - - { Name = "Asteroid Operations", Phases = { - - -- Nested Levels from [3] - { Name = "03_Approach", Phases = { - { Name = "DustSearch_v1", Phases = { - { Name = "Phase03_AP_DustSearch_1.bc", TimeRange = { Start = "2018-SEP-11 21:31:01.183", End = "2018-SEP-12 02:18:41.183" }}, - }}, - { Name = "LightCurve_v1", Phases = { - { Name = "Phase03_AP_LightCurve_1.bc", TimeRange = { Start = "2018-OCT-09 21:50:48.182", End = "2018-OCT-10 02:33:16.183" }}, - { Name = "Phase03_AP_LightCurve_2.bc", TimeRange = { Start = "2018-OCT-10 21:50:48.182", End = "2018-OCT-11 02:33:16.183" }}, - }}, - { Name = "NatSatSearch_v1", Phases = { - { Name = "Phase03_AP_SatSearch_1.bc", TimeRange = { Start = "2018-OCT-26 19:38:30.183", End = "2018-OCT-27 00:22:34.183" }}, - { Name = "Phase03_AP_SatSearch_2.bc", TimeRange = { Start = "2018-NOV-05 17:10:20.183", End = "2018-NOV-05 21:59:48.183" }}, - }}, - { Name = "PhaseFunction_v1", Phases = { - { Name = "Phase03_AP_PhaseFunction_1.bc", TimeRange = { Start = "2018-OCT-12 21:42:26.183", End = "2018-OCT-13 02:24:54.183" }}, - }}, - { Name = "ShapeModel_v1", Phases = { - { Name = "Phase03_AP_ShapeModel_1.bc", TimeRange = { Start = "2018-NOV-09 11:02:59.183", End = "2018-NOV-09 15:52:27.183" }}, - { Name = "Phase03_AP_ShapeModel_2.bc", TimeRange = { Start = "2018-NOV-10 11:02:59.183", End = "2018-NOV-10 15:52:27.183" }}, - { Name = "Phase03_AP_ShapeModel_3.bc", TimeRange = { Start = "2018-NOV-11 11:02:59.183", End = "2018-NOV-11 15:52:27.183" }}, - { Name = "Phase03_AP_ShapeModel_4.bc", TimeRange = { Start = "2018-NOV-12 11:02:59.183", End = "2018-NOV-12 15:52:27.183" }}, - { Name = "Phase03_AP_ShapeModel_5.bc", TimeRange = { Start = "2018-NOV-13 11:02:59.183", End = "2018-NOV-13 15:52:27.183" }}, - { Name = "Phase03_AP_ShapeModel_6.bc", TimeRange = { Start = "2018-NOV-14 11:03:53.183", End = "2018-NOV-14 15:51:33.183" }}, - { Name = "Phase03_AP_ShapeModel_7.bc", TimeRange = { Start = "2018-NOV-15 11:03:53.183", End = "2018-NOV-15 15:51:33.183" }}, - { Name = "Phase03_AP_ShapeModel_8.bc", TimeRange = { Start = "2018-NOV-16 11:03:53.183", End = "2018-NOV-16 15:51:33.183" }}, - { Name = "Phase03_AP_ShapeModel_9_Forced4x4.bc", TimeRange = { Start = "2018-NOV-17 11:03:54.183", End = "2018-NOV-17 15:51:34.183" }}, - }}, - { Name = "SpectraMap_v1", Phases = { - { Name = "Phase03_AP_SpectraMap_1.bc", TimeRange = { Start = "2018-OCT-30 20:44:53.183", End = "2018-OCT-31 01:34:21.183" }}, - }}, - }}, - { Name = "04_PrelimSurvey", Phases = { - { Name = "MapCamOLA_v1", Phases = { - { Name = "Phase04_PS_MC_1_v1_1a.bc", TimeRange = { Start = "2018-NOV-20 01:13:12.183", End = "2018-NOV-20 06:13:04.183" }}, - { Name = "Phase04_PS_MC_2_v1_1a.bc", TimeRange = { Start = "2018-NOV-28 01:13:12.183", End = "2018-NOV-28 06:13:04.183" }}, - }}, - { Name = "OLA_v1", Phases = { - { Name = "Phase04_PS_OLA_Nominal_1.bc", TimeRange = { Start = "2018-NOV-19 22:30:00.184", End = "2018-NOV-19 23:19:28.183" }}, - { Name = "Phase04_PS_OLA_Nominal_2.bc", TimeRange = { Start = "2018-NOV-23 22:19:34.184", End = "2018-NOV-23 23:19:26.183" }}, - { Name = "Phase04_PS_OLA_Nominal_3.bc", TimeRange = { Start = "2018-NOV-24 00:48:38.184", End = "2018-NOV-24 01:38:06.184" }}, - { Name = "Phase04_PS_OLA_Nominal_4.bc", TimeRange = { Start = "2018-NOV-27 22:29:58.184", End = "2018-NOV-27 23:19:26.183" }}, - }}, - { Name = "PolyCam_v1", Phases = { - { Name = "Phase04_PS_PolyCam_1.bc", TimeRange = { Start = "2018-NOV-19 12:00:33.183", End = "2018-NOV-19 16:46:25.183" }}, - { Name = "Phase04_PS_PolyCam_2.bc", TimeRange = { Start = "2018-NOV-20 07:10:26.183", End = "2018-NOV-20 12:10:18.183" }}, - { Name = "Phase04_PS_PolyCam_3.bc", TimeRange = { Start = "2018-NOV-23 11:51:29.184", End = "2018-NOV-23 16:51:21.184" }}, - { Name = "Phase04_PS_PolyCam_4.bc", TimeRange = { Start = "2018-NOV-24 07:17:39.184", End = "2018-NOV-24 12:03:31.184" }}, - { Name = "Phase04_PS_PolyCam_5.bc", TimeRange = { Start = "2018-NOV-27 12:00:20.184", End = "2018-NOV-27 16:46:12.184" }}, - { Name = "Phase04_PS_PolyCam_6.bc", TimeRange = { Start = "2018-NOV-28 07:10:35.183", End = "2018-NOV-28 12:10:27.183" }}, - }}, - }}, - { Name = "06_DetailedSurvey", Phases = { - { Name = "BaseballDiamond_v2", Phases = { - { Name = "atl_19013_18_BBD1_v2.bc", TimeRange = { Start = "2019-JAN-13 18:59:31.195", End = "2019-JAN-13 23:59:29.179" }}, - { Name = "atl_19014_16_BBD2_v2.bc", TimeRange = { Start = "2019-JAN-14 16:56:01.185", End = "2019-JAN-14 21:55:58.219" }}, - { Name = "atl_19020_18_BBD3_v2.bc", TimeRange = { Start = "2019-JAN-20 18:59:15.211", End = "2019-JAN-20 23:59:13.195" }}, - { Name = "atl_19021_19_BBD4_v2.bc", TimeRange = { Start = "2019-JAN-21 19:26:47.179", End = "2019-JAN-22 00:26:44.213" }}, - }}, - { Name = "EquatorialStations_v1", Phases = { - { Name = "Phase06_DS_Equatorial_Stations_1.bc", TimeRange = { Start = "2019-JAN-27 10:36:24.185", End = "2019-JAN-27 15:20:28.185" }}, - { Name = "Phase06_DS_Equatorial_Stations_2.bc", TimeRange = { Start = "2019-FEB-03 10:35:30.185", End = "2019-FEB-03 15:21:22.185" }}, - { Name = "Phase06_DS_Equatorial_Stations_3.bc", TimeRange = { Start = "2019-FEB-10 10:51:50.185", End = "2019-FEB-10 15:51:42.185" }}, - { Name = "Phase06_DS_Equatorial_Stations_4.bc", TimeRange = { Start = "2019-FEB-17 10:29:11.186", End = "2019-FEB-17 15:29:03.186" }}, - { Name = "Phase06_DS_Equatorial_Stations_5.bc", TimeRange = { Start = "2019-FEB-24 10:08:28.186", End = "2019-FEB-24 15:08:20.185" }}, - { Name = "Phase06_DS_Equatorial_Stations_6.bc", TimeRange = { Start = "2019-MAR-03 09:52:58.186", End = "2019-MAR-03 14:42:26.186" }}, - { Name = "Phase06_DS_Equatorial_Stations_7.bc", TimeRange = { Start = "2019-MAR-10 09:57:47.186", End = "2019-MAR-10 14:36:33.186" }}, - }}, - { Name = "PlumeSearch_v1", Phases = { - { Name = "Phase06_DS_Plume_Search_1.bc", TimeRange = { Start = "2019-JAN-28 10:34:36.185", End = "2019-JAN-28 15:22:16.185" }}, - { Name = "Phase06_DS_Plume_Search_2.bc", TimeRange = { Start = "2019-FEB-18 10:29:11.186", End = "2019-FEB-18 15:29:03.186" }}, - }}, - }}, - { Name = "07_OrbitalB", Phases = { - { Name = "CandidateSampleSite_v1", Phases = { - { Name = "Phase07_OB_CSS_Mapping_1.bc", TimeRange = { Start = "2019-APR-08 10:35:27.186", End = "2019-APR-08 15:22:06.186" }}, - { Name = "Phase07_OB_CSS_Mapping_2.bc", TimeRange = { Start = "2019-APR-08 16:16:06.186", End = "2019-APR-11 10:38:58.186" }}, - { Name = "Phase07_OB_CSS_Mapping_3.bc", TimeRange = { Start = "2019-APR-22 17:51:23.186", End = "2019-APR-29 19:41:03.186" }}, - }}, - }}, - { Name = "08_Recon", Phases = { - { Name = "225m_Sortie_v2", Phases = { - { Name = "Recon_225mSortie_Case02_0Latitude.bc", TimeRange = { Start = "2019-MAY-25 03:50:31.195", End = "2019-MAY-25 04:32:17.227" }}, - { Name = "Recon_225mSortie_Case05_20negLatitude.bc", TimeRange = { Start = "2019-MAY-25 03:50:48.216", End = "2019-MAY-25 04:37:10.209" }}, - { Name = "Recon_225mSortie_Case08_40negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:02:43.176", End = "2019-MAY-25 04:54:41.179" }}, - { Name = "Recon_225mSortie_Case11_60negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:21:46.161", End = "2019-MAY-25 05:18:44.232" }}, - }}, - { Name = "525m_Sortie_v2", Phases = { - { Name = "Recon_525mSortie_Case02_0Latitude.bc", TimeRange = { Start = "2019-MAY-25 04:06:39.220", End = "2019-MAY-25 04:44:17.198" }}, - { Name = "Recon_525mSortie_Case05_20negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:11:39.201", End = "2019-MAY-25 04:49:37.224" }}, - { Name = "Recon_525mSortie_Case05_NominalProfile.bc", TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" }}, - { Name = "Recon_525mSortie_Case08_NominalProfile.bc", TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" }}, - }}, - }}, +return { + Name = "OSIRIS-REx", + Phases = { + -- All 1-level phases based on [1] + { + Name = "Planning and Fabrication", + TimeRange = { Start = "2012 JAN 01 00:00:00", End = "2016 SEP 08 23:05:00" } + }, + { + Name = "Outbound Cruise", + TimeRange = { Start = "2016 SEP 03 00:00:00", End = "2018 SEP 01 01:00:00" }, + Phases = { + -- Phases from [4] + { + Name = "Pre Launch", + TimeRange = { Start = "2016 SEP 03 01:00:00", End = "2016 SEP 08 23:05:05" } + }, + { + Name = "Launch", TimeRange = { Start = "2016 SEP 08 23:05:05", End = "2016 SEP 08 23:09:00" } + }, + { + Name = "Earth Orbit", TimeRange = { Start = "2016 SEP 08 23:09:00", End = "2016 SEP 08 23:45:00" } + }, + { + Name = "Solar Orbit", TimeRange = { Start = "2016 SEP 08 23:45:00", End = "2018 SEP 01 00:00:00" } + }, + { + Name = "Upcoming Gravity Assist", TimeRange = { Start = "2017 JAN 22 15:00:00", End = "2017 SEP 22 15:00:00" } + }, + { + Name = "Gravity Assist", TimeRange = { Start = "2017 SEP 22 15:00:00", End = "2017 SEP 22 21:00:00" } + } + } + }, + { + Name = "Asteroid Operations", + Phases = { + -- Nested Levels from [3] + { + Name = "03_Approach", Phases = { + { + Name = "DustSearch_v1", + Phases = { + { + Name = "Phase03_AP_DustSearch_1.bc", + TimeRange = { Start = "2018-SEP-11 21:31:01.183", End = "2018-SEP-12 02:18:41.183" } + } + } + }, + { + Name = "LightCurve_v1", + Phases = { + { + Name = "Phase03_AP_LightCurve_1.bc", + TimeRange = { Start = "2018-OCT-09 21:50:48.182", End = "2018-OCT-10 02:33:16.183" } + }, + { + Name = "Phase03_AP_LightCurve_2.bc", + TimeRange = { Start = "2018-OCT-10 21:50:48.182", End = "2018-OCT-11 02:33:16.183" } + } + } + }, + { + Name = "NatSatSearch_v1", + Phases = { + { + Name = "Phase03_AP_SatSearch_1.bc", + TimeRange = { Start = "2018-OCT-26 19:38:30.183", End = "2018-OCT-27 00:22:34.183" } + }, + { + Name = "Phase03_AP_SatSearch_2.bc", + TimeRange = { Start = "2018-NOV-05 17:10:20.183", End = "2018-NOV-05 21:59:48.183" } + } + } + }, + { + Name = "PhaseFunction_v1", + Phases = { + { + Name = "Phase03_AP_PhaseFunction_1.bc", + TimeRange = { Start = "2018-OCT-12 21:42:26.183", End = "2018-OCT-13 02:24:54.183" } + } + } + }, + { + Name = "ShapeModel_v1", Phases = { + { + Name = "Phase03_AP_ShapeModel_1.bc", + TimeRange = { Start = "2018-NOV-09 11:02:59.183", End = "2018-NOV-09 15:52:27.183" } + }, + { + Name = "Phase03_AP_ShapeModel_2.bc", + TimeRange = { Start = "2018-NOV-10 11:02:59.183", End = "2018-NOV-10 15:52:27.183" } + }, + { + Name = "Phase03_AP_ShapeModel_3.bc", + TimeRange = { Start = "2018-NOV-11 11:02:59.183", End = "2018-NOV-11 15:52:27.183" } + }, + { + Name = "Phase03_AP_ShapeModel_4.bc", + TimeRange = { Start = "2018-NOV-12 11:02:59.183", End = "2018-NOV-12 15:52:27.183" } + }, + { + Name = "Phase03_AP_ShapeModel_5.bc", + TimeRange = { Start = "2018-NOV-13 11:02:59.183", End = "2018-NOV-13 15:52:27.183" } + }, + { + Name = "Phase03_AP_ShapeModel_6.bc", + TimeRange = { Start = "2018-NOV-14 11:03:53.183", End = "2018-NOV-14 15:51:33.183" } + }, + { + Name = "Phase03_AP_ShapeModel_7.bc", + TimeRange = { Start = "2018-NOV-15 11:03:53.183", End = "2018-NOV-15 15:51:33.183" } + }, + { + Name = "Phase03_AP_ShapeModel_8.bc", + TimeRange = { Start = "2018-NOV-16 11:03:53.183", End = "2018-NOV-16 15:51:33.183" } + }, + { + Name = "Phase03_AP_ShapeModel_9_Forced4x4.bc", + TimeRange = { Start = "2018-NOV-17 11:03:54.183", End = "2018-NOV-17 15:51:34.183" } + } + } + }, + { + Name = "SpectraMap_v1", + Phases = { + { + Name = "Phase03_AP_SpectraMap_1.bc", + TimeRange = { Start = "2018-OCT-30 20:44:53.183", End = "2018-OCT-31 01:34:21.183" } + } + } + } + } + }, + { + Name = "04_PrelimSurvey", + Phases = { + { + Name = "MapCamOLA_v1", + Phases = { + { + Name = "Phase04_PS_MC_1_v1_1a.bc", + TimeRange = { Start = "2018-NOV-20 01:13:12.183", End = "2018-NOV-20 06:13:04.183" } + }, + { + Name = "Phase04_PS_MC_2_v1_1a.bc", + TimeRange = { Start = "2018-NOV-28 01:13:12.183", End = "2018-NOV-28 06:13:04.183" } + } + } + }, + { + Name = "OLA_v1", + Phases = { + { + Name = "Phase04_PS_OLA_Nominal_1.bc", + TimeRange = { Start = "2018-NOV-19 22:30:00.184", End = "2018-NOV-19 23:19:28.183" } + }, + { + Name = "Phase04_PS_OLA_Nominal_2.bc", + TimeRange = { Start = "2018-NOV-23 22:19:34.184", End = "2018-NOV-23 23:19:26.183" } + }, + { + Name = "Phase04_PS_OLA_Nominal_3.bc", + TimeRange = { Start = "2018-NOV-24 00:48:38.184", End = "2018-NOV-24 01:38:06.184" } + }, + { + Name = "Phase04_PS_OLA_Nominal_4.bc", + TimeRange = { Start = "2018-NOV-27 22:29:58.184", End = "2018-NOV-27 23:19:26.183" } + } + } + }, + { + Name = "PolyCam_v1", + Phases = { + { + Name = "Phase04_PS_PolyCam_1.bc", + TimeRange = { Start = "2018-NOV-19 12:00:33.183", End = "2018-NOV-19 16:46:25.183" } + }, + { + Name = "Phase04_PS_PolyCam_2.bc", + TimeRange = { Start = "2018-NOV-20 07:10:26.183", End = "2018-NOV-20 12:10:18.183" } + }, + { + Name = "Phase04_PS_PolyCam_3.bc", + TimeRange = { Start = "2018-NOV-23 11:51:29.184", End = "2018-NOV-23 16:51:21.184" } + }, + { + Name = "Phase04_PS_PolyCam_4.bc", + TimeRange = { Start = "2018-NOV-24 07:17:39.184", End = "2018-NOV-24 12:03:31.184" } + }, + { + Name = "Phase04_PS_PolyCam_5.bc", + TimeRange = { Start = "2018-NOV-27 12:00:20.184", End = "2018-NOV-27 16:46:12.184" } + }, + { + Name = "Phase04_PS_PolyCam_6.bc", + TimeRange = { Start = "2018-NOV-28 07:10:35.183", End = "2018-NOV-28 12:10:27.183" } + } + } + }, + } + }, + { + Name = "06_DetailedSurvey", + Phases = { + { + Name = "BaseballDiamond_v2", + Phases = { + { + Name = "atl_19013_18_BBD1_v2.bc", + TimeRange = { Start = "2019-JAN-13 18:59:31.195", End = "2019-JAN-13 23:59:29.179" } + }, + { + Name = "atl_19014_16_BBD2_v2.bc", + TimeRange = { Start = "2019-JAN-14 16:56:01.185", End = "2019-JAN-14 21:55:58.219" } + }, + { + Name = "atl_19020_18_BBD3_v2.bc", + TimeRange = { Start = "2019-JAN-20 18:59:15.211", End = "2019-JAN-20 23:59:13.195" } + }, + { + Name = "atl_19021_19_BBD4_v2.bc", + TimeRange = { Start = "2019-JAN-21 19:26:47.179", End = "2019-JAN-22 00:26:44.213" } + } + } + }, + { + Name = "EquatorialStations_v1", + Phases = { + { + Name = "Phase06_DS_Equatorial_Stations_1.bc", + TimeRange = { Start = "2019-JAN-27 10:36:24.185", End = "2019-JAN-27 15:20:28.185" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_2.bc", + TimeRange = { Start = "2019-FEB-03 10:35:30.185", End = "2019-FEB-03 15:21:22.185" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_3.bc", + TimeRange = { Start = "2019-FEB-10 10:51:50.185", End = "2019-FEB-10 15:51:42.185" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_4.bc", + TimeRange = { Start = "2019-FEB-17 10:29:11.186", End = "2019-FEB-17 15:29:03.186" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_5.bc", + TimeRange = { Start = "2019-FEB-24 10:08:28.186", End = "2019-FEB-24 15:08:20.185" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_6.bc", + TimeRange = { Start = "2019-MAR-03 09:52:58.186", End = "2019-MAR-03 14:42:26.186" } + }, + { + Name = "Phase06_DS_Equatorial_Stations_7.bc", + TimeRange = { Start = "2019-MAR-10 09:57:47.186", End = "2019-MAR-10 14:36:33.186" } + } + } + }, + { + Name = "PlumeSearch_v1", + Phases = { + { + Name = "Phase06_DS_Plume_Search_1.bc", + TimeRange = { Start = "2019-JAN-28 10:34:36.185", End = "2019-JAN-28 15:22:16.185" } + }, + { + Name = "Phase06_DS_Plume_Search_2.bc", + TimeRange = { Start = "2019-FEB-18 10:29:11.186", End = "2019-FEB-18 15:29:03.186" } + } + } + } + } + }, + { + Name = "07_OrbitalB", + Phases = { + { + Name = "CandidateSampleSite_v1", + Phases = { + { + Name = "Phase07_OB_CSS_Mapping_1.bc", + TimeRange = { Start = "2019-APR-08 10:35:27.186", End = "2019-APR-08 15:22:06.186" } + }, + { + Name = "Phase07_OB_CSS_Mapping_2.bc", + TimeRange = { Start = "2019-APR-08 16:16:06.186", End = "2019-APR-11 10:38:58.186" } + }, + { + Name = "Phase07_OB_CSS_Mapping_3.bc", + TimeRange = { Start = "2019-APR-22 17:51:23.186", End = "2019-APR-29 19:41:03.186" } + } + } + } + } + }, + { + Name = "08_Recon", + Phases = { + { + Name = "225m_Sortie_v2", + Phases = { + { + Name = "Recon_225mSortie_Case02_0Latitude.bc", + TimeRange = { Start = "2019-MAY-25 03:50:31.195", End = "2019-MAY-25 04:32:17.227" } + }, + { + Name = "Recon_225mSortie_Case05_20negLatitude.bc", + TimeRange = { Start = "2019-MAY-25 03:50:48.216", End = "2019-MAY-25 04:37:10.209" } + }, + { + Name = "Recon_225mSortie_Case08_40negLatitude.bc", + TimeRange = { Start = "2019-MAY-25 04:02:43.176", End = "2019-MAY-25 04:54:41.179" } + }, + { + Name = "Recon_225mSortie_Case11_60negLatitude.bc", + TimeRange = { Start = "2019-MAY-25 04:21:46.161", End = "2019-MAY-25 05:18:44.232" } + } + } + }, + { + Name = "525m_Sortie_v2", + Phases = { + { + Name = "Recon_525mSortie_Case02_0Latitude.bc", + TimeRange = { Start = "2019-MAY-25 04:06:39.220", End = "2019-MAY-25 04:44:17.198" } + }, + { + Name = "Recon_525mSortie_Case05_20negLatitude.bc", + TimeRange = { Start = "2019-MAY-25 04:11:39.201", End = "2019-MAY-25 04:49:37.224" } + }, + { + Name = "Recon_525mSortie_Case05_NominalProfile.bc", + TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" } + }, + { + Name = "Recon_525mSortie_Case08_NominalProfile.bc", + TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" } + } + } + } + } + } -- End of [3] - }}, - - { Name = "Backup Time", TimeRange = { Start = "2020 JAN 01 00:00:00", End = "2021 JAN 01 00:00:00" } }, - { Name = "Return Cruise", TimeRange = { Start = "2021 JAN 01 00:00:00", End = "2023 SEP 20 00:00:00" } }, - + } + }, + { + Name = "Backup Time", + TimeRange = { Start = "2020 JAN 01 00:00:00", End = "2021 JAN 01 00:00:00" } + }, + { + Name = "Return Cruise", + TimeRange = { Start = "2021 JAN 01 00:00:00", End = "2023 SEP 20 00:00:00"} + }, -- Not too interesting in terms of space visualization --> out commented -- { Name = "Sample Analysis", TimeRange = { Start = "2023 JAN 01 00:00:00", End = "2025 JUN 01 00:00:00" } }, -- End of [1] -}} \ No newline at end of file + } +} diff --git a/data/scene/osirisrex/osirisrex/osirisrex.mod b/data/scene/osirisrex/osirisrex/osirisrex.mod index d0c8918d2f..51272879be 100644 --- a/data/scene/osirisrex/osirisrex/osirisrex.mod +++ b/data/scene/osirisrex/osirisrex/osirisrex.mod @@ -27,7 +27,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "OSIRIS-REX", Observer = "SUN", }, @@ -61,7 +61,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {-0.2476, 0.2710, 0.3364}, }, Rotation = { @@ -94,7 +94,7 @@ return { }, Transform = { Translation = { - Type = "StaticEphemeris", + Type = "StaticTranslation", Position = {0, 0.3371, 0.2712}, }, Rotation = { diff --git a/data/scene/pluto/pluto.data b/data/scene/pluto/pluto.data index 264a9ff830..db86c15f8c 100644 --- a/data/scene/pluto/pluto.data +++ b/data/scene/pluto/pluto.data @@ -1,6 +1,7 @@ return { FileRequest = { { Identifier = "charon_textures", Destination = "textures", Version = 1 }, - { Identifier = "pluto_textures", Destination = "textures", Version = 4 } + { Identifier = "pluto_textures", Destination = "textures", Version = 4 }, + { Identifier = "pluto_spice", Destination = "${SPICE}", Version = 1 } } } \ No newline at end of file diff --git a/data/scene/rosetta/67P/67P.mod b/data/scene/rosetta/67P/67P.mod index 84af8afce9..1b8d3a5aa7 100644 --- a/data/scene/rosetta/67P/67P.mod +++ b/data/scene/rosetta/67P/67P.mod @@ -5,7 +5,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "CHURYUMOV-GERASIMENKO", Observer = "SUN", }, diff --git a/data/scene/rosetta/rosetta/rosetta.mod b/data/scene/rosetta/rosetta/rosetta.mod index 73b8181ecc..1bb96fe198 100644 --- a/data/scene/rosetta/rosetta/rosetta.mod +++ b/data/scene/rosetta/rosetta/rosetta.mod @@ -62,7 +62,7 @@ return { Parent = "SolarSystemBarycenter", Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "ROSETTA", Observer = "SUN", Kernels = RosettaKernels @@ -273,7 +273,7 @@ return { -- translated Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "PHILAE", Observer = "CHURYUMOV-GERASIMENKO", Kernels = RosettaKernels diff --git a/data/scene/saturn/saturn.mod b/data/scene/saturn/saturn.mod index 1f7e68170d..ac59c3d9c9 100644 --- a/data/scene/saturn/saturn.mod +++ b/data/scene/saturn/saturn.mod @@ -30,7 +30,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "SATURN BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/scene/uranus/uranus.mod b/data/scene/uranus/uranus.mod index 77bac51663..1653178896 100644 --- a/data/scene/uranus/uranus.mod +++ b/data/scene/uranus/uranus.mod @@ -30,7 +30,7 @@ return { }, Transform = { Translation = { - Type = "SpiceEphemeris", + Type = "SpiceTranslation", Body = "URANUS BARYCENTER", Observer = "SUN", Kernels = "${OPENSPACE_DATA}/spice/de430_1850-2150.bsp" diff --git a/data/web/common/bootstrap.min.css b/data/web/common/bootstrap.min.css new file mode 100644 index 0000000000..ed3905e0e0 --- /dev/null +++ b/data/web/common/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/data/web/common/handlebars-v4.0.5.js b/data/web/common/handlebars-v4.0.5.js new file mode 100644 index 0000000000..289ae458ae --- /dev/null +++ b/data/web/common/handlebars-v4.0.5.js @@ -0,0 +1,4608 @@ +/*! + + handlebars v4.0.5 + +Copyright (C) 2011-2015 by Yehuda Katz + +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. + +@license +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Handlebars"] = factory(); + else + root["Handlebars"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsRuntime = __webpack_require__(2); + + var _handlebarsRuntime2 = _interopRequireDefault(_handlebarsRuntime); + + // Compiler imports + + var _handlebarsCompilerAst = __webpack_require__(21); + + var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst); + + var _handlebarsCompilerBase = __webpack_require__(22); + + var _handlebarsCompilerCompiler = __webpack_require__(27); + + var _handlebarsCompilerJavascriptCompiler = __webpack_require__(28); + + var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler); + + var _handlebarsCompilerVisitor = __webpack_require__(25); + + var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor); + + var _handlebarsNoConflict = __webpack_require__(20); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + var _create = _handlebarsRuntime2['default'].create; + function create() { + var hb = _create(); + + hb.compile = function (input, options) { + return _handlebarsCompilerCompiler.compile(input, options, hb); + }; + hb.precompile = function (input, options) { + return _handlebarsCompilerCompiler.precompile(input, options, hb); + }; + + hb.AST = _handlebarsCompilerAst2['default']; + hb.Compiler = _handlebarsCompilerCompiler.Compiler; + hb.JavaScriptCompiler = _handlebarsCompilerJavascriptCompiler2['default']; + hb.Parser = _handlebarsCompilerBase.parser; + hb.parse = _handlebarsCompilerBase.parse; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst.Visitor = _handlebarsCompilerVisitor2['default']; + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }, +/* 1 */ +/***/ function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; + }; + + exports.__esModule = true; + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsBase = __webpack_require__(4); + + var base = _interopRequireWildcard(_handlebarsBase); + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + + var _handlebarsSafeString = __webpack_require__(18); + + var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); + + var _handlebarsException = __webpack_require__(6); + + var _handlebarsException2 = _interopRequireDefault(_handlebarsException); + + var _handlebarsUtils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_handlebarsUtils); + + var _handlebarsRuntime = __webpack_require__(19); + + var runtime = _interopRequireWildcard(_handlebarsRuntime); + + var _handlebarsNoConflict = __webpack_require__(20); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + function create() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = _handlebarsSafeString2['default']; + hb.Exception = _handlebarsException2['default']; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function (spec) { + return runtime.template(spec, hb); + }; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + } + + newObj["default"] = obj; + return newObj; + } + }; + + exports.__esModule = true; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.HandlebarsEnvironment = HandlebarsEnvironment; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _helpers = __webpack_require__(7); + + var _decorators = __webpack_require__(15); + + var _logger = __webpack_require__(17); + + var _logger2 = _interopRequireDefault(_logger); + + var VERSION = '4.0.5'; + exports.VERSION = VERSION; + var COMPILER_REVISION = 7; + + exports.COMPILER_REVISION = COMPILER_REVISION; + var REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1', + 7: '>= 4.0.0' + }; + + exports.REVISION_CHANGES = REVISION_CHANGES; + var objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials, decorators) { + this.helpers = helpers || {}; + this.partials = partials || {}; + this.decorators = decorators || {}; + + _helpers.registerDefaultHelpers(this); + _decorators.registerDefaultDecorators(this); + } + + HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: _logger2['default'], + log: _logger2['default'].log, + + registerHelper: function registerHelper(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple helpers'); + } + _utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function unregisterHelper(name) { + delete this.helpers[name]; + }, + + registerPartial: function registerPartial(name, partial) { + if (_utils.toString.call(name) === objectType) { + _utils.extend(this.partials, name); + } else { + if (typeof partial === 'undefined') { + throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); + } + this.partials[name] = partial; + } + }, + unregisterPartial: function unregisterPartial(name) { + delete this.partials[name]; + }, + + registerDecorator: function registerDecorator(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple decorators'); + } + _utils.extend(this.decorators, name); + } else { + this.decorators[name] = fn; + } + }, + unregisterDecorator: function unregisterDecorator(name) { + delete this.decorators[name]; + } + }; + + var log = _logger2['default'].log; + + exports.log = log; + exports.createFrame = _utils.createFrame; + exports.logger = _logger2['default']; + +/***/ }, +/* 5 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.extend = extend; + exports.indexOf = indexOf; + exports.escapeExpression = escapeExpression; + exports.isEmpty = isEmpty; + exports.createFrame = createFrame; + exports.blockParams = blockParams; + exports.appendContextPath = appendContextPath; + var escape = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=' + }; + + var badChars = /[&<>"'`=]/g, + possible = /[&<>"'`=]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + var toString = Object.prototype.toString; + + exports.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + /* eslint-disable func-style */ + var isFunction = function isFunction(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + exports.isFunction = isFunction = function (value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + exports.isFunction = isFunction; + + /* eslint-enable func-style */ + + /* istanbul ignore next */ + var isArray = Array.isArray || function (value) { + return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; + }; + + exports.isArray = isArray; + // Older IE versions do not directly support indexOf so we must implement our own, sadly. + + function indexOf(array, value) { + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + return -1; + } + + function escapeExpression(string) { + if (typeof string !== 'string') { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ''; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = '' + string; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + } + + function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + function createFrame(object) { + var frame = extend({}, object); + frame._parent = object; + return frame; + } + + function blockParams(params, ids) { + params.path = ids; + return params; + } + + function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + var loc = node && node.loc, + line = undefined, + column = undefined; + if (loc) { + line = loc.start.line; + column = loc.start.column; + + message += ' - ' + line + ':' + column; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + /* istanbul ignore else */ + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Exception); + } + + if (loc) { + this.lineNumber = line; + this.column = column; + } + } + + Exception.prototype = new Error(); + + exports['default'] = Exception; + module.exports = exports['default']; + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultHelpers = registerDefaultHelpers; + + var _helpersBlockHelperMissing = __webpack_require__(8); + + var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); + + var _helpersEach = __webpack_require__(9); + + var _helpersEach2 = _interopRequireDefault(_helpersEach); + + var _helpersHelperMissing = __webpack_require__(10); + + var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); + + var _helpersIf = __webpack_require__(11); + + var _helpersIf2 = _interopRequireDefault(_helpersIf); + + var _helpersLog = __webpack_require__(12); + + var _helpersLog2 = _interopRequireDefault(_helpersLog); + + var _helpersLookup = __webpack_require__(13); + + var _helpersLookup2 = _interopRequireDefault(_helpersLookup); + + var _helpersWith = __webpack_require__(14); + + var _helpersWith2 = _interopRequireDefault(_helpersWith); + + function registerDefaultHelpers(instance) { + _helpersBlockHelperMissing2['default'](instance); + _helpersEach2['default'](instance); + _helpersHelperMissing2['default'](instance); + _helpersIf2['default'](instance); + _helpersLog2['default'](instance); + _helpersLookup2['default'](instance); + _helpersWith2['default'](instance); + } + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('blockHelperMissing', function (context, options) { + var inverse = options.inverse, + fn = options.fn; + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (_utils.isArray(context)) { + if (context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + var data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); + options = { data: data }; + } + + return fn(context, options); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('each', function (context, options) { + if (!options) { + throw new _exception2['default']('Must pass iterator to #each'); + } + + var fn = options.fn, + inverse = options.inverse, + i = 0, + ret = '', + data = undefined, + contextPath = undefined; + + if (options.data && options.ids) { + contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (_utils.isFunction(context)) { + context = context.call(this); + } + + if (options.data) { + data = _utils.createFrame(options.data); + } + + function execIteration(field, index, last) { + if (data) { + data.key = field; + data.index = index; + data.first = index === 0; + data.last = !!last; + + if (contextPath) { + data.contextPath = contextPath + field; + } + } + + ret = ret + fn(context[field], { + data: data, + blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) + }); + } + + if (context && typeof context === 'object') { + if (_utils.isArray(context)) { + for (var j = context.length; i < j; i++) { + if (i in context) { + execIteration(i, i, i === context.length - 1); + } + } + } else { + var priorKey = undefined; + + for (var key in context) { + if (context.hasOwnProperty(key)) { + // We're running the iterations one step out of sync so we can detect + // the last iteration without have to scan the object twice and create + // an itermediate keys array. + if (priorKey !== undefined) { + execIteration(priorKey, i - 1); + } + priorKey = key; + i++; + } + } + if (priorKey !== undefined) { + execIteration(priorKey, i - 1, true); + } + } + } + + if (i === 0) { + ret = inverse(this); + } + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('helperMissing', function () /* [args, ]options */{ + if (arguments.length === 1) { + // A missing field in a {{foo}} construct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('if', function (conditional, options) { + if (_utils.isFunction(conditional)) { + conditional = conditional.call(this); + } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function (conditional, options) { + return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash }); + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('log', function () /* message, options */{ + var args = [undefined], + options = arguments[arguments.length - 1]; + for (var i = 0; i < arguments.length - 1; i++) { + args.push(arguments[i]); + } + + var level = 1; + if (options.hash.level != null) { + level = options.hash.level; + } else if (options.data && options.data.level != null) { + level = options.data.level; + } + args[0] = level; + + instance.log.apply(instance, args); + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 13 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('lookup', function (obj, field) { + return obj && obj[field]; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('with', function (context, options) { + if (_utils.isFunction(context)) { + context = context.call(this); + } + + var fn = options.fn; + + if (!_utils.isEmpty(context)) { + var data = options.data; + if (options.data && options.ids) { + data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); + } + + return fn(context, { + data: data, + blockParams: _utils.blockParams([context], [data && data.contextPath]) + }); + } else { + return options.inverse(this); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultDecorators = registerDefaultDecorators; + + var _decoratorsInline = __webpack_require__(16); + + var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); + + function registerDefaultDecorators(instance) { + _decoratorsInline2['default'](instance); + } + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerDecorator('inline', function (fn, props, container, options) { + var ret = fn; + if (!props.partials) { + props.partials = {}; + ret = function (context, options) { + // Create a new partials stack frame prior to exec. + var original = container.partials; + container.partials = _utils.extend({}, original, props.partials); + var ret = fn(context, options); + container.partials = original; + return ret; + }; + } + + props.partials[options.args[0]] = options.fn; + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var logger = { + methodMap: ['debug', 'info', 'warn', 'error'], + level: 'info', + + // Maps a given level value to the `methodMap` indexes above. + lookupLevel: function lookupLevel(level) { + if (typeof level === 'string') { + var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); + if (levelMap >= 0) { + level = levelMap; + } else { + level = parseInt(level, 10); + } + } + + return level; + }, + + // Can be overridden in the host environment + log: function log(level) { + level = logger.lookupLevel(level); + + if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + var method = logger.methodMap[level]; + if (!console[method]) { + // eslint-disable-line no-console + method = 'log'; + } + + for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + message[_key - 1] = arguments[_key]; + } + + console[method].apply(console, message); // eslint-disable-line no-console + } + } + }; + + exports['default'] = logger; + module.exports = exports['default']; + +/***/ }, +/* 18 */ +/***/ function(module, exports) { + + // Build out our basic SafeString type + 'use strict'; + + exports.__esModule = true; + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function () { + return '' + this.string; + }; + + exports['default'] = SafeString; + module.exports = exports['default']; + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.checkRevision = checkRevision; + exports.template = template; + exports.wrapProgram = wrapProgram; + exports.resolvePartial = resolvePartial; + exports.invokePartial = invokePartial; + exports.noop = noop; + + var _utils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_utils); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _base = __webpack_require__(4); + + function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = _base.COMPILER_REVISION; + + if (compilerRevision !== currentRevision) { + if (compilerRevision < currentRevision) { + var runtimeVersions = _base.REVISION_CHANGES[currentRevision], + compilerVersions = _base.REVISION_CHANGES[compilerRevision]; + throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + } + } + } + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new _exception2['default']('No environment passed to template'); + } + if (!templateSpec || !templateSpec.main) { + throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); + } + + templateSpec.main.decorator = templateSpec.main_d; + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as psuedo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + function invokePartialWrapper(partial, context, options) { + if (options.hash) { + context = Utils.extend({}, context, options.hash); + if (options.ids) { + options.ids[0] = true; + } + } + + partial = env.VM.resolvePartial.call(this, partial, context, options); + var result = env.VM.invokePartial.call(this, partial, context, options); + + if (result == null && env.compile) { + options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + result = options.partials[options.name](context, options); + } + if (result != null) { + if (options.indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = options.indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + } + } + + // Just add water + var container = { + strict: function strict(obj, name) { + if (!(name in obj)) { + throw new _exception2['default']('"' + name + '" not defined in ' + obj); + } + return obj[name]; + }, + lookup: function lookup(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + if (depths[i] && depths[i][name] != null) { + return depths[i][name]; + } + } + }, + lambda: function lambda(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function fn(i) { + var ret = templateSpec[i]; + ret.decorator = templateSpec[i + '_d']; + return ret; + }, + + programs: [], + program: function program(i, data, declaredBlockParams, blockParams, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths || blockParams || declaredBlockParams) { + programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = wrapProgram(this, i, fn); + } + return programWrapper; + }, + + data: function data(value, depth) { + while (value && depth--) { + value = value._parent; + } + return value; + }, + merge: function merge(param, common) { + var obj = param || common; + + if (param && common && param !== common) { + obj = Utils.extend({}, common, param); + } + + return obj; + }, + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + function ret(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths = undefined, + blockParams = templateSpec.useBlockParams ? [] : undefined; + if (templateSpec.useDepths) { + if (options.depths) { + depths = context !== options.depths[0] ? [context].concat(options.depths) : options.depths; + } else { + depths = [context]; + } + } + + function main(context /*, options*/) { + return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + } + main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + return main(context, options); + } + ret.isTop = true; + + ret._setup = function (options) { + if (!options.partial) { + container.helpers = container.merge(options.helpers, env.helpers); + + if (templateSpec.usePartial) { + container.partials = container.merge(options.partials, env.partials); + } + if (templateSpec.usePartial || templateSpec.useDecorators) { + container.decorators = container.merge(options.decorators, env.decorators); + } + } else { + container.helpers = options.helpers; + container.partials = options.partials; + container.decorators = options.decorators; + } + }; + + ret._child = function (i, data, blockParams, depths) { + if (templateSpec.useBlockParams && !blockParams) { + throw new _exception2['default']('must pass block params'); + } + if (templateSpec.useDepths && !depths) { + throw new _exception2['default']('must pass parent depths'); + } + + return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + }; + return ret; + } + + function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { + function prog(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var currentDepths = depths; + if (depths && context !== depths[0]) { + currentDepths = [context].concat(depths); + } + + return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); + } + + prog = executeDecorators(fn, prog, container, depths, data, blockParams); + + prog.program = i; + prog.depth = depths ? depths.length : 0; + prog.blockParams = declaredBlockParams || 0; + return prog; + } + + function resolvePartial(partial, context, options) { + if (!partial) { + if (options.name === '@partial-block') { + partial = options.data['partial-block']; + } else { + partial = options.partials[options.name]; + } + } else if (!partial.call && !options.name) { + // This is a dynamic partial that returned a string + options.name = partial; + partial = options.partials[partial]; + } + return partial; + } + + function invokePartial(partial, context, options) { + options.partial = true; + if (options.ids) { + options.data.contextPath = options.ids[0] || options.data.contextPath; + } + + var partialBlock = undefined; + if (options.fn && options.fn !== noop) { + options.data = _base.createFrame(options.data); + partialBlock = options.data['partial-block'] = options.fn; + + if (partialBlock.partials) { + options.partials = Utils.extend({}, options.partials, partialBlock.partials); + } + } + + if (partial === undefined && partialBlock) { + partial = partialBlock; + } + + if (partial === undefined) { + throw new _exception2['default']('The partial ' + options.name + ' could not be found'); + } else if (partial instanceof Function) { + return partial(context, options); + } + } + + function noop() { + return ''; + } + + function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? _base.createFrame(data) : {}; + data.root = context; + } + return data; + } + + function executeDecorators(fn, prog, container, depths, data, blockParams) { + if (fn.decorator) { + var props = {}; + prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + Utils.extend(prog, props); + } + return prog; + } + +/***/ }, +/* 20 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/* global window */ + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (Handlebars) { + /* istanbul ignore next */ + var root = typeof global !== 'undefined' ? global : window, + $Handlebars = root.Handlebars; + /* istanbul ignore next */ + Handlebars.noConflict = function () { + if (root.Handlebars === Handlebars) { + root.Handlebars = $Handlebars; + } + return Handlebars; + }; + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 21 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + var AST = { + // Public API used to evaluate derived attributes regarding AST nodes + helpers: { + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + helperExpression: function helperExpression(node) { + return node.type === 'SubExpression' || (node.type === 'MustacheStatement' || node.type === 'BlockStatement') && !!(node.params && node.params.length || node.hash); + }, + + scopedId: function scopedId(path) { + return (/^\.|this\b/.test(path.original) + ); + }, + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + simpleId: function simpleId(path) { + return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth; + } + } + }; + + // Must be exported as an object rather than the root of the module as the jison lexer + // must modify the object to operate properly. + exports['default'] = AST; + module.exports = exports['default']; + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.parse = parse; + + var _parser = __webpack_require__(23); + + var _parser2 = _interopRequireDefault(_parser); + + var _whitespaceControl = __webpack_require__(24); + + var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl); + + var _helpers = __webpack_require__(26); + + var Helpers = _interopRequireWildcard(_helpers); + + var _utils = __webpack_require__(5); + + exports.parser = _parser2['default']; + + var yy = {}; + _utils.extend(yy, Helpers); + + function parse(input, options) { + // Just return if an already-compiled AST was passed in. + if (input.type === 'Program') { + return input; + } + + _parser2['default'].yy = yy; + + // Altering the shared object here, but this is ok as parser is a sync operation + yy.locInfo = function (locInfo) { + return new yy.SourceLocation(options && options.srcName, locInfo); + }; + + var strip = new _whitespaceControl2['default'](options); + return strip.accept(_parser2['default'].parse(input)); + } + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + /* istanbul ignore next */ + /* Jison generated parser */ + "use strict"; + + var handlebars = (function () { + var parser = { trace: function trace() {}, + yy: {}, + symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition_plus0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "mustache_repetition0": 49, "mustache_option0": 50, "OPEN_UNESCAPED": 51, "mustache_repetition1": 52, "mustache_option1": 53, "CLOSE_UNESCAPED": 54, "OPEN_PARTIAL": 55, "partialName": 56, "partial_repetition0": 57, "partial_option0": 58, "openPartialBlock": 59, "OPEN_PARTIAL_BLOCK": 60, "openPartialBlock_repetition0": 61, "openPartialBlock_option0": 62, "param": 63, "sexpr": 64, "OPEN_SEXPR": 65, "sexpr_repetition0": 66, "sexpr_option0": 67, "CLOSE_SEXPR": 68, "hash": 69, "hash_repetition_plus0": 70, "hashSegment": 71, "ID": 72, "EQUALS": 73, "blockParams": 74, "OPEN_BLOCK_PARAMS": 75, "blockParams_repetition_plus0": 76, "CLOSE_BLOCK_PARAMS": 77, "path": 78, "dataName": 79, "STRING": 80, "NUMBER": 81, "BOOLEAN": 82, "UNDEFINED": 83, "NULL": 84, "DATA": 85, "pathSegments": 86, "SEP": 87, "$accept": 0, "$end": 1 }, + terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 51: "OPEN_UNESCAPED", 54: "CLOSE_UNESCAPED", 55: "OPEN_PARTIAL", 60: "OPEN_PARTIAL_BLOCK", 65: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 72: "ID", 73: "EQUALS", 75: "OPEN_BLOCK_PARAMS", 77: "CLOSE_BLOCK_PARAMS", 80: "STRING", 81: "NUMBER", 82: "BOOLEAN", 83: "UNDEFINED", 84: "NULL", 85: "DATA", 87: "SEP" }, + productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 1], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$ + /**/) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: + return $$[$0 - 1]; + break; + case 2: + this.$ = yy.prepareProgram($$[$0]); + break; + case 3: + this.$ = $$[$0]; + break; + case 4: + this.$ = $$[$0]; + break; + case 5: + this.$ = $$[$0]; + break; + case 6: + this.$ = $$[$0]; + break; + case 7: + this.$ = $$[$0]; + break; + case 8: + this.$ = $$[$0]; + break; + case 9: + this.$ = { + type: 'CommentStatement', + value: yy.stripComment($$[$0]), + strip: yy.stripFlags($$[$0], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 10: + this.$ = { + type: 'ContentStatement', + original: $$[$0], + value: $$[$0], + loc: yy.locInfo(this._$) + }; + + break; + case 11: + this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 12: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] }; + break; + case 13: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$); + break; + case 14: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$); + break; + case 15: + this.$ = { open: $$[$0 - 5], path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 16: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 17: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 18: + this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] }; + break; + case 19: + var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$), + program = yy.prepareProgram([inverse], $$[$0 - 1].loc); + program.chained = true; + + this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true }; + + break; + case 20: + this.$ = $$[$0]; + break; + case 21: + this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) }; + break; + case 22: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 23: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 24: + this.$ = { + type: 'PartialStatement', + name: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + indent: '', + strip: yy.stripFlags($$[$0 - 4], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 25: + this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 26: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 4], $$[$0]) }; + break; + case 27: + this.$ = $$[$0]; + break; + case 28: + this.$ = $$[$0]; + break; + case 29: + this.$ = { + type: 'SubExpression', + path: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + loc: yy.locInfo(this._$) + }; + + break; + case 30: + this.$ = { type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 31: + this.$ = { type: 'HashPair', key: yy.id($$[$0 - 2]), value: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 32: + this.$ = yy.id($$[$0 - 1]); + break; + case 33: + this.$ = $$[$0]; + break; + case 34: + this.$ = $$[$0]; + break; + case 35: + this.$ = { type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 36: + this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) }; + break; + case 37: + this.$ = { type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$) }; + break; + case 38: + this.$ = { type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$) }; + break; + case 39: + this.$ = { type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$) }; + break; + case 40: + this.$ = $$[$0]; + break; + case 41: + this.$ = $$[$0]; + break; + case 42: + this.$ = yy.preparePath(true, $$[$0], this._$); + break; + case 43: + this.$ = yy.preparePath(false, $$[$0], this._$); + break; + case 44: + $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] });this.$ = $$[$0 - 2]; + break; + case 45: + this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }]; + break; + case 46: + this.$ = []; + break; + case 47: + $$[$0 - 1].push($$[$0]); + break; + case 48: + this.$ = [$$[$0]]; + break; + case 49: + $$[$0 - 1].push($$[$0]); + break; + case 50: + this.$ = []; + break; + case 51: + $$[$0 - 1].push($$[$0]); + break; + case 58: + this.$ = []; + break; + case 59: + $$[$0 - 1].push($$[$0]); + break; + case 64: + this.$ = []; + break; + case 65: + $$[$0 - 1].push($$[$0]); + break; + case 70: + this.$ = []; + break; + case 71: + $$[$0 - 1].push($$[$0]); + break; + case 78: + this.$ = []; + break; + case 79: + $$[$0 - 1].push($$[$0]); + break; + case 82: + this.$ = []; + break; + case 83: + $$[$0 - 1].push($$[$0]); + break; + case 86: + this.$ = []; + break; + case 87: + $$[$0 - 1].push($$[$0]); + break; + case 90: + this.$ = []; + break; + case 91: + $$[$0 - 1].push($$[$0]); + break; + case 94: + this.$ = []; + break; + case 95: + $$[$0 - 1].push($$[$0]); + break; + case 98: + this.$ = [$$[$0]]; + break; + case 99: + $$[$0 - 1].push($$[$0]); + break; + case 100: + this.$ = [$$[$0]]; + break; + case 101: + $$[$0 - 1].push($$[$0]); + break; + } + }, + table: [{ 3: 1, 4: 2, 5: [2, 46], 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 1: [3] }, { 5: [1, 4] }, { 5: [2, 2], 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: 11, 14: [1, 12], 15: [1, 20], 16: 17, 19: [1, 23], 24: 15, 27: 16, 29: [1, 21], 34: [1, 22], 39: [2, 2], 44: [2, 2], 47: [2, 2], 48: [1, 13], 51: [1, 14], 55: [1, 18], 59: 19, 60: [1, 24] }, { 1: [2, 1] }, { 5: [2, 47], 14: [2, 47], 15: [2, 47], 19: [2, 47], 29: [2, 47], 34: [2, 47], 39: [2, 47], 44: [2, 47], 47: [2, 47], 48: [2, 47], 51: [2, 47], 55: [2, 47], 60: [2, 47] }, { 5: [2, 3], 14: [2, 3], 15: [2, 3], 19: [2, 3], 29: [2, 3], 34: [2, 3], 39: [2, 3], 44: [2, 3], 47: [2, 3], 48: [2, 3], 51: [2, 3], 55: [2, 3], 60: [2, 3] }, { 5: [2, 4], 14: [2, 4], 15: [2, 4], 19: [2, 4], 29: [2, 4], 34: [2, 4], 39: [2, 4], 44: [2, 4], 47: [2, 4], 48: [2, 4], 51: [2, 4], 55: [2, 4], 60: [2, 4] }, { 5: [2, 5], 14: [2, 5], 15: [2, 5], 19: [2, 5], 29: [2, 5], 34: [2, 5], 39: [2, 5], 44: [2, 5], 47: [2, 5], 48: [2, 5], 51: [2, 5], 55: [2, 5], 60: [2, 5] }, { 5: [2, 6], 14: [2, 6], 15: [2, 6], 19: [2, 6], 29: [2, 6], 34: [2, 6], 39: [2, 6], 44: [2, 6], 47: [2, 6], 48: [2, 6], 51: [2, 6], 55: [2, 6], 60: [2, 6] }, { 5: [2, 7], 14: [2, 7], 15: [2, 7], 19: [2, 7], 29: [2, 7], 34: [2, 7], 39: [2, 7], 44: [2, 7], 47: [2, 7], 48: [2, 7], 51: [2, 7], 55: [2, 7], 60: [2, 7] }, { 5: [2, 8], 14: [2, 8], 15: [2, 8], 19: [2, 8], 29: [2, 8], 34: [2, 8], 39: [2, 8], 44: [2, 8], 47: [2, 8], 48: [2, 8], 51: [2, 8], 55: [2, 8], 60: [2, 8] }, { 5: [2, 9], 14: [2, 9], 15: [2, 9], 19: [2, 9], 29: [2, 9], 34: [2, 9], 39: [2, 9], 44: [2, 9], 47: [2, 9], 48: [2, 9], 51: [2, 9], 55: [2, 9], 60: [2, 9] }, { 20: 25, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 36, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 37, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 4: 38, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 13: 40, 15: [1, 20], 17: 39 }, { 20: 42, 56: 41, 64: 43, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 45, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 5: [2, 10], 14: [2, 10], 15: [2, 10], 18: [2, 10], 19: [2, 10], 29: [2, 10], 34: [2, 10], 39: [2, 10], 44: [2, 10], 47: [2, 10], 48: [2, 10], 51: [2, 10], 55: [2, 10], 60: [2, 10] }, { 20: 46, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 47, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 48, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 42, 56: 49, 64: 43, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [2, 78], 49: 50, 65: [2, 78], 72: [2, 78], 80: [2, 78], 81: [2, 78], 82: [2, 78], 83: [2, 78], 84: [2, 78], 85: [2, 78] }, { 23: [2, 33], 33: [2, 33], 54: [2, 33], 65: [2, 33], 68: [2, 33], 72: [2, 33], 75: [2, 33], 80: [2, 33], 81: [2, 33], 82: [2, 33], 83: [2, 33], 84: [2, 33], 85: [2, 33] }, { 23: [2, 34], 33: [2, 34], 54: [2, 34], 65: [2, 34], 68: [2, 34], 72: [2, 34], 75: [2, 34], 80: [2, 34], 81: [2, 34], 82: [2, 34], 83: [2, 34], 84: [2, 34], 85: [2, 34] }, { 23: [2, 35], 33: [2, 35], 54: [2, 35], 65: [2, 35], 68: [2, 35], 72: [2, 35], 75: [2, 35], 80: [2, 35], 81: [2, 35], 82: [2, 35], 83: [2, 35], 84: [2, 35], 85: [2, 35] }, { 23: [2, 36], 33: [2, 36], 54: [2, 36], 65: [2, 36], 68: [2, 36], 72: [2, 36], 75: [2, 36], 80: [2, 36], 81: [2, 36], 82: [2, 36], 83: [2, 36], 84: [2, 36], 85: [2, 36] }, { 23: [2, 37], 33: [2, 37], 54: [2, 37], 65: [2, 37], 68: [2, 37], 72: [2, 37], 75: [2, 37], 80: [2, 37], 81: [2, 37], 82: [2, 37], 83: [2, 37], 84: [2, 37], 85: [2, 37] }, { 23: [2, 38], 33: [2, 38], 54: [2, 38], 65: [2, 38], 68: [2, 38], 72: [2, 38], 75: [2, 38], 80: [2, 38], 81: [2, 38], 82: [2, 38], 83: [2, 38], 84: [2, 38], 85: [2, 38] }, { 23: [2, 39], 33: [2, 39], 54: [2, 39], 65: [2, 39], 68: [2, 39], 72: [2, 39], 75: [2, 39], 80: [2, 39], 81: [2, 39], 82: [2, 39], 83: [2, 39], 84: [2, 39], 85: [2, 39] }, { 23: [2, 43], 33: [2, 43], 54: [2, 43], 65: [2, 43], 68: [2, 43], 72: [2, 43], 75: [2, 43], 80: [2, 43], 81: [2, 43], 82: [2, 43], 83: [2, 43], 84: [2, 43], 85: [2, 43], 87: [1, 51] }, { 72: [1, 35], 86: 52 }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 52: 53, 54: [2, 82], 65: [2, 82], 72: [2, 82], 80: [2, 82], 81: [2, 82], 82: [2, 82], 83: [2, 82], 84: [2, 82], 85: [2, 82] }, { 25: 54, 38: 56, 39: [1, 58], 43: 57, 44: [1, 59], 45: 55, 47: [2, 54] }, { 28: 60, 43: 61, 44: [1, 59], 47: [2, 56] }, { 13: 63, 15: [1, 20], 18: [1, 62] }, { 15: [2, 48], 18: [2, 48] }, { 33: [2, 86], 57: 64, 65: [2, 86], 72: [2, 86], 80: [2, 86], 81: [2, 86], 82: [2, 86], 83: [2, 86], 84: [2, 86], 85: [2, 86] }, { 33: [2, 40], 65: [2, 40], 72: [2, 40], 80: [2, 40], 81: [2, 40], 82: [2, 40], 83: [2, 40], 84: [2, 40], 85: [2, 40] }, { 33: [2, 41], 65: [2, 41], 72: [2, 41], 80: [2, 41], 81: [2, 41], 82: [2, 41], 83: [2, 41], 84: [2, 41], 85: [2, 41] }, { 20: 65, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 66, 47: [1, 67] }, { 30: 68, 33: [2, 58], 65: [2, 58], 72: [2, 58], 75: [2, 58], 80: [2, 58], 81: [2, 58], 82: [2, 58], 83: [2, 58], 84: [2, 58], 85: [2, 58] }, { 33: [2, 64], 35: 69, 65: [2, 64], 72: [2, 64], 75: [2, 64], 80: [2, 64], 81: [2, 64], 82: [2, 64], 83: [2, 64], 84: [2, 64], 85: [2, 64] }, { 21: 70, 23: [2, 50], 65: [2, 50], 72: [2, 50], 80: [2, 50], 81: [2, 50], 82: [2, 50], 83: [2, 50], 84: [2, 50], 85: [2, 50] }, { 33: [2, 90], 61: 71, 65: [2, 90], 72: [2, 90], 80: [2, 90], 81: [2, 90], 82: [2, 90], 83: [2, 90], 84: [2, 90], 85: [2, 90] }, { 20: 75, 33: [2, 80], 50: 72, 63: 73, 64: 76, 65: [1, 44], 69: 74, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 72: [1, 80] }, { 23: [2, 42], 33: [2, 42], 54: [2, 42], 65: [2, 42], 68: [2, 42], 72: [2, 42], 75: [2, 42], 80: [2, 42], 81: [2, 42], 82: [2, 42], 83: [2, 42], 84: [2, 42], 85: [2, 42], 87: [1, 51] }, { 20: 75, 53: 81, 54: [2, 84], 63: 82, 64: 76, 65: [1, 44], 69: 83, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 84, 47: [1, 67] }, { 47: [2, 55] }, { 4: 85, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 47: [2, 20] }, { 20: 86, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 87, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 26: 88, 47: [1, 67] }, { 47: [2, 57] }, { 5: [2, 11], 14: [2, 11], 15: [2, 11], 19: [2, 11], 29: [2, 11], 34: [2, 11], 39: [2, 11], 44: [2, 11], 47: [2, 11], 48: [2, 11], 51: [2, 11], 55: [2, 11], 60: [2, 11] }, { 15: [2, 49], 18: [2, 49] }, { 20: 75, 33: [2, 88], 58: 89, 63: 90, 64: 76, 65: [1, 44], 69: 91, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 65: [2, 94], 66: 92, 68: [2, 94], 72: [2, 94], 80: [2, 94], 81: [2, 94], 82: [2, 94], 83: [2, 94], 84: [2, 94], 85: [2, 94] }, { 5: [2, 25], 14: [2, 25], 15: [2, 25], 19: [2, 25], 29: [2, 25], 34: [2, 25], 39: [2, 25], 44: [2, 25], 47: [2, 25], 48: [2, 25], 51: [2, 25], 55: [2, 25], 60: [2, 25] }, { 20: 93, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 31: 94, 33: [2, 60], 63: 95, 64: 76, 65: [1, 44], 69: 96, 70: 77, 71: 78, 72: [1, 79], 75: [2, 60], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 33: [2, 66], 36: 97, 63: 98, 64: 76, 65: [1, 44], 69: 99, 70: 77, 71: 78, 72: [1, 79], 75: [2, 66], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 22: 100, 23: [2, 52], 63: 101, 64: 76, 65: [1, 44], 69: 102, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 33: [2, 92], 62: 103, 63: 104, 64: 76, 65: [1, 44], 69: 105, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 106] }, { 33: [2, 79], 65: [2, 79], 72: [2, 79], 80: [2, 79], 81: [2, 79], 82: [2, 79], 83: [2, 79], 84: [2, 79], 85: [2, 79] }, { 33: [2, 81] }, { 23: [2, 27], 33: [2, 27], 54: [2, 27], 65: [2, 27], 68: [2, 27], 72: [2, 27], 75: [2, 27], 80: [2, 27], 81: [2, 27], 82: [2, 27], 83: [2, 27], 84: [2, 27], 85: [2, 27] }, { 23: [2, 28], 33: [2, 28], 54: [2, 28], 65: [2, 28], 68: [2, 28], 72: [2, 28], 75: [2, 28], 80: [2, 28], 81: [2, 28], 82: [2, 28], 83: [2, 28], 84: [2, 28], 85: [2, 28] }, { 23: [2, 30], 33: [2, 30], 54: [2, 30], 68: [2, 30], 71: 107, 72: [1, 108], 75: [2, 30] }, { 23: [2, 98], 33: [2, 98], 54: [2, 98], 68: [2, 98], 72: [2, 98], 75: [2, 98] }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 73: [1, 109], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 23: [2, 44], 33: [2, 44], 54: [2, 44], 65: [2, 44], 68: [2, 44], 72: [2, 44], 75: [2, 44], 80: [2, 44], 81: [2, 44], 82: [2, 44], 83: [2, 44], 84: [2, 44], 85: [2, 44], 87: [2, 44] }, { 54: [1, 110] }, { 54: [2, 83], 65: [2, 83], 72: [2, 83], 80: [2, 83], 81: [2, 83], 82: [2, 83], 83: [2, 83], 84: [2, 83], 85: [2, 83] }, { 54: [2, 85] }, { 5: [2, 13], 14: [2, 13], 15: [2, 13], 19: [2, 13], 29: [2, 13], 34: [2, 13], 39: [2, 13], 44: [2, 13], 47: [2, 13], 48: [2, 13], 51: [2, 13], 55: [2, 13], 60: [2, 13] }, { 38: 56, 39: [1, 58], 43: 57, 44: [1, 59], 45: 112, 46: 111, 47: [2, 76] }, { 33: [2, 70], 40: 113, 65: [2, 70], 72: [2, 70], 75: [2, 70], 80: [2, 70], 81: [2, 70], 82: [2, 70], 83: [2, 70], 84: [2, 70], 85: [2, 70] }, { 47: [2, 18] }, { 5: [2, 14], 14: [2, 14], 15: [2, 14], 19: [2, 14], 29: [2, 14], 34: [2, 14], 39: [2, 14], 44: [2, 14], 47: [2, 14], 48: [2, 14], 51: [2, 14], 55: [2, 14], 60: [2, 14] }, { 33: [1, 114] }, { 33: [2, 87], 65: [2, 87], 72: [2, 87], 80: [2, 87], 81: [2, 87], 82: [2, 87], 83: [2, 87], 84: [2, 87], 85: [2, 87] }, { 33: [2, 89] }, { 20: 75, 63: 116, 64: 76, 65: [1, 44], 67: 115, 68: [2, 96], 69: 117, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 118] }, { 32: 119, 33: [2, 62], 74: 120, 75: [1, 121] }, { 33: [2, 59], 65: [2, 59], 72: [2, 59], 75: [2, 59], 80: [2, 59], 81: [2, 59], 82: [2, 59], 83: [2, 59], 84: [2, 59], 85: [2, 59] }, { 33: [2, 61], 75: [2, 61] }, { 33: [2, 68], 37: 122, 74: 123, 75: [1, 121] }, { 33: [2, 65], 65: [2, 65], 72: [2, 65], 75: [2, 65], 80: [2, 65], 81: [2, 65], 82: [2, 65], 83: [2, 65], 84: [2, 65], 85: [2, 65] }, { 33: [2, 67], 75: [2, 67] }, { 23: [1, 124] }, { 23: [2, 51], 65: [2, 51], 72: [2, 51], 80: [2, 51], 81: [2, 51], 82: [2, 51], 83: [2, 51], 84: [2, 51], 85: [2, 51] }, { 23: [2, 53] }, { 33: [1, 125] }, { 33: [2, 91], 65: [2, 91], 72: [2, 91], 80: [2, 91], 81: [2, 91], 82: [2, 91], 83: [2, 91], 84: [2, 91], 85: [2, 91] }, { 33: [2, 93] }, { 5: [2, 22], 14: [2, 22], 15: [2, 22], 19: [2, 22], 29: [2, 22], 34: [2, 22], 39: [2, 22], 44: [2, 22], 47: [2, 22], 48: [2, 22], 51: [2, 22], 55: [2, 22], 60: [2, 22] }, { 23: [2, 99], 33: [2, 99], 54: [2, 99], 68: [2, 99], 72: [2, 99], 75: [2, 99] }, { 73: [1, 109] }, { 20: 75, 63: 126, 64: 76, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 23], 14: [2, 23], 15: [2, 23], 19: [2, 23], 29: [2, 23], 34: [2, 23], 39: [2, 23], 44: [2, 23], 47: [2, 23], 48: [2, 23], 51: [2, 23], 55: [2, 23], 60: [2, 23] }, { 47: [2, 19] }, { 47: [2, 77] }, { 20: 75, 33: [2, 72], 41: 127, 63: 128, 64: 76, 65: [1, 44], 69: 129, 70: 77, 71: 78, 72: [1, 79], 75: [2, 72], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 24], 14: [2, 24], 15: [2, 24], 19: [2, 24], 29: [2, 24], 34: [2, 24], 39: [2, 24], 44: [2, 24], 47: [2, 24], 48: [2, 24], 51: [2, 24], 55: [2, 24], 60: [2, 24] }, { 68: [1, 130] }, { 65: [2, 95], 68: [2, 95], 72: [2, 95], 80: [2, 95], 81: [2, 95], 82: [2, 95], 83: [2, 95], 84: [2, 95], 85: [2, 95] }, { 68: [2, 97] }, { 5: [2, 21], 14: [2, 21], 15: [2, 21], 19: [2, 21], 29: [2, 21], 34: [2, 21], 39: [2, 21], 44: [2, 21], 47: [2, 21], 48: [2, 21], 51: [2, 21], 55: [2, 21], 60: [2, 21] }, { 33: [1, 131] }, { 33: [2, 63] }, { 72: [1, 133], 76: 132 }, { 33: [1, 134] }, { 33: [2, 69] }, { 15: [2, 12] }, { 14: [2, 26], 15: [2, 26], 19: [2, 26], 29: [2, 26], 34: [2, 26], 47: [2, 26], 48: [2, 26], 51: [2, 26], 55: [2, 26], 60: [2, 26] }, { 23: [2, 31], 33: [2, 31], 54: [2, 31], 68: [2, 31], 72: [2, 31], 75: [2, 31] }, { 33: [2, 74], 42: 135, 74: 136, 75: [1, 121] }, { 33: [2, 71], 65: [2, 71], 72: [2, 71], 75: [2, 71], 80: [2, 71], 81: [2, 71], 82: [2, 71], 83: [2, 71], 84: [2, 71], 85: [2, 71] }, { 33: [2, 73], 75: [2, 73] }, { 23: [2, 29], 33: [2, 29], 54: [2, 29], 65: [2, 29], 68: [2, 29], 72: [2, 29], 75: [2, 29], 80: [2, 29], 81: [2, 29], 82: [2, 29], 83: [2, 29], 84: [2, 29], 85: [2, 29] }, { 14: [2, 15], 15: [2, 15], 19: [2, 15], 29: [2, 15], 34: [2, 15], 39: [2, 15], 44: [2, 15], 47: [2, 15], 48: [2, 15], 51: [2, 15], 55: [2, 15], 60: [2, 15] }, { 72: [1, 138], 77: [1, 137] }, { 72: [2, 100], 77: [2, 100] }, { 14: [2, 16], 15: [2, 16], 19: [2, 16], 29: [2, 16], 34: [2, 16], 44: [2, 16], 47: [2, 16], 48: [2, 16], 51: [2, 16], 55: [2, 16], 60: [2, 16] }, { 33: [1, 139] }, { 33: [2, 75] }, { 33: [2, 32] }, { 72: [2, 101], 77: [2, 101] }, { 14: [2, 17], 15: [2, 17], 19: [2, 17], 29: [2, 17], 34: [2, 17], 39: [2, 17], 44: [2, 17], 47: [2, 17], 48: [2, 17], 51: [2, 17], 55: [2, 17], 60: [2, 17] }], + defaultActions: { 4: [2, 1], 55: [2, 55], 57: [2, 20], 61: [2, 57], 74: [2, 81], 83: [2, 85], 87: [2, 18], 91: [2, 89], 102: [2, 53], 105: [2, 93], 111: [2, 19], 112: [2, 77], 117: [2, 97], 120: [2, 63], 123: [2, 69], 124: [2, 12], 136: [2, 75], 137: [2, 32] }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + lstack = [], + table = this.table, + yytext = "", + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected }); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column }; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + /* Jison generated lexer */ + var lexer = (function () { + var lexer = { EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function setInput(input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 }; + if (this.options.ranges) this.yylloc.range = [0, 0]; + this.offset = 0; + return this; + }, + input: function input() { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput: function unput(ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) this.yylineno -= lines.length - 1; + var r = this.yylloc.range; + + this.yylloc = { first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more: function more() { + this._more = true; + return this; + }, + less: function less(n) { + this.unput(this.match.slice(n)); + }, + pastInput: function pastInput() { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput: function upcomingInput() { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + showPosition: function showPosition() { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + next: function next() { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, match, tempMatch, index, col, lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) this.done = false; + if (token) return token;else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { text: "", token: null, line: this.yylineno }); + } + }, + lex: function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + }, + topState: function topState() { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } }; + lexer.options = {}; + lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START + /**/) { + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng - end); + } + + var YYSTATE = YY_START; + switch ($avoiding_name_collisions) { + case 0: + if (yy_.yytext.slice(-2) === "\\\\") { + strip(0, 1); + this.begin("mu"); + } else if (yy_.yytext.slice(-1) === "\\") { + strip(0, 1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if (yy_.yytext) return 15; + + break; + case 1: + return 15; + break; + case 2: + this.popState(); + return 15; + + break; + case 3: + this.begin('raw');return 15; + break; + case 4: + this.popState(); + // Should be using `this.topState()` below, but it currently + // returns the second top instead of the first top. Opened an + // issue about it at https://github.com/zaach/jison/issues/291 + if (this.conditionStack[this.conditionStack.length - 1] === 'raw') { + return 15; + } else { + yy_.yytext = yy_.yytext.substr(5, yy_.yyleng - 9); + return 'END_RAW_BLOCK'; + } + + break; + case 5: + return 15; + break; + case 6: + this.popState(); + return 14; + + break; + case 7: + return 65; + break; + case 8: + return 68; + break; + case 9: + return 19; + break; + case 10: + this.popState(); + this.begin('raw'); + return 23; + + break; + case 11: + return 55; + break; + case 12: + return 60; + break; + case 13: + return 29; + break; + case 14: + return 47; + break; + case 15: + this.popState();return 44; + break; + case 16: + this.popState();return 44; + break; + case 17: + return 34; + break; + case 18: + return 39; + break; + case 19: + return 51; + break; + case 20: + return 48; + break; + case 21: + this.unput(yy_.yytext); + this.popState(); + this.begin('com'); + + break; + case 22: + this.popState(); + return 14; + + break; + case 23: + return 48; + break; + case 24: + return 73; + break; + case 25: + return 72; + break; + case 26: + return 72; + break; + case 27: + return 87; + break; + case 28: + // ignore whitespace + break; + case 29: + this.popState();return 54; + break; + case 30: + this.popState();return 33; + break; + case 31: + yy_.yytext = strip(1, 2).replace(/\\"/g, '"');return 80; + break; + case 32: + yy_.yytext = strip(1, 2).replace(/\\'/g, "'");return 80; + break; + case 33: + return 85; + break; + case 34: + return 82; + break; + case 35: + return 82; + break; + case 36: + return 83; + break; + case 37: + return 84; + break; + case 38: + return 81; + break; + case 39: + return 75; + break; + case 40: + return 77; + break; + case 41: + return 72; + break; + case 42: + yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');return 72; + break; + case 43: + return 'INVALID'; + break; + case 44: + return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/]; + lexer.conditions = { "mu": { "rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], "inclusive": false }, "emu": { "rules": [2], "inclusive": false }, "com": { "rules": [6], "inclusive": false }, "raw": { "rules": [3, 4, 5], "inclusive": false }, "INITIAL": { "rules": [0, 1, 44], "inclusive": true } }; + return lexer; + })(); + parser.lexer = lexer; + function Parser() { + this.yy = {}; + }Parser.prototype = parser;parser.Parser = Parser; + return new Parser(); + })();exports.__esModule = true; + exports['default'] = handlebars; + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _visitor = __webpack_require__(25); + + var _visitor2 = _interopRequireDefault(_visitor); + + function WhitespaceControl() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + this.options = options; + } + WhitespaceControl.prototype = new _visitor2['default'](); + + WhitespaceControl.prototype.Program = function (program) { + var doStandalone = !this.options.ignoreStandalone; + + var isRoot = !this.isRootSeen; + this.isRootSeen = true; + + var body = program.body; + for (var i = 0, l = body.length; i < l; i++) { + var current = body[i], + strip = this.accept(current); + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), + _isNextWhitespace = isNextWhitespace(body, i, isRoot), + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.close) { + omitRight(body, i, true); + } + if (strip.open) { + omitLeft(body, i, true); + } + + if (doStandalone && inlineStandalone) { + omitRight(body, i); + + if (omitLeft(body, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'PartialStatement') { + // Pull out the whitespace from the final line + current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; + } + } + } + if (doStandalone && openStandalone) { + omitRight((current.program || current.inverse).body); + + // Strip out the previous content node if it's whitespace only + omitLeft(body, i); + } + if (doStandalone && closeStandalone) { + // Always strip the next node + omitRight(body, i); + + omitLeft((current.inverse || current.program).body); + } + } + + return program; + }; + + WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) { + this.accept(block.program); + this.accept(block.inverse); + + // Find the inverse program that is involed with whitespace stripping. + var program = block.program || block.inverse, + inverse = block.program && block.inverse, + firstInverse = inverse, + lastInverse = inverse; + + if (inverse && inverse.chained) { + firstInverse = inverse.body[0].program; + + // Walk the inverse chain to find the last inverse that is actually in the chain. + while (lastInverse.chained) { + lastInverse = lastInverse.body[lastInverse.body.length - 1].program; + } + } + + var strip = { + open: block.openStrip.open, + close: block.closeStrip.close, + + // Determine the standalone candiacy. Basically flag our content as being possibly standalone + // so our parent can determine if we actually are standalone + openStandalone: isNextWhitespace(program.body), + closeStandalone: isPrevWhitespace((firstInverse || program).body) + }; + + if (block.openStrip.close) { + omitRight(program.body, null, true); + } + + if (inverse) { + var inverseStrip = block.inverseStrip; + + if (inverseStrip.open) { + omitLeft(program.body, null, true); + } + + if (inverseStrip.close) { + omitRight(firstInverse.body, null, true); + } + if (block.closeStrip.open) { + omitLeft(lastInverse.body, null, true); + } + + // Find standalone else statments + if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) { + omitLeft(program.body); + omitRight(firstInverse.body); + } + } else if (block.closeStrip.open) { + omitLeft(program.body, null, true); + } + + return strip; + }; + + WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) { + return mustache.strip; + }; + + WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) { + /* istanbul ignore next */ + var strip = node.strip || {}; + return { + inlineStandalone: true, + open: strip.open, + close: strip.close + }; + }; + + function isPrevWhitespace(body, i, isRoot) { + if (i === undefined) { + i = body.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = body[i - 1], + sibling = body[i - 2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'ContentStatement') { + return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original); + } + } + function isNextWhitespace(body, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = body[i + 1], + sibling = body[i + 2]; + if (!next) { + return isRoot; + } + + if (next.type === 'ContentStatement') { + return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original); + } + } + + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. + // + // If i is undefined, then the first child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitRight(body, i, multiple) { + var current = body[i == null ? 0 : i + 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) { + return; + } + + var original = current.value; + current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, ''); + current.rightStripped = current.value !== original; + } + + // Marks the node to the left of the position as omitted. + // I.e. ' '{{foo}} will mark the ' ' node as omitted. + // + // If i is undefined then the last child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitLeft(body, i, multiple) { + var current = body[i == null ? body.length - 1 : i - 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) { + return; + } + + // We omit the last node if it's whitespace only and not preceeded by a non-content node. + var original = current.value; + current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); + current.leftStripped = current.value !== original; + return current.leftStripped; + } + + exports['default'] = WhitespaceControl; + module.exports = exports['default']; + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function Visitor() { + this.parents = []; + } + + Visitor.prototype = { + constructor: Visitor, + mutating: false, + + // Visits a given value. If mutating, will replace the value if necessary. + acceptKey: function acceptKey(node, name) { + var value = this.accept(node[name]); + if (this.mutating) { + // Hacky sanity check: This may have a few false positives for type for the helper + // methods but will generally do the right thing without a lot of overhead. + if (value && !Visitor.prototype[value.type]) { + throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); + } + node[name] = value; + } + }, + + // Performs an accept operation with added sanity check to ensure + // required keys are not removed. + acceptRequired: function acceptRequired(node, name) { + this.acceptKey(node, name); + + if (!node[name]) { + throw new _exception2['default'](node.type + ' requires ' + name); + } + }, + + // Traverses a given array. If mutating, empty respnses will be removed + // for child elements. + acceptArray: function acceptArray(array) { + for (var i = 0, l = array.length; i < l; i++) { + this.acceptKey(array, i); + + if (!array[i]) { + array.splice(i, 1); + i--; + l--; + } + } + }, + + accept: function accept(object) { + if (!object) { + return; + } + + /* istanbul ignore next: Sanity code */ + if (!this[object.type]) { + throw new _exception2['default']('Unknown type: ' + object.type, object); + } + + if (this.current) { + this.parents.unshift(this.current); + } + this.current = object; + + var ret = this[object.type](object); + + this.current = this.parents.shift(); + + if (!this.mutating || ret) { + return ret; + } else if (ret !== false) { + return object; + } + }, + + Program: function Program(program) { + this.acceptArray(program.body); + }, + + MustacheStatement: visitSubExpression, + Decorator: visitSubExpression, + + BlockStatement: visitBlock, + DecoratorBlock: visitBlock, + + PartialStatement: visitPartial, + PartialBlockStatement: function PartialBlockStatement(partial) { + visitPartial.call(this, partial); + + this.acceptKey(partial, 'program'); + }, + + ContentStatement: function ContentStatement() /* content */{}, + CommentStatement: function CommentStatement() /* comment */{}, + + SubExpression: visitSubExpression, + + PathExpression: function PathExpression() /* path */{}, + + StringLiteral: function StringLiteral() /* string */{}, + NumberLiteral: function NumberLiteral() /* number */{}, + BooleanLiteral: function BooleanLiteral() /* bool */{}, + UndefinedLiteral: function UndefinedLiteral() /* literal */{}, + NullLiteral: function NullLiteral() /* literal */{}, + + Hash: function Hash(hash) { + this.acceptArray(hash.pairs); + }, + HashPair: function HashPair(pair) { + this.acceptRequired(pair, 'value'); + } + }; + + function visitSubExpression(mustache) { + this.acceptRequired(mustache, 'path'); + this.acceptArray(mustache.params); + this.acceptKey(mustache, 'hash'); + } + function visitBlock(block) { + visitSubExpression.call(this, block); + + this.acceptKey(block, 'program'); + this.acceptKey(block, 'inverse'); + } + function visitPartial(partial) { + this.acceptRequired(partial, 'name'); + this.acceptArray(partial.params); + this.acceptKey(partial, 'hash'); + } + + exports['default'] = Visitor; + module.exports = exports['default']; + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.SourceLocation = SourceLocation; + exports.id = id; + exports.stripFlags = stripFlags; + exports.stripComment = stripComment; + exports.preparePath = preparePath; + exports.prepareMustache = prepareMustache; + exports.prepareRawBlock = prepareRawBlock; + exports.prepareBlock = prepareBlock; + exports.prepareProgram = prepareProgram; + exports.preparePartialBlock = preparePartialBlock; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function validateClose(open, close) { + close = close.path ? close.path.original : close; + + if (open.path.original !== close) { + var errorNode = { loc: open.path.loc }; + + throw new _exception2['default'](open.path.original + " doesn't match " + close, errorNode); + } + } + + function SourceLocation(source, locInfo) { + this.source = source; + this.start = { + line: locInfo.first_line, + column: locInfo.first_column + }; + this.end = { + line: locInfo.last_line, + column: locInfo.last_column + }; + } + + function id(token) { + if (/^\[.*\]$/.test(token)) { + return token.substr(1, token.length - 2); + } else { + return token; + } + } + + function stripFlags(open, close) { + return { + open: open.charAt(2) === '~', + close: close.charAt(close.length - 3) === '~' + }; + } + + function stripComment(comment) { + return comment.replace(/^\{\{~?\!-?-?/, '').replace(/-?-?~?\}\}$/, ''); + } + + function preparePath(data, parts, loc) { + loc = this.locInfo(loc); + + var original = data ? '@' : '', + dig = [], + depth = 0, + depthString = ''; + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i].part, + + // If we have [] syntax then we do not treat path references as operators, + // i.e. foo.[this] resolves to approximately context.foo['this'] + isLiteral = parts[i].original !== part; + original += (parts[i].separator || '') + part; + + if (!isLiteral && (part === '..' || part === '.' || part === 'this')) { + if (dig.length > 0) { + throw new _exception2['default']('Invalid path: ' + original, { loc: loc }); + } else if (part === '..') { + depth++; + depthString += '../'; + } + } else { + dig.push(part); + } + } + + return { + type: 'PathExpression', + data: data, + depth: depth, + parts: dig, + original: original, + loc: loc + }; + } + + function prepareMustache(path, params, hash, open, strip, locInfo) { + // Must use charAt to support IE pre-10 + var escapeFlag = open.charAt(3) || open.charAt(2), + escaped = escapeFlag !== '{' && escapeFlag !== '&'; + + var decorator = /\*/.test(open); + return { + type: decorator ? 'Decorator' : 'MustacheStatement', + path: path, + params: params, + hash: hash, + escaped: escaped, + strip: strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareRawBlock(openRawBlock, contents, close, locInfo) { + validateClose(openRawBlock, close); + + locInfo = this.locInfo(locInfo); + var program = { + type: 'Program', + body: contents, + strip: {}, + loc: locInfo + }; + + return { + type: 'BlockStatement', + path: openRawBlock.path, + params: openRawBlock.params, + hash: openRawBlock.hash, + program: program, + openStrip: {}, + inverseStrip: {}, + closeStrip: {}, + loc: locInfo + }; + } + + function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + if (close && close.path) { + validateClose(openBlock, close); + } + + var decorator = /\*/.test(openBlock.open); + + program.blockParams = openBlock.blockParams; + + var inverse = undefined, + inverseStrip = undefined; + + if (inverseAndProgram) { + if (decorator) { + throw new _exception2['default']('Unexpected inverse block on decorator', inverseAndProgram); + } + + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip; + } + + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } + + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } + + return { + type: decorator ? 'DecoratorBlock' : 'BlockStatement', + path: openBlock.path, + params: openBlock.params, + hash: openBlock.hash, + program: program, + inverse: inverse, + openStrip: openBlock.strip, + inverseStrip: inverseStrip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareProgram(statements, loc) { + if (!loc && statements.length) { + var firstLoc = statements[0].loc, + lastLoc = statements[statements.length - 1].loc; + + /* istanbul ignore else */ + if (firstLoc && lastLoc) { + loc = { + source: firstLoc.source, + start: { + line: firstLoc.start.line, + column: firstLoc.start.column + }, + end: { + line: lastLoc.end.line, + column: lastLoc.end.column + } + }; + } + } + + return { + type: 'Program', + body: statements, + strip: {}, + loc: loc + }; + } + + function preparePartialBlock(open, program, close, locInfo) { + validateClose(open, close); + + return { + type: 'PartialBlockStatement', + name: open.path, + params: open.params, + hash: open.hash, + program: program, + openStrip: open.strip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + /* eslint-disable new-cap */ + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.Compiler = Compiler; + exports.precompile = precompile; + exports.compile = compile; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _ast = __webpack_require__(21); + + var _ast2 = _interopRequireDefault(_ast); + + var slice = [].slice; + + function Compiler() {} + + // the foundHelper register will disambiguate helper lookup from finding a + // function in a context. This is necessary for mustache compatibility, which + // requires that context functions in blocks are evaluated by blockHelperMissing, + // and then proceed as if the resulting value was provided to blockHelperMissing. + + Compiler.prototype = { + compiler: Compiler, + + equals: function equals(other) { + var len = this.opcodes.length; + if (other.opcodes.length !== len) { + return false; + } + + for (var i = 0; i < len; i++) { + var opcode = this.opcodes[i], + otherOpcode = other.opcodes[i]; + if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { + return false; + } + } + + // We know that length is the same between the two arrays because they are directly tied + // to the opcode behavior above. + len = this.children.length; + for (var i = 0; i < len; i++) { + if (!this.children[i].equals(other.children[i])) { + return false; + } + } + + return true; + }, + + guid: 0, + + compile: function compile(program, options) { + this.sourceNode = []; + this.opcodes = []; + this.children = []; + this.options = options; + this.stringParams = options.stringParams; + this.trackIds = options.trackIds; + + options.blockParams = options.blockParams || []; + + // These changes will propagate to the other compiler components + var knownHelpers = options.knownHelpers; + options.knownHelpers = { + 'helperMissing': true, + 'blockHelperMissing': true, + 'each': true, + 'if': true, + 'unless': true, + 'with': true, + 'log': true, + 'lookup': true + }; + if (knownHelpers) { + for (var _name in knownHelpers) { + /* istanbul ignore else */ + if (_name in knownHelpers) { + options.knownHelpers[_name] = knownHelpers[_name]; + } + } + } + + return this.accept(program); + }, + + compileProgram: function compileProgram(program) { + var childCompiler = new this.compiler(), + // eslint-disable-line new-cap + result = childCompiler.compile(program, this.options), + guid = this.guid++; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + this.useDepths = this.useDepths || result.useDepths; + + return guid; + }, + + accept: function accept(node) { + /* istanbul ignore next: Sanity code */ + if (!this[node.type]) { + throw new _exception2['default']('Unknown type: ' + node.type, node); + } + + this.sourceNode.unshift(node); + var ret = this[node.type](node); + this.sourceNode.shift(); + return ret; + }, + + Program: function Program(program) { + this.options.blockParams.unshift(program.blockParams); + + var body = program.body, + bodyLength = body.length; + for (var i = 0; i < bodyLength; i++) { + this.accept(body[i]); + } + + this.options.blockParams.shift(); + + this.isSimple = bodyLength === 1; + this.blockParams = program.blockParams ? program.blockParams.length : 0; + + return this; + }, + + BlockStatement: function BlockStatement(block) { + transformLiteralToPath(block); + + var program = block.program, + inverse = block.inverse; + + program = program && this.compileProgram(program); + inverse = inverse && this.compileProgram(inverse); + + var type = this.classifySexpr(block); + + if (type === 'helper') { + this.helperSexpr(block, program, inverse); + } else if (type === 'simple') { + this.simpleSexpr(block); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('blockValue', block.path.original); + } else { + this.ambiguousSexpr(block, program, inverse); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('ambiguousBlockValue'); + } + + this.opcode('append'); + }, + + DecoratorBlock: function DecoratorBlock(decorator) { + var program = decorator.program && this.compileProgram(decorator.program); + var params = this.setupFullMustacheParams(decorator, program, undefined), + path = decorator.path; + + this.useDecorators = true; + this.opcode('registerDecorator', params.length, path.original); + }, + + PartialStatement: function PartialStatement(partial) { + this.usePartial = true; + + var program = partial.program; + if (program) { + program = this.compileProgram(partial.program); + } + + var params = partial.params; + if (params.length > 1) { + throw new _exception2['default']('Unsupported number of partial arguments: ' + params.length, partial); + } else if (!params.length) { + if (this.options.explicitPartialContext) { + this.opcode('pushLiteral', 'undefined'); + } else { + params.push({ type: 'PathExpression', parts: [], depth: 0 }); + } + } + + var partialName = partial.name.original, + isDynamic = partial.name.type === 'SubExpression'; + if (isDynamic) { + this.accept(partial.name); + } + + this.setupFullMustacheParams(partial, program, undefined, true); + + var indent = partial.indent || ''; + if (this.options.preventIndent && indent) { + this.opcode('appendContent', indent); + indent = ''; + } + + this.opcode('invokePartial', isDynamic, partialName, indent); + this.opcode('append'); + }, + PartialBlockStatement: function PartialBlockStatement(partialBlock) { + this.PartialStatement(partialBlock); + }, + + MustacheStatement: function MustacheStatement(mustache) { + this.SubExpression(mustache); + + if (mustache.escaped && !this.options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + Decorator: function Decorator(decorator) { + this.DecoratorBlock(decorator); + }, + + ContentStatement: function ContentStatement(content) { + if (content.value) { + this.opcode('appendContent', content.value); + } + }, + + CommentStatement: function CommentStatement() {}, + + SubExpression: function SubExpression(sexpr) { + transformLiteralToPath(sexpr); + var type = this.classifySexpr(sexpr); + + if (type === 'simple') { + this.simpleSexpr(sexpr); + } else if (type === 'helper') { + this.helperSexpr(sexpr); + } else { + this.ambiguousSexpr(sexpr); + } + }, + ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) { + var path = sexpr.path, + name = path.parts[0], + isBlock = program != null || inverse != null; + + this.opcode('getContext', path.depth); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + path.strict = true; + this.accept(path); + + this.opcode('invokeAmbiguous', name, isBlock); + }, + + simpleSexpr: function simpleSexpr(sexpr) { + var path = sexpr.path; + path.strict = true; + this.accept(path); + this.opcode('resolvePossibleLambda'); + }, + + helperSexpr: function helperSexpr(sexpr, program, inverse) { + var params = this.setupFullMustacheParams(sexpr, program, inverse), + path = sexpr.path, + name = path.parts[0]; + + if (this.options.knownHelpers[name]) { + this.opcode('invokeKnownHelper', params.length, name); + } else if (this.options.knownHelpersOnly) { + throw new _exception2['default']('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr); + } else { + path.strict = true; + path.falsy = true; + + this.accept(path); + this.opcode('invokeHelper', params.length, path.original, _ast2['default'].helpers.simpleId(path)); + } + }, + + PathExpression: function PathExpression(path) { + this.addDepth(path.depth); + this.opcode('getContext', path.depth); + + var name = path.parts[0], + scoped = _ast2['default'].helpers.scopedId(path), + blockParamId = !path.depth && !scoped && this.blockParamIndex(name); + + if (blockParamId) { + this.opcode('lookupBlockParam', blockParamId, path.parts); + } else if (!name) { + // Context reference, i.e. `{{foo .}}` or `{{foo ..}}` + this.opcode('pushContext'); + } else if (path.data) { + this.options.data = true; + this.opcode('lookupData', path.depth, path.parts, path.strict); + } else { + this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped); + } + }, + + StringLiteral: function StringLiteral(string) { + this.opcode('pushString', string.value); + }, + + NumberLiteral: function NumberLiteral(number) { + this.opcode('pushLiteral', number.value); + }, + + BooleanLiteral: function BooleanLiteral(bool) { + this.opcode('pushLiteral', bool.value); + }, + + UndefinedLiteral: function UndefinedLiteral() { + this.opcode('pushLiteral', 'undefined'); + }, + + NullLiteral: function NullLiteral() { + this.opcode('pushLiteral', 'null'); + }, + + Hash: function Hash(hash) { + var pairs = hash.pairs, + i = 0, + l = pairs.length; + + this.opcode('pushHash'); + + for (; i < l; i++) { + this.pushParam(pairs[i].value); + } + while (i--) { + this.opcode('assignToHash', pairs[i].key); + } + this.opcode('popHash'); + }, + + // HELPERS + opcode: function opcode(name) { + this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc }); + }, + + addDepth: function addDepth(depth) { + if (!depth) { + return; + } + + this.useDepths = true; + }, + + classifySexpr: function classifySexpr(sexpr) { + var isSimple = _ast2['default'].helpers.simpleId(sexpr.path); + + var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var isHelper = !isBlockParam && _ast2['default'].helpers.helperExpression(sexpr); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + var isEligible = !isBlockParam && (isHelper || isSimple); + + // if ambiguous, we can possibly resolve the ambiguity now + // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. + if (isEligible && !isHelper) { + var _name2 = sexpr.path.parts[0], + options = this.options; + + if (options.knownHelpers[_name2]) { + isHelper = true; + } else if (options.knownHelpersOnly) { + isEligible = false; + } + } + + if (isHelper) { + return 'helper'; + } else if (isEligible) { + return 'ambiguous'; + } else { + return 'simple'; + } + }, + + pushParams: function pushParams(params) { + for (var i = 0, l = params.length; i < l; i++) { + this.pushParam(params[i]); + } + }, + + pushParam: function pushParam(val) { + var value = val.value != null ? val.value : val.original || ''; + + if (this.stringParams) { + if (value.replace) { + value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.'); + } + + if (val.depth) { + this.addDepth(val.depth); + } + this.opcode('getContext', val.depth || 0); + this.opcode('pushStringParam', value, val.type); + + if (val.type === 'SubExpression') { + // SubExpressions get evaluated and passed in + // in string params mode. + this.accept(val); + } + } else { + if (this.trackIds) { + var blockParamIndex = undefined; + if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) { + blockParamIndex = this.blockParamIndex(val.parts[0]); + } + if (blockParamIndex) { + var blockParamChild = val.parts.slice(1).join('.'); + this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild); + } else { + value = val.original || value; + if (value.replace) { + value = value.replace(/^this(?:\.|$)/, '').replace(/^\.\//, '').replace(/^\.$/, ''); + } + + this.opcode('pushId', val.type, value); + } + } + this.accept(val); + } + }, + + setupFullMustacheParams: function setupFullMustacheParams(sexpr, program, inverse, omitEmpty) { + var params = sexpr.params; + this.pushParams(params); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + if (sexpr.hash) { + this.accept(sexpr.hash); + } else { + this.opcode('emptyHash', omitEmpty); + } + + return params; + }, + + blockParamIndex: function blockParamIndex(name) { + for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) { + var blockParams = this.options.blockParams[depth], + param = blockParams && _utils.indexOf(blockParams, name); + if (blockParams && param >= 0) { + return [depth, param]; + } + } + } + }; + + function precompile(input, options, env) { + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options); + return new env.JavaScriptCompiler().compile(environment, options); + } + + function compile(input, options, env) { + if (options === undefined) options = {}; + + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input); + } + + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var compiled = undefined; + + function compileInput() { + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options), + templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + return env.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + function ret(context, execOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled.call(this, context, execOptions); + } + ret._setup = function (setupOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._setup(setupOptions); + }; + ret._child = function (i, data, blockParams, depths) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._child(i, data, blockParams, depths); + }; + return ret; + } + + function argEquals(a, b) { + if (a === b) { + return true; + } + + if (_utils.isArray(a) && _utils.isArray(b) && a.length === b.length) { + for (var i = 0; i < a.length; i++) { + if (!argEquals(a[i], b[i])) { + return false; + } + } + return true; + } + } + + function transformLiteralToPath(sexpr) { + if (!sexpr.path.parts) { + var literal = sexpr.path; + // Casting to string here to make false and 0 literal values play nicely with the rest + // of the system. + sexpr.path = { + type: 'PathExpression', + data: false, + depth: 0, + parts: [literal.original + ''], + original: literal.original + '', + loc: literal.loc + }; + } + } + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _base = __webpack_require__(4); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _codeGen = __webpack_require__(29); + + var _codeGen2 = _interopRequireDefault(_codeGen); + + function Literal(value) { + this.value = value; + } + + function JavaScriptCompiler() {} + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function nameLookup(parent, name /* , type*/) { + if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return [parent, '.', name]; + } else { + return [parent, '[', JSON.stringify(name), ']']; + } + }, + depthedLookup: function depthedLookup(name) { + return [this.aliasable('container.lookup'), '(depths, "', name, '")']; + }, + + compilerInfo: function compilerInfo() { + var revision = _base.COMPILER_REVISION, + versions = _base.REVISION_CHANGES[revision]; + return [revision, versions]; + }, + + appendToBuffer: function appendToBuffer(source, location, explicit) { + // Force a source as this simplifies the merge logic. + if (!_utils.isArray(source)) { + source = [source]; + } + source = this.source.wrap(source, location); + + if (this.environment.isSimple) { + return ['return ', source, ';']; + } else if (explicit) { + // This is a case where the buffer operation occurs as a child of another + // construct, generally braces. We have to explicitly output these buffer + // operations to ensure that the emitted code goes in the correct location. + return ['buffer += ', source, ';']; + } else { + source.appendToBuffer = true; + return source; + } + }, + + initializeBuffer: function initializeBuffer() { + return this.quotedString(''); + }, + // END PUBLIC API + + compile: function compile(environment, options, context, asObject) { + this.environment = environment; + this.options = options; + this.stringParams = this.options.stringParams; + this.trackIds = this.options.trackIds; + this.precompile = !asObject; + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + decorators: [], + programs: [], + environments: [] + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.aliases = {}; + this.registers = { list: [] }; + this.hashes = []; + this.compileStack = []; + this.inlineStack = []; + this.blockParams = []; + + this.compileChildren(environment, options); + + this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat; + this.useBlockParams = this.useBlockParams || environment.useBlockParams; + + var opcodes = environment.opcodes, + opcode = undefined, + firstLoc = undefined, + i = undefined, + l = undefined; + + for (i = 0, l = opcodes.length; i < l; i++) { + opcode = opcodes[i]; + + this.source.currentLocation = opcode.loc; + firstLoc = firstLoc || opcode.loc; + this[opcode.opcode].apply(this, opcode.args); + } + + // Flush any trailing content that might be pending. + this.source.currentLocation = firstLoc; + this.pushSource(''); + + /* istanbul ignore next */ + if (this.stackSlot || this.inlineStack.length || this.compileStack.length) { + throw new _exception2['default']('Compile completed with content left on stack'); + } + + if (!this.decorators.isEmpty()) { + this.useDecorators = true; + + this.decorators.prepend('var decorators = container.decorators;\n'); + this.decorators.push('return fn;'); + + if (asObject) { + this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]); + } else { + this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n'); + this.decorators.push('}\n'); + this.decorators = this.decorators.merge(); + } + } else { + this.decorators = undefined; + } + + var fn = this.createFunctionContext(asObject); + if (!this.isChild) { + var ret = { + compiler: this.compilerInfo(), + main: fn + }; + + if (this.decorators) { + ret.main_d = this.decorators; // eslint-disable-line camelcase + ret.useDecorators = true; + } + + var _context = this.context; + var programs = _context.programs; + var decorators = _context.decorators; + + for (i = 0, l = programs.length; i < l; i++) { + if (programs[i]) { + ret[i] = programs[i]; + if (decorators[i]) { + ret[i + '_d'] = decorators[i]; + ret.useDecorators = true; + } + } + } + + if (this.environment.usePartial) { + ret.usePartial = true; + } + if (this.options.data) { + ret.useData = true; + } + if (this.useDepths) { + ret.useDepths = true; + } + if (this.useBlockParams) { + ret.useBlockParams = true; + } + if (this.options.compat) { + ret.compat = true; + } + + if (!asObject) { + ret.compiler = JSON.stringify(ret.compiler); + + this.source.currentLocation = { start: { line: 1, column: 0 } }; + ret = this.objectLiteral(ret); + + if (options.srcName) { + ret = ret.toStringWithSourceMap({ file: options.destName }); + ret.map = ret.map && ret.map.toString(); + } else { + ret = ret.toString(); + } + } else { + ret.compilerOptions = this.options; + } + + return ret; + } else { + return fn; + } + }, + + preamble: function preamble() { + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = new _codeGen2['default'](this.options.srcName); + this.decorators = new _codeGen2['default'](this.options.srcName); + }, + + createFunctionContext: function createFunctionContext(asObject) { + var varDeclarations = ''; + + var locals = this.stackVars.concat(this.registers.list); + if (locals.length > 0) { + varDeclarations += ', ' + locals.join(', '); + } + + // Generate minimizer alias mappings + // + // When using true SourceNodes, this will update all references to the given alias + // as the source nodes are reused in situ. For the non-source node compilation mode, + // aliases will not be used, but this case is already being run on the client and + // we aren't concern about minimizing the template size. + var aliasCount = 0; + for (var alias in this.aliases) { + // eslint-disable-line guard-for-in + var node = this.aliases[alias]; + + if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) { + varDeclarations += ', alias' + ++aliasCount + '=' + alias; + node.children[0] = 'alias' + aliasCount; + } + } + + var params = ['container', 'depth0', 'helpers', 'partials', 'data']; + + if (this.useBlockParams || this.useDepths) { + params.push('blockParams'); + } + if (this.useDepths) { + params.push('depths'); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(varDeclarations); + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']); + } + }, + mergeSource: function mergeSource(varDeclarations) { + var isSimple = this.environment.isSimple, + appendOnly = !this.forceBuffer, + appendFirst = undefined, + sourceSeen = undefined, + bufferStart = undefined, + bufferEnd = undefined; + this.source.each(function (line) { + if (line.appendToBuffer) { + if (bufferStart) { + line.prepend(' + '); + } else { + bufferStart = line; + } + bufferEnd = line; + } else { + if (bufferStart) { + if (!sourceSeen) { + appendFirst = true; + } else { + bufferStart.prepend('buffer += '); + } + bufferEnd.add(';'); + bufferStart = bufferEnd = undefined; + } + + sourceSeen = true; + if (!isSimple) { + appendOnly = false; + } + } + }); + + if (appendOnly) { + if (bufferStart) { + bufferStart.prepend('return '); + bufferEnd.add(';'); + } else if (!sourceSeen) { + this.source.push('return "";'); + } + } else { + varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + + if (bufferStart) { + bufferStart.prepend('return buffer + '); + bufferEnd.add(';'); + } else { + this.source.push('return buffer;'); + } + } + + if (varDeclarations) { + this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')); + } + + return this.source.merge(); + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function blockValue(name) { + var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs(name, 0, params); + + var blockName = this.popStack(); + params.splice(1, 0, blockName); + + this.push(this.source.functionCall(blockHelperMissing, 'call', params)); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function ambiguousBlockValue() { + // We're being a bit cheeky and reusing the options value from the prior exec + var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs('', 0, params, true); + + this.flushInline(); + + var current = this.topStack(); + params.splice(1, 0, current); + + this.pushSource(['if (!', this.lastHelper, ') { ', current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), '}']); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function appendContent(content) { + if (this.pendingContent) { + content = this.pendingContent + content; + } else { + this.pendingLocation = this.source.currentLocation; + } + + this.pendingContent = content; + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function append() { + if (this.isInline()) { + this.replaceStack(function (current) { + return [' != null ? ', current, ' : ""']; + }); + + this.pushSource(this.appendToBuffer(this.popStack())); + } else { + var local = this.popStack(); + this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']); + if (this.environment.isSimple) { + this.pushSource(['else { ', this.appendToBuffer("''", undefined, true), ' }']); + } + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function appendEscaped() { + this.pushSource(this.appendToBuffer([this.aliasable('container.escapeExpression'), '(', this.popStack(), ')'])); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function getContext(depth) { + this.lastContext = depth; + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function pushContext() { + this.pushStackLiteral(this.contextName(this.lastContext)); + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function lookupOnContext(parts, falsy, strict, scoped) { + var i = 0; + + if (!scoped && this.options.compat && !this.lastContext) { + // The depthed query is expected to handle the undefined logic for the root level that + // is implemented below, so we evaluate that directly in compat mode + this.push(this.depthedLookup(parts[i++])); + } else { + this.pushContext(); + } + + this.resolvePath('context', parts, i, falsy, strict); + }, + + // [lookupBlockParam] + // + // On stack, before: ... + // On stack, after: blockParam[name], ... + // + // Looks up the value of `parts` on the given block param and pushes + // it onto the stack. + lookupBlockParam: function lookupBlockParam(blockParamId, parts) { + this.useBlockParams = true; + + this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']); + this.resolvePath('context', parts, 1); + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data, ... + // + // Push the data lookup operator + lookupData: function lookupData(depth, parts, strict) { + if (!depth) { + this.pushStackLiteral('data'); + } else { + this.pushStackLiteral('container.data(data, ' + depth + ')'); + } + + this.resolvePath('data', parts, 0, true, strict); + }, + + resolvePath: function resolvePath(type, parts, i, falsy, strict) { + // istanbul ignore next + + var _this = this; + + if (this.options.strict || this.options.assumeObjects) { + this.push(strictLookup(this.options.strict && strict, this, parts, type)); + return; + } + + var len = parts.length; + for (; i < len; i++) { + /* eslint-disable no-loop-func */ + this.replaceStack(function (current) { + var lookup = _this.nameLookup(current, parts[i], type); + // We want to ensure that zero and false are handled properly if the context (falsy flag) + // needs to have the special handling for these values. + if (!falsy) { + return [' != null ? ', lookup, ' : ', current]; + } else { + // Otherwise we can use generic falsy handling + return [' && ', lookup]; + } + }); + /* eslint-enable no-loop-func */ + } + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function resolvePossibleLambda() { + this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function pushStringParam(string, type) { + this.pushContext(); + this.pushString(type); + + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'SubExpression') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + } + }, + + emptyHash: function emptyHash(omitEmpty) { + if (this.trackIds) { + this.push('{}'); // hashIds + } + if (this.stringParams) { + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes + } + this.pushStackLiteral(omitEmpty ? 'undefined' : '{}'); + }, + pushHash: function pushHash() { + if (this.hash) { + this.hashes.push(this.hash); + } + this.hash = { values: [], types: [], contexts: [], ids: [] }; + }, + popHash: function popHash() { + var hash = this.hash; + this.hash = this.hashes.pop(); + + if (this.trackIds) { + this.push(this.objectLiteral(hash.ids)); + } + if (this.stringParams) { + this.push(this.objectLiteral(hash.contexts)); + this.push(this.objectLiteral(hash.types)); + } + + this.push(this.objectLiteral(hash.values)); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function pushString(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function pushLiteral(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function pushProgram(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [registerDecorator] + // + // On stack, before: hash, program, params..., ... + // On stack, after: ... + // + // Pops off the decorator's parameters, invokes the decorator, + // and inserts the decorator into the decorators list. + registerDecorator: function registerDecorator(paramSize, name) { + var foundDecorator = this.nameLookup('decorators', name, 'decorator'), + options = this.setupHelperArgs(name, paramSize); + + this.decorators.push(['fn = ', this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), ' || fn;']); + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function invokeHelper(paramSize, name, isSimple) { + var nonHelper = this.popStack(), + helper = this.setupHelper(paramSize, name), + simple = isSimple ? [helper.name, ' || '] : ''; + + var lookup = ['('].concat(simple, nonHelper); + if (!this.options.strict) { + lookup.push(' || ', this.aliasable('helpers.helperMissing')); + } + lookup.push(')'); + + this.push(this.source.functionCall(lookup, 'call', helper.callParams)); + }, + + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function invokeKnownHelper(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(this.source.functionCall(helper.name, 'call', helper.callParams)); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function invokeAmbiguous(name, helperCall) { + this.useRegister('helper'); + + var nonHelper = this.popStack(); + + this.emptyHash(); + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + var lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')']; + if (!this.options.strict) { + lookup[0] = '(helper = '; + lookup.push(' != null ? helper : ', this.aliasable('helpers.helperMissing')); + } + + this.push(['(', lookup, helper.paramsInit ? ['),(', helper.paramsInit] : [], '),', '(typeof helper === ', this.aliasable('"function"'), ' ? ', this.source.functionCall('helper', 'call', helper.callParams), ' : helper))']); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function invokePartial(isDynamic, name, indent) { + var params = [], + options = this.setupParams(name, 1, params); + + if (isDynamic) { + name = this.popStack(); + delete options.name; + } + + if (indent) { + options.indent = JSON.stringify(indent); + } + options.helpers = 'helpers'; + options.partials = 'partials'; + options.decorators = 'container.decorators'; + + if (!isDynamic) { + params.unshift(this.nameLookup('partials', name, 'partial')); + } else { + params.unshift(name); + } + + if (this.options.compat) { + options.depths = 'depths'; + } + options = this.objectLiteral(options); + params.push(options); + + this.push(this.source.functionCall('container.invokePartial', '', params)); + }, + + // [assignToHash] + // + // On stack, before: value, ..., hash, ... + // On stack, after: ..., hash, ... + // + // Pops a value off the stack and assigns it to the current hash + assignToHash: function assignToHash(key) { + var value = this.popStack(), + context = undefined, + type = undefined, + id = undefined; + + if (this.trackIds) { + id = this.popStack(); + } + if (this.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts[key] = context; + } + if (type) { + hash.types[key] = type; + } + if (id) { + hash.ids[key] = id; + } + hash.values[key] = value; + }, + + pushId: function pushId(type, name, child) { + if (type === 'BlockParam') { + this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : '')); + } else if (type === 'PathExpression') { + this.pushString(name); + } else if (type === 'SubExpression') { + this.pushStackLiteral('true'); + } else { + this.pushStackLiteral('null'); + } + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function compileChildren(environment, options) { + var children = environment.children, + child = undefined, + compiler = undefined; + + for (var i = 0, l = children.length; i < l; i++) { + child = children[i]; + compiler = new this.compiler(); // eslint-disable-line new-cap + + var index = this.matchExistingProgram(child); + + if (index == null) { + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); + this.context.decorators[index] = compiler.decorators; + this.context.environments[index] = child; + + this.useDepths = this.useDepths || compiler.useDepths; + this.useBlockParams = this.useBlockParams || compiler.useBlockParams; + } else { + child.index = index; + child.name = 'program' + index; + + this.useDepths = this.useDepths || child.useDepths; + this.useBlockParams = this.useBlockParams || child.useBlockParams; + } + } + }, + matchExistingProgram: function matchExistingProgram(child) { + for (var i = 0, len = this.context.environments.length; i < len; i++) { + var environment = this.context.environments[i]; + if (environment && environment.equals(child)) { + return i; + } + } + }, + + programExpression: function programExpression(guid) { + var child = this.environment.children[guid], + programParams = [child.index, 'data', child.blockParams]; + + if (this.useBlockParams || this.useDepths) { + programParams.push('blockParams'); + } + if (this.useDepths) { + programParams.push('depths'); + } + + return 'container.program(' + programParams.join(', ') + ')'; + }, + + useRegister: function useRegister(name) { + if (!this.registers[name]) { + this.registers[name] = true; + this.registers.list.push(name); + } + }, + + push: function push(expr) { + if (!(expr instanceof Literal)) { + expr = this.source.wrap(expr); + } + + this.inlineStack.push(expr); + return expr; + }, + + pushStackLiteral: function pushStackLiteral(item) { + this.push(new Literal(item)); + }, + + pushSource: function pushSource(source) { + if (this.pendingContent) { + this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation)); + this.pendingContent = undefined; + } + + if (source) { + this.source.push(source); + } + }, + + replaceStack: function replaceStack(callback) { + var prefix = ['('], + stack = undefined, + createdStack = undefined, + usedLiteral = undefined; + + /* istanbul ignore next */ + if (!this.isInline()) { + throw new _exception2['default']('replaceStack on non-inline'); + } + + // We want to merge the inline statement into the replacement statement via ',' + var top = this.popStack(true); + + if (top instanceof Literal) { + // Literals do not need to be inlined + stack = [top.value]; + prefix = ['(', stack]; + usedLiteral = true; + } else { + // Get or create the current stack name for use by the inline + createdStack = true; + var _name = this.incrStack(); + + prefix = ['((', this.push(_name), ' = ', top, ')']; + stack = this.topStack(); + } + + var item = callback.call(this, stack); + + if (!usedLiteral) { + this.popStack(); + } + if (createdStack) { + this.stackSlot--; + } + this.push(prefix.concat(item, ')')); + }, + + incrStack: function incrStack() { + this.stackSlot++; + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } + return this.topStackName(); + }, + topStackName: function topStackName() { + return 'stack' + this.stackSlot; + }, + flushInline: function flushInline() { + var inlineStack = this.inlineStack; + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + /* istanbul ignore if */ + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + var stack = this.incrStack(); + this.pushSource([stack, ' = ', entry, ';']); + this.compileStack.push(stack); + } + } + }, + isInline: function isInline() { + return this.inlineStack.length; + }, + + popStack: function popStack(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && item instanceof Literal) { + return item.value; + } else { + if (!inline) { + /* istanbul ignore next */ + if (!this.stackSlot) { + throw new _exception2['default']('Invalid stack pop'); + } + this.stackSlot--; + } + return item; + } + }, + + topStack: function topStack() { + var stack = this.isInline() ? this.inlineStack : this.compileStack, + item = stack[stack.length - 1]; + + /* istanbul ignore if */ + if (item instanceof Literal) { + return item.value; + } else { + return item; + } + }, + + contextName: function contextName(context) { + if (this.useDepths && context) { + return 'depths[' + context + ']'; + } else { + return 'depth' + context; + } + }, + + quotedString: function quotedString(str) { + return this.source.quotedString(str); + }, + + objectLiteral: function objectLiteral(obj) { + return this.source.objectLiteral(obj); + }, + + aliasable: function aliasable(name) { + var ret = this.aliases[name]; + if (ret) { + ret.referenceCount++; + return ret; + } + + ret = this.aliases[name] = this.source.wrap(name); + ret.aliasable = true; + ret.referenceCount = 1; + + return ret; + }, + + setupHelper: function setupHelper(paramSize, name, blockHelper) { + var params = [], + paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); + var foundHelper = this.nameLookup('helpers', name, 'helper'), + callContext = this.aliasable(this.contextName(0) + ' != null ? ' + this.contextName(0) + ' : {}'); + + return { + params: params, + paramsInit: paramsInit, + name: foundHelper, + callParams: [callContext].concat(params) + }; + }, + + setupParams: function setupParams(helper, paramSize, params) { + var options = {}, + contexts = [], + types = [], + ids = [], + objectArgs = !params, + param = undefined; + + if (objectArgs) { + params = []; + } + + options.name = this.quotedString(helper); + options.hash = this.popStack(); + + if (this.trackIds) { + options.hashIds = this.popStack(); + } + if (this.stringParams) { + options.hashTypes = this.popStack(); + options.hashContexts = this.popStack(); + } + + var inverse = this.popStack(), + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + options.fn = program || 'container.noop'; + options.inverse = inverse || 'container.noop'; + } + + // The parameters go on to the stack in order (making sure that they are evaluated in order) + // so we need to pop them off the stack in reverse order + var i = paramSize; + while (i--) { + param = this.popStack(); + params[i] = param; + + if (this.trackIds) { + ids[i] = this.popStack(); + } + if (this.stringParams) { + types[i] = this.popStack(); + contexts[i] = this.popStack(); + } + } + + if (objectArgs) { + options.args = this.source.generateArray(params); + } + + if (this.trackIds) { + options.ids = this.source.generateArray(ids); + } + if (this.stringParams) { + options.types = this.source.generateArray(types); + options.contexts = this.source.generateArray(contexts); + } + + if (this.options.data) { + options.data = 'data'; + } + if (this.useBlockParams) { + options.blockParams = 'blockParams'; + } + return options; + }, + + setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) { + var options = this.setupParams(helper, paramSize, params); + options = this.objectLiteral(options); + if (useRegister) { + this.useRegister('options'); + params.push('options'); + return ['options=', options]; + } else if (params) { + params.push(options); + return ''; + } else { + return options; + } + } + }; + + (function () { + var reservedWords = ('break else new var' + ' case finally return void' + ' catch for switch while' + ' continue function this with' + ' default if throw' + ' delete in try' + ' do instanceof typeof' + ' abstract enum int short' + ' boolean export interface static' + ' byte extends long super' + ' char final native synchronized' + ' class float package throws' + ' const goto private transient' + ' debugger implements protected volatile' + ' double import public let yield await' + ' null true false').split(' '); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for (var i = 0, l = reservedWords.length; i < l; i++) { + compilerWords[reservedWords[i]] = true; + } + })(); + + JavaScriptCompiler.isValidJavaScriptVariableName = function (name) { + return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); + }; + + function strictLookup(requireTerminal, compiler, parts, type) { + var stack = compiler.popStack(), + i = 0, + len = parts.length; + if (requireTerminal) { + len--; + } + + for (; i < len; i++) { + stack = compiler.nameLookup(stack, parts[i], type); + } + + if (requireTerminal) { + return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ')']; + } else { + return stack; + } + } + + exports['default'] = JavaScriptCompiler; + module.exports = exports['default']; + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + /* global define */ + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var SourceNode = undefined; + + try { + /* istanbul ignore next */ + if (false) { + // We don't support this in AMD environments. For these environments, we asusme that + // they are running on the browser and thus have no need for the source-map library. + var SourceMap = require('source-map'); + SourceNode = SourceMap.SourceNode; + } + } catch (err) {} + /* NOP */ + + /* istanbul ignore if: tested but not covered in istanbul due to dist build */ + if (!SourceNode) { + SourceNode = function (line, column, srcFile, chunks) { + this.src = ''; + if (chunks) { + this.add(chunks); + } + }; + /* istanbul ignore next */ + SourceNode.prototype = { + add: function add(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src += chunks; + }, + prepend: function prepend(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src = chunks + this.src; + }, + toStringWithSourceMap: function toStringWithSourceMap() { + return { code: this.toString() }; + }, + toString: function toString() { + return this.src; + } + }; + } + + function castChunk(chunk, codeGen, loc) { + if (_utils.isArray(chunk)) { + var ret = []; + + for (var i = 0, len = chunk.length; i < len; i++) { + ret.push(codeGen.wrap(chunk[i], loc)); + } + return ret; + } else if (typeof chunk === 'boolean' || typeof chunk === 'number') { + // Handle primitives that the SourceNode will throw up on + return chunk + ''; + } + return chunk; + } + + function CodeGen(srcFile) { + this.srcFile = srcFile; + this.source = []; + } + + CodeGen.prototype = { + isEmpty: function isEmpty() { + return !this.source.length; + }, + prepend: function prepend(source, loc) { + this.source.unshift(this.wrap(source, loc)); + }, + push: function push(source, loc) { + this.source.push(this.wrap(source, loc)); + }, + + merge: function merge() { + var source = this.empty(); + this.each(function (line) { + source.add([' ', line, '\n']); + }); + return source; + }, + + each: function each(iter) { + for (var i = 0, len = this.source.length; i < len; i++) { + iter(this.source[i]); + } + }, + + empty: function empty() { + var loc = this.currentLocation || { start: {} }; + return new SourceNode(loc.start.line, loc.start.column, this.srcFile); + }, + wrap: function wrap(chunk) { + var loc = arguments.length <= 1 || arguments[1] === undefined ? this.currentLocation || { start: {} } : arguments[1]; + + if (chunk instanceof SourceNode) { + return chunk; + } + + chunk = castChunk(chunk, this, loc); + + return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); + }, + + functionCall: function functionCall(fn, type, params) { + params = this.generateList(params); + return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']); + }, + + quotedString: function quotedString(str) { + return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + objectLiteral: function objectLiteral(obj) { + var pairs = []; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = castChunk(obj[key], this); + if (value !== 'undefined') { + pairs.push([this.quotedString(key), ':', value]); + } + } + } + + var ret = this.generateList(pairs); + ret.prepend('{'); + ret.add('}'); + return ret; + }, + + generateList: function generateList(entries) { + var ret = this.empty(); + + for (var i = 0, len = entries.length; i < len; i++) { + if (i) { + ret.add(','); + } + + ret.add(castChunk(entries[i], this)); + } + + return ret; + }, + + generateArray: function generateArray(entries) { + var ret = this.generateList(entries); + ret.prepend('['); + ret.add(']'); + + return ret; + } + }; + + exports['default'] = CodeGen; + module.exports = exports['default']; + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/data/web/common/style.css b/data/web/common/style.css new file mode 100644 index 0000000000..cf497440ad --- /dev/null +++ b/data/web/common/style.css @@ -0,0 +1,185 @@ +/* Documentation */ + +.documentation-name, .documentation-name:hover, .documentation-name:focus { + text-decoration: none; + color: #333; + outline: none; +} + +.documentation-item { + background-color: #f4f4f4; + border-left: 2px solid #888; + padding-left: 10px; + padding-top: 5px; + padding-right: 5px; + margin-bottom: 5px; +} + +.documentation-item .documentation-item { + border-left: 2px solid #f00; + background-color: #fff; +} + +.documentation-item .documentation-item .documentation-item { + border-left: 2px solid #080; + background-color: #f4f4f4; +} + +.documentation-item .documentation-item .documentation-item .documentation-item { + border-left: 2px solid #00f; + background-color: #fff; +} + +.documentation-type { + color: #888; + font-size: 0.8em; + font-style: italic; +} + +.documentation-key { + color: #333; + font-weight: bold; +} + +.documentation-description { + font-size: 0.9em; +} + +.documentation-container { + font-size: 1.2em; +} + +.documentation-small { + font-size: 0.8em; +} + + +/*! + * Start Bootstrap - Simple Sidebar (http://startbootstrap.com/) + * Copyright 2013-2016 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) + */ + + body { + overflow-x: hidden; + } + +/* Toggle Styles */ + +#wrapper { + padding-left: 0; + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; +} + +#wrapper.toggled { + padding-left: 280px; +} + +#sidebar-wrapper { + z-index: 1000; + position: fixed; + left: 280px; + width: 0; + height: 100%; + margin-left: -280px; + overflow-y: auto; + background: #000; + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; +} + +#wrapper.toggled #sidebar-wrapper { + width: 280px; +} + +#page-content-wrapper { + width: 100%; + position: absolute; + padding: 15px; +} + +#wrapper.toggled #page-content-wrapper { + position: absolute; + margin-right: -280px; +} + +/* Sidebar Styles */ + +.sidebar-nav { + position: absolute; + top: 0; + width: 280px; + margin: 0; + padding: 0; + list-style: none; +} + +.sidebar-nav li { + text-indent: 20px; + line-height: 40px; +} + +.sidebar-nav li a { + display: block; + text-decoration: none; + color: #999999; +} + +.sidebar-nav li a:hover { + text-decoration: none; + color: #fff; + background: rgba(255,255,255,0.2); +} + +.sidebar-nav li a:active, +.sidebar-nav li a:focus { + text-decoration: none; +} + +.sidebar-nav > .sidebar-brand { + height: 65px; + font-size: 18px; + line-height: 60px; +} + +.sidebar-nav > .sidebar-brand a { + color: #999999; +} + +.sidebar-nav > .sidebar-brand a:hover { + color: #fff; + background: none; +} + +@media(min-width:768px) { + #wrapper { + padding-left: 280px; + } + + #wrapper.toggled { + padding-left: 0; + } + + #sidebar-wrapper { + width: 280px; + } + + #wrapper.toggled #sidebar-wrapper { + width: 0; + } + + #page-content-wrapper { + padding: 20px; + position: relative; + } + + #wrapper.toggled #page-content-wrapper { + position: relative; + margin-right: 0; + } +} \ No newline at end of file diff --git a/data/web/documentation/documentation.hbs b/data/web/documentation/documentation.hbs new file mode 100644 index 0000000000..93957bdecf --- /dev/null +++ b/data/web/documentation/documentation.hbs @@ -0,0 +1,28 @@ +{{#each entries}} +
+
+
+

+ + {{type}} + {{key}} + {{#if optional}} + (Optional) + {{/if}} + +

+

{{description}}

+ {{#with restrictions}} + {{>documentation}} + {{/with}} + {{#if reference}} + {{#if reference.found}} +

An object of type '{{reference.name}}'

+ {{else}} +

Missing reference

+ {{/if}} + {{/if}} +
+
+
+{{/each}} diff --git a/data/web/documentation/main.hbs b/data/web/documentation/main.hbs new file mode 100644 index 0000000000..e96f942df9 --- /dev/null +++ b/data/web/documentation/main.hbs @@ -0,0 +1,32 @@ +
+ + +
+
+

OpenSpace Documentation

+

Version: {{version.[0]}}.{{version.[1]}}.{{version.[2]}}

+ + {{#each documentation}} +
+
+

{{name}}

+ {{> documentation}} +
+
+ {{/each}} +
+
+
diff --git a/data/web/documentation/script.js b/data/web/documentation/script.js new file mode 100644 index 0000000000..c2b320ce70 --- /dev/null +++ b/data/web/documentation/script.js @@ -0,0 +1,28 @@ +window.onload = function () { + var mainTemplateElement = document.getElementById('mainTemplate'); + var mainTemplate = Handlebars.compile(mainTemplateElement.innerHTML); + + var documentationTemplateElement = document.getElementById('documentationTemplate'); + Handlebars.registerPartial('documentation', documentationTemplateElement.innerHTML); + + Handlebars.registerHelper('urlify', function(options, context) { + var data = context.data; + var identifier = options.replace(" ", "-").toLowerCase(); + + while (data = data._parent) { + if (data.key !== undefined) { + identifier = data.key + "-" + identifier; + } + } + + return identifier; + }); + + var data = { + documentation: documentation, + version: version + } + + var contents = mainTemplate(data); + document.body.innerHTML = contents; +} \ No newline at end of file diff --git a/data/web/factories/factory.hbs b/data/web/factories/factory.hbs new file mode 100644 index 0000000000..ea23b014fc --- /dev/null +++ b/data/web/factories/factory.hbs @@ -0,0 +1,13 @@ +{{#each classes}} +
+
+ +
+
+{{/each}} diff --git a/data/web/factories/main.hbs b/data/web/factories/main.hbs new file mode 100644 index 0000000000..56f1a3187d --- /dev/null +++ b/data/web/factories/main.hbs @@ -0,0 +1,35 @@ +
+ + +
+
+

OpenSpace Factories

+

Version: {{version.[0]}}.{{version.[1]}}.{{version.[2]}}

+ {{#each factories}} +
+
+

+ + {{name}} + +

+ {{> factory}} +
+
+ {{/each}} +
+
+
diff --git a/data/web/factories/script.js b/data/web/factories/script.js new file mode 100644 index 0000000000..6334c7b2a4 --- /dev/null +++ b/data/web/factories/script.js @@ -0,0 +1,28 @@ +window.onload = function () { + var mainTemplateElement = document.getElementById('mainTemplate'); + var mainTemplate = Handlebars.compile(mainTemplateElement.innerHTML); + + var factoryTemplateElement = document.getElementById('factoryTemplate'); + Handlebars.registerPartial('factory', factoryTemplateElement.innerHTML); + + Handlebars.registerHelper('urlify', function(options, context) { + var data = context.data; + var identifier = options.replace(" ", "-").toLowerCase(); + + while (data = data._parent) { + if (data.key !== undefined) { + identifier = data.key + "-" + identifier; + } + } + + return identifier; + }); + + var data = { + factories: factories, + version: version + } + + var contents = mainTemplate(data); + document.body.innerHTML = contents; +} \ No newline at end of file diff --git a/data/web/keybindings/keybinding.hbs b/data/web/keybindings/keybinding.hbs new file mode 100644 index 0000000000..1b4d5414c2 --- /dev/null +++ b/data/web/keybindings/keybinding.hbs @@ -0,0 +1,12 @@ +
+
+
+

+ + {{key}} + +

{{script}}

+

+
+
+
diff --git a/data/web/keybindings/main.hbs b/data/web/keybindings/main.hbs new file mode 100644 index 0000000000..655be726d7 --- /dev/null +++ b/data/web/keybindings/main.hbs @@ -0,0 +1,10 @@ +
+
+

OpenSpace Keybindings

+

Version: {{version.[0]}}.{{version.[1]}}.{{version.[2]}}

+

Generated: {{generationTime}}

+ {{#each keybindings}} + {{> keybinding}} + {{/each}} +
+
\ No newline at end of file diff --git a/data/web/keybindings/script.js b/data/web/keybindings/script.js new file mode 100644 index 0000000000..149cba1892 --- /dev/null +++ b/data/web/keybindings/script.js @@ -0,0 +1,29 @@ +window.onload = function () { + var mainTemplateElement = document.getElementById('mainTemplate'); + var mainTemplate = Handlebars.compile(mainTemplateElement.innerHTML); + + var keybindingTemplateElement = document.getElementById('keybindingTemplate'); + Handlebars.registerPartial('keybinding', keybindingTemplateElement.innerHTML); + + Handlebars.registerHelper('urlify', function(options, context) { + var data = context.data; + var identifier = options.replace(" ", "-").toLowerCase(); + + while (data = data._parent) { + if (data.key !== undefined) { + identifier = data.key + "-" + identifier; + } + } + + return identifier; + }); + + var data = { + keybindings: keybindings, + version: version, + generationTime: generationTime + } + + var contents = mainTemplate(data); + document.body.innerHTML = contents; +} \ No newline at end of file diff --git a/data/web/log/script.js b/data/web/log/script.js new file mode 100644 index 0000000000..fea743117f --- /dev/null +++ b/data/web/log/script.js @@ -0,0 +1,128 @@ +var levels = ['debug', 'info', 'warning', 'error', 'fatal']; +var filterLevel = 0; + +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +function remove(node) { + node.parentNode.removeChild(node); +} + +function scrollTo(selector) { + var element = document.querySelector(selector); + if (element && element.scrollIntoView) { + element.scrollIntoView(); + } +} + +function getLevel(element) { + return levels.findIndex(function (levelString, level) { + var className = 'log-level-' + levelString; + if (element.classList.contains(className)) { + return true; + } + }); +} + +function pluralize(nItems, singular, plural) { + if (nItems === 1) { + return [1, singular].join(' '); + } + plural = plural || [singular, 's'].join(''); + return [nItems.toString(), plural].join(' '); +} + +function scrollLink(content, selector) { + var html = '' + content + ""; + return html; +} + +function getSummary() { + var nFatals = document.getElementsByClassName('log-level-fatal').length; + var nErrors = document.getElementsByClassName('log-level-error').length; + var nWarnings = document.getElementsByClassName('log-level-warning').length; + + if (nFatals > 0) { + return '' + scrollLink(pluralize(nFatals, 'fatal error'), '.log-level-fatal') + + ', ' + scrollLink(pluralize(nErrors, 'other error'), '.log-level-error') + ' and ' + scrollLink(pluralize(nWarnings, 'warning'), '.log-level-warning') + ''; + } else if (nErrors > 0) { + return '' + scrollLink(pluralize(nErrors, 'error'), '.log-level-error') + ' and ' + + scrollLink(pluralize(nWarnings, 'warning'), '.log-level-warning') + ''; + } else if (nWarnings > 0) { + return '' + scrollLink(pluralize(nWarnings, 'warning'), '.log-level-warning') + ''; + } else { + return 'No errors or warnings'; + } +} + +function updateFilter() { + var table = document.getElementsByTagName('table')[0]; + table.classList.remove('hidden'); + + var noMessages = document.getElementById('no-messages'); + if (noMessages) { + remove(noMessages); + } + + var rows = document.getElementsByTagName('tr'); + var nShown = 0; + [].forEach.call(rows, function (row) { + if (row.classList.length === 0) { + return; + } + var rowLevel = getLevel(row); + if (rowLevel >= filterLevel) { + row.classList.remove('hidden'); + nShown++; + } else { + row.classList.add('hidden'); + } + }); + if (nShown === 0) { + var select = document.getElementsByTagName('select')[0]; + var p = document.createElement("p"); + p.id = "no-messages"; + p.innerHTML = "There are no log messages with the level '" + levels[filterLevel] + "' or higher."; + insertAfter(p, select); + table.classList.add('hidden'); + } +} + +window.onload = function () { + var header = document.getElementsByTagName('h1')[0]; + header.innerHTML = "OpenSpace Log"; + + var summary = document.createElement('p'); + summary.innerHTML = getSummary(); + + var select = document.createElement('select'); + select.id = 'filter-level-selector'; + + var selectLabel = document.createElement('label'); + selectLabel.for = 'filter-level-selector'; + selectLabel.innerHTML = "Lowest log level to show: "; + + levels.forEach(function (level) { + var option = document.createElement('option'); + option.value = level; + option.innerHTML = level; + select.appendChild(option); + }); + + insertAfter(summary, header); + insertAfter(selectLabel, summary); + insertAfter(select, selectLabel); + + var preselectedIndex = levels.indexOf(window.location.hash.slice(1)); + if (preselectedIndex >= 0) { + filterLevel = select.selectedIndex = preselectedIndex; + updateFilter(); + } + + select.onchange = function (evt) { + filterLevel = select.selectedIndex; + updateFilter(); + window.location.hash = '#' + select.options[select.selectedIndex].value; + }; +} \ No newline at end of file diff --git a/data/web/log/style.css b/data/web/log/style.css new file mode 100644 index 0000000000..3aa140b30e --- /dev/null +++ b/data/web/log/style.css @@ -0,0 +1,93 @@ +table { + width: 100%; +} + +td, th { + padding: 4px 15px 3px; +} + +.log-date { + width: 8em; +} + +h1 { + padding-left: 15px; +} + +.hidden { + display: none; +} + +p, label { + margin-left: 15px; + margin-bottom: 15px; +} + +label { + margin-right: 0.5em; +} + +.log-level-debug { + background-color: #bbdda9; + border-bottom: 1px solid #7bc142; + color: #265127; +} +.log-level-debug td:first-child { + border-left: 10px solid #7bc142; +} + +thead, .log-level-info { + color: #333333; + background-color: #ffffff; + border-bottom: 1px solid #eaeaea; +} +.log-level-info td:first-child { + border-left: 10px solid #eaeaea; +} + +thead th:first-child { + border-left 10px solid #fff; +} + +.log-level-warning { + color: #3e3115; + background-color: #fef8c3; + border-bottom: 1px solid #efcd3f; +} +.log-level-warning td:first-child { + border-left: 10px solid #efcd3f; +} + +.log-level-error { + color: #4c1315; + background-color: #fcdcdc; + border-bottom: 1px solid #d66767; +} +.log-level-error td:first-child { + border-left: 10px solid #d66767; +} + +.log-level-fatal { + color: #220f21; + background-color: #ff7dc1; + border-bottom: 1px solid #b84398; +} +.log-level-fatal td:first-child { + border-left: 10px solid #b84398; +} + +.summary { + padding: 5px; +} +.summary-warning { + background-color: #fef8c3; +} +.summary-error { + background-color: #fcdcdc; +} +.summary-fatal { + background-color: #ff7dc1; +} +.summary-ok { + background-color: #bbdda9; +} \ No newline at end of file diff --git a/data/web/luascripting/main.hbs b/data/web/luascripting/main.hbs new file mode 100644 index 0000000000..1d67289346 --- /dev/null +++ b/data/web/luascripting/main.hbs @@ -0,0 +1,35 @@ +
+ + +
+
+

OpenSpace Lua Scripting

+

Version: {{version.[0]}}.{{version.[1]}}.{{version.[2]}}

+ {{#each scripting}} + + {{/each}} +
+
+
diff --git a/data/web/luascripting/script.js b/data/web/luascripting/script.js new file mode 100644 index 0000000000..b43af6f884 --- /dev/null +++ b/data/web/luascripting/script.js @@ -0,0 +1,28 @@ +window.onload = function () { + var mainTemplateElement = document.getElementById('mainTemplate'); + var mainTemplate = Handlebars.compile(mainTemplateElement.innerHTML); + + var scriptingTemplateElement = document.getElementById('scriptingTemplate'); + Handlebars.registerPartial('scripting', scriptingTemplateElement.innerHTML); + + Handlebars.registerHelper('urlify', function(options, context) { + var data = context.data; + var identifier = options.replace(" ", "-").toLowerCase(); + + while (data = data._parent) { + if (data.key !== undefined) { + identifier = data.key + "-" + identifier; + } + } + + return identifier; + }); + + var data = { + scripting: scripting, + version: version + } + + var contents = mainTemplate(data); + document.body.innerHTML = contents; +} \ No newline at end of file diff --git a/data/web/luascripting/scripting.hbs b/data/web/luascripting/scripting.hbs new file mode 100644 index 0000000000..00313e3372 --- /dev/null +++ b/data/web/luascripting/scripting.hbs @@ -0,0 +1,17 @@ +{{#each functions}} +
+ +
+{{/each}} diff --git a/data/web/properties/main.hbs b/data/web/properties/main.hbs new file mode 100644 index 0000000000..fc56bca0b8 --- /dev/null +++ b/data/web/properties/main.hbs @@ -0,0 +1,41 @@ +
+ + +
+
+

OpenSpace Scene Properties

+

Version: {{version.[0]}}.{{version.[1]}}.{{version.[2]}}

+

Scene name: {{sceneFilename}}

+

Generated: {{generationTime}}

+ + {{#each propertyOwners}} +
+
+

{{name}}

+ {{#each properties}} + {{> property}} + {{/each}} + {{#each propertyOwners}} + {{> propertyOwner}} + {{else}} +

{{name}} has no property owners

+ {{/each}} +
+
+ {{/each}} +
+
+
diff --git a/data/web/properties/property.hbs b/data/web/properties/property.hbs new file mode 100644 index 0000000000..b01807229d --- /dev/null +++ b/data/web/properties/property.hbs @@ -0,0 +1,15 @@ +
+
+
+

+ + {{type}} + {{id}} + +

+

{{fullyQualifiedId}} + copy +

+
+
+
diff --git a/data/web/properties/propertyowner.hbs b/data/web/properties/propertyowner.hbs new file mode 100644 index 0000000000..9d0885e0a1 --- /dev/null +++ b/data/web/properties/propertyowner.hbs @@ -0,0 +1,18 @@ +
+
+
+

+ + {{name}} + +

+ {{#each properties}} + {{> property}} + {{/each}} + {{#each propertyOwners}} + {{> propertyOwner}} + {{/each}} +
+
+
+ diff --git a/data/web/properties/script.js b/data/web/properties/script.js new file mode 100644 index 0000000000..70f303f15f --- /dev/null +++ b/data/web/properties/script.js @@ -0,0 +1,58 @@ +function copyTextToClipboard(text) { + var textArea = document.createElement("textarea"); + textArea.style.position = 'fixed'; + textArea.style.top = 0; + textArea.style.left = 0; + + textArea.style.width = '2em'; + textArea.style.height = '2em'; + + textArea.style.padding = 0; + + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + + textArea.style.background = 'transparent'; + textArea.value = text; + + document.body.appendChild(textArea); + + textArea.select(); + document.execCommand('copy'); + + document.body.removeChild(textArea); +} + +window.onload = function () { + var mainTemplateElement = document.getElementById('mainTemplate'); + var mainTemplate = Handlebars.compile(mainTemplateElement.innerHTML); + + var propertyOwnerTemplateElement = document.getElementById('propertyOwnerTemplate'); + Handlebars.registerPartial('propertyOwner', propertyOwnerTemplateElement.innerHTML); + + var propertyTemplateElement = document.getElementById('propertyTemplate'); + Handlebars.registerPartial('property', propertyTemplateElement.innerHTML); + + Handlebars.registerHelper('urlify', function(options, context) { + var data = context.data; + var identifier = options.replace(" ", "-").toLowerCase(); + + while (data = data._parent) { + if (data.key !== undefined) { + identifier = data.key + "-" + identifier; + } + } + return identifier; + }); + + var data = { + propertyOwners: propertyOwners, + version: version, + sceneFilename: sceneFilename, + generationTime: generationTime + } + + var contents = mainTemplate(data); + document.body.innerHTML = contents; +} diff --git a/ext/ghoul b/ext/ghoul index e45911445d..f9e1207470 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit e45911445d85b884baa017e175d6a175ca421d76 +Subproject commit f9e12074702627b1feef9a09670270fed511ff5a diff --git a/ext/sgct b/ext/sgct index 9ad36dbaa5..fb95dc335b 160000 --- a/ext/sgct +++ b/ext/sgct @@ -1 +1 @@ -Subproject commit 9ad36dbaa55d5267556a32338f8776a85c463321 +Subproject commit fb95dc335ba7d396723fbfeca64aa3b7451376c8 diff --git a/include/openspace/documentation/core_registration.h b/include/openspace/documentation/core_registration.h index d29259daf2..61a462eddf 100644 --- a/include/openspace/documentation/core_registration.h +++ b/include/openspace/documentation/core_registration.h @@ -26,13 +26,13 @@ #define __CORE_REGISTRATION_H__ namespace openspace { -namespace documentation { -class DocumentationEngine; +namespace documentation { class DocumentationEngine; } +namespace scripting { class ScriptEngine; } void registerCoreClasses(documentation::DocumentationEngine& engine); +void registerCoreClasses(scripting::ScriptEngine& engine); -} // namespace documentation } // namespace openspace #endif // __CORE_REGISTRATION_H__ diff --git a/include/openspace/documentation/documentation.h b/include/openspace/documentation/documentation.h index 515863d546..39015df384 100644 --- a/include/openspace/documentation/documentation.h +++ b/include/openspace/documentation/documentation.h @@ -69,11 +69,36 @@ struct TestResult { /// The Reason that caused this offense Reason reason; }; + + /** + * A warning is some value that that does not exactly adhere to the specification, but + * that also does not violate so badly to warrant an Offense. This, for example, could + * be that a value is marked deprecated and should not be used anymore as the value + * might be removed in a latter version. + */ + struct Warning { + /** + * The reason for the warning + */ + enum class Reason { + Deprecated ///< The value is marked as deprecated and should not used + }; + + /// The offending key that caused the Warning. In the case of a nested table, + /// this value will be the fully qualified name of the key + std::string offender; + /// The Reason that caused this Warning + Reason reason; + }; + + /// Is \c true if the TestResult is positive, \c false otherwise bool success; /// Contains a list of offenses that were found in the test. Is empty if /// TestResult::Success is \c true std::vector offenses; + /// Contains a list of warnings that were found in the test + std::vector warnings; }; /** diff --git a/include/openspace/documentation/verifier.h b/include/openspace/documentation/verifier.h index d09a386fdc..1adcc9b140 100644 --- a/include/openspace/documentation/verifier.h +++ b/include/openspace/documentation/verifier.h @@ -665,6 +665,33 @@ struct AnnotationVerifier : public T { std::string annotation; }; +/** + * This Verifier is a marker that performs the same testing as the \c T parameter, but + * also adds a warning to the test result informing the user of the deprecation. + * Furthermore, the documentation will contain the word (deprecated) in + * addition to the documentation returned by \c + * \tparam T The Verifier that is to be marked deprecated + */ +template +struct DeprecatedVerifier : public T { + /** + * Tests the \p dictionary%s \p key using the Verifier \c T and adds a warning to the + * TestResult informing the caller of the deprecation. + * \param dictionary The ghoul::Dictionary whose \p key should be tested + * \param key The key inside the \p dictionary that is to be tested + * \return A TestResult that contains the results of the testing + */ + TestResult operator()(const ghoul::Dictionary& dictionary, + const std::string& key) const override; + + /** + * Returns the documentation as reported by \c T and adds the word + * (deprecated) to it. + * \return The deprecated version of \c T%'s documentation + */ + std::string documentation() const override; +}; + /** * This Verifier can reference and apply other Documentation%s that have been registered * with a DocumentationEngine. The dependency is only resolved when the operator() is @@ -884,6 +911,18 @@ using StringAnnotationVerifier = AnnotationVerifier; /// ghoul::Dictionary using TableAnnotationVerifier = AnnotationVerifier; +/// A short-hand definition for a DeprecatedVerifier with a type check for \c bool +using BoolDeprecatedVerifier = DeprecatedVerifier; +/// A short-hand definition for a DeprecatedVerifier with a type check for \c int +using IntDeprecatedVerifier = DeprecatedVerifier; +/// A short-hand definition for a DeprecatedVerifier with a type check for \c double +using DoubleDeprecatedVerifier = DeprecatedVerifier; +/// A short-hand definition for a DeprecatedVerifier with a type check for \c string +using StringDeprecatedVerifier = DeprecatedVerifier; +/// A short-hand definition for a DeprecatedVerifier with a type check for +/// ghoul::Dictionary +using TableDeprecatedVerifier = DeprecatedVerifier; + // Definitions of external templates that are instantiated in the cpp file // This cuts down the compilation times as almost all of the possible template types do // not need to be instantiated multiple times @@ -933,7 +972,30 @@ extern template struct AnnotationVerifier; extern template struct AnnotationVerifier; extern template struct AnnotationVerifier; extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct AnnotationVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; +extern template struct DeprecatedVerifier; } // namespace documentation } // namespace openspace diff --git a/include/openspace/documentation/verifier.inl b/include/openspace/documentation/verifier.inl index 98ba83a1fa..deaa984b3d 100644 --- a/include/openspace/documentation/verifier.inl +++ b/include/openspace/documentation/verifier.inl @@ -50,7 +50,7 @@ TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dict, template std::string TemplateVerifier::documentation() const { - return "Type testing of '" + type() + "'"; + return "Value of type '" + type() + "'"; } template @@ -263,7 +263,7 @@ NotInRangeVerifier::NotInRangeVerifier(typename T::Type lower, typename T::Ty template TestResult NotInRangeVerifier::operator()(const ghoul::Dictionary& dict, - const std::string& key) const { + const std::string& key) const { TestResult res = T::operator()(dict, key); if (res.success) { typename T::Type val = dict.value(key); @@ -299,5 +299,19 @@ std::string AnnotationVerifier::documentation() const { return annotation; } +template +TestResult DeprecatedVerifier::operator()(const ghoul::Dictionary& dict, + const std::string& key) const +{ + TestResult res = T::operator()(dict, key); + res.warnings.push_back(TestResult::Warning{ key, TestResult::Warning::Reason::Deprecated }); + return res; +} + +template +std::string DeprecatedVerifier::documentation() const { + return T::documentation() + " (deprecated)"; +} + } // namespace documentation } // namespace openspace diff --git a/include/openspace/engine/configurationmanager.h b/include/openspace/engine/configurationmanager.h index 06b46babef..b826094735 100644 --- a/include/openspace/engine/configurationmanager.h +++ b/include/openspace/engine/configurationmanager.h @@ -106,6 +106,19 @@ public: /// The key that stores the switch for enabling/disabling the rendering on a master /// computer static const std::string KeyRenderingMethod; + /// The key that stores the http proxy settings for the downloadmanager + static const std::string KeyHttpProxy; + /// The key that stores the address of the http proxy + static const std::string PartHttpProxyAddress; + /// The key that stores the port of the http proxy + static const std::string PartHttpProxyPort; + /// The key that stores the authentication method of the http proxy + static const std::string PartHttpProxyAuthentication; + /// The key that stores the username to use for authentication to access the http proxy + static const std::string PartHttpProxyUser; + /// The key that stores the password to use for authentication to access the http proxy + static const std::string PartHttpProxyPassword; + /** * Iteratively walks the directory structure starting with \p filename to find the diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 63e2451a1b..6e72f376c0 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -28,17 +28,14 @@ #include #include -#include -#include - #include -#include #include #include #include namespace ghoul { +class Dictionary; namespace cmdparser { class CommandlineParser; } namespace fontrendering { class FontManager; } } @@ -54,13 +51,17 @@ class RenderEngine; class ModuleEngine; class WindowWrapper; class SettingsEngine; +class TimeManager; class SyncEngine; +class ParallelConnection; namespace interaction { class InteractionHandler; } namespace gui { class GUI; } //namespace scripting { class ScriptEngine; } -namespace network { class ParallelConnection; } namespace properties { class PropertyOwner; } +namespace scripting { struct LuaLibrary; } +namespace scripting { class ScriptScheduler; } +namespace scripting { class ScriptEngine; } class OpenSpaceEngine { public: @@ -85,11 +86,12 @@ public: NetworkEngine& networkEngine(); LuaConsole& console(); ModuleEngine& moduleEngine(); - network::ParallelConnection& parallelConnection(); + ParallelConnection& parallelConnection(); properties::PropertyOwner& globalPropertyOwner(); WindowWrapper& windowWrapper(); ghoul::fontrendering::FontManager& fontManager(); DownloadManager& downloadManager(); + TimeManager& timeManager(); #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED gui::GUI& gui(); @@ -114,6 +116,7 @@ public: void enableBarrier(); void disableBarrier(); + void writeDocumentation(); void toggleShutdownMode(); bool useBusyWaitForDecode(); @@ -132,7 +135,7 @@ private: ~OpenSpaceEngine(); void clearAllWindows(); - bool gatherCommandlineArguments(); + void gatherCommandlineArguments(); void loadFonts(); void runScripts(const ghoul::Dictionary& scripts); void runPreInitializationScripts(const std::string& sceneDescription); @@ -150,11 +153,12 @@ private: std::unique_ptr _console; std::unique_ptr _moduleEngine; std::unique_ptr _settingsEngine; + std::unique_ptr _timeManager; std::unique_ptr _downloadManager; #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED std::unique_ptr _gui; #endif - std::unique_ptr _parallelConnection; + std::unique_ptr _parallelConnection; std::unique_ptr _windowWrapper; std::unique_ptr _fontManager; @@ -172,6 +176,10 @@ private: // The current state of the countdown; if it reaches '0', the application will close float _shutdownCountdown; + // The first frame might take some more time in the update loop, so we need to know to + // disable the synchronization; otherwise a hardware sync will kill us after 1 sec + bool _isFirstRenderingFirstFrame; + static OpenSpaceEngine* _engine; }; diff --git a/include/openspace/engine/wrapper/sgctwindowwrapper.h b/include/openspace/engine/wrapper/sgctwindowwrapper.h index da4d291edb..a9c4778005 100644 --- a/include/openspace/engine/wrapper/sgctwindowwrapper.h +++ b/include/openspace/engine/wrapper/sgctwindowwrapper.h @@ -38,6 +38,7 @@ class SGCTWindowWrapper : public WindowWrapper { public: void terminate() override; void setBarrier(bool enabled) override; + void setSynchronization(bool enabled) override; void clearAllWindows(const glm::vec4& clearColor) override; bool windowHasResized() const override; diff --git a/include/openspace/engine/wrapper/windowwrapper.h b/include/openspace/engine/wrapper/windowwrapper.h index 2d3d789dc8..af26f105b4 100644 --- a/include/openspace/engine/wrapper/windowwrapper.h +++ b/include/openspace/engine/wrapper/windowwrapper.h @@ -34,6 +34,8 @@ namespace openspace { +namespace scripting { struct LuaLibrary; } + /** * A WindowWrapper is a class that handles the abstraction between OpenSpace and a * specific window creation framework.
@@ -42,6 +44,12 @@ namespace openspace { */ class WindowWrapper { public: + /** + * Returns the Lua library that contains all Lua functions available to affect the + * windowing system. + */ + static scripting::LuaLibrary luaLibrary(); + /** * This method closes the application by calling the necessary terminate function of * the window management system @@ -55,6 +63,14 @@ public: * disables it */ virtual void setBarrier(bool enabled); + + /** + * This method enables or disables a framelock barrier. If the specific windowing + * framework does not provide a framelock, this method defaults to a no-op. + * \param enabled If true the framelock is enabled, false + * disables it + */ + virtual void setSynchronization(bool enabled); /** * This method clears all the rendering windows with the specified \p clearColor. In diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index a7cb9644f5..8ba106785b 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -47,113 +47,15 @@ class SceneGraphNode; namespace interaction { -//#define USE_OLD_INTERACTIONHANDLER -#ifdef USE_OLD_INTERACTIONHANDLER - -class InteractionHandler : public properties::PropertyOwner { -public: - InteractionHandler(); - ~InteractionHandler(); - - // Mutators - void setKeyboardController(KeyboardController* controller); - void setMouseController(MouseController* controller); - void setFocusNode(SceneGraphNode* node); - void setCamera(Camera* camera); - void setInteractionSensitivity(float sensitivity); - void resetKeyBindings(); - void setInvertRoll(bool invert); - void setInvertRotation(bool invert); - - void addController(Controller* controller); - void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf); - void clearKeyframes(); - - void bindKey(Key key, KeyModifier modifier, std::string lua); - - void lockControls(); - void unlockControls(); - - void update(double deltaTime); - - // Accessors - const SceneGraphNode* const focusNode() const; - const Camera* const camera() const; - double deltaTime() const; - float interactionSensitivity() const; - bool invertRoll() const; - bool invertRotation() const; - - /** - * Returns the Lua library that contains all Lua functions available to affect the - * interaction. The functions contained are - * - openspace::luascriptfunctions::setOrigin - * \return The Lua library that contains all Lua functions available to affect the - * interaction - */ - static scripting::LuaLibrary luaLibrary(); - - - // Callback functions - void keyboardCallback(Key key, KeyModifier modifier, KeyAction action); - void mouseButtonCallback(MouseButton button, MouseAction action); - void mousePositionCallback(double x, double y); - void mouseScrollWheelCallback(double pos); - - // Interaction functions - void orbitDelta(const glm::quat& rotation); - void orbit(const float &dx, const float &dy, const float &dz, const float &dist); - void rotateDelta(const glm::quat& rotation); - void distanceDelta(const PowerScaledScalar& distance, size_t iterations = 0); - void lookAt(const glm::quat& rotation); - void setRotation(const glm::quat& rotation); - - -private: - // Remove copy and move constructors - InteractionHandler(const InteractionHandler&) = delete; - InteractionHandler& operator=(const InteractionHandler&) = delete; - InteractionHandler(InteractionHandler&&) = delete; - InteractionHandler& operator=(InteractionHandler&&) = delete; - - // Settings - float _controllerSensitivity; - bool _invertRoll; - bool _invertRotation; - - // Pointers to entities to affect - Camera* _camera; - SceneGraphNode* _focusNode; - - // Cached data - double _deltaTime; - std::mutex _mutex; - - //bool _validKeyLua; - std::multimap _keyLua; - - - KeyboardController* _keyboardController; - MouseController* _mouseController; - std::vector _controllers; - - properties::StringProperty _origin; - properties::StringProperty _coordinateSystem; - - //remote controller - std::vector _keyframes; - double _currentKeyframeTime; - std::mutex _keyframeMutex; -}; - -#else // USE_OLD_INTERACTIONHANDLER - class InteractionHandler : public properties::PropertyOwner { public: InteractionHandler(); ~InteractionHandler(); + void initialize(); + void deinitialize(); + // Mutators void setFocusNode(SceneGraphNode* node); void setCamera(Camera* camera); @@ -165,9 +67,10 @@ public: void resetKeyBindings(); - void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf); + void addKeyframe(const datamessagestructures::CameraKeyframe &kf); void clearKeyframes(); + void bindKeyLocal(Key key, KeyModifier modifier, std::string lua); void bindKey(Key key, KeyModifier modifier, std::string lua); void lockControls(); void unlockControls(); @@ -206,7 +109,7 @@ private: bool _cameraUpdatedFromScript = false; - std::multimap _keyLua; + std::multimap> _keyLua; std::unique_ptr _inputState; Camera* _camera; @@ -228,8 +131,6 @@ private: properties::FloatProperty _rapidness; }; -#endif // USE_OLD_INTERACTIONHANDLER - } // namespace interaction } // namespace openspace diff --git a/include/openspace/interaction/interactionmode.h b/include/openspace/interaction/interactionmode.h index 03911f4b7e..b89cd36995 100644 --- a/include/openspace/interaction/interactionmode.h +++ b/include/openspace/interaction/interactionmode.h @@ -75,15 +75,16 @@ namespace interaction { void mouseScrollWheelCallback(double mouseScrollDelta); // Mutators - void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf); + void addKeyframe(const datamessagestructures::CameraKeyframe &kf); void clearKeyframes(); + void clearOldKeyframes(); // Accessors const std::list >& getPressedKeys() const; const std::list& getPressedMouseButtons() const; glm::dvec2 getMousePosition() const; double getMouseScrollDelta() const; - std::vector& getKeyFrames() const; + const std::vector& keyframes() const; bool isKeyPressed(std::pair keyModPair) const; bool isKeyPressed(Key key) const; @@ -96,8 +97,7 @@ namespace interaction { double _mouseScrollDelta; // Remote input via keyframes - std::vector _keyframes; - std::mutex _keyframeMutex; + std::vector _keyframes; }; @@ -192,6 +192,7 @@ public: virtual void updateCameraStateFromMouseStates(Camera& camera); private: + std::vector _keyframes; double _currentKeyframeTime; }; diff --git a/include/openspace/interaction/luaconsole.h b/include/openspace/interaction/luaconsole.h index 9273a717a7..13ef84051e 100644 --- a/include/openspace/interaction/luaconsole.h +++ b/include/openspace/interaction/luaconsole.h @@ -26,6 +26,7 @@ #define LUACONSOLE_H #include +#include #include @@ -50,12 +51,16 @@ public: bool isVisible() const; void setVisible(bool visible); - void toggleVisibility(); + bool isRemoteScripting() const; + void setRemoteScripting(bool remoteScripting); + + void toggleMode(); static scripting::LuaLibrary luaLibrary(); private: + void parallelConnectionChanged(const ParallelConnection::Status& status); void addToCommand(std::string c); std::string UnicodeToUTF8(unsigned int codepoint); @@ -73,6 +78,7 @@ private: } _autoCompleteInfo; bool _isVisible; + bool _remoteScripting; }; } // namespace openspace diff --git a/include/openspace/mission/mission.h b/include/openspace/mission/mission.h new file mode 100644 index 0000000000..7f2441699a --- /dev/null +++ b/include/openspace/mission/mission.h @@ -0,0 +1,146 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __MISSION_H__ +#define __MISSION_H__ + +#include +#include + +#include +#include +#include + +namespace ghoul { class Dictionary; } + +namespace openspace { + +/** + * Used to represent a named period of time within a mission. Allows nested phases, i.e. + * phases within phases. Designed for WORM usage (Write Once, Read Multiple), and, + * therefore, has only accessors. + * + * Each MissionPhase is characterized by its MissionPhase::name, a TimeRange, an + * optional MissionPhase::description, and optional subphases. + */ +class MissionPhase { +public: + /** + * Constructs a MissionPhase from the information provided in the \p dictionary. See + * the MissionPhase::Documentation for accepted ghoul::Dictionary values. + * \param dictionary The ghoul::Dictionary that contains information about the current + * MissionPhase + * \throw SpecificationError If the \p dictionary does not adhere to the Documentation + * \throw RuntimeError If the time range of subphases is smaller than the specified + * time range + * \throw RuntimeError If neither subphases or a time range is specified + */ + MissionPhase(const ghoul::Dictionary& dictionary); + + /** + * Returns the name of the MissionPhase. + * \return The name of the MissionPhase + */ + const std::string& name() const; + + /** + * Returns the TimeRange of the MissionPhase. + * \return The TimeRange of the MissionPhase + */ + const TimeRange& timeRange() const; + + /** + * Returns the description of the MissionPhase. + * \return The description of the MissionPhase + */ + const std::string& description() const; + + /** + * Returns all subphases sorted by start time. + * \return All subphases sorted by start time + */ + const std::vector& phases() const; + + + using Trace = std::vector>; + + /** + * Returns all MissionPhase%s whose MissionPhase::timeRange includes the provided + * \p time, up to a maximum subphase depth of \p maxDepth. + * \param time The time in which the subphases have to be active in order to be + * included + * \param maxDepth The maximum levels of subphases that will be considered. If this + * value is equal to -1, an infinite depth will be considered. + * \return A list of MissionPhases that cover the provided \p time + */ + Trace phaseTrace(double time, int maxDepth = -1) const; + + /** + * Returns the Documentation that describes the ghoul::Dictionarty that this + * MissionPhase can be constructed from. + * \return The Documentation that describes the required structure for a Dictionary + */ + static openspace::Documentation Documentation(); + +protected: + /** + * Recursive function that walks the subphases and adds the MissionPhase%s that cover + * the provided \p time and adds these to the list of \p trace%s. Each recursive call + * will decrease the \p maxDepth counter until it reaches 0. + * \param time The time which the subphases have to cover to be added to the \p trace + * \param trace The list of MissionPhase%s that are active during the time \p time + * \param maxDepth The maximum depth of levels that will be considered + */ + void phaseTrace(double time, Trace& trace, int maxDepth) const; + + /// The name of the MissionPhase + std::string _name; + /// The description of the MissionPhase + std::string _description; + /// The range in time that is covered by this MissionPhase + TimeRange _timeRange; + /// A list of subphases into which this MissionPhase is separated + std::vector _subphases; +}; + +/** + * A Mission is a list of MissionPhases that has a name, an optional description, a + * TimeRange for which the Mission is active, and a potential list of subphases. + */ +using Mission = MissionPhase; + +/** + * This function constructs a Mission from the provided \p filename. The file must be a + * Lua table that describes the Mission according to MissionPhase::Documentation + * \param filename The file that is used to create the Mission + * \return The constructed Mission + * \pre \p filename must not be empty + * \pre \p filename must not contain tokens + * \pre \p filename must exist + */ +Mission missionFromFile(std::string filename); + +} // namespace openspace + +#endif // __MISSION_H__ diff --git a/modules/newhorizons/util/missionmanager.h b/include/openspace/mission/missionmanager.h similarity index 58% rename from modules/newhorizons/util/missionmanager.h rename to include/openspace/mission/missionmanager.h index c23046f004..a25d3df657 100644 --- a/modules/newhorizons/util/missionmanager.h +++ b/include/openspace/mission/missionmanager.h @@ -22,102 +22,61 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __MISSIONPHASEEQUENCER_H__ -#define __MISSIONPHASEEQUENCER_H__ +#ifndef __MISSIONMANAGER_H__ +#define __MISSIONMANAGER_H__ +#include -#include -#include +#include +#include + +#include #include -#include -#include - -#include -#include - namespace openspace { - -/** -* Used to represent a named period of time within a mission. Allows nested phases, i.e. -* phases within phases. Designed for WORM usage (Write Once, Read Multiple), and therefor -* has only accessors. -*/ -class MissionPhase { -public: - MissionPhase() : _name(""), _description("") {}; - MissionPhase(const ghoul::Dictionary& dict); - - const std::string& name() const { return _name; } - - const TimeRange& timeRange() const { return _timeRange; }; - - const std::string& description() const { return _description; }; - - /** - * Returns all subphases sorted by start time - */ - const std::vector& phases() const { return _subphases; } - - /** - * Returns the i:th subphase, sorted by start time - */ - const MissionPhase& phase(size_t i) const { return _subphases[i]; } - - std::list phaseTrace(double time, int maxDepth = -1) const; - -protected: - - bool phaseTrace(double time, std::list& trace, int maxDepth) const; - - - std::string _name; - std::string _description; - TimeRange _timeRange; - std::vector _subphases; -}; - - - -class Mission : public MissionPhase { -public: - Mission() {}; - Mission(std::string filename); - -private: - static ghoul::Dictionary readDictFromFile(std::string filepath); - std::string _filepath; -}; +namespace scripting { struct LuaLibrary; } /** * Singleton class keeping track of space missions. */ -class MissionManager { +class MissionManager : public ghoul::Singleton { public: + struct MissionManagerException : public ghoul::RuntimeError { + explicit MissionManagerException(std::string error); + }; - static MissionManager& ref(); - - static void initialize(); - static void deinitialize(); + MissionManager(); /** * Reads a mission from file and maps the mission name to the Mission object. If * this is the first mission to be loaded, the mission will also be set as the * current active mission. + * \pre \p filename must not be empty + * \pre \p filename must not contain tokens + * \pre \p filename must exist */ - void loadMission(const std::string& fileName); + void loadMission(const std::string& filename); + + /** + * Returns whether the provided \p missionName has previously been added to the + * MissionManager. + * \param missionName The name of the mission that is to be tested + * \return \c true if the \p missionName has been added before + */ + bool hasMission(const std::string& missionName); /** * Sets the mission with the name as the current mission. The current * mission is what is return by `currentMission()`. + * \pre missionName must not be empty */ - void setCurrentMission(const std::string missionName); + void setCurrentMission(const std::string& missionName); /** * Returns true if a current mission exists */ - bool hasCurrentMission() const { return _currentMissionIter != _missionMap.end(); } + bool hasCurrentMission() const; /** * Returns the latest mission specified to `setCurrentMission()`. If no mission has @@ -126,22 +85,15 @@ public: */ const Mission& currentMission(); - -private: - static scripting::LuaLibrary luaLibrary(); - static MissionManager* _instance; - typedef std::unordered_map MissionMap; +private: + using MissionMap = std::map; MissionMap _missionMap; - MissionMap::iterator _currentMissionIter; - // Singleton - MissionManager() : _currentMissionIter(_missionMap.end()) { }; + MissionMap::iterator _currentMission; }; } // namespace openspace - -#endif // __MISSIONPHASEEQUENCER_H__ - +#endif // __MISSIONMANAGER_H__ diff --git a/include/openspace/network/messagestructures.h b/include/openspace/network/messagestructures.h index e2e3adc313..a5f999f030 100644 --- a/include/openspace/network/messagestructures.h +++ b/include/openspace/network/messagestructures.h @@ -33,133 +33,135 @@ #include //openspace includes -#include +#include namespace openspace{ - - namespace network{ - - namespace datamessagestructures{ - enum type{ - PositionData = 0, - TimeData, - ScriptData - }; - - struct PositionKeyframe{ - glm::quat _viewRotationQuat; - psc _position; - double _timeStamp; - - void serialize(std::vector &buffer){ - //add position - buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); - - //add orientation - buffer.insert(buffer.end(), reinterpret_cast(&_viewRotationQuat), reinterpret_cast(&_viewRotationQuat) + sizeof(_viewRotationQuat)); - - //add timestamp - buffer.insert(buffer.end(), reinterpret_cast(&_timeStamp), reinterpret_cast(&_timeStamp) + sizeof(_timeStamp)); - }; - - void deserialize(const std::vector &buffer){ - int offset = 0; - int size = 0; - - //position - size = sizeof(_position); - memcpy(&_position, buffer.data() + offset, size); - offset += size; - - //orientation - size = sizeof(_viewRotationQuat); - memcpy(&_viewRotationQuat, buffer.data() + offset, size); - offset += size; - - //timestamp - size = sizeof(_timeStamp); - memcpy(&_timeStamp, buffer.data() + offset, size); - }; - }; - - struct TimeKeyframe{ +namespace datamessagestructures { +enum class Type : uint32_t { + CameraData = 0, + TimeData, + ScriptData +}; - double _time; - double _dt; - bool _paused; - bool _requiresTimeJump; - - void serialize(std::vector &buffer){ - //add current time - buffer.insert(buffer.end(), reinterpret_cast(&_time), reinterpret_cast(&_time) + sizeof(_time)); - - //add delta time - buffer.insert(buffer.end(), reinterpret_cast(&_dt), reinterpret_cast(&_dt) + sizeof(_dt)); - - //add wether time is paused or not - buffer.insert(buffer.end(), reinterpret_cast(&_paused), reinterpret_cast(&_paused) + sizeof(_paused)); - - //add wether a time jump is necessary (recompute paths etc) - buffer.insert(buffer.end(), reinterpret_cast(&_requiresTimeJump), reinterpret_cast(&_requiresTimeJump) + sizeof(_requiresTimeJump)); - }; - - void deserialize(const std::vector &buffer){ - int offset = 0; - int size = 0; - - //current time - size = sizeof(_time); - memcpy(&_time, buffer.data() + offset, size); - offset += size; - - //delta time - size = sizeof(_dt); - memcpy(&_dt, buffer.data() + offset, size); - offset += size; - - //is time paused? - size = sizeof(_paused); - memcpy(&_paused, buffer.data() + offset, size); - offset += sizeof(_paused); - - //is a time jump required? - size = sizeof(_requiresTimeJump); - memcpy(&_requiresTimeJump, buffer.data() + offset, size); - }; - }; - - struct ScriptMessage{ - - uint16_t _scriptlen; - std::string _script; - - void serialize(std::vector &buffer){ - //add script length - buffer.insert(buffer.end(), reinterpret_cast(&_scriptlen), reinterpret_cast(&_scriptlen) + sizeof(_scriptlen)); - - //add script - buffer.insert(buffer.end(), _script.begin(), _script.end()); - - }; - - void deserialize(const std::vector &buffer){ - int offset = 0; - int size = 0; - - //size of script - size = sizeof(uint16_t); - memcpy(&_scriptlen, buffer.data() + offset, size); - offset += size; - - //actual script - _script.assign(buffer.begin() + offset, buffer.end()); - }; - }; - - } //namespace messagestructures +struct CameraKeyframe { + CameraKeyframe() {} + CameraKeyframe(const std::vector &buffer) { + deserialize(buffer); + } - } // namespace network + glm::dvec3 _position; + glm::dquat _rotation; + double _timestamp; + + void serialize(std::vector &buffer){ + //add position + buffer.insert(buffer.end(), reinterpret_cast(&_position), reinterpret_cast(&_position) + sizeof(_position)); + + //add orientation + buffer.insert(buffer.end(), reinterpret_cast(&_rotation), reinterpret_cast(&_rotation) + sizeof(_rotation)); + + //add timestamp + buffer.insert(buffer.end(), reinterpret_cast(&_timestamp), reinterpret_cast(&_timestamp) + sizeof(_timestamp)); + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //position + size = sizeof(_position); + memcpy(&_position, buffer.data() + offset, size); + offset += size; + + //orientation + size = sizeof(_rotation); + memcpy(&_rotation, buffer.data() + offset, size); + offset += size; + + //timestamp + size = sizeof(_timestamp); + memcpy(&_timestamp, buffer.data() + offset, size); + }; +}; + +struct TimeKeyframe { + TimeKeyframe() {} + TimeKeyframe(const std::vector &buffer) { + deserialize(buffer); + } + + double _time; + double _dt; + bool _paused; + bool _requiresTimeJump; + double _timestamp; + + void serialize(std::vector &buffer){ + //add current time + buffer.insert(buffer.end(), reinterpret_cast(&_time), reinterpret_cast(&_time) + sizeof(_time)); + + //add delta time + buffer.insert(buffer.end(), reinterpret_cast(&_dt), reinterpret_cast(&_dt) + sizeof(_dt)); + + //add wether time is paused or not + buffer.insert(buffer.end(), reinterpret_cast(&_paused), reinterpret_cast(&_paused) + sizeof(_paused)); + + //add wether a time jump is necessary (recompute paths etc) + buffer.insert(buffer.end(), reinterpret_cast(&_requiresTimeJump), reinterpret_cast(&_requiresTimeJump) + sizeof(_requiresTimeJump)); + + //add timestamp + buffer.insert(buffer.end(), reinterpret_cast(&_timestamp), reinterpret_cast(&_timestamp) + sizeof(_timestamp)); + }; + + void deserialize(const std::vector &buffer){ + int offset = 0; + int size = 0; + + //current time + size = sizeof(_time); + memcpy(&_time, buffer.data() + offset, size); + offset += size; + + //delta time + size = sizeof(_dt); + memcpy(&_dt, buffer.data() + offset, size); + offset += size; + + //is time paused? + size = sizeof(_paused); + memcpy(&_paused, buffer.data() + offset, size); + offset += sizeof(_paused); + //is a time jump required? + size = sizeof(_requiresTimeJump); + memcpy(&_requiresTimeJump, buffer.data() + offset, size); + offset += size; + + // timestamp + size = sizeof(_timestamp); + memcpy(&_timestamp, buffer.data() + offset, size); + offset += size; + }; +}; + +struct ScriptMessage { + ScriptMessage() {} + ScriptMessage(const std::vector &buffer) { + deserialize(buffer); + } + + std::string _script; + + void serialize(std::vector &buffer){ + buffer.insert(buffer.end(), _script.begin(), _script.end()); + }; + + void deserialize(const std::vector &buffer){ + _script.assign(buffer.begin(), buffer.end()); + }; +}; + +} //namespace messagestructures } // namespace openspace #endif // __MESSAGESTRUCTURES_H__ diff --git a/include/openspace/network/parallelconnection.h b/include/openspace/network/parallelconnection.h index 9073d1f18b..e372d75001 100644 --- a/include/openspace/network/parallelconnection.h +++ b/include/openspace/network/parallelconnection.h @@ -26,19 +26,20 @@ #define __PARALLELCONNECTION_H__ //openspace includes -#include -#include #include //glm includes #include +//ghoul includes +#include + //std includes #include #include +#include #include #include -#include #include #include #include @@ -59,150 +60,149 @@ typedef int _SOCKET; #include #endif -namespace openspace{ - - namespace network{ - - class ParallelConnection{ - public: - - ParallelConnection(); - - ~ParallelConnection(); - - void clientConnect(); - - void setPort(const std::string &port); - - void setAddress(const std::string &address); - - void setName(const std::string& name); - - bool isHost(); - - void requestHostship(const std::string &password); +namespace openspace { - void setPassword(const std::string &password); - - void signalDisconnect(); - - void preSynchronization(); - - void scriptMessage(const std::string propIdentifier, const std::string propValue); - - enum MessageTypes{ - Authentication=0, - Initialization, - Data, - Script, //obsolete now - HostInfo, - InitializationRequest, - HostshipRequest, - InitializationCompleted - }; - - /** - * Returns the Lua library that contains all Lua functions available to affect the - * remote OS parallel connection. The functions contained are - * - - * \return The Lua library that contains all Lua functions available to affect the - * interaction - */ - static scripting::LuaLibrary luaLibrary(); - - protected: - - private: - //@TODO change this into the ghoul hasher for client AND server - uint32_t hash(const std::string &val){ - uint32_t hashVal = 0, i; - size_t len = val.length(); +class ParallelConnection { + public: + enum class Status : uint32_t { + Disconnected = 0, + ClientWithoutHost, + ClientWithHost, + Host + }; - for (hashVal = i = 0; i < len; ++i){ - hashVal += val.c_str()[i]; - hashVal += (hashVal << 10); - hashVal ^= (hashVal >> 6); - } + enum class MessageType : uint32_t { + Authentication = 0, + Data, + ConnectionStatus, + HostshipRequest, + HostshipResignation, + NConnections + }; - hashVal += (hashVal << 3); - hashVal ^= (hashVal >> 11); - hashVal += (hashVal << 15); + struct Message { + Message() {}; + Message(MessageType t, const std::vector& c) + : type(t) + , content(c) + {}; + + MessageType type; + std::vector content; + }; + + struct DataMessage { + DataMessage() {}; + DataMessage(datamessagestructures::Type t, const std::vector& c) + : type(t) + , content(c) + {}; + datamessagestructures::Type type; + std::vector content; + }; + + ParallelConnection(); + ~ParallelConnection(); + void clientConnect(); + void setPort(const std::string &port); + void setAddress(const std::string &address); + void setName(const std::string& name); + bool isHost(); + const std::string& hostName(); + void requestHostship(const std::string &password); + void resignHostship(); + void setPassword(const std::string &password); + void signalDisconnect(); + void preSynchronization(); + void sendScript(const std::string& script); + + /** + * Returns the Lua library that contains all Lua functions available to affect the + * remote OS parallel connection. The functions contained are + * - + * \return The Lua library that contains all Lua functions available to affect the + * interaction + */ + static scripting::LuaLibrary luaLibrary(); + Status status(); + size_t nConnections(); + std::shared_ptr> connectionEvent(); - return hashVal; - }; - void queueMessage(std::vector message); - void disconnect(); +private: + //@TODO change this into the ghoul hasher for client AND server + uint32_t hash(const std::string &val); + void queueOutMessage(const Message& message); + void queueOutDataMessage(const DataMessage& dataMessage); + void queueInMessage(const Message& message); + + void disconnect(); + void closeSocket(); + bool initNetworkAPI(); + void establishConnection(addrinfo *info); + void sendAuthentication(); + void listenCommunication(); + int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); + + void handleMessage(const Message&); + void dataMessageReceived(const std::vector& messageContent); + void connectionStatusMessageReceived(const std::vector& messageContent); + void nConnectionsMessageReceived(const std::vector& messageContent); + + void broadcast(); + void sendCameraKeyframe(); + void sendTimeKeyframe(); + + void sendFunc(); + void threadManagement(); + + void setStatus(Status status); + void setHostName(const std::string& hostName); + void setNConnections(size_t nConnections); + + double calculateBufferedKeyframeTime(double originalTime); + + uint32_t _passCode; + std::string _port; + std::string _address; + std::string _name; - void writeHeader(std::vector &buffer, uint32_t messageType); + _SOCKET _clientSocket; - void closeSocket(); + std::atomic _isConnected; + std::atomic _isRunning; + std::atomic _tryConnect; + std::atomic _disconnect; + std::atomic _initializationTimejumpRequired; - bool initNetworkAPI(); + std::atomic _nConnections; + std::atomic _status; + std::string _hostName; - void establishConnection(addrinfo *info); - - void sendAuthentication(); - - void listenCommunication(); - - void delegateDecoding(uint32_t type); - - void initializationMessageReceived(); - - void dataMessageReceived(); - - void hostInfoMessageReceived(); + std::condition_variable _disconnectCondition; + std::mutex _disconnectMutex; - void initializationRequestMessageReceived(); + std::condition_variable _sendCondition; + std::deque _sendBuffer; + std::mutex _sendBufferMutex; - void broadcast(); + std::deque _receiveBuffer; + std::mutex _receiveBufferMutex; - int headerSize(); + std::atomic _timeJumped; + std::mutex _latencyMutex; + std::deque _latencyDiffs; + double _initialTimeDiff; - int receiveData(_SOCKET & socket, std::vector &buffer, int length, int flags); - - void sendFunc(); - - bool parseHints(addrinfo &info); - - void threadManagement(); - - std::string scriptFromPropertyAndValue(const std::string property, const std::string value); - - uint32_t _passCode; - std::string _port; - std::string _address; - std::string _name; - _SOCKET _clientSocket; - std::thread *_connectionThread; - std::thread *_broadcastThread; - std::thread *_sendThread; - std::thread *_listenThread; - std::thread *_handlerThread; - std::atomic _isHost; - std::atomic _isConnected; - std::atomic _performDisconnect; - std::atomic _isRunning; - std::atomic _tryConnect; - std::atomic _initializationTimejumpRequired; + std::unique_ptr _connectionThread; + std::unique_ptr _broadcastThread; + std::unique_ptr _sendThread; + std::unique_ptr _listenThread; + std::unique_ptr _handlerThread; + std::shared_ptr> _connectionEvent; +}; - std::condition_variable _disconnectCondition; - std::mutex _disconnectMutex; - - std::vector> _sendBuffer; - std::mutex _sendBufferMutex; - std::condition_variable _sendCondition; - - network::datamessagestructures::TimeKeyframe _latestTimeKeyframe; - std::mutex _timeKeyframeMutex; - std::atomic _latestTimeKeyframeValid; - std::map _currentState; - std::mutex _currentStateMutex; - }; - } // namespace network - } // namespace openspace #endif // __OSPARALLELCONNECTION_H__ diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index 0e748a2f65..1c1fe3599f 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -124,7 +124,7 @@ public: private: bool loadSceneInternal(const std::string& sceneDescriptionFilePath); - void writePropertyDocumentation(const std::string& filename, const std::string& type); + void writePropertyDocumentation(const std::string& filename, const std::string& type, const std::string& sceneFilename); std::string _focus; diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index a7a3c4cac7..365e8662be 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include @@ -103,9 +103,9 @@ public: // @TODO Remove once the scalegraph is in effect ---abock - void setEphemeris(Ephemeris* eph) { - delete _ephemeris; - _ephemeris = eph; + void setEphemeris(Translation* eph) { + delete _translation; + _translation = eph; } static documentation::Documentation Documentation(); @@ -129,7 +129,7 @@ private: PowerScaledScalar _boundingSphere; // Transformation defined by ephemeris, rotation and scale - Ephemeris* _ephemeris; + Translation* _translation; Rotation* _rotation; Scale* _scale; diff --git a/include/openspace/scene/ephemeris.h b/include/openspace/scene/translation.h similarity index 91% rename from include/openspace/scene/ephemeris.h rename to include/openspace/scene/translation.h index ae56fe8569..d2d64118fa 100644 --- a/include/openspace/scene/ephemeris.h +++ b/include/openspace/scene/translation.h @@ -22,8 +22,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __EPHEMERIS_H__ -#define __EPHEMERIS_H__ +#ifndef __TRANSLATION_H__ +#define __TRANSLATION_H__ #include @@ -34,11 +34,11 @@ namespace openspace { -class Ephemeris : public properties::PropertyOwner { +class Translation : public properties::PropertyOwner { public: - static Ephemeris* createFromDictionary(const ghoul::Dictionary& dictionary); + static Translation* createFromDictionary(const ghoul::Dictionary& dictionary); - virtual ~Ephemeris(); + virtual ~Translation(); virtual bool initialize(); virtual glm::dvec3 position() const = 0; virtual void update(const UpdateData& data); @@ -48,4 +48,4 @@ public: } // namespace openspace -#endif // __EPHEMERIS_H__ +#endif // __TRANSLATION_H__ diff --git a/include/openspace/scripting/lualibrary.h b/include/openspace/scripting/lualibrary.h index 6787575a84..36f4c05d1d 100644 --- a/include/openspace/scripting/lualibrary.h +++ b/include/openspace/scripting/lualibrary.h @@ -37,9 +37,8 @@ namespace scripting { struct LuaLibrary { /** * This structure represents a Lua function with its #name, #function pointer - * #argumentText describing the arguments this function takes, the #helpText - * describing the function, and whether it should be shared in a parallel - * connection (#parallelShared) + * #argumentText describing the arguments this function takes, and the the #helpText + * describing the function. */ struct Function { /// The name of the function @@ -50,9 +49,6 @@ struct LuaLibrary { std::string argumentText; /// A help text describing what the function does/ std::string helpText; - /// If true, this function will be shared with other parallel - /// connections - bool parallelShared; }; /// The name of the library std::string name; diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 89edffff5d..9e02e117ed 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -50,6 +50,7 @@ namespace scripting { */ class ScriptEngine : public Syncable { public: + using RemoteScripting = ghoul::Boolean; /** * Initializes the internal Lua state and registers a common set of library functions * \throw LuaRuntimeException If the creation of the new Lua state fails @@ -79,7 +80,7 @@ public: virtual void decode(SyncBuffer* syncBuffer); virtual void postsync(bool isMaster); - void queueScript(const std::string &script); + void queueScript(const std::string &script, RemoteScripting remoteScripting); void setLogFile(const std::string& filename, const std::string& type); @@ -88,9 +89,9 @@ public: std::vector allLuaFunctions() const; //parallel functions - bool parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script); - bool shouldScriptBeSent(const std::string &library, const std::string &function); - void cacheScript(const std::string &library, const std::string &function, const std::string &script); + //bool parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script); + //bool shouldScriptBeSent(const std::string &library, const std::string &function); + //void cacheScript(const std::string &library, const std::string &function, const std::string &script); private: @@ -107,13 +108,13 @@ private: //sync variables std::mutex _mutex; - std::vector _queuedScripts; + std::vector> _queuedScripts; std::vector _receivedScripts; std::string _currentSyncedScript; //parallel variables - std::map> _cachedScripts; - std::mutex _cachedScriptsMutex; + //std::map> _cachedScripts; + //std::mutex _cachedScriptsMutex; //logging variables bool _logFileExists = false; diff --git a/include/openspace/util/timemanager.h b/include/openspace/util/timemanager.h new file mode 100644 index 0000000000..fd0b9c4e19 --- /dev/null +++ b/include/openspace/util/timemanager.h @@ -0,0 +1,51 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2016 * +* * +* 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. * +****************************************************************************************/ + +#ifndef __TIMEMANAGER_H__ +#define __TIMEMANAGER_H__ + +#include +#include + +namespace openspace { + +class TimeManager { +public: + void preSynchronization(double dt); + void addKeyframe(const datamessagestructures::TimeKeyframe& kf); + void removeKeyframesBefore(double timestamp); + void clearKeyframes(); +private: + void consumeKeyframes(double dt); + std::deque _keyframes; + static bool compareKeyframeTimes( + const datamessagestructures::TimeKeyframe& a, + const datamessagestructures::TimeKeyframe& b); + double _latestConsumedTimestamp; +}; + +} + + +#endif // __TIMEMANAGER_H__ \ No newline at end of file diff --git a/include/openspace/util/timerange.h b/include/openspace/util/timerange.h index 059a0d8670..4e82e0dacf 100644 --- a/include/openspace/util/timerange.h +++ b/include/openspace/util/timerange.h @@ -1,37 +1,33 @@ /***************************************************************************************** -* * -* OpenSpace * -* * -* Copyright (c) 2014-2016 * -* * -* 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. * -****************************************************************************************/ - -#include -#include // ephemerisTimeFromDate + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ #ifndef __TIMERANGE_H__ #define __TIMERANGE_H__ -namespace { - const std::string KEY_START = "Start"; - const std::string KEY_END = "End"; -} +#include + +namespace ghoul { class Dictionary; } namespace openspace { @@ -40,79 +36,41 @@ struct TimeRange { /** * Default constructor initializes an empty time range. */ - TimeRange() : start(DBL_MAX), end(-DBL_MAX) { }; + TimeRange(); /** * Initializes a TimeRange with both start and end time. Initializing empty timeranges * is OK. */ - TimeRange(double startTime, double endTime) : start(startTime) , end(endTime) { }; + TimeRange(double startTime, double endTime); /** * Throws exception if unable to parse the provided \class ghoul::Dictionary */ - TimeRange(const ghoul::Dictionary& dict) { - if (!initializeFromDictionary(dict, *this)) { - throw std::runtime_error("Unable to read TimeRange from dictionary"); - } - } + TimeRange(const ghoul::Dictionary& dict); /** * \returns true if timeRange could be initialized from the dictionary, false otherwise. */ - static bool initializeFromDictionary(const ghoul::Dictionary& dict, TimeRange& timeRange) { - std::string startTimeStr; - std::string endTimeStr; + static bool initializeFromDictionary(const ghoul::Dictionary& dict, TimeRange& timeRange); - bool success = true; - success &= dict.getValue(KEY_START, startTimeStr); - success &= dict.getValue(KEY_END, endTimeStr); - if (success) { - // Parse to date. - // @TODO converting string to time stamp should not rely on Spice - timeRange.start = SpiceManager::ref().ephemerisTimeFromDate(startTimeStr); - timeRange.end = SpiceManager::ref().ephemerisTimeFromDate(endTimeStr); - return true; - } - else { - // Could not read TimeRange from Dict - return false; - } - } + void include(double val); - void include(double val){ - if (start > val) start = val; - if (end < val) end = val; - }; + void include(const TimeRange& other); + + double duration() const; - void include(const TimeRange& other) { - if (other.start < start) start = other.start; - if (other.end > end) end = other.end; - } + bool isDefined() const; - double duration() const { - return end - start; - } + bool isEmpty() const; - bool isDefined() const { - return start <= end; - } + bool inRange(double min, double max); - bool isEmpty() const { - return !isDefined(); - } + bool includes(double val) const; - bool inRange(double min, double max){ - return (min >= start && max <= end); - } + bool includes(const TimeRange& o) const; - bool includes(double val) const { - return (start <= val && val <= end); - } - - bool includes(const TimeRange& o) const { - return start <= o.start && o.end <= end; - } + static openspace::Documentation Documentation(); double start; double end; diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 163dc6e976..26ca3f53be 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -42,8 +42,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspaceframebuffer.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspaceimage.h - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.h - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.h + ${CMAKE_CURRENT_SOURCE_DIR}/translation/spicetranslation.h + ${CMAKE_CURRENT_SOURCE_DIR}/translation/statictranslation.h ${CMAKE_CURRENT_SOURCE_DIR}/rotation/spicerotation.h ${CMAKE_CURRENT_SOURCE_DIR}/rotation/staticrotation.h ${CMAKE_CURRENT_SOURCE_DIR}/scale/staticscale.h @@ -68,8 +68,8 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/simplespheregeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspaceframebuffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/screenspaceimage.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/spiceephemeris.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ephemeris/staticephemeris.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/translation/spicetranslation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/translation/statictranslation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rotation/spicerotation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rotation/staticrotation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/scale/staticscale.cpp diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index e1725442fe..d5a0d62923 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -48,8 +48,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -108,11 +108,11 @@ void BaseModule::internalInitialize() { fRenderable->registerClass("RenderableTrail"); fRenderable->registerClass("RenderableTrailNew"); - auto fEphemeris = FactoryManager::ref().factory(); - ghoul_assert(fEphemeris, "Ephemeris factory was not created"); + auto fTranslation = FactoryManager::ref().factory(); + ghoul_assert(fTranslation, "Ephemeris factory was not created"); - fEphemeris->registerClass("StaticEphemeris"); - fEphemeris->registerClass("SpiceEphemeris"); + fTranslation->registerClass("StaticTranslation"); + fTranslation->registerClass("SpiceTranslation"); auto fRotation = FactoryManager::ref().factory(); ghoul_assert(fRotation, "Rotation factory was not created"); @@ -137,8 +137,8 @@ void BaseModule::internalInitialize() { std::vector BaseModule::documentations() const { return { StaticScale::Documentation(), - StaticEphemeris::Documentation(), - SpiceEphemeris::Documentation() + StaticTranslation::Documentation(), + SpiceTranslation::Documentation() }; } diff --git a/modules/base/scale/staticscale.cpp b/modules/base/scale/staticscale.cpp index 214847d290..c80a27f992 100644 --- a/modules/base/scale/staticscale.cpp +++ b/modules/base/scale/staticscale.cpp @@ -37,6 +37,7 @@ Documentation StaticScale::Documentation() { using namespace openspace::documentation; return { "Static Scaling", + "base_scale_static", {{ KeyValue, new DoubleVerifier, diff --git a/modules/base/ephemeris/spiceephemeris.cpp b/modules/base/translation/spicetranslation.cpp similarity index 94% rename from modules/base/ephemeris/spiceephemeris.cpp rename to modules/base/translation/spicetranslation.cpp index 59d6e6a1b4..dbb6060933 100644 --- a/modules/base/ephemeris/spiceephemeris.cpp +++ b/modules/base/translation/spicetranslation.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include @@ -41,7 +41,7 @@ namespace { namespace openspace { -Documentation SpiceEphemeris::Documentation() { +Documentation SpiceTranslation::Documentation() { using namespace openspace::documentation; return { @@ -50,7 +50,7 @@ Documentation SpiceEphemeris::Documentation() { { { "Type", - new StringEqualVerifier("SpiceEphemeris"), + new StringEqualVerifier("SpiceTranslation"), "", Optional::No }, @@ -89,7 +89,7 @@ Documentation SpiceEphemeris::Documentation() { }; } -SpiceEphemeris::SpiceEphemeris(const ghoul::Dictionary& dictionary) +SpiceTranslation::SpiceTranslation(const ghoul::Dictionary& dictionary) : _target("target", "Target", "") , _origin("origin", "Origin", "") , _kernelsLoadedSuccessfully(true) @@ -97,7 +97,7 @@ SpiceEphemeris::SpiceEphemeris(const ghoul::Dictionary& dictionary) documentation::testSpecificationAndThrow( Documentation(), dictionary, - "SpiceEphemeris" + "SpiceTranslation" ); _target = dictionary.value(KeyBody); @@ -132,11 +132,11 @@ SpiceEphemeris::SpiceEphemeris(const ghoul::Dictionary& dictionary) } } -glm::dvec3 SpiceEphemeris::position() const { +glm::dvec3 SpiceTranslation::position() const { return _position; } -void SpiceEphemeris::update(const UpdateData& data) { +void SpiceTranslation::update(const UpdateData& data) { double lightTime = 0.0; _position = SpiceManager::ref().targetPosition( _target, _origin, ReferenceFrame, {}, data.time, lightTime diff --git a/modules/base/ephemeris/spiceephemeris.h b/modules/base/translation/spicetranslation.h similarity index 91% rename from modules/base/ephemeris/spiceephemeris.h rename to modules/base/translation/spicetranslation.h index 92d9991e1e..38a19c35fa 100644 --- a/modules/base/ephemeris/spiceephemeris.h +++ b/modules/base/translation/spicetranslation.h @@ -22,19 +22,19 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __SPICEEPHEMERIS_H__ -#define __SPICEEPHEMERIS_H__ +#ifndef __SPICETRANSLATION_H__ +#define __SPICETRANSLATION_H__ -#include +#include #include #include namespace openspace { -class SpiceEphemeris : public Ephemeris { +class SpiceTranslation : public Translation { public: - SpiceEphemeris(const ghoul::Dictionary& dictionary); + SpiceTranslation(const ghoul::Dictionary& dictionary); glm::dvec3 position() const; void update(const UpdateData& data) override; @@ -50,4 +50,4 @@ private: } // namespace openspace -#endif // __SPICEEPHEMERIS_H__ +#endif // __SPICETRANSLATION_H__ diff --git a/modules/base/ephemeris/staticephemeris.cpp b/modules/base/translation/statictranslation.cpp similarity index 87% rename from modules/base/ephemeris/staticephemeris.cpp rename to modules/base/translation/statictranslation.cpp index 551da7a05c..1b6bb288ce 100644 --- a/modules/base/ephemeris/staticephemeris.cpp +++ b/modules/base/translation/statictranslation.cpp @@ -22,7 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include @@ -32,7 +32,7 @@ namespace { namespace openspace { -Documentation StaticEphemeris::Documentation() { +Documentation StaticTranslation::Documentation() { using namespace openspace::documentation; return { "Static Translation", @@ -40,7 +40,7 @@ Documentation StaticEphemeris::Documentation() { { { "Type", - new StringEqualVerifier("StaticEphemeris"), + new StringEqualVerifier("StaticTranslation"), "", Optional::No }, @@ -57,7 +57,7 @@ Documentation StaticEphemeris::Documentation() { } -StaticEphemeris::StaticEphemeris() +StaticTranslation::StaticTranslation() : _position( "position", "Position", @@ -69,8 +69,8 @@ StaticEphemeris::StaticEphemeris() addProperty(_position); } -StaticEphemeris::StaticEphemeris(const ghoul::Dictionary& dictionary) - : StaticEphemeris() +StaticTranslation::StaticTranslation(const ghoul::Dictionary& dictionary) + : StaticTranslation() { documentation::testSpecificationAndThrow( Documentation(), @@ -81,12 +81,12 @@ StaticEphemeris::StaticEphemeris(const ghoul::Dictionary& dictionary) _position = dictionary.value(KeyPosition); } -StaticEphemeris::~StaticEphemeris() {} +StaticTranslation::~StaticTranslation() {} -glm::dvec3 StaticEphemeris::position() const { +glm::dvec3 StaticTranslation::position() const { return _position; } -void StaticEphemeris::update(const UpdateData&) {} +void StaticTranslation::update(const UpdateData&) {} } // namespace openspace diff --git a/modules/base/ephemeris/staticephemeris.h b/modules/base/translation/statictranslation.h similarity index 89% rename from modules/base/ephemeris/staticephemeris.h rename to modules/base/translation/statictranslation.h index d85f3eba41..3a71266c28 100644 --- a/modules/base/ephemeris/staticephemeris.h +++ b/modules/base/translation/statictranslation.h @@ -22,21 +22,21 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __STATICEPHEMERIS_H__ -#define __STATICEPHEMERIS_H__ +#ifndef __STATICTRANSLATION_H__ +#define __STATICTRANSLATION_H__ -#include +#include #include #include namespace openspace { -class StaticEphemeris : public Ephemeris { +class StaticTranslation : public Translation { public: - StaticEphemeris(); - StaticEphemeris(const ghoul::Dictionary& dictionary); - virtual ~StaticEphemeris(); + StaticTranslation(); + StaticTranslation(const ghoul::Dictionary& dictionary); + virtual ~StaticTranslation(); virtual glm::dvec3 position() const; virtual void update(const UpdateData& data) override; @@ -48,4 +48,4 @@ private: } // namespace openspace -#endif // __STATICEPHEMERIS_H__ +#endif // __STATICTRANSLATION_H__ diff --git a/modules/galaxy/rendering/galaxyraycaster.cpp b/modules/galaxy/rendering/galaxyraycaster.cpp index 91a6689f35..84b12f6be8 100644 --- a/modules/galaxy/rendering/galaxyraycaster.cpp +++ b/modules/galaxy/rendering/galaxyraycaster.cpp @@ -134,7 +134,7 @@ bool GalaxyRaycaster::cameraIsInside(const RenderData& data, glm::vec3& localPos glm::vec4 modelPos = (glm::inverse(scaledModelTransform) / divisor) * camWorldPos; - localPosition = (modelPos.xyz() + glm::vec3(0.5)); + localPosition = (glm::vec3(modelPos) + glm::vec3(0.5)); return (localPosition.x > 0 && localPosition.y > 0 && localPosition.z > 0 && localPosition.x < 1 && localPosition.y < 1 && localPosition.z < 1); } diff --git a/modules/globebrowsing/tile/tiledataset.cpp b/modules/globebrowsing/tile/tiledataset.cpp index b448c4df46..4051f3e14b 100644 --- a/modules/globebrowsing/tile/tiledataset.cpp +++ b/modules/globebrowsing/tile/tiledataset.cpp @@ -41,8 +41,8 @@ #include #include - - +#include +#include namespace { @@ -170,10 +170,55 @@ namespace openspace { if (!GdalHasBeenInitialized) { GDALAllRegister(); CPLSetConfigOption("GDAL_DATA", absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()); + setGdalProxyConfiguration(); GdalHasBeenInitialized = true; } } + void TileDataset::setGdalProxyConfiguration() { + ghoul::Dictionary proxySettings; + bool proxyEnabled = OsEng.configurationManager().getValue(ConfigurationManager::KeyHttpProxy, proxySettings); + if (proxyEnabled) { + std::string proxyAddress, proxyPort, proxyUser, proxyPassword, proxyAuth; + + bool success = proxySettings.getValue(ConfigurationManager::PartHttpProxyAddress, proxyAddress); + success &= proxySettings.getValue(ConfigurationManager::PartHttpProxyPort, proxyPort); + proxySettings.getValue(ConfigurationManager::PartHttpProxyAuthentication, proxyAuth); + + std::string proxyAuthString = "BASIC"; + if (proxyAuth == "basic" || proxyAuth == "") { + proxyAuthString = "BASIC"; + } else if (proxyAuth == "ntlm") { + proxyAuthString = "NTLM"; + } else if (proxyAuth == "digest") { + proxyAuthString = "DIGEST"; + } else if (proxyAuth == "any") { + proxyAuthString = "ANY"; + } else { + success = false; + } + + bool userAndPassword = proxySettings.getValue(ConfigurationManager::PartHttpProxyUser, proxyUser); + userAndPassword &= proxySettings.getValue(ConfigurationManager::PartHttpProxyPassword, proxyPassword); + + if (success) { + std::string proxy = proxyAddress + ":" + proxyPort; + CPLSetConfigOption("GDAL_HTTP_PROXY", proxy.c_str()); + LDEBUG("Using proxy server " << proxy); + if (userAndPassword) { + std::string proxyUserPwd = proxyUser + ":" + proxyPassword; + CPLSetConfigOption("GDAL_HTTP_PROXYUSERPWD", proxyUserPwd.c_str()); + CPLSetConfigOption("GDAL_HTTP_PROXYAUTH", proxyAuthString.c_str()); + LDEBUG("Using authentication method: " << proxyAuthString); + } + } else { + LERROR("Invalid proxy settings for GDAL"); + } + } else { + LDEBUG("Setting up GDAL without proxy server"); + } + } + GDALDataset* TileDataset::gdalDataset(const std::string& gdalDatasetDesc) { GDALDataset* dataset = (GDALDataset *)GDALOpen(gdalDatasetDesc.c_str(), GA_ReadOnly); if (!dataset) { diff --git a/modules/globebrowsing/tile/tiledataset.h b/modules/globebrowsing/tile/tiledataset.h index ec1930aa57..400c9a6ae0 100644 --- a/modules/globebrowsing/tile/tiledataset.h +++ b/modules/globebrowsing/tile/tiledataset.h @@ -136,6 +136,7 @@ namespace openspace { ////////////////////////////////////////////////////////////////////////////////// void gdalEnsureInitialized(); + void setGdalProxyConfiguration(); GDALDataset* gdalDataset(const std::string& gdalDatasetDesc); bool gdalHasOverviews() const; int gdalOverview(const PixelRange& baseRegionSize) const; diff --git a/modules/iswa/rendering/datacygnet.h b/modules/iswa/rendering/datacygnet.h index 48265635dc..f24f15c7e3 100644 --- a/modules/iswa/rendering/datacygnet.h +++ b/modules/iswa/rendering/datacygnet.h @@ -108,7 +108,7 @@ protected: private: bool readyToRender() const override; - bool downloadTextureResource(double timestamp = Time::ref().currentTime()) override; + bool downloadTextureResource(double timestamp = Time::ref().j2000Seconds()) override; }; } //namespace openspace diff --git a/modules/iswa/rendering/iswacygnet.cpp b/modules/iswa/rendering/iswacygnet.cpp index ee964d75e7..53610bf633 100644 --- a/modules/iswa/rendering/iswacygnet.cpp +++ b/modules/iswa/rendering/iswacygnet.cpp @@ -178,7 +178,7 @@ void IswaCygnet::update(const UpdateData& data){ // the texture resource is downloaded ahead of time, so we need to // now if we are going backwards or forwards double clockwiseSign = (Time::ref().deltaTime()>0) ? 1.0 : -1.0; - _openSpaceTime = Time::ref().currentTime(); + _openSpaceTime = Time::ref().j2000Seconds(); _realTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); _stateMatrix = TransformationManager::ref().frameTransformationMatrix(_data->frame, "GALACTIC", _openSpaceTime); @@ -222,7 +222,7 @@ void IswaCygnet::unregisterProperties(){ } void IswaCygnet::initializeTime(){ - _openSpaceTime = Time::ref().currentTime(); + _openSpaceTime = Time::ref().j2000Seconds(); _lastUpdateOpenSpaceTime = 0.0; _realTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); diff --git a/modules/iswa/rendering/screenspacecygnet.cpp b/modules/iswa/rendering/screenspacecygnet.cpp index 7b493ea866..73eb771e28 100644 --- a/modules/iswa/rendering/screenspacecygnet.cpp +++ b/modules/iswa/rendering/screenspacecygnet.cpp @@ -50,7 +50,7 @@ ScreenSpaceCygnet::ScreenSpaceCygnet(const ghoul::Dictionary& dictionary) _downloadImage = true; _url = IswaManager::ref().iswaUrl(_cygnetId); - _openSpaceTime = Time::ref().currentTime(); + _openSpaceTime = Time::ref().j2000Seconds(); _lastUpdateOpenSpaceTime = _openSpaceTime; _realTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); @@ -69,7 +69,7 @@ ScreenSpaceCygnet::ScreenSpaceCygnet(const ghoul::Dictionary& dictionary) ScreenSpaceCygnet::~ScreenSpaceCygnet(){} void ScreenSpaceCygnet::update(){ - _openSpaceTime = Time::ref().currentTime(); + _openSpaceTime = Time::ref().j2000Seconds(); _realTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); bool timeToUpdate = (fabs(_openSpaceTime-_lastUpdateOpenSpaceTime) >= _updateTime && diff --git a/modules/iswa/rendering/texturecygnet.h b/modules/iswa/rendering/texturecygnet.h index 1beef542c2..c451c6c60f 100644 --- a/modules/iswa/rendering/texturecygnet.h +++ b/modules/iswa/rendering/texturecygnet.h @@ -43,7 +43,7 @@ public: protected: bool updateTexture() override; - bool downloadTextureResource(double timestamp = Time::ref().currentTime()) override; + bool downloadTextureResource(double timestamp = Time::ref().j2000Seconds()) override; bool readyToRender() const override; bool updateTextureResource() override; diff --git a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp index 1d08f19e18..332e5d3ce9 100644 --- a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp +++ b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp @@ -149,7 +149,7 @@ bool MultiresVolumeRaycaster::cameraIsInside(const RenderData& data, glm::vec3& glm::vec4 modelPos = (glm::inverse(scaledModelTransform) / divisor) * camWorldPos; - localPosition = (modelPos.xyz() + glm::vec3(0.5)); + localPosition = (glm::vec3(modelPos) + glm::vec3(0.5)); return (localPosition.x > 0 && localPosition.y > 0 && localPosition.z > 0 && localPosition.x < 1 && localPosition.y < 1 && localPosition.z < 1); } diff --git a/modules/newhorizons/CMakeLists.txt b/modules/newhorizons/CMakeLists.txt index 24f465e439..131d181ccf 100644 --- a/modules/newhorizons/CMakeLists.txt +++ b/modules/newhorizons/CMakeLists.txt @@ -36,7 +36,6 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumenttimesparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.h - ${CMAKE_CURRENT_SOURCE_DIR}/util/missionmanager.h ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.h ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.h ${CMAKE_CURRENT_SOURCE_DIR}/util/projectioncomponent.h @@ -59,7 +58,6 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/util/hongkangparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumenttimesparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/imagesequencer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/util/missionmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/instrumentdecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/labelparser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util/projectioncomponent.cpp diff --git a/modules/newhorizons/util/missionmanager.cpp b/modules/newhorizons/util/missionmanager.cpp deleted file mode 100644 index ee7267e3e7..0000000000 --- a/modules/newhorizons/util/missionmanager.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * 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. * - ****************************************************************************************/ - -#include -#include -#include -#include -#include - - - -namespace { - const std::string _loggerCat = "MissionPhaseSequencer"; - - const std::string KEY_PHASE_NAME = "Name"; - const std::string KEY_PHASE_DESCRIPTION = "Description"; - const std::string KEY_PHASE_SUBPHASES = "Phases"; - const std::string KEY_TIME_RANGE = "TimeRange"; -} - - - - - -namespace openspace { - -MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { - const auto byPhaseStartTime = [](const MissionPhase& a, const MissionPhase& b)->bool{ - return a.timeRange().start < b.timeRange().start; - }; - - _name = dict.value(KEY_PHASE_NAME); - if (!dict.getValue(KEY_PHASE_DESCRIPTION, _description)) { - // If no description specified, just init to empty string - _description = ""; - } - - ghoul::Dictionary childDicts; - if (dict.getValue(KEY_PHASE_SUBPHASES, childDicts)) { - // This is a nested mission phase - _subphases.resize(childDicts.size()); - for (size_t i = 0; i < childDicts.size(); ++i) { - std::string key = std::to_string(i + 1); - _subphases[i] = MissionPhase(childDicts.value(key)); - } - - // Ensure subphases are sorted - std::stable_sort(_subphases.begin(), _subphases.end(), byPhaseStartTime); - - // Calculate the total time range of all subphases - TimeRange timeRangeSubPhases; - timeRangeSubPhases.start = _subphases[0].timeRange().start; - timeRangeSubPhases.end = _subphases.back().timeRange().end; - - // user may specify an overall time range. In that case expand this timerange. - ghoul::Dictionary timeRangeDict; - if (dict.getValue(KEY_TIME_RANGE, timeRangeDict)) { - TimeRange overallTimeRange(timeRangeDict); - ghoul_assert(overallTimeRange.includes(timeRangeSubPhases), - "User specified time range must at least include its subphases'"); - _timeRange.include(overallTimeRange); - } - else { - // Its OK to not specify an overall time range, the time range for the - // subphases will simply be used. - _timeRange.include(timeRangeSubPhases); - } - } - else { - ghoul::Dictionary timeRangeDict; - if (dict.getValue(KEY_TIME_RANGE, timeRangeDict)) { - _timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse - } - else { - throw std::runtime_error("Must specify key: " + KEY_TIME_RANGE); - } - } -}; - -std::list MissionPhase::phaseTrace(double time, int maxDepth) const { - std::list trace; - if (_timeRange.includes(time)) { - trace.push_back(this); - phaseTrace(time, trace, maxDepth); - } - return std::move(trace); -} - -bool MissionPhase::phaseTrace(double time, std::list& trace, int maxDepth) const { - if (maxDepth == 0) { - return false; - } - - for (int i = 0; i < _subphases.size(); ++i) { - if (_subphases[i]._timeRange.includes(time)) { - trace.push_back(&_subphases[i]); - _subphases[i].phaseTrace(time, trace, maxDepth - 1); - return true; // only add the first one - } - // Since time ranges are sorted we can do early termination - else if (_subphases[i]._timeRange.start > time) { - return false; - } - } - return true; -} - - - - - -Mission::Mission(std::string filepath) - : MissionPhase(readDictFromFile(filepath)) - , _filepath(filepath) -{ - -} - -ghoul::Dictionary Mission::readDictFromFile(std::string filepath) { - filepath = absPath(filepath); - LINFO("Reading mission phases fomr file: " << filepath); - if (!FileSys.fileExists(filepath)) - throw ghoul::FileNotFoundError(filepath, "Mission file path"); - - ghoul::Dictionary missionDict; - try { - ghoul::lua::loadDictionaryFromFile(filepath, missionDict); - return missionDict; - } - catch (ghoul::RuntimeError& e) { - LERROR("Unable to load mission phases"); - LERROR(e.message); - } - return {}; -} - - - - -MissionManager* MissionManager::_instance = nullptr; - -MissionManager& MissionManager::ref() { - assert(_instance != nullptr); - return *_instance; -} - -void MissionManager::initialize() { - assert(_instance == nullptr); - _instance = new MissionManager; - OsEng.scriptEngine().addLibrary(MissionManager::luaLibrary()); -} - -void MissionManager::deinitialize() { - delete _instance; - _instance = nullptr; -} - -void MissionManager::setCurrentMission(const std::string missionName) { - auto it = _missionMap.find(missionName); - if (it == _missionMap.end()) { - LWARNING("Mission with name \"" << missionName << "\" has not been loaded!"); - } - else { - _currentMissionIter = it; - } -} - -void MissionManager::loadMission(const std::string& filepath) { - Mission mission(filepath); - _missionMap[mission.name()] = mission; - if (_missionMap.size() == 1) { - setCurrentMission(mission.name()); - } -} - -const Mission& MissionManager::currentMission() { - if (_currentMissionIter == _missionMap.end()) { - LWARNING("No current mission has been specified. returning dummy mission"); - return Mission(); - } - return _currentMissionIter->second; -} - -namespace luascriptfunctions { - int loadMission(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string missionFileName = luaL_checkstring(L, -1); - if (missionFileName.empty()) { - return luaL_error(L, "filepath string is empty"); - } - MissionManager::ref().loadMission(missionFileName); - } - - int setCurrentMission(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string missionName = luaL_checkstring(L, -1); - if (missionName.empty()) { - return luaL_error(L, "mission name string is empty"); - } - MissionManager::ref().setCurrentMission(missionName); - } -} // namespace luascriptfunction - -scripting::LuaLibrary MissionManager::luaLibrary() { - return{ - "", - { - { - "loadMission", - &luascriptfunctions::loadMission, - "string", - "Load mission phases from file" - }, - { - "setCurrentMission", - &luascriptfunctions::setCurrentMission, - "string", - "Set the currnet mission" - }, - } - }; -} - -} // namespace openspace diff --git a/modules/newhorizons/util/missionphasesequencer.cpp b/modules/newhorizons/util/missionphasesequencer.cpp deleted file mode 100644 index 174c03e6c5..0000000000 --- a/modules/newhorizons/util/missionphasesequencer.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * 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. * - ****************************************************************************************/ - -#include -#include -#include -#include -#include - - - -namespace { - const std::string _loggerCat = "MissionPhaseSequencer"; -} - - - - - -namespace openspace { - -MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { - const auto byPhaseStartTime = [](const MissionPhase& a, const MissionPhase& b)->bool{ - return a.timeRange().start < b.timeRange().start; - }; - - _name = dict.value("Name"); - ghoul::Dictionary childDicts; - if (dict.getValue("Phases", childDicts)) { - // This is a nested mission phase - size_t numSubPhases = childDicts.size(); - _subphases.resize(numSubPhases); - for (size_t i = 0; i < numSubPhases; ++i) { - std::string key = std::to_string(i + 1); - _subphases[i] = MissionPhase(childDicts.value(key)); - } - - std::stable_sort(_subphases.begin(), _subphases.end(), byPhaseStartTime); - - // The subphases will have a total time phases - TimeRange timeRangeSubPhases; - timeRangeSubPhases.start = _subphases[0].timeRange().start; - timeRangeSubPhases.end = _subphases.back().timeRange().end; - - // user may specify an overall time range. In that case expand this timerange. - TimeRange overallTimeRange; - try { - overallTimeRange = parseTimeRange(dict); - ghoul_assert(overallTimeRange.includes(timeRangeSubPhases), - "User specified time range must at least include its subphases'"); - _timeRange.include(overallTimeRange); - } - catch (...) { - // Its OK to not specify an overall time range, the time range for the - // subphases will simply be used. - _timeRange.include(timeRangeSubPhases); - } - } - else { - _timeRange = parseTimeRange(dict); - } -}; - -TimeRange MissionPhase::parseTimeRange(const ghoul::Dictionary& dict) { - std::string startTimeStr; - std::string endTimeStr; - bool success = true; - success &= dict.getValue("StartTime", startTimeStr); - success &= dict.getValue("EndTime", endTimeStr); - - if (!success) { - // Had to do this because ghoul::Dictionary::value<>(std::string key) throws - // uncatchable xtree error on my AMNH windwos machine/ eb) - throw "meh"; - } - // Parse to date - TimeRange timeRange; - timeRange.start = SpiceManager::ref().ephemerisTimeFromDate(startTimeStr); - timeRange.end = SpiceManager::ref().ephemerisTimeFromDate(endTimeStr); - return timeRange; -} - - - - - -Mission::Mission(std::string filepath) - : MissionPhase(readDictFromFile(filepath)) - , _filepath(filepath) -{ - -} - -ghoul::Dictionary Mission::readDictFromFile(std::string filepath) { - filepath = absPath(filepath); - LINFO("Reading mission phases fomr file: " << filepath); - if (!FileSys.fileExists(filepath)) - throw ghoul::FileNotFoundError(filepath, "Mission file path"); - - ghoul::Dictionary missionDict; - try { - ghoul::lua::loadDictionaryFromFile(filepath, missionDict); - return missionDict; - } - catch (ghoul::RuntimeError& e) { - LWARNING("Unable to load mission phases"); - LWARNING(e.message); - } - return {}; -} - - - - -MissionManager* MissionManager::_instance = nullptr; - -MissionManager& MissionManager::ref() { - assert(_instance != nullptr); - return *_instance; -} - -void MissionManager::initialize() { - assert(_instance == nullptr); - _instance = new MissionManager; - OsEng.scriptEngine().addLibrary(MissionManager::luaLibrary()); -} - -void MissionManager::deinitialize() { - delete _instance; - _instance = nullptr; -} - -void MissionManager::setCurrentMission(const std::string missionName) { - auto it = _missionMap.find(missionName); - if (it == _missionMap.end()) { - LWARNING("Mission with name \"" << missionName << "\" has not been loaded!"); - } - else { - _currentMissionIter = it; - } -} - -void MissionManager::loadMission(const std::string& filepath) { - Mission mission(filepath); - _missionMap[mission.name()] = mission; - if (_missionMap.size() == 1) { - setCurrentMission(mission.name()); - } -} - -const Mission& MissionManager::currentMission() { - if (_currentMissionIter == _missionMap.end()) { - LWARNING("No current mission has been specified. returning dummy mission"); - } - return _currentMissionIter->second; -} - -namespace luascriptfunctions { - int loadMission(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string missionFileName = luaL_checkstring(L, -1); - if (missionFileName.empty()) { - return luaL_error(L, "filepath string is empty"); - } - MissionManager::ref().loadMission(missionFileName); - } - - int setCurrentMission(lua_State* L) { - using ghoul::lua::luaTypeToString; - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - std::string missionName = luaL_checkstring(L, -1); - if (missionName.empty()) { - return luaL_error(L, "mission name string is empty"); - } - MissionManager::ref().setCurrentMission(missionName); - } -} // namespace luascriptfunction - -scripting::LuaLibrary MissionManager::luaLibrary() { - return{ - "", - { - { - "loadMission", - &luascriptfunctions::loadMission, - "string", - "Load mission phases from file" - }, - { - "setCurrentMission", - &luascriptfunctions::setCurrentMission, - "string", - "Set the currnet mission" - }, - } - }; -} - -} // namespace openspace diff --git a/modules/onscreengui/src/gui.cpp b/modules/onscreengui/src/gui.cpp index 2057c64a38..9955a25cbc 100644 --- a/modules/onscreengui/src/gui.cpp +++ b/modules/onscreengui/src/gui.cpp @@ -155,7 +155,7 @@ void addScreenSpaceRenderable(std::string texturePath) { std::string luaTable = "{Type = 'ScreenSpaceImage', TexturePath = '" + absPath(texturePath) + "' }"; std::string script = "openspace.registerScreenSpaceRenderable(" + luaTable + ");"; - OsEng.scriptEngine().queueScript(script); + OsEng.scriptEngine().queueScript(script, openspace::scripting::ScriptEngine::RemoteScripting::Yes); } } // namespace @@ -453,22 +453,26 @@ void GUI::render() { if (toSun) { OsEng.scriptEngine().queueScript( - "openspace.setPropertyValue('Interaction.coordinateSystem', 'Sun');" + "openspace.setPropertyValue('Interaction.coordinateSystem', 'Sun');", + scripting::ScriptEngine::RemoteScripting::Yes ); } if (toPluto) { OsEng.scriptEngine().queueScript( - "openspace.setPropertyValue('Interaction.coordinateSystem', 'Pluto');" + "openspace.setPropertyValue('Interaction.coordinateSystem', 'Pluto');", + scripting::ScriptEngine::RemoteScripting::Yes ); } if (toJupiter) { OsEng.scriptEngine().queueScript( - "openspace.setPropertyValue('Interaction.coordinateSystem', 'Jupiter');" + "openspace.setPropertyValue('Interaction.coordinateSystem', 'Jupiter');", + scripting::ScriptEngine::RemoteScripting::Yes ); } if (to67P) { OsEng.scriptEngine().queueScript( - "openspace.setPropertyValue('Interaction.coordinateSystem', '67P');" + "openspace.setPropertyValue('Interaction.coordinateSystem', '67P');", + scripting::ScriptEngine::RemoteScripting::Yes ); } diff --git a/modules/onscreengui/src/guiorigincomponent.cpp b/modules/onscreengui/src/guiorigincomponent.cpp index 07bf244161..228e667878 100644 --- a/modules/onscreengui/src/guiorigincomponent.cpp +++ b/modules/onscreengui/src/guiorigincomponent.cpp @@ -65,7 +65,8 @@ void GuiOriginComponent::render() { if (hasChanged) { OsEng.scriptEngine().queueScript( "openspace.setPropertyValue('Interaction.origin', '" + - nodes[currentPosition]->name() + "');" + nodes[currentPosition]->name() + "');", + scripting::ScriptEngine::RemoteScripting::Yes ); } } diff --git a/modules/onscreengui/src/guitimecomponent.cpp b/modules/onscreengui/src/guitimecomponent.cpp index e98c2d5059..c2b1d15d0d 100644 --- a/modules/onscreengui/src/guitimecomponent.cpp +++ b/modules/onscreengui/src/guitimecomponent.cpp @@ -38,7 +38,8 @@ void GuiTimeComponent::render() { bool changed = ImGui::SliderFloat("Delta Time", &deltaTime, -5000.f, 5000.f); if (changed) { OsEng.scriptEngine().queueScript( - "openspace.time.setDeltaTime(" + std::to_string(deltaTime) + ")" + "openspace.time.setDeltaTime(" + std::to_string(deltaTime) + ")", + scripting::ScriptEngine::RemoteScripting::Yes ); } } diff --git a/modules/onscreengui/src/renderproperties.cpp b/modules/onscreengui/src/renderproperties.cpp index 47014d4f11..60d3ce1724 100644 --- a/modules/onscreengui/src/renderproperties.cpp +++ b/modules/onscreengui/src/renderproperties.cpp @@ -47,7 +47,7 @@ void renderTooltip(Property* prop) { void executeScript(const std::string& id, const std::string& value) { std::string script = "openspace.setPropertyValueSingle('" + id + "', " + value + ");"; - OsEng.scriptEngine().queueScript(script); + OsEng.scriptEngine().queueScript(script, scripting::ScriptEngine::RemoteScripting::Yes); } void renderBoolProperty(Property* prop, const std::string& ownerName) { diff --git a/modules/toyvolume/rendering/renderabletoyvolume.cpp b/modules/toyvolume/rendering/renderabletoyvolume.cpp index 0ce1f7b70a..73ac923689 100644 --- a/modules/toyvolume/rendering/renderabletoyvolume.cpp +++ b/modules/toyvolume/rendering/renderabletoyvolume.cpp @@ -33,13 +33,6 @@ #include #include - -namespace { - const std::string GlslRayCastPath = "${MODULES}/toyvolume/shaders/rayCast.glsl"; - const std::string GlslBoundsVsPath = "${MODULES}/toyvolume/shaders/boundsVs.glsl"; - const std::string GlslBoundsFsPath = "${MODULES}/toyvolume/shaders/boundsFs.glsl"; -} - namespace openspace { RenderableToyVolume::RenderableToyVolume(const ghoul::Dictionary& dictionary) diff --git a/modules/toyvolume/rendering/toyvolumeraycaster.cpp b/modules/toyvolume/rendering/toyvolumeraycaster.cpp index 8687f93744..ef4c46fea8 100644 --- a/modules/toyvolume/rendering/toyvolumeraycaster.cpp +++ b/modules/toyvolume/rendering/toyvolumeraycaster.cpp @@ -34,8 +34,8 @@ namespace { const std::string GlslRaycastPath = "${MODULES}/toyvolume/shaders/raycast.glsl"; - const std::string GlslBoundsVsPath = "${MODULES}/toyvolume/shaders/boundsVs.glsl"; - const std::string GlslBoundsFsPath = "${MODULES}/toyvolume/shaders/boundsFs.glsl"; + const std::string GlslBoundsVsPath = "${MODULES}/toyvolume/shaders/boundsvs.glsl"; + const std::string GlslBoundsFsPath = "${MODULES}/toyvolume/shaders/boundsfs.glsl"; } namespace openspace { diff --git a/modules/toyvolume/shaders/boundsFs.glsl b/modules/toyvolume/shaders/boundsfs.glsl similarity index 100% rename from modules/toyvolume/shaders/boundsFs.glsl rename to modules/toyvolume/shaders/boundsfs.glsl diff --git a/modules/toyvolume/shaders/boundsVs.glsl b/modules/toyvolume/shaders/boundsvs.glsl similarity index 100% rename from modules/toyvolume/shaders/boundsVs.glsl rename to modules/toyvolume/shaders/boundsvs.glsl diff --git a/modules/toyvolume/shaders/raycast.glsl b/modules/toyvolume/shaders/raycast.glsl index e78478f058..28c53a33d7 100644 --- a/modules/toyvolume/shaders/raycast.glsl +++ b/modules/toyvolume/shaders/raycast.glsl @@ -27,30 +27,49 @@ uniform vec4 color#{id}; uniform float time#{id}; uniform float maxStepSize#{id} = 0.02; -vec4 sample#{id}(vec3 samplePos, vec3 dir, vec4 foregroundColor, inout float maxStepSize) { - maxStepSize = maxStepSize#{id}; +void sample#{id}(vec3 samplePos, + vec3 dir, + inout vec3 accumulatedColor, + inout vec3 accumulatedAlpha, + inout float stepSize) { - // Generate an arbitrary procedural volume. + // Generate a procedural placeholder volume. // In real situations, the sample function would sample a // 3D texture to retrieve the color contribution of a given point. vec3 fromCenter = vec3(0.5, 0.5, 0.5) - samplePos; - vec4 c = color#{id}; - float r = length(fromCenter); - c.a *= (1.0 - smoothstep(0.4, 0.45, r)); - c.a *= (1.0 - smoothstep(0.35, 0.3, r)); - c.a *= (1.0 - smoothstep(0.1, 0.2, abs(fromCenter.y))); - float theta = atan(fromCenter.x, fromCenter.z); float angularRatio = (theta + 3.1415) / 6.283; - angularRatio = mod(angularRatio + time#{id}*0.01, 1.0); - - c.a *= smoothstep(0.0, 0.2, clamp(angularRatio, 0.0, 1.0)); - c.a *= smoothstep(1.0, 0.8, clamp(angularRatio, 0.0, 1.0)); - return c; + + float timeWave = sin(mod(time#{id}*0.05, 2.0 * 3.1415)); + float rDisplacement = 0.1 * timeWave; + + vec4 c = color#{id}; + float r = length(fromCenter); + c.a *= (1.0 - smoothstep(0.35 + rDisplacement, 0.40 + rDisplacement, r)); + c.a *= (1.0 - smoothstep(0.30 + rDisplacement, 0.25 + rDisplacement, r)); + c.a *= (1.0 - smoothstep(0.1, 0.2, abs(fromCenter.y) / angularRatio * 0.5)); + + c.a *= 1.0 - smoothstep(0.0, 1.0, clamp(angularRatio, 0.0, 1.0)); + c.a *= smoothstep(0.0, 0.1, clamp(angularRatio, 0.0, 1.0)); + + vec3 backAlpha = c.aaa * 10.0; + vec3 backColor = c.rgb * backAlpha; + + backColor *= stepSize; + backAlpha *= stepSize; + + backColor = clamp(backColor, 0.0, 1.0); + backAlpha = clamp(backAlpha, 0.0, 1.0); + + vec3 oneMinusFrontAlpha = vec3(1.0) - accumulatedAlpha; + accumulatedColor += oneMinusFrontAlpha * backColor; + accumulatedAlpha += oneMinusFrontAlpha * backAlpha; + + stepSize = maxStepSize#{id}; } float stepSize#{id}(vec3 samplePos, vec3 dir) { diff --git a/scripts/bind_keys.lua b/scripts/bind_keys.lua index 1a0c56dcad..3cd8f4cee0 100644 --- a/scripts/bind_keys.lua +++ b/scripts/bind_keys.lua @@ -24,10 +24,13 @@ openspace.bindKey("q", helper.renderable.toggle('SunMarker')) openspace.bindKey("e", helper.renderable.toggle('EarthMarker')) openspace.bindKey("x", helper.renderable.toggle('Constellation Bounds')) -openspace.bindKey("c", "openspace.parallel.setAddress('130.236.142.51');openspace.parallel.setPassword('newhorizons-20150714');openspace.parallel.connect();") +--openspace.bindKey("c", "openspace.parallel.setAddress('130.236.142.51');openspace.parallel.setPassword('newhorizons-20150714');openspace.parallel.connect();") -openspace.bindKey("h", "openspace.iswa.setBaseUrl('https://iswa-demo-server.herokuapp.com/')"); +--openspace.bindKey("h", "openspace.iswa.setBaseUrl('https://iswa-demo-server.herokuapp.com/')"); openspace.bindKey("g", "openspace.iswa.setBaseUrl('http://128.183.168.116:3000/')"); openspace.bindKey("l", "openspace.iswa.setBaseUrl('http://localhost:3000/')"); -openspace.bindKey("v", "openspace.time.setTime('2015-03-15T02:00:00.00')"); \ No newline at end of file +openspace.bindKey("v", "openspace.time.setTime('2015-03-15T02:00:00.00')"); + +openspace.bindKeyLocal("h", "openspace.parallel.setAddress('127.0.0.1');openspace.parallel.setPort('25001');openspace.parallel.setPassword('test');openspace.parallel.connect();openspace.parallel.requestHostship('test');") +openspace.bindKeyLocal("c", "openspace.parallel.setAddress('127.0.0.1');openspace.parallel.setPort('25001');openspace.parallel.setPassword('test');openspace.parallel.connect();") diff --git a/scripts/bind_keys_rosetta.lua b/scripts/bind_keys_rosetta.lua index 0837db0c7c..a81ff43db5 100644 --- a/scripts/bind_keys_rosetta.lua +++ b/scripts/bind_keys_rosetta.lua @@ -17,6 +17,7 @@ openspace.bindKey("s", "openspace.setPropertyValue('Interaction.origin', 'Rosett -- openspace.bindKey("F5", "openspace.setPropertyValue('Interaction.coordinateSystem', 'Sun'); openspace.printInfo('Changing Viewpoint to Sun');"); openspace.bindKey("F6", "openspace.setPropertyValue('Interaction.coordinateSystem', '67P'); openspace.printInfo('Changing Viewpoint to 67P');"); +openspace.bindKey("F7", "openspace.time.setTime('2014-08-15T03:05:18.101')"); openspace.bindKey("F8", "openspace.setPropertyValue('67P.renderable.ProjectionComponent.clearAllProjections', true);"); openspace.bindKey("i", helper.renderable.toggle('ImagePlaneRosetta')) @@ -24,4 +25,5 @@ openspace.bindKey("q", helper.renderable.toggle('SunMarker')) openspace.bindKey("e", helper.renderable.toggle('JupiterTrail') .. helper.renderable.toggle('SaturnTrail') .. helper.renderable.toggle('UranusTrail') .. helper.renderable.toggle('NeptuneTrail')) openspace.bindKey("f", helper.renderable.toggle('PhilaeTrail')) -openspace.bindKey("c", "openspace.parallel.setAddress('130.236.142.51');openspace.parallel.setPassword('newhorizons-20150714');openspace.parallel.connect();") +openspace.bindKeyLocal("h", "openspace.parallel.setAddress('127.0.0.1');openspace.parallel.setPort('25001');openspace.parallel.setPassword('test');openspace.parallel.connect();openspace.parallel.requestHostship('test');") +openspace.bindKeyLocal("c", "openspace.parallel.setAddress('127.0.0.1');openspace.parallel.setPort('25001');openspace.parallel.setPassword('test');openspace.parallel.connect();") diff --git a/scripts/common.lua b/scripts/common.lua index 8bd1add4ac..00fa5b2163 100644 --- a/scripts/common.lua +++ b/scripts/common.lua @@ -12,16 +12,16 @@ helper.scheduledScript.reversible = {} -- Function that sets the most common key bindings that are common to most (all?) -- scenes helper.setCommonKeys = function() - openspace.bindKey("F1", "openspace.gui.toggle()") - openspace.bindKey("F2", "openspace.setPerformanceMeasurement(true)") - openspace.bindKey("F3", "openspace.setPerformanceMeasurement(false)") + openspace.bindKeyLocal("F1", "openspace.gui.toggle()") + openspace.bindKeyLocal("F2", "openspace.setPerformanceMeasurement(true)") + openspace.bindKeyLocal("F3", "openspace.setPerformanceMeasurement(false)") - openspace.bindKey("t", "openspace.toggleFrametimeType(1)") - openspace.bindKey("Shift+t", "openspace.toggleFrametimeType(0)") + openspace.bindKeyLocal("t", "openspace.toggleFrametimeType(1)") + openspace.bindKeyLocal("Shift+t", "openspace.toggleFrametimeType(0)") - openspace.bindKey("ESC", "openspace.toggleShutdown()") + openspace.bindKeyLocal("ESC", "openspace.toggleShutdown()") - openspace.bindKey("PRINT_SCREEN", "openspace.takeScreenshot()") + openspace.bindKeyLocal("PRINT_SCREEN", "openspace.takeScreenshot()") openspace.bindKey("SPACE", "openspace.time.togglePause()") openspace.bindKey("COMMA", "openspace.setRenderer('Framebuffer');") @@ -102,4 +102,4 @@ helper.scheduledScript.setEnabled = function(time, renderable, enabled) Backward = "" } } -end \ No newline at end of file +end diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f23b24e3bf..7d65f2e49f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,10 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/mouseexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/pythonexternalcontrol.cpp ${OPENSPACE_BASE_DIR}/src/interaction/externalcontrol/randomexternalcontrol.cpp + ${OPENSPACE_BASE_DIR}/src/mission/mission.cpp + ${OPENSPACE_BASE_DIR}/src/mission/missionmanager.cpp + ${OPENSPACE_BASE_DIR}/src/mission/missionmanager_lua.inl + # ${OPENSPACE_BASE_DIR}/src/mission/missionphasesequencer.cpp ${OPENSPACE_BASE_DIR}/src/network/networkengine.cpp ${OPENSPACE_BASE_DIR}/src/network/parallelconnection.cpp ${OPENSPACE_BASE_DIR}/src/network/parallelconnection_lua.inl @@ -79,7 +83,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/rendering/renderengine_lua.inl ${OPENSPACE_BASE_DIR}/src/rendering/screenspacerenderable.cpp ${OPENSPACE_BASE_DIR}/src/rendering/transferfunction.cpp - ${OPENSPACE_BASE_DIR}/src/scene/ephemeris.cpp + ${OPENSPACE_BASE_DIR}/src/scene/translation.cpp ${OPENSPACE_BASE_DIR}/src/scene/rotation.cpp ${OPENSPACE_BASE_DIR}/src/scene/scale.cpp ${OPENSPACE_BASE_DIR}/src/scene/scene.cpp @@ -109,7 +113,9 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/util/syncdata.cpp ${OPENSPACE_BASE_DIR}/src/util/histogram.cpp ${OPENSPACE_BASE_DIR}/src/util/time.cpp + ${OPENSPACE_BASE_DIR}/src/util/timemanager.cpp ${OPENSPACE_BASE_DIR}/src/util/time_lua.inl + ${OPENSPACE_BASE_DIR}/src/util/timerange.cpp ${OPENSPACE_BASE_DIR}/src/util/transformationmanager.cpp ) @@ -143,6 +149,8 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/mouseexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/pythonexternalcontrol.h ${OPENSPACE_BASE_DIR}/include/openspace/interaction/externalcontrol/randomexternalcontrol.h + ${OPENSPACE_BASE_DIR}/include/openspace/mission/mission.h + ${OPENSPACE_BASE_DIR}/include/openspace/mission/missionmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h ${OPENSPACE_BASE_DIR}/include/openspace/network/parallelconnection.h ${OPENSPACE_BASE_DIR}/include/openspace/network/messagestructures.h @@ -176,7 +184,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/rendering/screenspacerenderable.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/volumeraycaster.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/transferfunction.h - ${OPENSPACE_BASE_DIR}/include/openspace/scene/ephemeris.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/translation.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/rotation.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scale.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scene.h @@ -203,6 +211,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/util/syncbuffer.h ${OPENSPACE_BASE_DIR}/include/openspace/util/syncdata.h ${OPENSPACE_BASE_DIR}/include/openspace/util/time.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/timemanager.h ${OPENSPACE_BASE_DIR}/include/openspace/util/updatestructures.h ${OPENSPACE_BASE_DIR}/include/openspace/util/transformationmanager.h ${OPENSPACE_BASE_DIR}/include/openspace/util/histogram.h diff --git a/src/documentation/core_registration.cpp b/src/documentation/core_registration.cpp index 9f938c477e..8777700213 100644 --- a/src/documentation/core_registration.cpp +++ b/src/documentation/core_registration.cpp @@ -26,28 +26,59 @@ #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED +#include +#endif namespace openspace { -namespace documentation { void registerCoreClasses(documentation::DocumentationEngine& engine) { engine.addDocumentation(ConfigurationManager::Documentation()); - engine.addDocumentation(Ephemeris::Documentation()); + engine.addDocumentation(Translation::Documentation()); + engine.addDocumentation(Mission::Documentation()); engine.addDocumentation(Renderable::Documentation()); engine.addDocumentation(Rotation::Documentation()); engine.addDocumentation(Scale::Documentation()); engine.addDocumentation(Scene::Documentation()); engine.addDocumentation(SceneGraphNode::Documentation()); engine.addDocumentation(ScreenSpaceRenderable::Documentation()); + engine.addDocumentation(TimeRange::Documentation()); +} + +void registerCoreClasses(scripting::ScriptEngine& engine) { + engine.addLibrary(OpenSpaceEngine::luaLibrary()); + engine.addLibrary(SpiceManager::luaLibrary()); + engine.addLibrary(RenderEngine::luaLibrary()); + engine.addLibrary(Scene::luaLibrary()); + engine.addLibrary(Time::luaLibrary()); + engine.addLibrary(interaction::InteractionHandler::luaLibrary()); + engine.addLibrary(LuaConsole::luaLibrary()); + engine.addLibrary(gui::GUI::luaLibrary()); + engine.addLibrary(ParallelConnection::luaLibrary()); + engine.addLibrary(ModuleEngine::luaLibrary()); + engine.addLibrary(scripting::ScriptScheduler::luaLibrary()); + engine.addLibrary(WindowWrapper::luaLibrary()); + engine.addLibrary(MissionManager::luaLibrary()); } -} // namespace documentation } // namespace openspace diff --git a/src/documentation/documentation.cpp b/src/documentation/documentation.cpp index 52a652ed92..a866edd869 100644 --- a/src/documentation/documentation.cpp +++ b/src/documentation/documentation.cpp @@ -31,8 +31,7 @@ namespace { // Structure used to make offenses unique struct OffenseCompare { using Offense = openspace::documentation::TestResult::Offense; - bool operator()(const Offense& lhs, const Offense& rhs) const - { + bool operator()(const Offense& lhs, const Offense& rhs) const { if (lhs.offender != rhs.offender) { return lhs.offender < rhs.offender; } @@ -41,8 +40,21 @@ namespace { std::underlying_type_t(rhs.reason); } } - }; + + struct WarningCompare { + using Warning = openspace::documentation::TestResult::Warning; + bool operator()(const Warning& lhs, const Warning& rhs) const { + if (lhs.offender != rhs.offender) { + return lhs.offender < rhs.offender; + } + else { + return std::underlying_type_t(lhs.reason) < + std::underlying_type_t(rhs.reason); + } + } + }; + } // namespace // Unfortunately, the standard library does not contain a no-op for the to_string method @@ -128,6 +140,11 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di res.offenses.end() ); } + result.warnings.insert( + result.warnings.end(), + res.warnings.begin(), + res.warnings.end() + ); }; for (const auto& p : d.entries) { @@ -181,6 +198,28 @@ TestResult testSpecification(const Documentation& d, const ghoul::Dictionary& di result.offenses = std::vector( uniqueOffenders.begin(), uniqueOffenders.end() ); + // Remove duplicate warnings. This should normally not happen, but we want to be sure + std::set uniqueWarnings( + result.warnings.begin(), result.warnings.end() + ); + result.warnings = std::vector( + uniqueWarnings.begin(), uniqueWarnings.end() + ); + + std::sort( + result.offenses.begin(), + result.offenses.end(), + [](const TestResult::Offense& lhs, const TestResult::Offense& rhs) { + return OffenseCompare()(lhs, rhs); + } + ); + std::sort( + result.warnings.begin(), + result.warnings.end(), + [](const TestResult::Warning& lhs, const TestResult::Warning& rhs) { + return WarningCompare()(lhs, rhs); + } + ); return result; } diff --git a/src/documentation/documentationengine.cpp b/src/documentation/documentationengine.cpp index d1a0e41ca6..0bcab0e7c3 100644 --- a/src/documentation/documentationengine.cpp +++ b/src/documentation/documentationengine.cpp @@ -24,14 +24,26 @@ #include +#include #include #include +#include #include +#include #include +namespace { + const std::string MainTemplateFilename = "${OPENSPACE_DATA}/web/documentation/main.hbs"; + const std::string DocumentationTemplateFilename = "${OPENSPACE_DATA}/web/documentation/documentation.hbs"; + const std::string HandlebarsFilename = "${OPENSPACE_DATA}/web/common/handlebars-v4.0.5.js"; + const std::string JsFilename = "${OPENSPACE_DATA}/web/documentation/script.js"; + const std::string BootstrapFilename = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssFilename = "${OPENSPACE_DATA}/web/common/style.css"; +} + namespace openspace { namespace documentation { @@ -72,7 +84,25 @@ std::string generateTextDocumentation(const Documentation& d, int& indentLevel) result += indentMessage("Optional", (p.optional ? "true" : "false")); result += indentMessage("Type", p.verifier->type()); TableVerifier* tv = dynamic_cast(p.verifier.get()); - if (tv) { + ReferencingVerifier* rv = dynamic_cast(p.verifier.get()); + + // We have to check ReferencingVerifier first as a ReferencingVerifier is also a + // TableVerifier + if (rv) { + std::vector documentations = DocEng.documentations(); + auto it = std::find_if( + documentations.begin(), + documentations.end(), + [rv](const Documentation& doc) { return doc.id == rv->identifier; } + ); + + if (it == documentations.end()) { + result += indentMessage("Referencing", rv->identifier + "(NOT FOUND)"); + } + else { + result += indentMessage("Referencing", it->name); + } + } else if (tv) { // We have a TableVerifier, so we need to recurse ++indentLevel; result += generateTextDocumentation({ "", "", tv->documentations }, indentLevel); @@ -97,22 +127,46 @@ std::string generateJsonDocumentation(const Documentation& d) { result << "{"; result << "\"name\": \"" << d.name << "\","; + result << "\"id\": \"" << d.id << "\","; result << "\"entries\": ["; for (const auto& p : d.entries) { result << "{"; result << "\"key\": \"" << p.key << "\","; - result << "\"optional\": \"" << (p.optional ? "true" : "false") << "\","; + result << "\"optional\": " << (p.optional ? "true" : "false") << ","; result << "\"type\": \"" << p.verifier->type() << "\","; TableVerifier* tv = dynamic_cast(p.verifier.get()); - if (tv) { + ReferencingVerifier* rv = dynamic_cast(p.verifier.get()); + + if (rv) { + std::vector documentations = DocEng.documentations(); + auto it = std::find_if( + documentations.begin(), + documentations.end(), + [rv](const Documentation& doc) { return doc.id == rv->identifier; } + ); + + if (it == documentations.end()) { + result << "\"reference\": { \"found\": false }"; + } else { + result << "\"reference\": {" + << "\"found\": true," + << "\"name\": \"" << it->name << "\"," + << "\"identifier\": \"" << rv->identifier << "\"" + << "}"; + } + } + else if (tv) { std::string json = generateJsonDocumentation({ "", "", tv->documentations }); // We have a TableVerifier, so we need to recurse - result << "\"restrictions\": " << json << ","; + result << "\"restrictions\": " << json; } else { - result << "\"restrictions\": \"" << p.verifier->documentation() << "\","; + result << "\"description\": \"" << p.verifier->documentation() << "\""; + } + result << "}"; + if (&p != &d.entries.back()) { + result << ", "; } - result << "},"; } result << ']'; @@ -147,14 +201,23 @@ std::string generateHtmlDocumentation(const Documentation& d) { [rv](const Documentation& doc) { return doc.id == rv->identifier; } ); - html << "\t\t" - << "\t\t\tReferencing: " - << "identifier << "\">" << it->name << "" - << "\t\t"; + if (it == documentations.end()) { + html << "\t\t" + << "" + << "Could not find identifier: " << rv->identifier + << "" + << ""; + } + else { + html << "\t\t" + << "\t\t\tReferencing: " + << "identifier << "\">" << it->name << "" + << "\t\t"; + } } else if (tv) { // We have a TableVerifier, so we need to recurse - html << "\n" + html << "
\n" << "\t\n" << "\t\t\n" << "\t\t\t\n" @@ -194,33 +257,87 @@ void DocumentationEngine::writeDocumentation(const std::string& f, const std::st } } else if (t == "html") { + std::ifstream handlebarsInput(absPath(HandlebarsFilename)); + std::ifstream jsInput(absPath(JsFilename)); + + std::string jsContent; + std::back_insert_iterator jsInserter(jsContent); + + std::copy(std::istreambuf_iterator{handlebarsInput}, std::istreambuf_iterator(), jsInserter); + std::copy(std::istreambuf_iterator{jsInput}, std::istreambuf_iterator(), jsInserter); + + std::ifstream bootstrapInput(absPath(BootstrapFilename)); + std::ifstream cssInput(absPath(CssFilename)); + + std::string cssContent; + std::back_insert_iterator cssInserter(cssContent); + + std::copy(std::istreambuf_iterator{bootstrapInput}, std::istreambuf_iterator(), cssInserter); + std::copy(std::istreambuf_iterator{cssInput}, std::istreambuf_iterator(), cssInserter); + + std::ifstream mainTemplateInput(absPath(MainTemplateFilename)); + std::string mainTemplateContent{ std::istreambuf_iterator{mainTemplateInput}, + std::istreambuf_iterator{}}; + + std::ifstream documentationTemplateInput(absPath(DocumentationTemplateFilename)); + std::string documentationTemplateContent{ std::istreambuf_iterator{documentationTemplateInput}, + std::istreambuf_iterator{} }; + std::ofstream file; file.exceptions(~std::ofstream::goodbit); file.open(f); -#ifdef JSON std::stringstream json; json << "["; for (const Documentation& d : _documentations) { json << generateJsonDocumentation(d); - json << ","; + if (&d != &_documentations.back()) { + json << ", "; + } } json << "]"; - std::string jsonText = json.str(); -#else - std::stringstream html; + std::string jsonString = ""; + for (const char& c : json.str()) { + if (c == '\'') { + jsonString += "\\'"; + } else { + jsonString += c; + } + } - html << "\n" + std::stringstream html; + html << "\n" + << "\n" << "\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\n" + << "\t\n" << "\t\tDocumentation\n" << "\t\n" - << "\n"; + << "\t\n" + << "\t\n" + << "\n"; + file << html.str(); - html << "
\n" +/* + Use this for generating documentation in raw html: + + html << "
\n" << "\t\n\n" << "\t\n" << "\t\t\n" @@ -244,12 +361,7 @@ void DocumentationEngine::writeDocumentation(const std::string& f, const std::st html << "\t\n" << "
Documentation
\n"; - - html << "\n"; - html << "\n"; - - file << html.str(); -#endif +*/ } } diff --git a/src/documentation/verifier.cpp b/src/documentation/verifier.cpp index 7e90e34be3..4c68386f7c 100644 --- a/src/documentation/verifier.cpp +++ b/src/documentation/verifier.cpp @@ -72,11 +72,36 @@ template struct InRangeVerifier; template struct NotInRangeVerifier; template struct NotInRangeVerifier; + template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; +template struct AnnotationVerifier; + +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; +template struct DeprecatedVerifier; std::string BoolVerifier::type() const { return "Boolean"; @@ -127,27 +152,34 @@ std::string StringVerifier::type() const { TableVerifier::TableVerifier(std::vector d, Exhaustive exhaustive) : documentations(std::move(d)) - , exhaustive(std::move(exhaustive)) {} + , exhaustive(std::move(exhaustive)) +{} TestResult TableVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { if (dict.hasKeyAndValue(key)) { ghoul::Dictionary d = dict.value(key); - TestResult res = testSpecification({ "", documentations, exhaustive }, d); + TestResult res = testSpecification({documentations, exhaustive}, d); + // Add the 'key' as a prefix to make the new offender a fully qualified identifer for (TestResult::Offense& s : res.offenses) { s.offender = key + "." + s.offender; } + // Add the 'key' as a prefix to make the new warning a fully qualified identifer + for (TestResult::Warning& w : res.warnings) { + w.offender = key + "." + w.offender; + } + return res; } else { if (dict.hasKey(key)) { - return{ false, { { key, TestResult::Offense::Reason::WrongType } } }; + return { false, { { key, TestResult::Offense::Reason::WrongType } } }; } else { - return{ false, { { key, TestResult::Offense::Reason::MissingKey } } }; + return { false, { { key, TestResult::Offense::Reason::MissingKey } } }; } } } @@ -182,10 +214,16 @@ TestResult ReferencingVerifier::operator()(const ghoul::Dictionary& dictionary, ghoul::Dictionary d = dictionary.value(key); TestResult res = testSpecification(*it, d); + // Add the 'key' as a prefix to make the offender a fully qualified identifer for (TestResult::Offense& s : res.offenses) { s.offender = key + "." + s.offender; } + // Add the 'key' as a prefix to make the warning a fully qualified identifer + for (TestResult::Warning& w : res.warnings) { + w.offender = key + "." + w.offender; + } + return res; } } diff --git a/src/engine/configurationmanager.cpp b/src/engine/configurationmanager.cpp index fb0972d471..881a8dfcb2 100644 --- a/src/engine/configurationmanager.cpp +++ b/src/engine/configurationmanager.cpp @@ -72,6 +72,13 @@ const string ConfigurationManager::KeyDisableMasterRendering = "DisableRendering const string ConfigurationManager::KeyDownloadRequestURL = "DownloadRequestURL"; const string ConfigurationManager::KeyRenderingMethod = "RenderingMethod"; +const string ConfigurationManager::KeyHttpProxy = "HttpProxy"; +const string ConfigurationManager::PartHttpProxyAddress = "Address"; +const string ConfigurationManager::PartHttpProxyPort = "Port"; +const string ConfigurationManager::PartHttpProxyAuthentication = "Authentication"; +const string ConfigurationManager::PartHttpProxyUser = "User"; +const string ConfigurationManager::PartHttpProxyPassword = "Password"; + string ConfigurationManager::findConfiguration(const string& filename) { using ghoul::filesystem::Directory; diff --git a/src/engine/configurationmanager_doc.inl b/src/engine/configurationmanager_doc.inl index e83cda2239..f260d37f79 100644 --- a/src/engine/configurationmanager_doc.inl +++ b/src/engine/configurationmanager_doc.inl @@ -31,6 +31,7 @@ Documentation ConfigurationManager::Documentation() { return { "OpenSpace Configuration", + "openspace_configuraion", { { ConfigurationManager::KeyConfigSgct, @@ -324,7 +325,45 @@ Documentation ConfigurationManager::Documentation() { "or just managing the state of the network. This is desired in cases where " "the master computer does not have the resources to render a scene.", Optional::Yes - } + }, + { + ConfigurationManager::KeyHttpProxy, + new TableVerifier({ + { + ConfigurationManager::PartHttpProxyAddress, + new StringVerifier, + "The address of the http proxy" + }, + { + ConfigurationManager::PartHttpProxyPort, + new StringVerifier, + "The port of the http proxy" + }, + { + ConfigurationManager::PartHttpProxyAuthentication, + new StringInListVerifier( + { "basic", "ntlm", "digest", "any" } + ), + "The authentication method of the http proxy", + Optional::Yes + }, + { + ConfigurationManager::PartHttpProxyUser, + new StringVerifier, + "The user of the http proxy", + Optional::Yes + }, + { + ConfigurationManager::PartHttpProxyPassword, + new StringVerifier, + "The password of the http proxy", + Optional::Yes + } + }), + "This defines the use for a proxy when fetching data over http." + "No proxy will be used if this is left out.", + Optional::Yes + } } }; }; diff --git a/src/engine/logfactory.cpp b/src/engine/logfactory.cpp index ffc145d95e..c1ec813a07 100644 --- a/src/engine/logfactory.cpp +++ b/src/engine/logfactory.cpp @@ -41,6 +41,10 @@ namespace { const std::string valueHtmlLog = "html"; const std::string valueTextLog = "Text"; + + const std::string BootstrapPath = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssPath = "${OPENSPACE_DATA}/web/log/style.css"; + const std::string JsPath = "${OPENSPACE_DATA}/web/log/script.js"; } namespace openspace { @@ -81,13 +85,18 @@ std::unique_ptr createLog(const ghoul::Dictionary& dictiona using LogLevelStamping = ghoul::logging::Log::LogLevelStamping; if (type == valueHtmlLog) { + + std::vector cssFiles{absPath(BootstrapPath), absPath(CssPath)}; + std::vector jsFiles{absPath(JsPath)}; + return std::make_unique( filename, append ? Append::Yes : Append::No, timeStamp ? TimeStamping::Yes : TimeStamping::No, dateStamp ? DateStamping::Yes : DateStamping::No, categoryStamp ? CategoryStamping::Yes : CategoryStamping::No, - logLevelStamp ? LogLevelStamping::Yes : LogLevelStamping::No + logLevelStamp ? LogLevelStamping::Yes : LogLevelStamping::No, + cssFiles, jsFiles ); } else if (type == valueTextLog) { diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 35e5e41805..4883b1cd36 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -39,21 +39,22 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include #include #include #include +#include #include #include #include - #include #include #include @@ -65,8 +66,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -139,11 +141,12 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, , _console(new LuaConsole) , _moduleEngine(new ModuleEngine) , _settingsEngine(new SettingsEngine) + , _timeManager(new TimeManager) , _downloadManager(nullptr) #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED , _gui(new gui::GUI) #endif - , _parallelConnection(new network::ParallelConnection) + , _parallelConnection(new ParallelConnection) , _windowWrapper(std::move(windowWrapper)) , _globalPropertyNamespace(new properties::PropertyOwner) , _isMaster(false) @@ -151,7 +154,9 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, , _isInShutdownMode(false) , _shutdownCountdown(0.f) , _shutdownWait(0.f) + , _isFirstRenderingFirstFrame(true) { + _interactionHandler->setPropertyOwner(_globalPropertyNamespace.get()); _globalPropertyNamespace->addPropertySubOwner(_interactionHandler.get()); _globalPropertyNamespace->addPropertySubOwner(_settingsEngine.get()); @@ -162,8 +167,8 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, "Renderable" ); FactoryManager::ref().addFactory( - std::make_unique>(), - "Ephemeris" + std::make_unique>(), + "Translation" ); SpiceManager::initialize(); Time::initialize(); @@ -177,6 +182,7 @@ OpenSpaceEngine::~OpenSpaceEngine() { #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED _gui->deinitializeGL(); #endif + _interactionHandler->deinitialize(); _renderEngine->deinitialize(); _globalPropertyNamespace = nullptr; @@ -242,9 +248,7 @@ bool OpenSpaceEngine::create(int argc, char** argv, _engine = new OpenSpaceEngine(std::string(argv[0]), std::move(windowWrapper)); // Query modules for commandline arguments - bool gatherSuccess = _engine->gatherCommandlineArguments(); - if (!gatherSuccess) - return false; + _engine->gatherCommandlineArguments(); // Parse commandline arguments std::vector args(argv, argv + argc); @@ -313,7 +317,7 @@ bool OpenSpaceEngine::create(int argc, char** argv, // Register modules _engine->_moduleEngine->initialize(); - documentation::registerCoreClasses(DocEng); + registerCoreClasses(DocEng); // After registering the modules, the documentations for the available classes // can be added as well for (OpenSpaceModule* m : _engine->_moduleEngine->modules()) { @@ -408,17 +412,7 @@ bool OpenSpaceEngine::initialize() { // Register Lua script functions LDEBUG("Registering Lua libraries"); - _scriptEngine->addLibrary(OpenSpaceEngine::luaLibrary()); - _scriptEngine->addLibrary(SpiceManager::luaLibrary()); - _scriptEngine->addLibrary(RenderEngine::luaLibrary()); - _scriptEngine->addLibrary(Scene::luaLibrary()); - _scriptEngine->addLibrary(Time::luaLibrary()); - _scriptEngine->addLibrary(interaction::InteractionHandler::luaLibrary()); - _scriptEngine->addLibrary(LuaConsole::luaLibrary()); - _scriptEngine->addLibrary(gui::GUI::luaLibrary()); - _scriptEngine->addLibrary(network::ParallelConnection::luaLibrary()); - _scriptEngine->addLibrary(ModuleEngine::luaLibrary()); - _scriptEngine->addLibrary(ScriptScheduler::luaLibrary()); + registerCoreClasses(*_scriptEngine); #ifdef OPENSPACE_MODULE_ISWA_ENABLED _scriptEngine->addLibrary(IswaManager::luaLibrary()); @@ -427,54 +421,7 @@ bool OpenSpaceEngine::initialize() { // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL scriptEngine().initialize(); - // If a LuaDocumentationFile was specified, generate it now - const std::string LuaDocumentationType = - ConfigurationManager::KeyLuaDocumentation + "." + ConfigurationManager::PartType; - const std::string LuaDocumentationFile = - ConfigurationManager::KeyLuaDocumentation + "." + ConfigurationManager::PartFile; - - const bool hasLuaDocType = configurationManager().hasKey(LuaDocumentationType); - const bool hasLuaDocFile = configurationManager().hasKey(LuaDocumentationFile); - if (hasLuaDocType && hasLuaDocFile) { - std::string luaDocumentationType; - configurationManager().getValue(LuaDocumentationType, luaDocumentationType); - std::string luaDocumentationFile; - configurationManager().getValue(LuaDocumentationFile, luaDocumentationFile); - - luaDocumentationFile = absPath(luaDocumentationFile); - _scriptEngine->writeDocumentation(luaDocumentationFile, luaDocumentationType); - } - - // If a general documentation was specified, generate it now - const std::string DocumentationType = - ConfigurationManager::KeyDocumentation + '.' + ConfigurationManager::PartType; - const std::string DocumentationFile = - ConfigurationManager::KeyDocumentation + '.' + ConfigurationManager::PartFile; - - const bool hasDocumentationType = configurationManager().hasKey(DocumentationType); - const bool hasDocumentationFile = configurationManager().hasKey(DocumentationFile); - if (hasDocumentationType && hasDocumentationFile) { - std::string documentationType; - configurationManager().getValue(DocumentationType, documentationType); - std::string documentationFile; - configurationManager().getValue(DocumentationFile, documentationFile); - documentationFile = absPath(documentationFile); - DocEng.writeDocumentation(documentationFile, documentationType); - } - - const std::string FactoryDocumentationType = - ConfigurationManager::KeyFactoryDocumentation + '.' + ConfigurationManager::PartType; - - const std::string FactoryDocumentationFile = - ConfigurationManager::KeyFactoryDocumentation + '.' + ConfigurationManager::PartFile; - bool hasFactoryDocumentationType = configurationManager().hasKey(FactoryDocumentationType); - bool hasFactoryDocumentationFile = configurationManager().hasKey(FactoryDocumentationFile); - if (hasFactoryDocumentationType && hasFactoryDocumentationFile) { - std::string type = configurationManager().value(FactoryDocumentationType); - std::string file = configurationManager().value(FactoryDocumentationFile); - - FactoryManager::ref().writeDocumentation(absPath(file), type); - } + writeDocumentation(); bool disableMasterRendering = false; configurationManager().getValue( @@ -494,6 +441,9 @@ bool OpenSpaceEngine::initialize() { _settingsEngine->initialize(); _settingsEngine->setModules(_moduleEngine->modules()); + // Initialize the InteractionHandler + _interactionHandler->initialize(); + // Load a light and a monospaced font loadFonts(); @@ -578,6 +528,58 @@ bool OpenSpaceEngine::initialize() { return true; } + +void OpenSpaceEngine::writeDocumentation() { + // If a LuaDocumentationFile was specified, generate it now + const std::string LuaDocumentationType = + ConfigurationManager::KeyLuaDocumentation + "." + ConfigurationManager::PartType; + const std::string LuaDocumentationFile = + ConfigurationManager::KeyLuaDocumentation + "." + ConfigurationManager::PartFile; + + const bool hasLuaDocType = configurationManager().hasKey(LuaDocumentationType); + const bool hasLuaDocFile = configurationManager().hasKey(LuaDocumentationFile); + if (hasLuaDocType && hasLuaDocFile) { + std::string luaDocumentationType; + configurationManager().getValue(LuaDocumentationType, luaDocumentationType); + std::string luaDocumentationFile; + configurationManager().getValue(LuaDocumentationFile, luaDocumentationFile); + + luaDocumentationFile = absPath(luaDocumentationFile); + _scriptEngine->writeDocumentation(luaDocumentationFile, luaDocumentationType); + } + + // If a general documentation was specified, generate it now + const std::string DocumentationType = + ConfigurationManager::KeyDocumentation + '.' + ConfigurationManager::PartType; + const std::string DocumentationFile = + ConfigurationManager::KeyDocumentation + '.' + ConfigurationManager::PartFile; + + const bool hasDocumentationType = configurationManager().hasKey(DocumentationType); + const bool hasDocumentationFile = configurationManager().hasKey(DocumentationFile); + if (hasDocumentationType && hasDocumentationFile) { + std::string documentationType; + configurationManager().getValue(DocumentationType, documentationType); + std::string documentationFile; + configurationManager().getValue(DocumentationFile, documentationFile); + documentationFile = absPath(documentationFile); + DocEng.writeDocumentation(documentationFile, documentationType); + } + + const std::string FactoryDocumentationType = + ConfigurationManager::KeyFactoryDocumentation + '.' + ConfigurationManager::PartType; + + const std::string FactoryDocumentationFile = + ConfigurationManager::KeyFactoryDocumentation + '.' + ConfigurationManager::PartFile; + bool hasFactoryDocumentationType = configurationManager().hasKey(FactoryDocumentationType); + bool hasFactoryDocumentationFile = configurationManager().hasKey(FactoryDocumentationFile); + if (hasFactoryDocumentationType && hasFactoryDocumentationFile) { + std::string type = configurationManager().value(FactoryDocumentationType); + std::string file = configurationManager().value(FactoryDocumentationFile); + + FactoryManager::ref().writeDocumentation(absPath(file), type); + } +} + bool OpenSpaceEngine::isInitialized() { return _engine != nullptr; } @@ -586,9 +588,7 @@ void OpenSpaceEngine::clearAllWindows() { _windowWrapper->clearAllWindows(glm::vec4(0.f, 0.f, 0.f, 1.f)); } -bool OpenSpaceEngine::gatherCommandlineArguments() { - // TODO: Get commandline arguments from all modules - +void OpenSpaceEngine::gatherCommandlineArguments() { commandlineArgumentPlaceholders.configurationName = ""; _commandlineParser->addCommand(std::make_unique>( &commandlineArgumentPlaceholders.configurationName, "-config", "-c", @@ -614,9 +614,6 @@ bool OpenSpaceEngine::gatherCommandlineArguments() { "path to a cache file, overriding the value set in the OpenSpace configuration " "file" )); - - - return true; } void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { @@ -638,6 +635,7 @@ void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { //@JK //temporary solution to ensure that startup scripts may be syncrhonized over parallel connection + /* std::ifstream scriptFile; scriptFile.open(absoluteScriptPath.c_str()); std::string line; @@ -651,7 +649,7 @@ void OpenSpaceEngine::runScripts(const ghoul::Dictionary& scripts) { _engine->scriptEngine().cacheScript(lib, func, line); } } - } + }*/ } } @@ -690,9 +688,9 @@ void OpenSpaceEngine::runPostInitializationScripts(const std::string& sceneDescr LINFO("Running Setup scripts"); lua_State* state = ghoul::lua::createNewLuaState(); OnExit( - // Delete the Lua state at the end of the scope, no matter what - [state](){ghoul::lua::destroyLuaState(state);} - ); + // Delete the Lua state at the end of the scope, no matter what + [state](){ghoul::lua::destroyLuaState(state);} + ); OsEng.scriptEngine().initializeLuaState(state); // First execute the script to get all global variables @@ -712,7 +710,8 @@ void OpenSpaceEngine::runPostInitializationScripts(const std::string& sceneDescr int success = lua_pcall(state, 0, 0, 0); if (success != 0) { LERROR("Error executing '" << PostInitializationFunction << "': " << - lua_tostring(state, -1)); + lua_tostring(state, -1) + ); } } @@ -744,7 +743,9 @@ void OpenSpaceEngine::loadFonts() { if (!initSuccess) LERROR("Error initializing default font renderer"); - ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize(glm::vec2(_windowWrapper->currentDrawBufferResolution())); + ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize( + glm::vec2(_windowWrapper->currentWindowSize()) + ); } @@ -756,8 +757,6 @@ void OpenSpaceEngine::configureLogging() { const std::string KeyLogs = ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartLogs; - - if (configurationManager().hasKeyAndValue(KeyLogLevel)) { std::string logLevel; configurationManager().getValue(KeyLogLevel, logLevel); @@ -837,18 +836,21 @@ void OpenSpaceEngine::setRunTime(double d){ void OpenSpaceEngine::preSynchronization() { FileSys.triggerFilesystemEvents(); + + if (_isFirstRenderingFirstFrame) { + _windowWrapper->setSynchronization(false); + } _syncEngine->presync(_isMaster); if (_isMaster) { double dt = _windowWrapper->averageDeltaTime(); - - Time::ref().advanceTime(dt); + _timeManager->preSynchronization(dt); auto scheduledScripts = _scriptScheduler->progressTo(Time::ref().j2000Seconds()); while(scheduledScripts.size()){ auto scheduledScript = scheduledScripts.front(); LINFO(scheduledScript); - _scriptEngine->queueScript(scheduledScript); + _scriptEngine->queueScript(scheduledScript, ScriptEngine::RemoteScripting::Yes); scheduledScripts.pop(); } @@ -892,21 +894,24 @@ void OpenSpaceEngine::postSynchronizationPreDraw() { #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED if (_isMaster && _gui->isEnabled() && _windowWrapper->isRegularRendering()) { glm::vec2 mousePosition = _windowWrapper->mousePosition(); - glm::ivec2 drawBufferResolution = _windowWrapper->currentDrawBufferResolution(); + //glm::ivec2 drawBufferResolution = _windowWrapper->currentDrawBufferResolution(); glm::ivec2 windowSize = _windowWrapper->currentWindowSize(); uint32_t mouseButtons = _windowWrapper->mouseButtons(2); - glm::vec2 windowBufferCorrectionFactor = glm::vec2( - static_cast(drawBufferResolution.x) / static_cast(windowSize.x), - static_cast(drawBufferResolution.y) / static_cast(windowSize.y) - ); + //glm::vec2 windowBufferCorrectionFactor = glm::vec2( + // static_cast(drawBufferResolution.x) / static_cast(windowSize.x), + // static_cast(drawBufferResolution.y) / static_cast(windowSize.y) + //); + + //LINFO("DrawBufferResolution: " << std::to_string(drawBufferResolution)); + //LINFO("Window Size: " << std::to_string(windowSize)); double dt = _windowWrapper->averageDeltaTime(); _gui->startFrame( static_cast(dt), - glm::vec2(drawBufferResolution), - windowBufferCorrectionFactor, + glm::vec2(windowSize), + glm::vec2(1.f), mousePosition, mouseButtons ); @@ -949,8 +954,15 @@ void OpenSpaceEngine::postDraw() { #endif } - if (_isInShutdownMode) + if (_isInShutdownMode) { _renderEngine->renderShutdownInformation(_shutdownCountdown, _shutdownWait); + } + + if (_isFirstRenderingFirstFrame) { + _windowWrapper->setSynchronization(true); + _isFirstRenderingFirstFrame = false; + } + } void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action) { @@ -962,14 +974,13 @@ void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction actio return; } #endif - - if (key == _console->commandInputButton() && (action == KeyAction::Press || action == KeyAction::Repeat)) - _console->toggleVisibility(); - - if (!_console->isVisible()) { + if (key == _console->commandInputButton()) { + if (action == KeyAction::Press) { + _console->toggleMode(); + } + } else if (!_console->isVisible()) { _interactionHandler->keyboardCallback(key, mod, action); - } - else { + } else { _console->keyboardCallback(key, mod, action); } } @@ -1066,6 +1077,12 @@ scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { "", "Toggles the shutdown mode that will close the application after the count" "down timer is reached" + }, + { + "writeDocumentation", + &luascriptfunctions::writeDocumentation, + "", + "Writes out documentation files" } } }; @@ -1134,7 +1151,7 @@ gui::GUI& OpenSpaceEngine::gui() { } #endif -network::ParallelConnection& OpenSpaceEngine::parallelConnection() { +ParallelConnection& OpenSpaceEngine::parallelConnection() { ghoul_assert(_parallelConnection, "ParallelConnection must not be nullptr"); return *_parallelConnection; } @@ -1162,5 +1179,10 @@ DownloadManager& OpenSpaceEngine::downloadManager() { return *_downloadManager; } +TimeManager& OpenSpaceEngine::timeManager() { + ghoul_assert(_timeManager, "Download Manager must not be nullptr"); + return *_timeManager; +} + } // namespace openspace diff --git a/src/engine/openspaceengine_lua.inl b/src/engine/openspaceengine_lua.inl index 7d33f6af76..3d4de94b78 100644 --- a/src/engine/openspaceengine_lua.inl +++ b/src/engine/openspaceengine_lua.inl @@ -41,5 +41,21 @@ int toggleShutdown(lua_State* L) { return 1; } +/** +* \ingroup LuaScripts +* writeDocumentation(): +* Writes out documentation files +*/ +int writeDocumentation(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + + OsEng.writeDocumentation(); + + return 1; +} + + } // namespace luascriptfunctions } // namespace openspace diff --git a/src/engine/wrapper/sgctwindowwrapper.cpp b/src/engine/wrapper/sgctwindowwrapper.cpp index adaf0d848b..c6a2f8a9cd 100644 --- a/src/engine/wrapper/sgctwindowwrapper.cpp +++ b/src/engine/wrapper/sgctwindowwrapper.cpp @@ -44,6 +44,10 @@ void SGCTWindowWrapper::setBarrier(bool enabled) { sgct::SGCTWindow::setBarrier(enabled); } +void SGCTWindowWrapper::setSynchronization(bool enabled) { + sgct_core::ClusterManager::instance()->setUseIgnoreSync(enabled); +} + void SGCTWindowWrapper::clearAllWindows(const glm::vec4& clearColor) { size_t n = sgct::Engine::instance()->getNumberOfWindows(); for (size_t i = 0; i < n; ++i) { diff --git a/src/engine/wrapper/windowwrapper.cpp b/src/engine/wrapper/windowwrapper.cpp index 10c1e5c21b..6a2ac79009 100644 --- a/src/engine/wrapper/windowwrapper.cpp +++ b/src/engine/wrapper/windowwrapper.cpp @@ -23,18 +23,57 @@ ****************************************************************************************/ #include + +#include +#include +#include + #include #include +namespace luascriptfunctions { + +int setSynchronization(lua_State* L) { + int nArguments = lua_gettop(L); + if (nArguments != 1) { + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + } + + bool b = lua_toboolean(L, -1) != 0; + OsEng.windowWrapper().setSynchronization(b); + return 0; +} + +} // namespace luascriptfunctions + namespace openspace { WindowWrapper::WindowWrapperException::WindowWrapperException(const std::string& msg) : ghoul::RuntimeError(msg, "WindowWrapper") {} +scripting::LuaLibrary WindowWrapper::luaLibrary() { + return { + "cluster", + { + { + "setSynchronization", + &luascriptfunctions::setSynchronization, + "bool", + "Enables or disables the frame synchronization of the cluster. If the " + "synchronization is enabled, the computers in the cluster will swap " + "their backbuffers at the same time, either supported by hardware or by " + "network signals." + } + } + }; +} + void WindowWrapper::terminate() {} void WindowWrapper::setBarrier(bool) {} + +void WindowWrapper::setSynchronization(bool) {} void WindowWrapper::clearAllWindows(const glm::vec4& clearColor) {} diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 24d29d70a3..067c097385 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -23,7 +23,9 @@ ****************************************************************************************/ #include +#include +#include #include #include #include @@ -49,6 +51,13 @@ namespace { const std::string KeyFocus = "Focus"; const std::string KeyPosition = "Position"; const std::string KeyRotation = "Rotation"; + + const std::string MainTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/main.hbs"; + const std::string KeybindingTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/keybinding.hbs"; + const std::string HandlebarsFilename = "${OPENSPACE_DATA}/web/common/handlebars-v4.0.5.js"; + const std::string JsFilename = "${OPENSPACE_DATA}/web/keybindings/script.js"; + const std::string BootstrapFilename = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssFilename = "${OPENSPACE_DATA}/web/common/style.css"; } #include "interactionhandler_lua.inl" @@ -56,574 +65,6 @@ namespace { namespace openspace { namespace interaction { -#ifdef USE_OLD_INTERACTIONHANDLER - - -InteractionHandler::InteractionHandler() - : properties::PropertyOwner() - , _camera(nullptr) - , _focusNode(nullptr) - , _deltaTime(0.0) - //, _validKeyLua(false) - , _controllerSensitivity(1.f) - , _invertRoll(true) - , _invertRotation(false) - , _keyboardController(nullptr) - , _mouseController(nullptr) - , _origin("origin", "Origin", "") - , _coordinateSystem("coordinateSystem", "Coordinate System", "") - , _currentKeyframeTime(-1.0) -{ - setName("Interaction"); - - _origin.onChange([this](){ - SceneGraphNode* node = sceneGraphNode(_origin.value()); - if (!node) { - LWARNING("Could not find a node in scenegraph called '" << _origin.value() <<"'"); - return; - } - setFocusNode(node); - }); - addProperty(_origin); - - _coordinateSystem.onChange([this](){ - OsEng.renderEngine().changeViewPoint(_coordinateSystem.value()); - }); - addProperty(_coordinateSystem); -} - -InteractionHandler::~InteractionHandler() { - delete _keyboardController; - delete _mouseController; - for (size_t i = 0; i < _controllers.size(); ++i) - delete _controllers[i]; -} - -void InteractionHandler::setKeyboardController(KeyboardController* controller) { - assert(controller); - delete _keyboardController; - _keyboardController = controller; - _keyboardController->setHandler(this); -} - -void InteractionHandler::setMouseController(MouseController* controller) { - assert(controller); - delete _mouseController; - _mouseController = controller; - _mouseController->setHandler(this); -} - -void InteractionHandler::addController(Controller* controller) { - assert(controller); - _controllers.push_back(controller); - controller->setHandler(this); -} - -void InteractionHandler::lockControls() { - _mutex.lock(); -} - -void InteractionHandler::unlockControls() { - _mutex.unlock(); -} - -void InteractionHandler::update(double deltaTime) { - _deltaTime = deltaTime; - _mouseController->update(deltaTime); - - bool hasKeys = false; - psc pos; - glm::quat q; - - _keyframeMutex.lock(); - - if (_keyframes.size() > 4){ //wait until enough samples are buffered - hasKeys = true; - - openspace::network::datamessagestructures::PositionKeyframe p0, p1, p2, p3; - - p0 = _keyframes[0]; - p1 = _keyframes[1]; - p2 = _keyframes[2]; - p3 = _keyframes[3]; - - //interval check - if (_currentKeyframeTime < p1._timeStamp){ - _currentKeyframeTime = p1._timeStamp; - } - - double t0 = p1._timeStamp; - double t1 = p2._timeStamp; - double fact = (_currentKeyframeTime - t0) / (t1 - t0); - - - - //glm::dvec4 v = positionInterpCR.interpolate(fact, _keyframes[0]._position.dvec4(), _keyframes[1]._position.dvec4(), _keyframes[2]._position.dvec4(), _keyframes[3]._position.dvec4()); - glm::dvec4 v = ghoul::interpolateLinear(fact, p1._position.dvec4(), p2._position.dvec4()); - - pos = psc(v.x, v.y, v.z, v.w); - q = ghoul::interpolateLinear(fact, p1._viewRotationQuat, p2._viewRotationQuat); - - //we're done with this sample interval - if (_currentKeyframeTime >= p2._timeStamp){ - _keyframes.erase(_keyframes.begin()); - _currentKeyframeTime = p1._timeStamp; - } - - _currentKeyframeTime += deltaTime; - - } - - _keyframeMutex.unlock(); - - if (hasKeys) { - _camera->setPosition(pos); - _camera->setRotation(q); - } - - - - - -} - -void InteractionHandler::setFocusNode(SceneGraphNode* node) { - - if (_focusNode == node){ - return; - } - - _focusNode = node; - - //orient the camera to the new node - psc focusPos = node->worldPosition(); - psc camToFocus = focusPos - _camera->position(); - glm::vec3 viewDir = glm::normalize(camToFocus.vec3()); - glm::vec3 cameraView = glm::normalize(_camera->viewDirectionWorldSpace()); - //set new focus position - _camera->setFocusPosition(node->worldPosition()); - float dot = glm::dot(viewDir, cameraView); - - //static const float Epsilon = 0.001f; - if (dot < 1.f && dot > -1.f) { - //if (glm::length(viewDir - cameraView) < 0.001) { - //if (viewDir != cameraView) { - glm::vec3 rotAxis = glm::normalize(glm::cross(viewDir, cameraView)); - float angle = glm::angle(viewDir, cameraView); - glm::quat q = glm::angleAxis(angle, rotAxis); - - //rotate view to target new focus - _camera->rotate(q); - } -} - -const SceneGraphNode* const InteractionHandler::focusNode() const { - return _focusNode; -} - -void InteractionHandler::setCamera(Camera* camera) { - assert(camera); - _camera = camera; -} -const Camera* const InteractionHandler::camera() const { - return _camera; -} - -//void InteractionHandler::keyboardCallback(int key, int action) { -// if (_keyboardController) { -// auto start = ghoul::HighResClock::now(); -// _keyboardController->keyPressed(KeyAction(action), Key(key), KeyModifier::None); -// auto end = ghoul::HighResClock::now(); -// LINFO("Keyboard timing: " << std::chrono::duration_cast(end - start).count() << "ns"); -// } -//} - -void InteractionHandler::mouseButtonCallback(MouseButton button, MouseAction action) { - if (_mouseController) - _mouseController->button(button, action); -} - -void InteractionHandler::mousePositionCallback(double x, double y) { - if (_mouseController) - // TODO Remap screen coordinates to [0,1] - _mouseController->move(static_cast(x), static_cast(y)); -} - -void InteractionHandler::mouseScrollWheelCallback(double pos) { - if (_mouseController) - _mouseController->scrollWheel(static_cast(pos)); -} - -void InteractionHandler::orbit(const float &dx, const float &dy, const float &dz, const float &dist){ - - lockControls(); - - glm::vec3 cameraUp = glm::normalize((glm::inverse(_camera->viewRotationMatrix()) * glm::vec4(_camera->lookUpVectorCameraSpace(), 0))).xyz(); - glm::vec3 cameraRight = glm::cross(glm::vec3(_camera->viewDirectionWorldSpace()), cameraUp); - - glm::mat4 transform; - transform = glm::rotate(glm::radians(dx * 100.f), cameraUp) * transform; - transform = glm::rotate(glm::radians(dy * 100.f), cameraRight) * transform; - transform = glm::rotate(glm::radians(dz * 100.f), glm::vec3(_camera->viewDirectionWorldSpace())) * transform; - - //get "old" focus position - - psc camPos = _camera->position(); - psc focusPos = _camera->focusPosition(); - float distToFocusNodeCenter = (focusPos - camPos).length().lengthf(); - float focusNodeBounds = _focusNode ? _focusNode->boundingSphere().lengthf() : 0.f; - - float speedFactor = distToFocusNodeCenter - 0.098*focusNodeBounds; - - float rotationSpeed = glm::min(0.00001f * speedFactor, 100.0f); - float zoomSpeed = glm::min(0.0000001f * speedFactor, 1.0f); - float rollSpeed = 100.0f; - - - glm::mat4 transform; - transform = glm::rotate(glm::radians(dx * rotationSpeed), cameraUp) * transform; - transform = glm::rotate(glm::radians(dy * rotationSpeed), cameraRight) * transform; - transform = glm::rotate(glm::radians(dz * rollSpeed), _camera->viewDirection()) * transform; - - - - - //// get camera position - //psc relative = _camera->position(); - - // get camera position (UNSYNCHRONIZED) - psc relative = _camera->unsynchedPosition(); - - //get relative vector - psc relative_focus_coordinate = relative - focusPos; - //rotate relative vector - relative_focus_coordinate = glm::inverse(transform) * relative_focus_coordinate.vec4(); - - //get new new position of focus node - psc origin; - if (_focusNode) { - origin = _focusNode->worldPosition(); - } - - //new camera position - relative = origin + relative_focus_coordinate; - - - psc target = relative + relative_focus_coordinate * dist * zoomSpeed; - - //don't fly into objects - if ((target - origin).length() < focusNodeBounds){ - //target = relative; - } - - unlockControls(); - - - _camera->setFocusPosition(origin); - _camera->setPosition(target); - _camera->rotate(glm::quat_cast(transform)); - -} - -//void InteractionHandler::distance(const float &d){ -// -// lockControls(); -// -// psc relative = _camera->position(); -// const psc origin = (_focusNode) ? _focusNode->worldPosition() : psc(); -// psc relative_origin_coordinate = relative - origin; -// // addition 100% of bounds (fix later to something node specific?) -// float bounds = 2.f * (_focusNode ? _focusNode->boundingSphere().lengthf() : 0.f); -// -// psc target = relative + relative_origin_coordinate * d;// *fmaxf(bounds, (1.f - d)); -// //don't fly into objects -// if ((target - origin).length() < bounds){ -// target = relative; -// } -// _camera->setPosition(target); -// -// unlockControls(); -//} - -void InteractionHandler::orbitDelta(const glm::quat& rotation) -{ - lockControls(); - - // the camera position - psc relative = _camera->position(); - - // should be changed to something more dynamic =) - psc origin; - if (_focusNode) { - origin = _focusNode->worldPosition(); - } - - psc relative_origin_coordinate = relative - origin; - //glm::mat4 rotation_matrix = glm::mat4_cast(glm::inverse(rotation)); - //relative_origin_coordinate = relative_origin_coordinate.vec4() * glm::inverse(rotation); - relative_origin_coordinate = glm::inverse(rotation) * relative_origin_coordinate.vec4(); - relative = relative_origin_coordinate + origin; - glm::mat4 la = glm::lookAt(_camera->position().vec3(), origin.vec3(), glm::rotate(rotation, glm::vec3(_camera->lookUpVectorCameraSpace()))); - - unlockControls(); - - _camera->setPosition(relative); - //camera_->rotate(rotation); - //camera_->setRotation(glm::mat4_cast(rotation)); - - - _camera->setRotation(glm::quat_cast(la)); - //camera_->setLookUpVector(); - - -} - -//<<<<<<< HEAD -//void InteractionHandler::distance(const PowerScaledScalar &dist, size_t iterations) { -// if (iterations > 5) -// return; -// //assert(this_); -// lockControls(); -// -// psc relative = _camera->position(); -// const psc origin = (_node) ? _node->worldPosition() : psc(); -// -// psc relative_origin_coordinate = relative - origin; -// const glm::vec3 dir(relative_origin_coordinate.direction()); -// glm::vec3 newdir = dir * dist[0]; -//======= -void InteractionHandler::rotateDelta(const glm::quat& rotation) -{ - _camera->rotate(rotation); -} - -void InteractionHandler::distanceDelta(const PowerScaledScalar& distance, size_t iterations) -{ - if (iterations > 5) - return; - //assert(this_); - lockControls(); - - psc relative = _camera->position(); - const psc origin = (_focusNode) ? _focusNode->worldPosition() : psc(); - - unlockControls(); - - psc relative_origin_coordinate = relative - origin; - const glm::vec3 dir(relative_origin_coordinate.direction()); - glm::vec3 newdir = dir * distance[0]; - - relative_origin_coordinate = newdir; - relative_origin_coordinate[3] = distance[1]; - relative = relative + relative_origin_coordinate; - - relative_origin_coordinate = relative - origin; - if (relative_origin_coordinate.vec4().x == 0.f && relative_origin_coordinate.vec4().y == 0.f && relative_origin_coordinate.vec4().z == 0.f) - // TODO: this shouldn't be allowed to happen; a mechanism to prevent the camera to coincide with the origin is necessary (ab) - return; - - newdir = relative_origin_coordinate.direction(); - - // update only if on the same side of the origin - if (glm::angle(newdir, dir) < 90.0f) { - _camera->setPosition(relative); - } - else { - PowerScaledScalar d2 = distance; - d2[0] *= 0.75f; - d2[1] *= 0.85f; - distanceDelta(d2, iterations + 1); - } -} - -void InteractionHandler::lookAt(const glm::quat& rotation) -{ -} - -void InteractionHandler::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) { - // TODO package in script - const float speed = _controllerSensitivity; - const float dt = static_cast(_deltaTime); - if (action == KeyAction::Press || action == KeyAction::Repeat) { - if ((key == Key::Right) && (modifier == KeyModifier::NoModifier)) { - glm::vec3 euler(0.0, speed * dt*0.4, 0.0); - glm::quat rot = glm::quat(euler); - rotateDelta(rot); - } - if ((key == Key::Left) && (modifier == KeyModifier::NoModifier)) { - glm::vec3 euler(0.0, -speed * dt*0.4, 0.0); - glm::quat rot = glm::quat(euler); - rotateDelta(rot); - } - if ((key == Key::Down) && (modifier == KeyModifier::NoModifier)) { - glm::vec3 euler(speed * dt*0.4, 0.0, 0.0); - glm::quat rot = glm::quat(euler); - rotateDelta(rot); - } - if ((key == Key::Up) && (modifier == KeyModifier::NoModifier)) { - glm::vec3 euler(-speed * dt*0.4, 0.0, 0.0); - glm::quat rot = glm::quat(euler); - rotateDelta(rot); - } - if ((key == Key::KeypadSubtract) && (modifier == KeyModifier::NoModifier)) { - glm::vec2 s = OsEng.renderEngine().camera()->scaling(); - s[1] -= 0.5f; - OsEng.renderEngine().camera()->setScaling(s); - } - if ((key == Key::KeypadAdd) && (modifier == KeyModifier::NoModifier)) { - glm::vec2 s = OsEng.renderEngine().camera()->scaling(); - s[1] += 0.5f; - OsEng.renderEngine().camera()->setScaling(s); - } - - // iterate over key bindings - //_validKeyLua = true; - auto ret = _keyLua.equal_range({ key, modifier }); - for (auto it = ret.first; it != ret.second; ++it) { - //OsEng.scriptEngine()->runScript(it->second); - OsEng.scriptEngine().queueScript(it->second); - //if (!_validKeyLua) { - // break; - //} - } - } -} - -void InteractionHandler::resetKeyBindings() { - _keyLua.clear(); - //_validKeyLua = false; -} - -void InteractionHandler::bindKey(Key key, KeyModifier modifier, std::string lua) { - _keyLua.insert({ - {key, modifier}, - lua - }); -} - -scripting::LuaLibrary InteractionHandler::luaLibrary() { - return { - "", - { - { - "clearKeys", - &luascriptfunctions::clearKeys, - "", - "Clear all key bindings" - }, - { - "bindKey", - &luascriptfunctions::bindKey, - "string, string", - "Binds a key by name to a lua string command" - }, - { - "dt", - &luascriptfunctions::dt, - "", - "Get current frame time" - }, - { - "distance", - &luascriptfunctions::distance, - "number", - "Change distance to origin", - true - }, - { - "setInteractionSensitivity", - &luascriptfunctions::setInteractionSensitivity, - "number", - "Sets the global interaction sensitivity" - }, - { - "interactionSensitivity", - &luascriptfunctions::interactionSensitivity, - "", - "Gets the current global interaction sensitivity" - }, - { - "setInvertRoll", - &luascriptfunctions::setInvertRoll, - "bool", - "Sets the setting if roll movements are inverted" - }, - { - "invertRoll", - &luascriptfunctions::invertRoll, - "", - "Returns the status of roll movement inversion" - }, - { - "setInvertRotation", - &luascriptfunctions::setInvertRotation, - "bool", - "Sets the setting if rotation movements are inverted" - }, - { - "invertRotation", - &luascriptfunctions::invertRotation, - "", - "Returns the status of rotation movement inversion" - } - - } - }; -} - -void InteractionHandler::setRotation(const glm::quat& rotation) -{ - _camera->setRotation(rotation); -} - -double InteractionHandler::deltaTime() const { - return _deltaTime; -} - -void InteractionHandler::setInteractionSensitivity(float sensitivity) { - _controllerSensitivity = sensitivity; -} - -float InteractionHandler::interactionSensitivity() const { - return _controllerSensitivity; -} - -void InteractionHandler::setInvertRoll(bool invert) { - _invertRoll = invert; -} - -bool InteractionHandler::invertRoll() const { - return _invertRoll; -} - -void InteractionHandler::setInvertRotation(bool invert) { - _invertRotation = invert; -} - -bool InteractionHandler::invertRotation() const { - return _invertRotation; -} - - void InteractionHandler::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf){ - _keyframeMutex.lock(); - - //save a maximum of 10 samples (1 seconds of buffer) - if (_keyframes.size() >= 10){ - _keyframes.erase(_keyframes.begin()); - } - _keyframes.push_back(kf); - - _keyframeMutex.unlock(); -} - -void InteractionHandler::clearKeyframes(){ - _keyframeMutex.lock(); - _keyframes.clear(); - _keyframeMutex.unlock(); -} - -#else // USE_OLD_INTERACTIONHANDLER - // InteractionHandler InteractionHandler::InteractionHandler() : _origin("origin", "Origin", "") @@ -664,6 +105,11 @@ InteractionHandler::InteractionHandler() "GlobeBrowsing", std::make_shared(_mouseStates) )); + _interactionModes.insert( + std::pair>( + "Keyframe", + std::make_shared() + )); // Set the interactionMode _currentInteractionMode = _interactionModes["Orbital"]; @@ -700,6 +146,25 @@ InteractionHandler::~InteractionHandler() { } +void InteractionHandler::initialize() { + OsEng.parallelConnection().connectionEvent()->subscribe("interactionHandler", "statusChanged", [this]() { + if (OsEng.parallelConnection().status() == ParallelConnection::Status::ClientWithHost) { + setInteractionMode("Keyframe"); + } else { + auto keyframeModeIter = _interactionModes.find("Keyframe"); + if (keyframeModeIter != _interactionModes.end()) { + if (_currentInteractionMode == keyframeModeIter->second) { + setInteractionMode("Orbital"); + } + } + } + }); +} + +void InteractionHandler::deinitialize() { + OsEng.parallelConnection().connectionEvent()->unsubscribe("interactionHandler"); +} + void InteractionHandler::setFocusNode(SceneGraphNode* node) { _currentInteractionMode->setFocusNode(node); } @@ -800,8 +265,10 @@ void InteractionHandler::keyboardCallback(Key key, KeyModifier modifier, KeyActi // iterate over key bindings auto ret = _keyLua.equal_range({ key, modifier }); for (auto it = ret.first; it != ret.second; ++it) { - //OsEng.scriptEngine()->runScript(it->second); - OsEng.scriptEngine().queueScript(it->second); + OsEng.scriptEngine().queueScript(it->second.first, + it->second.second ? + scripting::ScriptEngine::RemoteScripting::Yes : + scripting::ScriptEngine::RemoteScripting::No); } } } @@ -905,12 +372,20 @@ void InteractionHandler::resetKeyBindings() { _keyLua.clear(); } +void InteractionHandler::bindKeyLocal(Key key, KeyModifier modifier, std::string lua) { + _keyLua.insert({ + { key, modifier }, + std::make_pair(lua, false) + }); +} + void InteractionHandler::bindKey(Key key, KeyModifier modifier, std::string lua) { _keyLua.insert({ { key, modifier }, - lua + std::make_pair(lua, true) }); } + void InteractionHandler::writeKeyboardDocumentation(const std::string& type, const std::string& file) { @@ -920,8 +395,14 @@ void InteractionHandler::writeKeyboardDocumentation(const std::string& type, con f.open(absPath(file)); for (const auto& p : _keyLua) { + std::string remoteScriptingInfo; + bool remoteScripting = p.second.second; + + if (!remoteScripting) { + remoteScriptingInfo = " (LOCAL)"; + } f << std::to_string(p.first) << ": " << - p.second << std::endl; + p.second.first << remoteScriptingInfo << std::endl; } } else if (type == "html") { @@ -929,51 +410,94 @@ void InteractionHandler::writeKeyboardDocumentation(const std::string& type, con f.exceptions(~std::ofstream::goodbit); f.open(absPath(file)); -#ifdef JSON + std::ifstream handlebarsInput(absPath(HandlebarsFilename)); + std::ifstream jsInput(absPath(JsFilename)); + + std::string jsContent; + std::back_insert_iterator jsInserter(jsContent); + + std::copy(std::istreambuf_iterator{handlebarsInput}, std::istreambuf_iterator(), jsInserter); + std::copy(std::istreambuf_iterator{jsInput}, std::istreambuf_iterator(), jsInserter); + + std::ifstream bootstrapInput(absPath(BootstrapFilename)); + std::ifstream cssInput(absPath(CssFilename)); + + std::string cssContent; + std::back_insert_iterator cssInserter(cssContent); + + std::copy(std::istreambuf_iterator{bootstrapInput}, std::istreambuf_iterator(), cssInserter); + std::copy(std::istreambuf_iterator{cssInput}, std::istreambuf_iterator(), cssInserter); + + std::ifstream mainTemplateInput(absPath(MainTemplateFilename)); + std::string mainTemplateContent{ std::istreambuf_iterator{mainTemplateInput}, + std::istreambuf_iterator{} }; + + std::ifstream keybindingTemplateInput(absPath(KeybindingTemplateFilename)); + std::string keybindingTemplateContent{ std::istreambuf_iterator{keybindingTemplateInput}, + std::istreambuf_iterator{} }; + std::stringstream json; json << "["; + bool first = true; for (const auto& p : _keyLua) { + if (!first) { + json << ","; + } + first = false; json << "{"; json << "\"key\": \"" << std::to_string(p.first) << "\","; - json << "\"script\": \"" << p.second << "\","; - json << "},"; + json << "\"script\": \"" << p.second.first << "\""; + json << "\"remoteScripting\": \"" << (p.second.second ? "true" : "false") << "\""; + json << "}"; } json << "]"; - std::string jsonText = json.str(); + std::string jsonString = ""; + for (const char& c : json.str()) { + if (c == '\'') { + jsonString += "\\'"; + } else { + jsonString += c; + } + } + -#else std::stringstream html; + html << "\n" + << "\n" + << "\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\n" + << "\t\n" + << "\t\tDocumentation\n" + << "\t\n" + << "\t\n" + << "\t\n" + << "\n"; - html << "\n" - << "\t\n" - << "\t\tKey Bindings\n" - << "\t\n" - << "\n" - << "\n" - << "\t\n\n" - << "\t\n" - << "\t\t\n" - << "\t\t\t\n" - << "\t\t\t\n" - << "\t\t\n" - << "\t\n" - << "\t\n"; + f << html.str(); + + /* for (const auto& p : _keyLua) { html << "\t\t\n" << "\t\t\t\n" - << "\t\t\t\n" + << "\t\t\t\n" + << "\t\t\t\n" << "\t\t\n"; - } - - html << "\t\n" - << "
Key Bindings
KeyBinding
" << std::to_string(p.first) << "" << p.second << "" << p.second.first << "" << (p.second.second ? "Yes" : "No") << "
\n" - << ""; - - f << html.str(); -#endif - + }*/ } else { throw ghoul::RuntimeError( @@ -997,7 +521,14 @@ scripting::LuaLibrary InteractionHandler::luaLibrary() { "bindKey", &luascriptfunctions::bindKey, "string, string", - "Binds a key by name to a lua string command" + "Binds a key by name to a lua string command to execute both locally " + "and to broadcast to clients if this is the host of a parallel session" + }, + { + "bindKeyLocal", + &luascriptfunctions::bindKeyLocal, + "string, string", + "Binds a key by name to a lua string command to execute only locally" }, { "setInteractionMode", @@ -1027,7 +558,7 @@ scripting::LuaLibrary InteractionHandler::luaLibrary() { }; } -void InteractionHandler::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf) { +void InteractionHandler::addKeyframe(const datamessagestructures::CameraKeyframe &kf) { _inputState->addKeyframe(kf); } @@ -1035,21 +566,5 @@ void InteractionHandler::clearKeyframes() { _inputState->clearKeyframes(); } -//void InteractionHandler::serialize(SyncBuffer* syncBuffer) { -// for (auto var : _interactionModes) -// { -// var.second->serialize(syncBuffer); -// } -//} -// -//void InteractionHandler::deserialize(SyncBuffer* syncBuffer) { -// for (auto var : _interactionModes) -// { -// var.second->deserialize(syncBuffer); -// } -//} - -#endif // USE_OLD_INTERACTIONHANDLER - } // namespace interaction } // namespace openspace diff --git a/src/interaction/interactionhandler_lua.inl b/src/interaction/interactionhandler_lua.inl index 5c52f33131..7fb9388858 100644 --- a/src/interaction/interactionhandler_lua.inl +++ b/src/interaction/interactionhandler_lua.inl @@ -60,7 +60,9 @@ int setOrigin(lua_State* L) { /** * \ingroup LuaScripts * bindKey(): -* Binds a key to Lua command +* Binds a key to Lua command to both execute locally +* and broadcast to all clients if this node is hosting +* a parallel connection. */ int bindKey(lua_State* L) { using ghoul::lua::luaTypeToString; @@ -93,6 +95,42 @@ int bindKey(lua_State* L) { return 0; } +/** +* \ingroup LuaScripts +* bindKey(): +* Binds a key to Lua command to execute only locally +*/ +int bindKeyLocal(lua_State* L) { + using ghoul::lua::luaTypeToString; + + int nArguments = lua_gettop(L); + if (nArguments != 2) + return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); + + + std::string key = luaL_checkstring(L, -2); + std::string command = luaL_checkstring(L, -1); + + if (command.empty()) + return luaL_error(L, "Command string is empty"); + + openspace::KeyWithModifier iKey = openspace::stringToKey(key); + + if (iKey.key == openspace::Key::Unknown) { + LERRORC("lua.bindKey", "Could not find key '" << key << "'"); + return 0; + } + + OsEng.interactionHandler().bindKeyLocal( + iKey.key, + iKey.modifier, + command + ); + + return 0; +} + + /** * \ingroup LuaScripts * clearKeys(): @@ -176,120 +214,7 @@ int resetCameraDirection(lua_State* L) { OsEng.interactionHandler().resetCameraDirection(); } -#ifdef USE_OLD_INTERACTIONHANDLER -/** -* \ingroup LuaScripts -* dt(bool): -* Get current frame time -*/ -int dt(lua_State* L) { - /* - int nArguments = lua_gettop(L); - if (nArguments != 0) - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - - lua_pushnumber(L,OsEng.interactionHandler().deltaTime()); - */return 1; -} - -/** -* \ingroup LuaScripts -* distance(double, double): -* Change distance to origin -*/ -int distance(lua_State* L) { - /* - int nArguments = lua_gettop(L); - if (nArguments != 2) - return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments); - - double d1 = luaL_checknumber(L, -2); - double d2 = luaL_checknumber(L, -1); - PowerScaledScalar dist(static_cast(d1), static_cast(d2)); - OsEng.interactionHandler().distanceDelta(dist); - */ - return 0; -} - -/** - * \ingroup LuaScripts - * setInteractionSensitivity(double): - * Changes the global interaction sensitivity to the passed value - */ -int setInteractionSensitivity(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - float sensitivity = static_cast(luaL_checknumber(L, -1)); - //OsEng.interactionHandler().setInteractionSensitivity(sensitivity); - return 0; -} - -/** - * \ingroup LuaScripts - * interactionSensitivity(): - * Returns the current, global interaction sensitivity - */ -int interactionSensitivity(lua_State* L) { - //float sensitivity = OsEng.interactionHandler().interactionSensitivity(); - //lua_pushnumber(L, sensitivity); - return 1; -} - -/** - * \ingroup LuaScripts - * setInvertRoll(bool): - * Determines if the roll movement is inverted - */ -int setInvertRoll(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool invert = lua_toboolean(L, -1) == 1; - //OsEng.interactionHandler().setInvertRoll(invert); - return 0; -} - -/** - * \ingroup LuaScripts - * invertRoll(): - * Returns the current setting for inversion of roll movement - */ -int invertRoll(lua_State* L) { - //bool invert = OsEng.interactionHandler().invertRoll(); - //lua_pushboolean(L, invert); - return 1; -} - -/** - * \ingroup LuaScripts - * setInvertRotation(bool): - * Determines if the rotation movement is inverted - */ -int setInvertRotation(lua_State* L) { - int nArguments = lua_gettop(L); - if (nArguments != 1) - return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); - - bool invert = lua_toboolean(L, -1) == 1; - //OsEng.interactionHandler().setInvertRotation(invert); - return 0; -} - -/** - * \ingroup LuaScripts - * invertRotation(): - * Returns the current setting for inversion of rotation movement - */ -int invertRotation(lua_State* L) { - //bool invert = OsEng.interactionHandler().invertRotation(); - //lua_pushboolean(L, invert); - return 1; -} -#endif USE_OLD_INTERACTIONHANDLER } // namespace luascriptfunctions } // namespace openspace diff --git a/src/interaction/interactionmode.cpp b/src/interaction/interactionmode.cpp index 3556643b04..fbcc57a2fc 100644 --- a/src/interaction/interactionmode.cpp +++ b/src/interaction/interactionmode.cpp @@ -57,22 +57,39 @@ namespace interaction { } - void InputState::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf) { - _keyframeMutex.lock(); + const std::vector& InputState::keyframes() const { + return _keyframes; + } + + void InputState::addKeyframe(const datamessagestructures::CameraKeyframe &kf) { + clearOldKeyframes(); + + auto compareTimestamps = [](const datamessagestructures::CameraKeyframe a, + datamessagestructures::CameraKeyframe b) { + return a._timestamp < b._timestamp; + }; + + // Remove keyframes after the inserted keyframe. + _keyframes.erase(std::upper_bound(_keyframes.begin(), _keyframes.end(), kf, compareTimestamps), _keyframes.end()); - //save a maximum of 10 samples (1 seconds of buffer) - if (_keyframes.size() >= 10) { - _keyframes.erase(_keyframes.begin()); - } _keyframes.push_back(kf); + } - _keyframeMutex.unlock(); + void InputState::clearOldKeyframes() { + double now = OsEng.runTime(); + auto isLater = [now](const datamessagestructures::CameraKeyframe kf) { + return kf._timestamp > now; + }; + + // Remote keyframes with earlier timestamps than the current time. + auto nextKeyframe = std::find_if(_keyframes.begin(), _keyframes.end(), isLater); + if (nextKeyframe != _keyframes.begin()) { + _keyframes.erase(_keyframes.begin(), nextKeyframe - 1); + } } void InputState::clearKeyframes() { - _keyframeMutex.lock(); _keyframes.clear(); - _keyframeMutex.unlock(); } void InputState::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) { @@ -192,11 +209,38 @@ KeyframeInteractionMode::~KeyframeInteractionMode() { } void KeyframeInteractionMode::updateMouseStatesFromInput(const InputState& inputState, double deltaTime) { - + _keyframes = inputState.keyframes(); } void KeyframeInteractionMode::updateCameraStateFromMouseStates(Camera& camera) { + if (_keyframes.size() == 0) { + return; + } + double now = OsEng.runTime(); + auto isLater = [now](const datamessagestructures::CameraKeyframe kf) { + return kf._timestamp > now; + }; + + auto nextKeyframe = std::find_if(_keyframes.begin(), _keyframes.end(), isLater); + if (nextKeyframe == _keyframes.end()) { + return; + } + + if (nextKeyframe == _keyframes.begin()) { + camera.setPositionVec3(_keyframes[0]._position); + camera.setRotation(_keyframes[0]._rotation); + return; + } + auto prevKeyframe = nextKeyframe - 1; + + double prevTime = prevKeyframe->_timestamp; + double nextTime = nextKeyframe->_timestamp; + + double t = (now - prevTime) / (nextTime - prevTime); + + camera.setPositionVec3(prevKeyframe->_position * (1 - t) + nextKeyframe->_position * t); + camera.setRotation(glm::slerp(prevKeyframe->_rotation, nextKeyframe->_rotation, t)); } // OrbitalInteractionMode diff --git a/src/interaction/luaconsole.cpp b/src/interaction/luaconsole.cpp index 1073dfd43e..9cf06207b7 100644 --- a/src/interaction/luaconsole.cpp +++ b/src/interaction/luaconsole.cpp @@ -24,6 +24,7 @@ #include +#include #include #include @@ -58,6 +59,7 @@ LuaConsole::LuaConsole() , _filename("") , _autoCompleteInfo({NoAutoComplete, false, ""}) , _isVisible(false) + , _remoteScripting(true) { // _commands.push_back(""); // _activeCommand = _commands.size() - 1; @@ -89,6 +91,13 @@ void LuaConsole::initialize() { } _commands.push_back(""); _activeCommand = _commands.size() - 1; + + OsEng.parallelConnection().connectionEvent()->subscribe("luaConsole", + "statusChanged", [this]() { + ParallelConnection::Status status = OsEng.parallelConnection().status(); + parallelConnectionChanged(status); + }); + } void LuaConsole::deinitialize() { @@ -102,6 +111,8 @@ void LuaConsole::deinitialize() { file.write(s.c_str(), length); } } + + OsEng.parallelConnection().connectionEvent()->unsubscribe("luaConsole"); } void LuaConsole::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) { @@ -169,7 +180,8 @@ void LuaConsole::keyboardCallback(Key key, KeyModifier modifier, KeyAction actio else { std::string cmd = _commands.at(_activeCommand); if (cmd != "") { - OsEng.scriptEngine().queueScript(cmd); + OsEng.scriptEngine().queueScript(cmd, + _remoteScripting ? scripting::ScriptEngine::RemoteScripting::Yes : scripting::ScriptEngine::RemoteScripting::No); // Only add the current command to the history if it hasn't been // executed before. We don't want two of the same commands in a row @@ -303,19 +315,35 @@ void LuaConsole::charCallback(unsigned int codepoint, KeyModifier modifier) { void LuaConsole::render() { const float font_size = 10.0f; - int ySize = OsEng.windowWrapper().viewportPixelCoordinates().w; + int ySize = OsEng.windowWrapper().currentWindowSize().y; + //int ySize = OsEng.windowWrapper().viewportPixelCoordinates().w; float startY = static_cast(ySize) - 2.0f * font_size; startY = startY - font_size * 15.0f * 2.0f; const glm::vec4 red(1, 0, 0, 1); + const glm::vec4 lightBlue(0.4, 0.4, 1, 1); const glm::vec4 green(0, 1, 0, 1); const glm::vec4 white(1, 1, 1, 1); std::shared_ptr font = OsEng.fontManager().font("Mono", font_size); using ghoul::fontrendering::RenderFont; - RenderFont(*font, glm::vec2(15.f, startY), red, "$"); + if (_remoteScripting) { + int nClients = OsEng.parallelConnection().nConnections() - 1; + if (nClients == 1) { + RenderFont(*font, glm::vec2(15.f, startY + 20.0f), red, "Broadcasting script to 1 client"); + } else { + RenderFont(*font, glm::vec2(15.f, startY + 20.0f), red, ("Broadcasting script to " + std::to_string(nClients) + " clients").c_str()); + } + RenderFont(*font, glm::vec2(15.f, startY), red, "$"); + } + else { + if (OsEng.parallelConnection().isHost()) { + RenderFont(*font, glm::vec2(15.f, startY + 20.0f), lightBlue, "Local script execution"); + } + RenderFont(*font, glm::vec2(15.f, startY), lightBlue, "$"); + } RenderFont(*font, glm::vec2(15.f + font_size, startY), white, "%s", _commands.at(_activeCommand).c_str()); size_t n = std::count(_commands.at(_activeCommand).begin(), _commands.at(_activeCommand).begin() + _inputPosition, '\n'); @@ -390,10 +418,24 @@ void LuaConsole::setVisible(bool visible) { _isVisible = visible; } -void LuaConsole::toggleVisibility() { - _isVisible = !_isVisible; +void LuaConsole::toggleMode() { + if (_isVisible) { + if (_remoteScripting) { + _remoteScripting = false; + } else { + _isVisible = false; + } + } else { + _remoteScripting = OsEng.parallelConnection().isHost(); + _isVisible = true; + } } +void LuaConsole::parallelConnectionChanged(const ParallelConnection::Status& status) { + _remoteScripting = status == ParallelConnection::Status::Host; +} + + scripting::LuaLibrary LuaConsole::luaLibrary() { return { "console", diff --git a/src/interaction/luaconsole_lua.inl b/src/interaction/luaconsole_lua.inl index 99497bfaeb..97d6ec0b51 100644 --- a/src/interaction/luaconsole_lua.inl +++ b/src/interaction/luaconsole_lua.inl @@ -64,7 +64,7 @@ int toggle(lua_State* L) { if (nArguments != 0) return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); - OsEng.console().toggleVisibility(); + OsEng.console().toggleMode(); return 0; } diff --git a/src/mission/mission.cpp b/src/mission/mission.cpp new file mode 100644 index 0000000000..b8bdb20db8 --- /dev/null +++ b/src/mission/mission.cpp @@ -0,0 +1,196 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +#include + +#include + +#include +#include + +namespace { + const std::string KeyName = "Name"; + const std::string KeyDescription = "Description"; + const std::string KeyPhases = "Phases"; + const std::string KeyTimeRange = "TimeRange"; +} + +namespace openspace { + +Documentation MissionPhase::Documentation() { + using namespace documentation; + + return { + "Missions and Mission Phases", + "core_mission_mission", + { + { + KeyName, + new StringVerifier, + "The human readable name of this mission or mission phase that is " + "displayed to the user.", + Optional::No + }, + { + KeyDescription, + new StringVerifier, + "A description of this mission or mission phase.", + Optional::Yes + }, + { + KeyTimeRange, + new ReferencingVerifier("core_util_timerange"), + "The time range for which this mission or mission phase is valid. If no " + "time range is specified, the ranges of sub mission phases are used " + "instead.", + Optional::Yes + }, + { + KeyPhases, + new ReferencingVerifier("core_mission_mission"), + "The phases into which this mission or mission phase is separated.", + Optional::Yes + } + }, + Exhaustive::Yes + }; +} + +MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { + _name = dict.value(KeyName); + dict.getValue(KeyDescription, _description); + + ghoul::Dictionary childDicts; + if (dict.getValue(KeyPhases, childDicts)) { + // This is a nested mission phase + _subphases.reserve(childDicts.size()); + for (size_t i = 0; i < childDicts.size(); ++i) { + std::string key = std::to_string(i + 1); + _subphases.emplace_back(childDicts.value(key)); + } + + // Ensure subphases are sorted + std::stable_sort( + _subphases.begin(), + _subphases.end(), + [](const MissionPhase& a, const MissionPhase& b) { + return a.timeRange().start < b.timeRange().start; + } + ); + + // Calculate the total time range of all subphases + TimeRange timeRangeSubPhases; + timeRangeSubPhases.start = _subphases[0].timeRange().start; + timeRangeSubPhases.end = _subphases.back().timeRange().end; + + // user may specify an overall time range. In that case expand this timerange. + ghoul::Dictionary timeRangeDict; + if (dict.getValue(KeyTimeRange, timeRangeDict)) { + TimeRange overallTimeRange(timeRangeDict); + if (!overallTimeRange.includes(timeRangeSubPhases)) { + throw ghoul::RuntimeError( + "User specified time range must at least include its subphases'", + "Mission (" + _name + ")" + ); + } + + _timeRange.include(overallTimeRange); + } + else { + // Its OK to not specify an overall time range, the time range for the + // subphases will simply be used. + _timeRange.include(timeRangeSubPhases); + } + } + else { + ghoul::Dictionary timeRangeDict; + if (dict.getValue(KeyTimeRange, timeRangeDict)) { + _timeRange = TimeRange(timeRangeDict); // throws exception if unable to parse + } + else { + throw ghoul::RuntimeError( + "If there are no subphases specified, the time range has to be specified", + "Mission (" + _name + ")" + ); + } + } +} + +const std::string & MissionPhase::name() const { + return _name; +} + +const TimeRange & MissionPhase::timeRange() const { + return _timeRange; +} + +const std::string & MissionPhase::description() const { + return _description; +} + +const std::vector& MissionPhase::phases() const { + return _subphases; +} + +MissionPhase::Trace MissionPhase::phaseTrace(double time, int maxDepth) const { + Trace trace; + if (_timeRange.includes(time)) { + trace.push_back(std::cref(*this)); + phaseTrace(time, trace, maxDepth); + } + return std::move(trace); +} + +void MissionPhase::phaseTrace(double time, Trace& trace, int maxDepth) const { + if (maxDepth == 0) { + return; + } + + for (const MissionPhase& phase : _subphases) { + if (phase.timeRange().includes(time)) { + trace.push_back(phase); + phase.phaseTrace(time, trace, maxDepth - 1); + return; + } + else if (phase.timeRange().start > time) { + // Since time ranges are sorted we can do early termination + return; + } + } +} + +Mission missionFromFile(std::string filename) { + ghoul_assert(!filename.empty(), "filename must not be empty"); + ghoul_assert(!FileSys.containsToken(filename), "filename must not contain tokens"); + ghoul_assert(FileSys.fileExists(filename), "filename must exist"); + + ghoul::Dictionary missionDict; + ghoul::lua::loadDictionaryFromFile(filename, missionDict); + + documentation::testSpecificationAndThrow(Documentation(), missionDict, "Mission"); + + return MissionPhase(missionDict); +} + +} // namespace openspace diff --git a/src/mission/missionmanager.cpp b/src/mission/missionmanager.cpp new file mode 100644 index 0000000000..56cb58be64 --- /dev/null +++ b/src/mission/missionmanager.cpp @@ -0,0 +1,115 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "missionmanager_lua.inl" + +namespace openspace { + +MissionManager::MissionManagerException::MissionManagerException(std::string error) + : ghoul::RuntimeError(std::move(error), "MissionManager") +{} + + +MissionManager::MissionManager() + : _currentMission(_missionMap.end()) +{} + +void MissionManager::setCurrentMission(const std::string& missionName) { + ghoul_assert(!missionName.empty(), "missionName must not be empty"); + auto it = _missionMap.find(missionName); + if (it == _missionMap.end()) { + throw MissionManagerException("Mission has not been loaded"); + } + else { + _currentMission = it; + } +} + +bool MissionManager::hasCurrentMission() const { + return _currentMission != _missionMap.end(); +} + +void MissionManager::loadMission(const std::string& filename) { + ghoul_assert(!filename.empty(), "filename must not be empty"); + ghoul_assert(!FileSys.containsToken(filename), "filename must not contain tokens"); + ghoul_assert(FileSys.fileExists(filename), "filename must exist"); + + // Changing the values might invalidate the _currentMission iterator + std::string currentMission = _currentMission != _missionMap.end() ? _currentMission->first : ""; + + Mission mission = missionFromFile(filename); + std::string missionName = mission.name(); + _missionMap.insert({ missionName, std::move(mission) }); + if (_missionMap.size() == 1) { + setCurrentMission(missionName); + } + + if (!currentMission.empty()) { + setCurrentMission(currentMission); + } +} + +bool MissionManager::hasMission(const std::string& missionName) { + return _missionMap.find(missionName) != _missionMap.end(); +} + +const Mission& MissionManager::currentMission() { + if (_currentMission == _missionMap.end()) { + throw MissionManagerException("No current mission has been specified"); + } + return _currentMission->second; +} + +scripting::LuaLibrary MissionManager::luaLibrary() { + return { + "", + { + { + "loadMission", + &luascriptfunctions::loadMission, + "string", + "Load mission phases from file" + }, + { + "setCurrentMission", + &luascriptfunctions::setCurrentMission, + "string", + "Set the currnet mission" + }, + } + }; +} + +// Singleton + + +} // namespace openspace diff --git a/src/mission/missionmanager_lua.inl b/src/mission/missionmanager_lua.inl new file mode 100644 index 0000000000..75840b3f27 --- /dev/null +++ b/src/mission/missionmanager_lua.inl @@ -0,0 +1,55 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +namespace openspace { + +namespace luascriptfunctions { + int loadMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionFileName = luaL_checkstring(L, -1); + if (missionFileName.empty()) { + return luaL_error(L, "filepath string is empty"); + } + MissionManager::ref().loadMission(absPath(missionFileName)); + } + + int setCurrentMission(lua_State* L) { + using ghoul::lua::luaTypeToString; + int nArguments = lua_gettop(L); + if (nArguments != 1) + return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments); + + std::string missionName = luaL_checkstring(L, -1); + if (missionName.empty()) { + return luaL_error(L, "mission name string is empty"); + } + MissionManager::ref().setCurrentMission(missionName); + } + +} // namespace luascriptfunction +} // namespace openspace diff --git a/src/network/networkengine.cpp b/src/network/networkengine.cpp index 6b8b58b54f..e485d1b140 100644 --- a/src/network/networkengine.cpp +++ b/src/network/networkengine.cpp @@ -67,7 +67,7 @@ bool NetworkEngine::handleMessage(const std::string& message) { { std::string script = message.substr(1); //LINFO("Received Lua Script: '" << script << "'"); - OsEng.scriptEngine().queueScript(script); + OsEng.scriptEngine().queueScript(script, scripting::ScriptEngine::RemoteScripting::No); return true; } case MessageTypeExternalControlConnected: diff --git a/src/network/parallelconnection.cpp b/src/network/parallelconnection.cpp index f8579a0f5e..02c64e2afb 100644 --- a/src/network/parallelconnection.cpp +++ b/src/network/parallelconnection.cpp @@ -55,7 +55,9 @@ //openspace includes #include #include +#include #include +#include #include #include #include @@ -64,13 +66,16 @@ #include "parallelconnection_lua.inl" namespace { + const uint32_t ProtocolVersion = 2; + const size_t MaxLatencyDiffs = 64; + const double BroadcastIntervalMilliseconds = 100; const std::string _loggerCat = "ParallelConnection"; + const int nFrametimesBuffer = 4; + const int nBroadcastIntervalsBuffer = 2; } namespace openspace { -namespace network { - ParallelConnection::ParallelConnection() : _passCode(0) , _port("20501") @@ -83,15 +88,16 @@ ParallelConnection::ParallelConnection() , _listenThread(nullptr) , _handlerThread(nullptr) , _isRunning(true) - , _isHost(false) + , _nConnections(0) + , _status(Status::Disconnected) + , _hostName("") , _isConnected(false) , _tryConnect(false) - , _performDisconnect(false) - , _latestTimeKeyframeValid(false) + , _disconnect(false) , _initializationTimejumpRequired(false) { - //create handler thread - _handlerThread = new (std::nothrow) std::thread(&ParallelConnection::threadManagement, this); + _connectionEvent = std::make_shared>(); + _handlerThread = std::make_unique(&ParallelConnection::threadManagement, this); } ParallelConnection::~ParallelConnection(){ @@ -104,9 +110,6 @@ ParallelConnection::~ParallelConnection(){ //join handler _handlerThread->join(); - - //and delete handler - delete _handlerThread; } void ParallelConnection::threadManagement(){ @@ -114,13 +117,13 @@ void ParallelConnection::threadManagement(){ // How about moving this out of the thread and into the destructor? ---abock //while we're still running - while(_isRunning.load()){ + while(_isRunning){ { //lock disconnect mutex mutex //not really needed since no data is modified but conditions need a mutex - std::unique_lock unqlock(_disconnectMutex); + std::unique_lock disconnectLock(_disconnectMutex); //wait for a signal to disconnect - _disconnectCondition.wait(unqlock); + _disconnectCondition.wait(disconnectLock, [this]() { return _disconnect.load(); }); //perform actual disconnect disconnect(); @@ -131,7 +134,9 @@ void ParallelConnection::threadManagement(){ void ParallelConnection::signalDisconnect(){ //signal handler thread to disconnect - _disconnectCondition.notify_all(); + _disconnect = true; + _sendCondition.notify_all(); // Allow send function to terminate. + _disconnectCondition.notify_all(); // Unblock thread management thread. } void ParallelConnection::closeSocket(){ @@ -174,41 +179,34 @@ void ParallelConnection::disconnect(){ _isConnected.store(false); //tell broadcast thread to stop broadcasting (we're no longer host) - _isHost.store(false); - - //signal send thread to stop waiting and finish current run - _sendCondition.notify_all(); - + setStatus(Status::Disconnected); + //join connection thread and delete it if(_connectionThread != nullptr){ _connectionThread->join(); - delete _connectionThread; _connectionThread = nullptr; } //join send thread and delete it if (_sendThread != nullptr){ _sendThread->join(); - delete _sendThread; _sendThread = nullptr; } //join listen thread and delete it if( _listenThread != nullptr){ _listenThread->join(); - delete _listenThread; _listenThread = nullptr; } //join broadcast thread and delete it if(_broadcastThread != nullptr){ _broadcastThread->join(); - delete _broadcastThread; _broadcastThread = nullptr; } // disconnect and cleanup completed - _performDisconnect.store(false); + _disconnect = false; } } @@ -225,11 +223,9 @@ void ParallelConnection::clientConnect(){ } struct addrinfo *addresult = NULL, *ptr = NULL, hints; - #ifdef __WIN32__ //WinSock - ZeroMemory(&hints, sizeof(hints)); - #else - memset(&hints, 0, sizeof(hints)); - #endif + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; @@ -250,7 +246,7 @@ void ParallelConnection::clientConnect(){ _tryConnect.store(true); //start connection thread - _connectionThread = new (std::nothrow) std::thread(&ParallelConnection::establishConnection, this, addresult); + _connectionThread = std::make_unique(&ParallelConnection::establishConnection, this, addresult); } @@ -266,14 +262,15 @@ void ParallelConnection::establishConnection(addrinfo *info){ signalDisconnect(); } - int flag = 1; + int trueFlag = 1; + int falseFlag = 0; int result; //set no delay result = setsockopt(_clientSocket, /* socket affected */ IPPROTO_TCP, /* set option at TCP level */ TCP_NODELAY, /* name of option */ - (char *)&flag, /* the cast is historical cruft */ + (char *)&trueFlag, /* the cast is historical cruft */ sizeof(int)); /* length of option value */ //set send timeout @@ -293,11 +290,11 @@ void ParallelConnection::establishConnection(addrinfo *info){ (char *)&timeout, sizeof(timeout)); - result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(int)); + result = setsockopt(_clientSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&falseFlag, sizeof(int)); if (result == SOCKET_ERROR) LERROR("Failed to set socket option 'reuse address'. Error code: " << _ERRNO); - result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(int)); + result = setsockopt(_clientSocket, SOL_SOCKET, SO_KEEPALIVE, (char*)&trueFlag, sizeof(int)); if (result == SOCKET_ERROR) LERROR("Failed to set socket option 'keep alive'. Error code: " << _ERRNO); @@ -320,19 +317,25 @@ void ParallelConnection::establishConnection(addrinfo *info){ _isConnected.store(true); //start sending messages - _sendThread = new (std::nothrow) std::thread(&ParallelConnection::sendFunc, this); + _sendThread = std::make_unique(&ParallelConnection::sendFunc, this); //start listening for communication - _listenThread = new (std::nothrow) std::thread(&ParallelConnection::listenCommunication, this); + _listenThread = std::make_unique(&ParallelConnection::listenCommunication, this); //we no longer need to try to establish connection _tryConnect.store(false); + _sendBufferMutex.lock(); + _sendBuffer.clear(); + _sendBufferMutex.unlock(); + //send authentication sendAuthentication(); + } else { + LINFO("Connection attempt failed."); } -#ifdef __WIN32__ +#ifdef WIN32 //on windows: try to connect once per second std::this_thread::sleep_for(std::chrono::seconds(1)); #else @@ -353,52 +356,46 @@ void ParallelConnection::establishConnection(addrinfo *info){ freeaddrinfo(info); } -void ParallelConnection::sendAuthentication(){ +void ParallelConnection::sendAuthentication() { //length of this nodes name - uint16_t namelen = static_cast(_name.length()); + uint32_t nameLength = static_cast(_name.length()); - //total size of the buffer, header + size of passcodde + namelength + size of namelength - int size = headerSize() + sizeof(uint32_t) + sizeof(namelen) + static_cast(namelen); + //total size of the buffer: (passcode + namelength + name) + size_t size = 2 * sizeof(uint32_t) + nameLength; //create and reserve buffer std::vector buffer; buffer.reserve(size); - //write header to buffer - writeHeader(buffer, MessageTypes::Authentication); - //write passcode to buffer buffer.insert(buffer.end(), reinterpret_cast(&_passCode), reinterpret_cast(&_passCode) + sizeof(uint32_t)); //write the length of the nodes name to buffer - buffer.insert(buffer.end(), reinterpret_cast(&namelen), reinterpret_cast(&namelen) + sizeof(uint16_t)); + buffer.insert(buffer.end(), reinterpret_cast(&nameLength), reinterpret_cast(&nameLength) + sizeof(uint32_t)); - //write this nodes name to buffer + //write this node's name to buffer buffer.insert(buffer.end(), _name.begin(), _name.end()); //send buffer - queueMessage(buffer); + queueOutMessage(Message(MessageType::Authentication, buffer)); } -void ParallelConnection::delegateDecoding(uint32_t type){ - switch (type){ - case MessageTypes::Authentication: - //not used + +void ParallelConnection::queueInMessage(const Message& message) { + std::unique_lock unqlock(_receiveBufferMutex); + _receiveBuffer.push_back(message); +} + +void ParallelConnection::handleMessage(const Message& message) { + switch (message.type){ + case MessageType::Data: + dataMessageReceived(message.content); break; - case MessageTypes::Initialization: - initializationMessageReceived(); + case MessageType::ConnectionStatus: + connectionStatusMessageReceived(message.content); break; - case MessageTypes::Data: - dataMessageReceived(); - break; - case MessageTypes::Script: - //not used - break; - case MessageTypes::HostInfo: - hostInfoMessageReceived(); - break; - case MessageTypes::InitializationRequest: - initializationRequestMessageReceived(); + case MessageType::NConnections: + nConnectionsMessageReceived(message.content); break; default: //unknown message type @@ -406,6 +403,7 @@ void ParallelConnection::delegateDecoding(uint32_t type){ } } +/* void ParallelConnection::initializationMessageReceived(){ int result; @@ -476,116 +474,77 @@ void ParallelConnection::initializationMessageReceived(){ writeHeader(buffer, MessageTypes::InitializationCompleted); //let the server know - queueMessage(buffer); + std::cout << "initialization message recieved queue" << std::endl; + queueMessage(InitializationCompleted, buffer); //we also need to force a time jump just to ensure that the server and client are synced _initializationTimejumpRequired.store(true); } +*/ -void ParallelConnection::dataMessageReceived(){ - int result; - uint16_t msglen; - uint16_t type; +double ParallelConnection::calculateBufferedKeyframeTime(double originalTime) { + std::lock_guard latencyLock(_latencyMutex); - //create a buffer to hold the size of streamdata message - std::vector buffer; - buffer.resize(sizeof(type)); - - //read type of data message - result = receiveData(_clientSocket, buffer, sizeof(type), 0); - - if (result <= 0){ - //error - LERROR("Failed to read type of data message received."); - return; + double timeDiff = OsEng.runTime() - originalTime; + if (_latencyDiffs.size() == 0) { + _initialTimeDiff = timeDiff; } - + double latencyDiff = timeDiff - _initialTimeDiff; + if (_latencyDiffs.size() >= MaxLatencyDiffs) { + _latencyDiffs.pop_front(); + } + _latencyDiffs.push_back(latencyDiff); + + double accumulatedLatencyDiffSquared = 0; + double accumulatedLatencyDiff = 0; + for (double diff : _latencyDiffs) { + accumulatedLatencyDiff += diff; + accumulatedLatencyDiffSquared += diff*diff; + } + double expectedLatencyDiffSquared = accumulatedLatencyDiffSquared / _latencyDiffs.size(); + double expectedLatencyDiff = accumulatedLatencyDiff / _latencyDiffs.size(); + + // V(X) = E(x^2) - E(x)^2 + double latencyVariance = expectedLatencyDiffSquared - expectedLatencyDiff*expectedLatencyDiff; + double latencyStandardDeviation = std::sqrt(latencyVariance); + + double frametime = OsEng.windowWrapper().averageDeltaTime(); + + double latencyCompensation = std::max(expectedLatencyDiff + 2 * latencyStandardDeviation, latencyDiff); + + return originalTime + timeDiff + nBroadcastIntervalsBuffer * BroadcastIntervalMilliseconds / 1000 + latencyCompensation + nFrametimesBuffer * frametime; + +} + +void ParallelConnection::dataMessageReceived(const std::vector& messageContent) { + //the type of data message received - type =(*(reinterpret_cast(buffer.data()))); - - //read size of streamdata message - result = receiveData(_clientSocket, buffer, sizeof(msglen), 0); - - if (result <= 0){ - //error - LERROR("Failed to read size of data message received."); - return; - } - - //the size in bytes of the streamdata message - msglen = (*(reinterpret_cast(buffer.data()))); + uint32_t type = *(reinterpret_cast(messageContent.data())); + std::vector buffer(messageContent.begin() + sizeof(uint32_t), messageContent.end()); + + switch(static_cast(type)) { + case datamessagestructures::Type::CameraData: { - //resize the buffer to be able to read the streamdata - buffer.clear(); - buffer.resize(msglen); + datamessagestructures::CameraKeyframe kf(buffer); + kf._timestamp = calculateBufferedKeyframeTime(kf._timestamp); - //read the data into buffer - result = receiveData(_clientSocket, buffer, msglen, 0); - - if (result <= 0){ - //error - LERROR("Failed to read data message."); - return; - } - - //which type of data message was received? - switch(type){ - case network::datamessagestructures::PositionData:{ - //position data message - //create and read a position keyframe from the data buffer - network::datamessagestructures::PositionKeyframe kf; - kf.deserialize(buffer); - - //add the keyframe to the interaction handler OsEng.interactionHandler().addKeyframe(kf); break; } - case network::datamessagestructures::TimeData:{ - //time data message - //create and read a time keyframe from the data buffer - network::datamessagestructures::TimeKeyframe tf; - tf.deserialize(buffer); - - //lock mutex and assign latest time keyframe parameters - _timeKeyframeMutex.lock(); - - _latestTimeKeyframe._dt = tf._dt; - _latestTimeKeyframe._time = tf._time; - _latestTimeKeyframe._paused = tf._paused; - - //ensure that we never miss a timejump - //if last keyframe required a jump and that keyframe has not been used yet - if(_latestTimeKeyframe._requiresTimeJump && _latestTimeKeyframeValid){ - //do nothing to the boolean. Old value must be executed - }else{ - //either the latest keyframe didnt require a jump, or we have already spent that keyframe. - //in either case we can go ahead and write the bool value of newest frame - _latestTimeKeyframe._requiresTimeJump = tf._requiresTimeJump; - } - - //if we're just initialized we need to perform a time jump as soon as a valid keyframe has been received - if(_initializationTimejumpRequired.load() && _latestTimeKeyframeValid){ - _latestTimeKeyframe._requiresTimeJump = true; - _initializationTimejumpRequired.store(false); - } - - //unlock mutex - _timeKeyframeMutex.unlock(); - - //the keyframe is now valid for use - _latestTimeKeyframeValid.store(true); - + case datamessagestructures::Type::TimeData: { + + datamessagestructures::TimeKeyframe kf(buffer); + kf._timestamp = calculateBufferedKeyframeTime(kf._timestamp); + + OsEng.timeManager().addKeyframe(kf); break; } - case network::datamessagestructures::ScriptData:{ - //script data message - //create and read a script message from data buffer - network::datamessagestructures::ScriptMessage sm; + case datamessagestructures::Type::ScriptData: { + datamessagestructures::ScriptMessage sm; sm.deserialize(buffer); - - //Que script to be executed by script engine - OsEng.scriptEngine().queueScript(sm._script); + OsEng.scriptEngine().queueScript(sm._script, scripting::ScriptEngine::RemoteScripting::No); + break; } default:{ @@ -595,113 +554,152 @@ void ParallelConnection::dataMessageReceived(){ } } -void ParallelConnection::queueMessage(std::vector message){ - { - std::unique_lock unqlock(_sendBufferMutex); - _sendBuffer.push_back(message); - _sendCondition.notify_all(); - } +void ParallelConnection::queueOutDataMessage(const DataMessage& dataMessage) { + uint32_t dataMessageTypeOut = static_cast(dataMessage.type); + + std::vector messageContent; + messageContent.insert(messageContent.end(), + reinterpret_cast(&dataMessageTypeOut), + reinterpret_cast(&dataMessageTypeOut) + sizeof(uint32_t)); + + messageContent.insert(messageContent.end(), + dataMessage.content.begin(), + dataMessage.content.end()); + + queueOutMessage(Message(MessageType::Data, messageContent)); +} + +void ParallelConnection::queueOutMessage(const Message& message) { + std::unique_lock unqlock(_sendBufferMutex); + _sendBuffer.push_back(message); + _sendCondition.notify_all(); } void ParallelConnection::sendFunc(){ int result; //while we're connected - while(_isConnected.load()){ - { - //wait for signal then lock mutex and send first queued message - std::unique_lock unqlock(_sendBufferMutex); - _sendCondition.wait_for(unqlock, std::chrono::milliseconds(500)); - - if(!_sendBuffer.empty()){ - while(!_sendBuffer.empty()){ - result = send(_clientSocket, _sendBuffer.front().data(), _sendBuffer.front().size(), 0); - _sendBuffer.erase(_sendBuffer.begin()); - - //make sure everything went well - if (result == SOCKET_ERROR){ - //failed to send message - LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); - - //signal that a disconnect should be performed - signalDisconnect(); - } - } - } + while(_isConnected && !_disconnect) { + //wait for signal then lock mutex and send first queued message + std::unique_lock unqlock(_sendBufferMutex); + _sendCondition.wait(unqlock); + + if (_disconnect) { + break; + } + + while (!_sendBuffer.empty()) { + Message message = _sendBuffer.front(); + unqlock.unlock(); + std::vector header; + + //insert header into buffer + header.push_back('O'); + header.push_back('S'); + + uint32_t messageTypeOut = static_cast(message.type); + uint32_t messageSizeOut = static_cast(message.content.size()); + + + header.insert(header.end(), + reinterpret_cast(&ProtocolVersion), + reinterpret_cast(&ProtocolVersion) + sizeof(uint32_t)); + + header.insert(header.end(), + reinterpret_cast(&messageTypeOut), + reinterpret_cast(&messageTypeOut) + sizeof(uint32_t)); + + header.insert(header.end(), + reinterpret_cast(&messageSizeOut), + reinterpret_cast(&messageSizeOut) + sizeof(uint32_t)); + + result = send(_clientSocket, header.data(), header.size(), 0); + result = send(_clientSocket, message.content.data(), message.content.size(), 0); + + if (result == SOCKET_ERROR) { + LERROR("Failed to send message.\nError: " << _ERRNO << " detected in connection, disconnecting."); + signalDisconnect(); + } + + unqlock.lock(); + _sendBuffer.erase(_sendBuffer.begin()); } - } + std::lock_guard sendLock(_sendBufferMutex); + _sendBuffer.clear(); + } -void ParallelConnection::hostInfoMessageReceived(){ - //create buffer - std::vector hostflag; - //resize to hold a flag saying if we're host or not - hostflag.resize(1); - - //read data into buffer - int result = receiveData(_clientSocket, hostflag, 1, 0); +void ParallelConnection::connectionStatusMessageReceived(const std::vector& message) { + if (message.size() < 2 * sizeof(uint32_t)) { + LERROR("Malformed connection status message."); + return; + } + size_t pointer = 0; + uint32_t statusIn = *(reinterpret_cast(&message[pointer])); + ParallelConnection::Status status = static_cast(statusIn); + pointer += sizeof(uint32_t); - //enough data was read - if (result > 0){ - - //we've been assigned as host - if (hostflag.at(0) == 1){ - - //we're already host, do nothing (dummy check) - if (_isHost.load()){ - return; - } - else{ - - //we're the host - _isHost.store(true); + size_t hostNameSize = *(reinterpret_cast(&message[pointer])); + pointer += sizeof(uint32_t); - //start broadcasting - _broadcastThread = new (std::nothrow) std::thread(&ParallelConnection::broadcast, this); - } - } - else{ //we've been assigned as client - - //we were broadcasting but should stop now - if (_isHost.load()){ + if (hostNameSize > message.size() - pointer) { + LERROR("Malformed connection status message."); + return; + } - //stop broadcast loop - _isHost.store(false); - - //and delete broadcasting thread - if (_broadcastThread != nullptr){ - _broadcastThread->join(); - delete _broadcastThread; - _broadcastThread = nullptr; - } - } - else{ - //we were not broadcasting so nothing to do - } - - //clear buffered any keyframes - OsEng.interactionHandler().clearKeyframes(); - - //request init package from the host - int size = headerSize(); - std::vector buffer; - buffer.reserve(size); - - //write header - writeHeader(buffer, MessageTypes::InitializationRequest); + std::string hostName = ""; + if (hostNameSize > 0) { + hostName = std::string(&message[pointer], hostNameSize); + } + pointer += hostNameSize; + + if (status > Status::Host) { + LERROR("Invalid status."); + return; + } - //send message - queueMessage(buffer); + _latencyMutex.lock(); + _latencyDiffs.clear(); + _latencyMutex.unlock(); + setHostName(hostName); + + if (status == _status) { + //status remains unchanged. + return; + } + + setStatus(status); + + if (status == Status::Host) { // assigned as host + _broadcastThread = std::make_unique(&ParallelConnection::broadcast, this); + } else { // assigned as client + + // delete broadcasting thread + // (the thread is allowed to terminate once the status is set to non-host.) + if (_broadcastThread != nullptr) { + _broadcastThread->join(); + _broadcastThread = nullptr; } } - else{ - LERROR("Error " << _ERRNO << " detected in connection, disconnecting."); - signalDisconnect(); - } + OsEng.interactionHandler().clearKeyframes(); + OsEng.timeManager().clearKeyframes(); + } -void ParallelConnection::initializationRequestMessageReceived(){ - +void ParallelConnection::nConnectionsMessageReceived(const std::vector& message) { + if (message.size() < sizeof(uint32_t)) { + LERROR("Malformed host info message."); + return; + } + uint32_t nConnections = *(reinterpret_cast(&message[0])); + setNConnections(nConnections); +} + + + + +//void ParallelConnection::initializationRequestMessageReceived(const std::vector& message){ + /* //get current state as scripts std::vector scripts; std::map::iterator state_it; @@ -731,7 +729,7 @@ void ParallelConnection::initializationRequestMessageReceived(){ //serialize and encode all scripts into scriptbuffer std::vector::iterator script_it; - network::datamessagestructures::ScriptMessage sm; + datamessagestructures::ScriptMessage sm; for(script_it = scripts.begin(); script_it != scripts.end(); ++script_it){ @@ -767,56 +765,70 @@ void ParallelConnection::initializationRequestMessageReceived(){ buffer.insert(buffer.end(), scriptbuffer.begin(), scriptbuffer.end()); //queue message - queueMessage(buffer); -} + std::cout << "initializationRequest queue" << std::endl; + queueMessage(MessageType::Initialization, buffer); + */ +//} -void ParallelConnection::listenCommunication(){ +void ParallelConnection::listenCommunication() { + constexpr size_t headerSize = 2 * sizeof(char) + 3 * sizeof(uint32_t); + //create basic buffer for receiving first part of messages - std::vector buffer; - //size of the header - buffer.resize(headerSize()); - - int result; + std::vector headerBuffer(headerSize); + std::vector messageBuffer; + + int nBytesRead = 0; //while we're still connected - while (_isConnected.load()){ - //receive the first parts of a message - result = receiveData(_clientSocket, buffer, headerSize(), 0); + while (_isConnected.load()) { + //receive the header data + nBytesRead = receiveData(_clientSocket, headerBuffer, headerSize, 0); //if enough data was received - if (result > 0){ - - //make sure that header matches this version of OpenSpace - if (buffer[0] == 'O' && //Open - buffer[1] == 'S' && //Space - buffer[2] == OPENSPACE_VERSION_MAJOR && // major version - buffer[3] == OPENSPACE_VERSION_MINOR // minor version - ) - { - //parse type - uint32_t type = (*(reinterpret_cast(&buffer[4]))); - - //and delegate decoding depending on type - delegateDecoding(type); - } - else{ - LERROR("Error: Client OpenSpace version " << OPENSPACE_VERSION_MAJOR << ", " << OPENSPACE_VERSION_MINOR << " does not match server version " << buffer[2] <<", " << buffer[3] << std::endl << "Message not decoded."); + if (nBytesRead <= 0) { + if (!_disconnect) { + LERROR("Error " << _ERRNO << " detected in connection when reading header, disconnecting!"); + signalDisconnect(); } + break; } - else{ - if (result == 0){ - //connection rejected - LERROR("Parallel connection rejected, disconnecting..."); - } - else{ - LERROR("Error " << _ERRNO << " detected in connection, disconnecting!"); - } - //signal that a disconnect should be performed + //make sure that header matches this version of OpenSpace + if (!(headerBuffer[0] == 'O' && headerBuffer[1] && 'S')) { + LERROR("Expected to read message header 'OS' from socket."); signalDisconnect(); break; } + + uint32_t *ptr = reinterpret_cast(&headerBuffer[2]); + + uint32_t protocolVersionIn = *(ptr++); + uint32_t messageTypeIn = *(ptr++); + uint32_t messageSizeIn = *(ptr++); + + if (protocolVersionIn != ProtocolVersion) { + LERROR("Protocol versions do not match. Server version: " << protocolVersionIn << ", Client version: " << ProtocolVersion); + signalDisconnect(); + break; + } + + size_t messageSize = messageSizeIn; + + // receive the payload + messageBuffer.resize(messageSize); + nBytesRead = receiveData(_clientSocket, messageBuffer, messageSize, 0); + + if (nBytesRead <= 0) { + if (!_disconnect) { + LERROR("Error " << _ERRNO << " detected in connection when reading message, disconnecting!"); + signalDisconnect(); + } + break; + } + + //and delegate decoding depending on type + queueInMessage(Message(static_cast(messageTypeIn), messageBuffer)); } } @@ -853,24 +865,17 @@ void ParallelConnection::setName(const std::string& name){ _name = name; } -bool ParallelConnection::isHost(){ - return _isHost.load(); -} - + void ParallelConnection::requestHostship(const std::string &password){ std::vector buffer; - buffer.reserve(headerSize()); - uint32_t passcode = hash(password); - - //write header - writeHeader(buffer, MessageTypes::HostshipRequest); - - //write passcode buffer.insert(buffer.end(), reinterpret_cast(&passcode), reinterpret_cast(&passcode) + sizeof(uint32_t)); - - //send message - queueMessage(buffer); + queueOutMessage(Message(MessageType::HostshipRequest, buffer)); +} + +void ParallelConnection::resignHostship() { + std::vector buffer; + queueOutMessage(Message(MessageType::HostshipResignation, buffer)); } void ParallelConnection::setPassword(const std::string& pwd){ @@ -878,7 +883,7 @@ void ParallelConnection::setPassword(const std::string& pwd){ } bool ParallelConnection::initNetworkAPI(){ - #if defined(__WIN32__) + #if defined(WIN32) WSADATA wsaData; WORD version; int error; @@ -903,203 +908,148 @@ bool ParallelConnection::initNetworkAPI(){ return true; } +void ParallelConnection::sendScript(const std::string& script) { + if (!isHost()) return; + + datamessagestructures::ScriptMessage sm; + sm._script = script; + + std::vector buffer; + sm.serialize(buffer); + + queueOutDataMessage(DataMessage(datamessagestructures::Type::ScriptData, buffer)); +} + void ParallelConnection::preSynchronization(){ - - //if we're the host - if(_isHost){ - //get current time parameters and create a keyframe - network::datamessagestructures::TimeKeyframe tf; - tf._dt = Time::ref().deltaTime(); - tf._paused = Time::ref().paused(); - tf._requiresTimeJump = Time::ref().timeJumped(); - tf._time = Time::ref().j2000Seconds(); - - //create a buffer and serialize message - std::vector tbuffer; - tf.serialize(tbuffer); - - //get the size of the keyframebuffer - uint16_t msglen = static_cast(tbuffer.size()); - - //the type of data message - uint16_t type = static_cast(network::datamessagestructures::TimeData); - - //create the full buffer - std::vector buffer; - buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Data); - - //type of message - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), tbuffer.begin(), tbuffer.end()); - - //send message - queueMessage(buffer); + + std::unique_lock unqlock(_receiveBufferMutex); + while (!_receiveBuffer.empty()) { + Message& message = _receiveBuffer.front(); + handleMessage(message); + _receiveBuffer.pop_front(); } - else{ - //if we're not the host and we have a valid keyframe (one that hasnt been used before) - if(_latestTimeKeyframeValid.load()){ - - //lock mutex and retrieve parameters from latest keyframe - _timeKeyframeMutex.lock(); - - double dt = _latestTimeKeyframe._dt; - double time = _latestTimeKeyframe._time; - bool jump = _latestTimeKeyframe._requiresTimeJump; - bool paused = _latestTimeKeyframe._paused; - - _timeKeyframeMutex.unlock(); - - //this keyframe is now spent - _latestTimeKeyframeValid.store(false); - - //assign latest params - Time::ref().setDeltaTime(dt); - Time::ref().setTime(time, jump); - Time::ref().setPause(paused); - + + if (status() == Status::Host) { + if (Time::ref().timeJumped()) { + _timeJumped = true; } } } - -void ParallelConnection::scriptMessage(const std::string propIdentifier, const std::string propValue){ - - //save script as current state - { - //mutex protect - std::lock_guard lock(_currentStateMutex); - _currentState[propIdentifier] = propValue; - } - - //if we're connected and we're the host, also send the script - if(_isConnected.load() && _isHost.load()){ - //construct script - std::string script = scriptFromPropertyAndValue(propIdentifier, propValue); - - //create a script message - network::datamessagestructures::ScriptMessage sm; - sm._script = script; - sm._scriptlen = static_cast(script.length()); - - //create a buffer for the script - std::vector sbuffer; - - //fill the script buffer - sm.serialize(sbuffer); - - //get the size of the keyframebuffer - uint16_t msglen = static_cast(sbuffer.size()); - - //the type of message - uint16_t type = static_cast(network::datamessagestructures::ScriptData); - - //create the full buffer - std::vector buffer; - buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Data); - - //type of message - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), sbuffer.begin(), sbuffer.end()); - - //send message - queueMessage(buffer); + +void ParallelConnection::setStatus(Status status) { + if (_status != status) { + _status = status; + _connectionEvent->publish("statusChanged"); } +} +ParallelConnection::Status ParallelConnection::status() { + return _status; } - -std::string ParallelConnection::scriptFromPropertyAndValue(const std::string property, const std::string value){ - //consruct script - std::string script = "openspace.setPropertyValue(\"" + property + "\"," + value + ");"; - return script; + +void ParallelConnection::setNConnections(size_t nConnections) { + if (_nConnections != nConnections) { + _nConnections = nConnections; + _connectionEvent->publish("nConnectionsChanged"); + } } - + +size_t ParallelConnection::nConnections() { + return _nConnections; +} + +bool ParallelConnection::isHost() { + return _status == Status::Host; +} + +void ParallelConnection::setHostName(const std::string& hostName) { + if (_hostName != hostName) { + _hostName = hostName; + _connectionEvent->publish("hostNameChanged"); + } +} + +const std::string& ParallelConnection::hostName() { + return _hostName; +} + +void ParallelConnection::sendCameraKeyframe() { + //create a keyframe with current position and orientation of camera + datamessagestructures::CameraKeyframe kf; + kf._position = OsEng.interactionHandler().camera()->positionVec3(); + kf._rotation = OsEng.interactionHandler().camera()->rotationQuaternion(); + + //timestamp as current runtime of OpenSpace instance + kf._timestamp = OsEng.runTime(); + + //create a buffer for the keyframe + std::vector buffer; + + //fill the keyframe buffer + kf.serialize(buffer); + + //send message + queueOutDataMessage(DataMessage(datamessagestructures::Type::CameraData, buffer)); +} + +void ParallelConnection::sendTimeKeyframe() { + //create a keyframe with current position and orientation of camera + datamessagestructures::TimeKeyframe kf; + + kf._dt = Time::ref().deltaTime(); + kf._paused = Time::ref().paused(); + kf._requiresTimeJump = _timeJumped; + kf._time = Time::ref().j2000Seconds(); + + //timestamp as current runtime of OpenSpace instance + kf._timestamp = OsEng.runTime(); + + //create a buffer for the keyframe + std::vector buffer; + + //fill the keyframe buffer + kf.serialize(buffer); + + //send message + queueOutDataMessage(DataMessage(datamessagestructures::Type::TimeData, buffer)); + _timeJumped = false; +} + + void ParallelConnection::broadcast(){ - + _timeJumped = true; //while we're still connected and we're the host - while (_isConnected.load() && _isHost.load()){ - - //create a keyframe with current position and orientation of camera - network::datamessagestructures::PositionKeyframe kf; - kf._position = OsEng.interactionHandler().camera()->position(); - kf._viewRotationQuat = glm::quat_cast(glm::mat4(OsEng.interactionHandler().camera()->viewRotationMatrix())); - - //timestamp as current runtime of OpenSpace instance - kf._timeStamp = OsEng.runTime(); - - //create a buffer for the keyframe - std::vector kfBuffer; - - //fill the keyframe buffer - kf.serialize(kfBuffer); - - //get the size of the keyframebuffer - uint16_t msglen = static_cast(kfBuffer.size()); - - //the type of message - uint16_t type = static_cast(network::datamessagestructures::PositionData); - - //create the full buffer - std::vector buffer; - buffer.reserve(headerSize() + sizeof(type) + sizeof(msglen) + msglen); - - //write header - writeHeader(buffer, MessageTypes::Data); - - //type of message - buffer.insert(buffer.end(), reinterpret_cast(&type), reinterpret_cast(&type) + sizeof(type)); - - //size of message - buffer.insert(buffer.end(), reinterpret_cast(&msglen), reinterpret_cast(&msglen) + sizeof(msglen)); - - //actual message - buffer.insert(buffer.end(), kfBuffer.begin(), kfBuffer.end()); - - //send message - queueMessage(buffer); + while (_isConnected && isHost()) { + sendCameraKeyframe(); + sendTimeKeyframe(); //100 ms sleep - send keyframes 10 times per second - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(BroadcastIntervalMilliseconds))); } } - -void ParallelConnection::writeHeader(std::vector &buffer, uint32_t messageType){ - //make sure the buffer is large enough to hold at least the header - if(buffer.size() < headerSize()){ - buffer.reserve(headerSize()); - } - - //get the current running version of openspace - uint8_t versionMajor = static_cast(OPENSPACE_VERSION_MAJOR); - uint8_t versionMinor = static_cast(OPENSPACE_VERSION_MINOR); - - //insert header into buffer - buffer.insert(buffer.end(), 'O'); - buffer.insert(buffer.end(), 'S'); - buffer.insert(buffer.end(), versionMajor); - buffer.insert(buffer.end(), versionMinor); - buffer.insert(buffer.end(), reinterpret_cast(&messageType), reinterpret_cast(&messageType) + sizeof(messageType)); -} - -int ParallelConnection::headerSize(){ - //minor and major version (as uint8_t -> 1 byte) + two bytes for the chars 'O' and 'S' + 4 bytes for type of message - return 2 * sizeof(uint8_t) + 2 + sizeof(uint32_t); -} +uint32_t ParallelConnection::hash(const std::string &val) { + uint32_t hashVal = 0, i; + size_t len = val.length(); + + for (hashVal = i = 0; i < len; ++i) { + hashVal += val.c_str()[i]; + hashVal += (hashVal << 10); + hashVal ^= (hashVal >> 6); + } + + hashVal += (hashVal << 3); + hashVal ^= (hashVal >> 11); + hashVal += (hashVal << 15); + + return hashVal; +}; + + +std::shared_ptr> ParallelConnection::connectionEvent() { + return _connectionEvent; +} + scripting::LuaLibrary ParallelConnection::luaLibrary() { return { "parallel", @@ -1146,10 +1096,14 @@ scripting::LuaLibrary ParallelConnection::luaLibrary() { "string", "Request to be the host for this session" }, + { + "resignHostship", + &luascriptfunctions::resignHostship, + "", + "Resign hostship" + }, } }; } - -} // namespace network - + } // namespace openspace diff --git a/src/network/parallelconnection_lua.inl b/src/network/parallelconnection_lua.inl index 59ef2b3730..13b3b4bcf8 100644 --- a/src/network/parallelconnection_lua.inl +++ b/src/network/parallelconnection_lua.inl @@ -184,6 +184,17 @@ int requestHostship(lua_State* L) { return 0; } +int resignHostship(lua_State* L) { + + int nArguments = lua_gettop(L); + if (nArguments != 0) + return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + if (OsEng.isMaster()) { + OsEng.parallelConnection().resignHostship(); + } + return 0; +} + } // namespace luascriptfunctions } // namespace openspace diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 25c0ffb798..8ca2af7cd7 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -24,10 +24,12 @@ #include +#include + #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED #include -#include #endif +#include #include #include @@ -81,13 +83,8 @@ #include #include -#include #include -// These are temporary ---abock -#include -#include - // ABuffer defines #define RENDERER_FRAMEBUFFER 0 #define RENDERER_ABUFFER 1 @@ -155,9 +152,7 @@ bool RenderEngine::deinitialize() { screenspacerenderable->deinitialize(); } -#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED MissionManager::deinitialize(); -#endif _sceneGraph->clearSceneGraph(); return true; @@ -228,10 +223,7 @@ bool RenderEngine::initialize() { ghoul::io::TextureReader::ref().addReader(std::make_shared()); -#ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED MissionManager::initialize(); -#endif - return true; } @@ -378,9 +370,12 @@ void RenderEngine::updateRenderer() { bool windowResized = OsEng.windowWrapper().windowHasResized(); if (windowResized) { - glm::ivec2 res = OsEng.windowWrapper().currentDrawBufferResolution(); + glm::ivec2 res = OsEng.windowWrapper().currentWindowSize(); + //glm::ivec2 res = OsEng.windowWrapper().currentDrawBufferResolution(); _renderer->setResolution(res); - ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize(glm::vec2(res)); + ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize( + glm::vec2(res) + ); } _renderer->update(); @@ -714,7 +709,7 @@ void RenderEngine::setNAaSamples(int nAaSamples) { } scripting::LuaLibrary RenderEngine::luaLibrary() { - return{ + return { "", { { @@ -759,23 +754,20 @@ scripting::LuaLibrary RenderEngine::luaLibrary() { "toggleFade", &luascriptfunctions::toggleFade, "number", - "Toggles fading in or out", - true + "Toggles fading in or out" }, { "fadeIn", &luascriptfunctions::fadeIn, "number", - "", - true + "" }, //also temporary @JK { "fadeOut", &luascriptfunctions::fadeOut, "number", - "", - true + "" }, { "registerScreenSpaceRenderable", @@ -814,391 +806,391 @@ performance::PerformanceManager* RenderEngine::performanceManager() { // This method is temporary and will be removed once the scalegraph is in effect ---abock void RenderEngine::changeViewPoint(std::string origin) { - SceneGraphNode* solarSystemBarycenterNode = scene()->sceneGraphNode("SolarSystemBarycenter"); - SceneGraphNode* plutoBarycenterNode = scene()->sceneGraphNode("PlutoBarycenter"); - SceneGraphNode* newHorizonsNode = scene()->sceneGraphNode("NewHorizons"); - SceneGraphNode* newHorizonsPathNodeJ = scene()->sceneGraphNode("NewHorizonsPathJupiter"); - SceneGraphNode* newHorizonsPathNodeP = scene()->sceneGraphNode("NewHorizonsPathPluto"); -// SceneGraphNode* cg67pNode = scene()->sceneGraphNode("67P"); -// SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); - - RenderablePath* nhPath; - - SceneGraphNode* jupiterBarycenterNode = scene()->sceneGraphNode("JupiterBarycenter"); - - //SceneGraphNode* newHorizonsGhostNode = scene()->sceneGraphNode("NewHorizonsGhost"); - //SceneGraphNode* dawnNode = scene()->sceneGraphNode("Dawn"); - //SceneGraphNode* vestaNode = scene()->sceneGraphNode("Vesta"); - - // if (solarSystemBarycenterNode == nullptr || plutoBarycenterNode == nullptr || - //jupiterBarycenterNode == nullptr) { - // LERROR("Necessary nodes does not exist"); - //return; - // } - - if (origin == "Pluto") { - if (newHorizonsPathNodeP) { - Renderable* R = newHorizonsPathNodeP->renderable(); - newHorizonsPathNodeP->setParent(plutoBarycenterNode); - nhPath = static_cast(R); - nhPath->calculatePath("PLUTO BARYCENTER"); - } - - plutoBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); - plutoBarycenterNode->setEphemeris(new StaticEphemeris); - - solarSystemBarycenterNode->setParent(plutoBarycenterNode); - newHorizonsNode->setParent(plutoBarycenterNode); - //newHorizonsGhostNode->setParent(plutoBarycenterNode); - - //dawnNode->setParent(plutoBarycenterNode); - //vestaNode->setParent(plutoBarycenterNode); - - //newHorizonsTrailNode->setParent(plutoBarycenterNode); - ghoul::Dictionary solarDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("SUN") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - ghoul::Dictionary jupiterDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("JUPITER BARYCENTER") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - ghoul::Dictionary newHorizonsDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("NEW HORIZONS") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); - jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); - newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - - - //ghoul::Dictionary dawnDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("DAWN") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); - // - //ghoul::Dictionary vestaDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("VESTA") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); - - - //ghoul::Dictionary newHorizonsGhostDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("NEW HORIZONS") }, - // { std::string("EphmerisGhosting"), std::string("TRUE") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); - - return; - } - if (origin == "Sun") { - solarSystemBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); - - if (plutoBarycenterNode) - plutoBarycenterNode->setParent(solarSystemBarycenterNode); - jupiterBarycenterNode->setParent(solarSystemBarycenterNode); - if (newHorizonsNode) - newHorizonsNode->setParent(solarSystemBarycenterNode); - //newHorizonsGhostNode->setParent(solarSystemBarycenterNode); - - //newHorizonsTrailNode->setParent(solarSystemBarycenterNode); - //dawnNode->setParent(solarSystemBarycenterNode); - //vestaNode->setParent(solarSystemBarycenterNode); - - ghoul::Dictionary plutoDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("PLUTO BARYCENTER") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("SUN") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - ghoul::Dictionary jupiterDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("JUPITER BARYCENTER") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("SUN") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - solarSystemBarycenterNode->setEphemeris(new StaticEphemeris); - jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); - if (plutoBarycenterNode) - plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); - - ghoul::Dictionary newHorizonsDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("NEW HORIZONS") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("SUN") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - if (newHorizonsNode) - newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - - - //ghoul::Dictionary dawnDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("DAWN") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("SUN") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); - // - //ghoul::Dictionary vestaDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("VESTA") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("SUN") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); - - - //ghoul::Dictionary newHorizonsGhostDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("NEW HORIZONS") }, - // { std::string("EphmerisGhosting"), std::string("TRUE") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); - - return; - } - if (origin == "Jupiter") { - if (newHorizonsPathNodeJ) { - Renderable* R = newHorizonsPathNodeJ->renderable(); - newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); - nhPath = static_cast(R); - nhPath->calculatePath("JUPITER BARYCENTER"); - } - - jupiterBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); - jupiterBarycenterNode->setEphemeris(new StaticEphemeris); - - solarSystemBarycenterNode->setParent(jupiterBarycenterNode); - if (newHorizonsNode) - newHorizonsNode->setParent(jupiterBarycenterNode); - //newHorizonsTrailNode->setParent(jupiterBarycenterNode); - - //dawnNode->setParent(jupiterBarycenterNode); - //vestaNode->setParent(jupiterBarycenterNode); - - - ghoul::Dictionary solarDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("SUN") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - ghoul::Dictionary plutoDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("PlUTO BARYCENTER") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - ghoul::Dictionary newHorizonsDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("NEW HORIZONS") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); - if (plutoBarycenterNode) - plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); - if (newHorizonsNode) - newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - //newHorizonsGhostNode->setParent(jupiterBarycenterNode); - //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - - - //ghoul::Dictionary dawnDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("DAWN") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); - // - //ghoul::Dictionary vestaDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("VESTA") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); - - - - //ghoul::Dictionary newHorizonsGhostDictionary = - //{ - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("NEW HORIZONS") }, - // { std::string("EphmerisGhosting"), std::string("TRUE") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, - // { std::string("Kernels"), ghoul::Dictionary() } - //}; - //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); - //newHorizonsGhostNode->setParent(jupiterBarycenterNode); - - - return; - } - //if (origin == "Vesta") { - // - // vestaNode->setParent(scene()->sceneGraphNode("SolarSystem")); - // vestaNode->setEphemeris(new StaticEphemeris); - // - // solarSystemBarycenterNode->setParent(vestaNode); - // newHorizonsNode->setParent(vestaNode); - // - // dawnNode->setParent(vestaNode); - // plutoBarycenterNode->setParent(vestaNode); - // - // - // ghoul::Dictionary plutoDictionary = - // { - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("PLUTO BARYCENTER") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("VESTA") }, - // { std::string("Kernels"), ghoul::Dictionary() } - // }; - // ghoul::Dictionary solarDictionary = - // { - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("SUN") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("VESTA") }, - // { std::string("Kernels"), ghoul::Dictionary() } - // }; - // - // ghoul::Dictionary jupiterDictionary = - // { - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("JUPITER BARYCENTER") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("VESTA") }, - // { std::string("Kernels"), ghoul::Dictionary() } - // }; - // - // solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); - // plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); - // jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); - // - // ghoul::Dictionary newHorizonsDictionary = - // { - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("NEW HORIZONS") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("VESTA") }, - // { std::string("Kernels"), ghoul::Dictionary() } - // }; - // newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); - // - // ghoul::Dictionary dawnDictionary = - // { - // { std::string("Type"), std::string("Spice") }, - // { std::string("Body"), std::string("DAWN") }, - // { std::string("Reference"), std::string("GALACTIC") }, - // { std::string("Observer"), std::string("VESTA") }, - // { std::string("Kernels"), ghoul::Dictionary() } - // }; - // dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); - // vestaNode->setEphemeris(new StaticEphemeris); - // - // return; - //} - - if (origin == "67P") { - SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); - SceneGraphNode* cgNode = scene()->sceneGraphNode("67P"); - //jupiterBarycenterNode->setParent(solarSystemBarycenterNode); - //plutoBarycenterNode->setParent(solarSystemBarycenterNode); - solarSystemBarycenterNode->setParent(cgNode); - rosettaNode->setParent(cgNode); - - ghoul::Dictionary solarDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("SUN") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); - - ghoul::Dictionary rosettaDictionary = - { - { std::string("Type"), std::string("Spice") }, - { std::string("Body"), std::string("ROSETTA") }, - { std::string("Reference"), std::string("GALACTIC") }, - { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, - { std::string("Kernels"), ghoul::Dictionary() } - }; - - cgNode->setParent(scene()->sceneGraphNode("SolarSystem")); - rosettaNode->setEphemeris(new SpiceEphemeris(rosettaDictionary)); - cgNode->setEphemeris(new StaticEphemeris); - - return; - - } - - LFATAL("This function is being misused with an argument of '" << origin << "'"); +// SceneGraphNode* solarSystemBarycenterNode = scene()->sceneGraphNode("SolarSystemBarycenter"); +// SceneGraphNode* plutoBarycenterNode = scene()->sceneGraphNode("PlutoBarycenter"); +// SceneGraphNode* newHorizonsNode = scene()->sceneGraphNode("NewHorizons"); +// SceneGraphNode* newHorizonsPathNodeJ = scene()->sceneGraphNode("NewHorizonsPathJupiter"); +// SceneGraphNode* newHorizonsPathNodeP = scene()->sceneGraphNode("NewHorizonsPathPluto"); +//// SceneGraphNode* cg67pNode = scene()->sceneGraphNode("67P"); +//// SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); +// +// RenderablePath* nhPath; +// +// SceneGraphNode* jupiterBarycenterNode = scene()->sceneGraphNode("JupiterBarycenter"); +// +// //SceneGraphNode* newHorizonsGhostNode = scene()->sceneGraphNode("NewHorizonsGhost"); +// //SceneGraphNode* dawnNode = scene()->sceneGraphNode("Dawn"); +// //SceneGraphNode* vestaNode = scene()->sceneGraphNode("Vesta"); +// +// // if (solarSystemBarycenterNode == nullptr || plutoBarycenterNode == nullptr || +// //jupiterBarycenterNode == nullptr) { +// // LERROR("Necessary nodes does not exist"); +// //return; +// // } +// +// if (origin == "Pluto") { +// if (newHorizonsPathNodeP) { +// Renderable* R = newHorizonsPathNodeP->renderable(); +// newHorizonsPathNodeP->setParent(plutoBarycenterNode); +// nhPath = static_cast(R); +// nhPath->calculatePath("PLUTO BARYCENTER"); +// } +// +// plutoBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); +// plutoBarycenterNode->setEphemeris(new StaticEphemeris); +// +// solarSystemBarycenterNode->setParent(plutoBarycenterNode); +// newHorizonsNode->setParent(plutoBarycenterNode); +// //newHorizonsGhostNode->setParent(plutoBarycenterNode); +// +// //dawnNode->setParent(plutoBarycenterNode); +// //vestaNode->setParent(plutoBarycenterNode); +// +// //newHorizonsTrailNode->setParent(plutoBarycenterNode); +// ghoul::Dictionary solarDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("SUN") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// ghoul::Dictionary jupiterDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("JUPITER BARYCENTER") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// ghoul::Dictionary newHorizonsDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("NEW HORIZONS") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); +// jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); +// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// +// +// //ghoul::Dictionary dawnDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("DAWN") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); +// // +// //ghoul::Dictionary vestaDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("VESTA") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); +// +// +// //ghoul::Dictionary newHorizonsGhostDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("NEW HORIZONS") }, +// // { std::string("EphmerisGhosting"), std::string("TRUE") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); +// +// return; +// } +// if (origin == "Sun") { +// solarSystemBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); +// +// if (plutoBarycenterNode) +// plutoBarycenterNode->setParent(solarSystemBarycenterNode); +// jupiterBarycenterNode->setParent(solarSystemBarycenterNode); +// if (newHorizonsNode) +// newHorizonsNode->setParent(solarSystemBarycenterNode); +// //newHorizonsGhostNode->setParent(solarSystemBarycenterNode); +// +// //newHorizonsTrailNode->setParent(solarSystemBarycenterNode); +// //dawnNode->setParent(solarSystemBarycenterNode); +// //vestaNode->setParent(solarSystemBarycenterNode); +// +// ghoul::Dictionary plutoDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("PLUTO BARYCENTER") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("SUN") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// ghoul::Dictionary jupiterDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("JUPITER BARYCENTER") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("SUN") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// solarSystemBarycenterNode->setEphemeris(new StaticEphemeris); +// jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); +// if (plutoBarycenterNode) +// plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); +// +// ghoul::Dictionary newHorizonsDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("NEW HORIZONS") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("SUN") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// if (newHorizonsNode) +// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// +// +// //ghoul::Dictionary dawnDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("DAWN") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("SUN") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); +// // +// //ghoul::Dictionary vestaDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("VESTA") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("SUN") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); +// +// +// //ghoul::Dictionary newHorizonsGhostDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("NEW HORIZONS") }, +// // { std::string("EphmerisGhosting"), std::string("TRUE") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); +// +// return; +// } +// if (origin == "Jupiter") { +// if (newHorizonsPathNodeJ) { +// Renderable* R = newHorizonsPathNodeJ->renderable(); +// newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); +// nhPath = static_cast(R); +// nhPath->calculatePath("JUPITER BARYCENTER"); +// } +// +// jupiterBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); +// jupiterBarycenterNode->setEphemeris(new StaticEphemeris); +// +// solarSystemBarycenterNode->setParent(jupiterBarycenterNode); +// if (newHorizonsNode) +// newHorizonsNode->setParent(jupiterBarycenterNode); +// //newHorizonsTrailNode->setParent(jupiterBarycenterNode); +// +// //dawnNode->setParent(jupiterBarycenterNode); +// //vestaNode->setParent(jupiterBarycenterNode); +// +// +// ghoul::Dictionary solarDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("SUN") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// ghoul::Dictionary plutoDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("PlUTO BARYCENTER") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// ghoul::Dictionary newHorizonsDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("NEW HORIZONS") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); +// if (plutoBarycenterNode) +// plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); +// if (newHorizonsNode) +// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// //newHorizonsGhostNode->setParent(jupiterBarycenterNode); +// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// +// +// //ghoul::Dictionary dawnDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("DAWN") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); +// // +// //ghoul::Dictionary vestaDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("VESTA") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); +// +// +// +// //ghoul::Dictionary newHorizonsGhostDictionary = +// //{ +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("NEW HORIZONS") }, +// // { std::string("EphmerisGhosting"), std::string("TRUE") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// //}; +// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); +// //newHorizonsGhostNode->setParent(jupiterBarycenterNode); +// +// +// return; +// } +// //if (origin == "Vesta") { +// // +// // vestaNode->setParent(scene()->sceneGraphNode("SolarSystem")); +// // vestaNode->setEphemeris(new StaticEphemeris); +// // +// // solarSystemBarycenterNode->setParent(vestaNode); +// // newHorizonsNode->setParent(vestaNode); +// // +// // dawnNode->setParent(vestaNode); +// // plutoBarycenterNode->setParent(vestaNode); +// // +// // +// // ghoul::Dictionary plutoDictionary = +// // { +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("PLUTO BARYCENTER") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("VESTA") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// // }; +// // ghoul::Dictionary solarDictionary = +// // { +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("SUN") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("VESTA") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// // }; +// // +// // ghoul::Dictionary jupiterDictionary = +// // { +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("JUPITER BARYCENTER") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("VESTA") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// // }; +// // +// // solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); +// // plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); +// // jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); +// // +// // ghoul::Dictionary newHorizonsDictionary = +// // { +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("NEW HORIZONS") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("VESTA") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// // }; +// // newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); +// // +// // ghoul::Dictionary dawnDictionary = +// // { +// // { std::string("Type"), std::string("Spice") }, +// // { std::string("Body"), std::string("DAWN") }, +// // { std::string("Reference"), std::string("GALACTIC") }, +// // { std::string("Observer"), std::string("VESTA") }, +// // { std::string("Kernels"), ghoul::Dictionary() } +// // }; +// // dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); +// // vestaNode->setEphemeris(new StaticEphemeris); +// // +// // return; +// //} +// +// if (origin == "67P") { +// SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); +// SceneGraphNode* cgNode = scene()->sceneGraphNode("67P"); +// //jupiterBarycenterNode->setParent(solarSystemBarycenterNode); +// //plutoBarycenterNode->setParent(solarSystemBarycenterNode); +// solarSystemBarycenterNode->setParent(cgNode); +// rosettaNode->setParent(cgNode); +// +// ghoul::Dictionary solarDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("SUN") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); +// +// ghoul::Dictionary rosettaDictionary = +// { +// { std::string("Type"), std::string("Spice") }, +// { std::string("Body"), std::string("ROSETTA") }, +// { std::string("Reference"), std::string("GALACTIC") }, +// { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, +// { std::string("Kernels"), ghoul::Dictionary() } +// }; +// +// cgNode->setParent(scene()->sceneGraphNode("SolarSystem")); +// rosettaNode->setEphemeris(new SpiceEphemeris(rosettaDictionary)); +// cgNode->setEphemeris(new StaticEphemeris); +// +// return; +// +// } +// +// LFATAL("This function is being misused with an argument of '" << origin << "'"); } void RenderEngine::setShowFrameNumber(bool enabled){ @@ -1335,6 +1327,46 @@ void RenderEngine::renderInformation() { break; } + ParallelConnection::Status status = OsEng.parallelConnection().status(); + size_t nConnections = OsEng.parallelConnection().nConnections(); + const std::string& hostName = OsEng.parallelConnection().hostName(); + + std::string connectionInfo = ""; + int nClients = nConnections; + if (status == ParallelConnection::Status::Host) { + nClients--; + if (nClients == 1) { + connectionInfo = "Hosting session with 1 client"; + } else { + connectionInfo = "Hosting session with " + std::to_string(nClients) + " clients"; + } + } else if (status == ParallelConnection::Status::ClientWithHost) { + nClients--; + connectionInfo = "Session hosted by '" + hostName + "'"; + } else if (status == ParallelConnection::Status::ClientWithoutHost) { + connectionInfo = "Host is disconnected"; + } + + if (status == ParallelConnection::Status::ClientWithHost || + status == ParallelConnection::Status::ClientWithoutHost) { + connectionInfo += "\n"; + if (nClients > 2) { + connectionInfo += "You and " + std::to_string(nClients - 1) + " more clients are tuned in"; + } else if (nClients == 2) { + connectionInfo += "You and " + std::to_string(nClients - 1) + " more client are tuned in"; + } else if (nClients == 1) { + connectionInfo += "You are the only client"; + } + } + + if (connectionInfo != "") { + RenderFontCr(*_fontInfo, + penPosition, + connectionInfo.c_str() + ); + } + + #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED //<<<<<<< HEAD bool hasNewHorizons = scene()->sceneGraphNode("NewHorizons"); @@ -1364,13 +1396,14 @@ void RenderEngine::renderInformation() { // Add spacing RenderFontCr(*_fontInfo, penPosition, nonCurrentMissionColor, " "); - std::list phaseTrace = mission.phaseTrace(currentTime); + auto phaseTrace = mission.phaseTrace(currentTime); if (phaseTrace.size()) { - std::string title = "Current Mission Phase: " + phaseTrace.back()->name(); + const MissionPhase& phase = phaseTrace.back().get(); + std::string title = "Current Mission Phase: " + phase.name(); RenderFontCr(*_fontInfo, penPosition, missionProgressColor, title.c_str()); - double remaining = phaseTrace.back()->timeRange().end - currentTime; - float t = static_cast(1.0 - remaining / phaseTrace.back()->timeRange().duration()); + double remaining = phase.timeRange().end - currentTime; + float t = static_cast(1.0 - remaining / phase.timeRange().duration()); std::string progress = progressToStr(25, t); //RenderFontCr(*_fontInfo, penPosition, missionProgressColor, // "%.0f s %s %.1f %%", remaining, progress.c_str(), t * 100); @@ -1417,7 +1450,7 @@ void RenderEngine::renderInformation() { // last-in-first-out from the stack, so add them in reversed order. int indexLastPhase = phase->phases().size() - 1; for (int i = indexLastPhase; 0 <= i; --i) { - S.push({ &phase->phase(i), depth + 1 }); + S.push({ &phase->phases()[i], depth + 1 }); } } } diff --git a/src/rendering/renderengine_lua.inl b/src/rendering/renderengine_lua.inl index e6aa0781b8..de2aa3b519 100644 --- a/src/rendering/renderengine_lua.inl +++ b/src/rendering/renderengine_lua.inl @@ -43,7 +43,7 @@ int takeScreenshot(lua_State* L) { return 0; } else { - return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments); + return luaL_error(L, "Expected %i or %i arguments, got %i", 0, 1, nArguments); } } diff --git a/src/rendering/screenspacerenderable.cpp b/src/rendering/screenspacerenderable.cpp index c95734a2b9..a48ed62025 100644 --- a/src/rendering/screenspacerenderable.cpp +++ b/src/rendering/screenspacerenderable.cpp @@ -159,7 +159,7 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary _delete.onChange([this](){ std::string script = "openspace.unregisterScreenSpaceRenderable('" + name() + "');"; - OsEng.scriptEngine().queueScript(script); + OsEng.scriptEngine().queueScript(script, scripting::ScriptEngine::RemoteScripting::Yes); }); } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index f016dd2159..3fef4d209b 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -24,8 +24,10 @@ #include +#include #include #include +#include #include #include #include @@ -67,6 +69,14 @@ namespace { const std::string KeyFocusObject = "Focus"; const std::string KeyPositionObject = "Position"; const std::string KeyViewOffset = "Offset"; + + const std::string MainTemplateFilename = "${OPENSPACE_DATA}/web/properties/main.hbs"; + const std::string PropertyOwnerTemplateFilename = "${OPENSPACE_DATA}/web/properties/propertyowner.hbs"; + const std::string PropertyTemplateFilename = "${OPENSPACE_DATA}/web/properties/property.hbs"; + const std::string HandlebarsFilename = "${OPENSPACE_DATA}/web/common/handlebars-v4.0.5.js"; + const std::string JsFilename = "${OPENSPACE_DATA}/web/properties/script.js"; + const std::string BootstrapFilename = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssFilename = "${OPENSPACE_DATA}/web/common/style.css"; } namespace openspace { @@ -182,11 +192,15 @@ void Scene::clearSceneGraph() { bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { ghoul::Dictionary dictionary; + OsEng.windowWrapper().setSynchronization(false); + OnExit( + [](){ OsEng.windowWrapper().setSynchronization(true); } + ); lua_State* state = ghoul::lua::createNewLuaState(); OnExit( // Delete the Lua state at the end of the scope, no matter what - [state](){ghoul::lua::destroyLuaState(state);} + [state](){ ghoul::lua::destroyLuaState(state); } ); OsEng.scriptEngine().initializeLuaState(state); @@ -263,7 +277,7 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { OsEng.configurationManager().getValue(KeyPropertyDocumentationFile, propertyDocumentationFile); propertyDocumentationFile = absPath(propertyDocumentationFile); - writePropertyDocumentation(propertyDocumentationFile, propertyDocumentationType); + writePropertyDocumentation(propertyDocumentationFile, propertyDocumentationType, sceneDescriptionFilePath); } @@ -437,7 +451,7 @@ SceneGraph& Scene::sceneGraph() { return _graph; } -void Scene::writePropertyDocumentation(const std::string& filename, const std::string& type) { +void Scene::writePropertyDocumentation(const std::string& filename, const std::string& type, const std::string& sceneFilename) { LDEBUG("Writing documentation for properties"); if (type == "text") { std::ofstream file; @@ -464,7 +478,37 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s file.exceptions(~std::ofstream::goodbit); file.open(filename); -#ifdef JSON + + std::ifstream handlebarsInput(absPath(HandlebarsFilename)); + std::ifstream jsInput(absPath(JsFilename)); + + std::string jsContent; + std::back_insert_iterator jsInserter(jsContent); + + std::copy(std::istreambuf_iterator{handlebarsInput}, std::istreambuf_iterator(), jsInserter); + std::copy(std::istreambuf_iterator{jsInput}, std::istreambuf_iterator(), jsInserter); + + std::ifstream bootstrapInput(absPath(BootstrapFilename)); + std::ifstream cssInput(absPath(CssFilename)); + + std::string cssContent; + std::back_insert_iterator cssInserter(cssContent); + + std::copy(std::istreambuf_iterator{bootstrapInput}, std::istreambuf_iterator(), cssInserter); + std::copy(std::istreambuf_iterator{cssInput}, std::istreambuf_iterator(), cssInserter); + + std::ifstream mainTemplateInput(absPath(MainTemplateFilename)); + std::string mainTemplateContent{ std::istreambuf_iterator{mainTemplateInput}, + std::istreambuf_iterator{} }; + + std::ifstream propertyOwnerTemplateInput(absPath(PropertyOwnerTemplateFilename)); + std::string propertyOwnerTemplateContent{ std::istreambuf_iterator{propertyOwnerTemplateInput}, + std::istreambuf_iterator{} }; + + std::ifstream propertyTemplateInput(absPath(PropertyTemplateFilename)); + std::string propertyTemplateContent{ std::istreambuf_iterator{propertyTemplateInput}, + std::istreambuf_iterator{} }; + // Create JSON std::function createJson = [&createJson](properties::PropertyOwner* owner) -> std::string @@ -474,22 +518,30 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s json << "\"name\": \"" << owner->name() << "\","; json << "\"properties\": ["; - for (properties::Property* p : owner->properties()) { + auto properties = owner->properties(); + for (properties::Property* p : properties) { json << "{"; json << "\"id\": \"" << p->identifier() << "\","; json << "\"type\": \"" << p->className() << "\","; json << "\"fullyQualifiedId\": \"" << p->fullyQualifiedIdentifier() << "\","; - json << "\"guiName\": \"" << p->guiName() << "\","; - json << "},"; + json << "\"guiName\": \"" << p->guiName() << "\""; + json << "}"; + if (p != properties.back()) { + json << ","; + } } json << "],"; - json << "\"propertyOwner\": ["; - for (properties::PropertyOwner* o : owner->propertySubOwners()) { + json << "\"propertyOwners\": ["; + auto propertyOwners = owner->propertySubOwners(); + for (properties::PropertyOwner* o : propertyOwners) { json << createJson(o); + if (o != propertyOwners.back()) { + json << ","; + } } - json << "],"; - json << "},"; + json << "]"; + json << "}"; return json.str(); }; @@ -497,15 +549,56 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s std::stringstream json; json << "["; - for (SceneGraphNode* node : _graph.nodes()) { + auto nodes = _graph.nodes(); + for (SceneGraphNode* node : nodes) { json << createJson(node); + if (node != nodes.back()) { + json << ","; + } } json << "]"; - std::string jsonText = json.str(); -#else + std::string jsonString = ""; + for (const char& c : json.str()) { + if (c == '\'') { + jsonString += "\\'"; + } else { + jsonString += c; + } + } + std::stringstream html; + html << "\n" + << "\n" + << "\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\n" + << "\t\n" + << "\t\tDocumentation\n" + << "\t\n" + << "\t\n" + << "\t\n" + << "\n"; + + /* + html << "\n" << "\t\n" << "\t\tProperties\n" @@ -541,9 +634,8 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s << "
\n" << ";"; + */ file << html.str(); -#endif - } else LERROR("Undefined type '" << type << "' for Property documentation"); @@ -577,7 +669,6 @@ scripting::LuaLibrary Scene::luaLibrary() { "Sets a property identified by the URI in " "the first argument. The second argument can be any type, but it has to " "match the type that the property expects.", - true }, { "getPropertyValue", diff --git a/src/scene/scene_doc.inl b/src/scene/scene_doc.inl index 9b069572c1..5968782602 100644 --- a/src/scene/scene_doc.inl +++ b/src/scene/scene_doc.inl @@ -31,6 +31,7 @@ Documentation Scene::Documentation() { return { "Scene Description", + "core_scene", { { "ScenePath", diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 4733a8f588..68d7b3bd4e 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -52,10 +52,10 @@ void applyRegularExpression(lua_State* L, std::regex regex, std::vectorgetStringValue(value); - OsEng.parallelConnection().scriptMessage( +/* OsEng.parallelConnection().scriptMessage( prop->fullyQualifiedIdentifier(), value - ); + );*/ } } @@ -101,7 +101,7 @@ int property_setValueSingle(lua_State* L) { //ensure properties are synced over parallel connection std::string value; prop->getStringValue(value); - OsEng.parallelConnection().scriptMessage(prop->fullyQualifiedIdentifier(), value); + //OsEng.parallelConnection().scriptMessage(prop->fullyQualifiedIdentifier(), value); } return 0; diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index e3334e178d..a87e7a9b52 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -39,7 +39,7 @@ #include #include -#include +#include #include #include @@ -106,9 +106,9 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (dictionary.hasKey(keyTransformTranslation)) { ghoul::Dictionary translationDictionary; dictionary.getValue(keyTransformTranslation, translationDictionary); - result->_ephemeris = - (Ephemeris::createFromDictionary(translationDictionary)); - if (result->_ephemeris == nullptr) { + result->_translation = + (Translation::createFromDictionary(translationDictionary)); + if (result->_translation == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" << result->name() << "'"); delete result; @@ -167,7 +167,7 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di SceneGraphNode::SceneGraphNode() : _parent(nullptr) - , _ephemeris(new StaticEphemeris()) + , _translation(new StaticTranslation()) , _rotation(new StaticRotation()) , _scale(new StaticScale()) , _performanceRecord({0, 0, 0}) @@ -185,8 +185,8 @@ bool SceneGraphNode::initialize() { if (_renderable) _renderable->initialize(); - if (_ephemeris) - _ephemeris->initialize(); + if (_translation) + _translation->initialize(); if (_rotation) _rotation->initialize(); if (_scale) @@ -203,9 +203,9 @@ bool SceneGraphNode::deinitialize() { delete _renderable; _renderable = nullptr; } - if (_ephemeris) { - delete _ephemeris; - _ephemeris = nullptr; + if (_translation) { + delete _translation; + _translation = nullptr; } if (_rotation) { delete _rotation; @@ -235,19 +235,19 @@ bool SceneGraphNode::deinitialize() { } void SceneGraphNode::update(const UpdateData& data) { - if (_ephemeris) { + if (_translation) { if (data.doPerformanceMeasurement) { glFinish(); auto start = std::chrono::high_resolution_clock::now(); - _ephemeris->update(data); + _translation->update(data); glFinish(); auto end = std::chrono::high_resolution_clock::now(); _performanceRecord.updateTimeEphemeris = (end - start).count(); } else - _ephemeris->update(data); + _translation->update(data); } if (_rotation) { @@ -431,7 +431,7 @@ void SceneGraphNode::addChild(SceneGraphNode* child) { glm::dvec3 SceneGraphNode::position() const { - return _ephemeris->position(); + return _translation->position(); } const glm::dmat3& SceneGraphNode::rotationMatrix() const diff --git a/src/scene/scenegraphnode_doc.inl b/src/scene/scenegraphnode_doc.inl index 5a491fb139..27b6ba7293 100644 --- a/src/scene/scenegraphnode_doc.inl +++ b/src/scene/scenegraphnode_doc.inl @@ -31,6 +31,7 @@ Documentation SceneGraphNode::Documentation() { return { "Scenegraph Node", + "core_scene_node", { { "Name", diff --git a/src/scene/ephemeris.cpp b/src/scene/translation.cpp similarity index 80% rename from src/scene/ephemeris.cpp rename to src/scene/translation.cpp index 9051d5a977..b355f50163 100644 --- a/src/scene/ephemeris.cpp +++ b/src/scene/translation.cpp @@ -22,20 +22,21 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include + #include #include #include namespace { - const std::string _loggerCat = "Ephemeris"; + const std::string _loggerCat = "Translation"; const std::string KeyType = "Type"; } namespace openspace { -Documentation Ephemeris::Documentation() { +Documentation Translation::Documentation() { using namespace openspace::documentation; return{ @@ -56,31 +57,31 @@ Documentation Ephemeris::Documentation() { }; } -Ephemeris* Ephemeris::createFromDictionary(const ghoul::Dictionary& dictionary) { +Translation* Translation::createFromDictionary(const ghoul::Dictionary& dictionary) { if (!dictionary.hasValue(KeyType)) { - LERROR("Ephemeris did not have key '" << KeyType << "'"); + LERROR("Translation did not have key '" << KeyType << "'"); return nullptr; } - std::string ephemerisType; - dictionary.getValue(KeyType, ephemerisType); - ghoul::TemplateFactory* factory - = FactoryManager::ref().factory(); - Ephemeris* result = factory->create(ephemerisType, dictionary); + std::string translationType; + dictionary.getValue(KeyType, translationType); + ghoul::TemplateFactory* factory + = FactoryManager::ref().factory(); + Translation* result = factory->create(translationType, dictionary); if (result == nullptr) { - LERROR("Failed creating Ephemeris object of type '" << ephemerisType << "'"); + LERROR("Failed creating Translation object of type '" << translationType << "'"); return nullptr; } return result; } -Ephemeris::~Ephemeris() {} +Translation::~Translation() {} -bool Ephemeris::initialize() { +bool Translation::initialize() { return true; } -void Ephemeris::update(const UpdateData& data) {} +void Translation::update(const UpdateData& data) {} } // namespace openspace diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 7ac37b5eb3..4bb3892a4a 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -23,6 +23,7 @@ ****************************************************************************************/ #include +#include #include #include @@ -39,6 +40,15 @@ #include "scriptengine_lua.inl" +namespace { + const std::string MainTemplateFilename = "${OPENSPACE_DATA}/web/luascripting/main.hbs"; + const std::string ScriptingTemplateFilename = "${OPENSPACE_DATA}/web/luascripting/scripting.hbs"; + const std::string HandlebarsFilename = "${OPENSPACE_DATA}/web/common/handlebars-v4.0.5.js"; + const std::string JsFilename = "${OPENSPACE_DATA}/web/luascripting/script.js"; + const std::string BootstrapFilename = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssFilename = "${OPENSPACE_DATA}/web/common/style.css"; +} + namespace openspace { namespace scripting { @@ -156,16 +166,7 @@ bool ScriptEngine::runScript(const std::string& script) { LERRORC(e.component, e.message); return false; } - - // if we're currently hosting the parallel session, find out if script should be synchronized. - if (OsEng.parallelConnection().isHost()) { - std::string lib, func; - if (parseLibraryAndFunctionNames(lib, func, script) && shouldScriptBeSent(lib, func)){ -// OsEng.parallelConnection()->sendScript(script); -// cacheScript(lib, func, script); - } - } - + return true; } @@ -194,7 +195,7 @@ bool ScriptEngine::runScriptFile(const std::string& filename) { return true; } -bool ScriptEngine::shouldScriptBeSent(const std::string& library, const std::string& function) { +/*bool ScriptEngine::shouldScriptBeSent(const std::string& library, const std::string& function) { std::set::const_iterator libit; for (libit = _registeredLibraries.cbegin(); libit != _registeredLibraries.cend(); @@ -219,9 +220,9 @@ bool ScriptEngine::shouldScriptBeSent(const std::string& library, const std::str } return false; -} +}*/ -void ScriptEngine::cacheScript(const std::string &library, const std::string &function, const std::string &script){ +/*void ScriptEngine::cacheScript(const std::string &library, const std::string &function, const std::string &script){ _cachedScriptsMutex.lock(); _cachedScripts[library][function] = script; _cachedScriptsMutex.unlock(); @@ -246,8 +247,9 @@ std::vector ScriptEngine::cachedScripts(){ _cachedScriptsMutex.unlock(); return retVal; -} +}*/ +/* bool ScriptEngine::parseLibraryAndFunctionNames(std::string &library, std::string &function, const std::string &script){ //"deconstruct the script to find library and function name @@ -289,7 +291,7 @@ bool ScriptEngine::parseLibraryAndFunctionNames(std::string &library, std::strin //if we found a function all is good return !function.empty(); } - +*/ bool ScriptEngine::isLibraryNameAllowed(lua_State* state, const std::string& name) { bool result = false; lua_getglobal(state, _openspaceLibraryName.c_str()); @@ -562,18 +564,48 @@ void ScriptEngine::writeDocumentation(const std::string& filename, const std::st } } else if (type == "html") { + std::ifstream handlebarsInput(absPath(HandlebarsFilename)); + std::ifstream jsInput(absPath(JsFilename)); + + std::string jsContent; + std::back_insert_iterator jsInserter(jsContent); + + std::copy(std::istreambuf_iterator{handlebarsInput}, std::istreambuf_iterator(), jsInserter); + std::copy(std::istreambuf_iterator{jsInput}, std::istreambuf_iterator(), jsInserter); + + std::ifstream bootstrapInput(absPath(BootstrapFilename)); + std::ifstream cssInput(absPath(CssFilename)); + + std::string cssContent; + std::back_insert_iterator cssInserter(cssContent); + + std::copy(std::istreambuf_iterator{bootstrapInput}, std::istreambuf_iterator(), cssInserter); + std::copy(std::istreambuf_iterator{cssInput}, std::istreambuf_iterator(), cssInserter); + + std::ifstream mainTemplateInput(absPath(MainTemplateFilename)); + std::string mainTemplateContent{ std::istreambuf_iterator{mainTemplateInput}, + std::istreambuf_iterator{} }; + + std::ifstream scriptingTemplateInput(absPath(ScriptingTemplateFilename)); + std::string scriptingTemplateContent{ std::istreambuf_iterator{scriptingTemplateInput}, + std::istreambuf_iterator{} }; + std::ofstream file; file.exceptions(~std::ofstream::goodbit); file.open(filename); -#ifdef JSON // Create JSON std::stringstream json; json << "["; + bool first = true; for (const LuaLibrary& l : _registeredLibraries) { - json << "{"; + if (!first) { + json << ","; + } + first = false; + json << "{"; json << "\"library\": \"" << l.name << "\","; json << "\"functions\": ["; @@ -582,16 +614,53 @@ void ScriptEngine::writeDocumentation(const std::string& filename, const std::st json << "\"name\": \"" << f.name << "\", "; json << "\"arguments\": \"" << f.argumentText << "\", "; json << "\"help\": \"" << f.helpText << "\""; - json << "},"; + json << "}"; + if (&f != &l.functions.back()) { + json << ","; + } } - json << "]},"; + json << "]}"; + } json << "]"; + std::string jsonString = ""; + for (const char& c : json.str()) { + if (c == '\'') { + jsonString += "\\'"; + } + else { + jsonString += c; + } + } - std::string jsonText = json.str(); -#else std::stringstream html; + html << "\n" + << "\n" + << "\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\n" + << "\t\n" + << "\t\tDocumentation\n" + << "\t\n" + << "\t\n" + << "\t\n" + << "\n"; + + file << html.str(); + + /* html << "\n" << "\t\n" @@ -644,7 +713,7 @@ void ScriptEngine::writeDocumentation(const std::string& filename, const std::st << ""; file << html.str(); -#endif +*/ } else { throw ghoul::RuntimeError("Undefined type '" + type + "' for Lua documentation"); @@ -716,20 +785,27 @@ bool ScriptEngine::writeLog(const std::string& script) { } void ScriptEngine::presync(bool isMaster) { - if (isMaster) { - _mutex.lock(); + if (!isMaster) return; - if (!_queuedScripts.empty()) { - _currentSyncedScript = _queuedScripts.back(); - _queuedScripts.pop_back(); + _mutex.lock(); - //Not really a received script but the master also needs to run the script... - _receivedScripts.push_back(_currentSyncedScript); + if (!_queuedScripts.empty()) { + _currentSyncedScript = _queuedScripts.back().first; + bool remoteScripting = _queuedScripts.back().second; + + + //Not really a received script but the master also needs to run the script... + _receivedScripts.push_back(_currentSyncedScript); + _queuedScripts.pop_back(); + + if (OsEng.parallelConnection().isHost() && remoteScripting) { + OsEng.parallelConnection().sendScript(_currentSyncedScript); } - _mutex.unlock(); } + _mutex.unlock(); + } void ScriptEngine::encode(SyncBuffer* syncBuffer) { @@ -766,13 +842,13 @@ void ScriptEngine::postsync(bool isMaster) { } } -void ScriptEngine::queueScript(const std::string &script){ +void ScriptEngine::queueScript(const std::string &script, ScriptEngine::RemoteScripting remoteScripting){ if (script.empty()) return; _mutex.lock(); - _queuedScripts.insert(_queuedScripts.begin(), script); + _queuedScripts.insert(_queuedScripts.begin(), std::make_pair(script, remoteScripting)); _mutex.unlock(); } diff --git a/src/util/factorymanager.cpp b/src/util/factorymanager.cpp index dd0f60cdce..4d979bbad6 100644 --- a/src/util/factorymanager.cpp +++ b/src/util/factorymanager.cpp @@ -23,11 +23,23 @@ ****************************************************************************************/ #include +#include +#include #include +#include #include +namespace { + const std::string MainTemplateFilename = "${OPENSPACE_DATA}/web/factories/main.hbs"; + const std::string FactoryTemplateFilename = "${OPENSPACE_DATA}/web/factories/factory.hbs"; + const std::string HandlebarsFilename = "${OPENSPACE_DATA}/web/common/handlebars-v4.0.5.js"; + const std::string JsFilename = "${OPENSPACE_DATA}/web/factories/script.js"; + const std::string BootstrapFilename = "${OPENSPACE_DATA}/web/common/bootstrap.min.css"; + const std::string CssFilename = "${OPENSPACE_DATA}/web/common/style.css"; +} + namespace openspace { FactoryManager* FactoryManager::_manager = nullptr; @@ -84,8 +96,32 @@ void FactoryManager::writeDocumentation(const std::string& file, const std::stri } else if (type == "html") { + std::ifstream handlebarsInput(absPath(HandlebarsFilename)); + std::ifstream jsInput(absPath(JsFilename)); + + std::string jsContent; + std::back_insert_iterator jsInserter(jsContent); + + std::copy(std::istreambuf_iterator{handlebarsInput}, std::istreambuf_iterator(), jsInserter); + std::copy(std::istreambuf_iterator{jsInput}, std::istreambuf_iterator(), jsInserter); + + std::ifstream bootstrapInput(absPath(BootstrapFilename)); + std::ifstream cssInput(absPath(CssFilename)); + + std::string cssContent; + std::back_insert_iterator cssInserter(cssContent); + + std::copy(std::istreambuf_iterator{bootstrapInput}, std::istreambuf_iterator(), cssInserter); + std::copy(std::istreambuf_iterator{cssInput}, std::istreambuf_iterator(), cssInserter); + + std::ifstream mainTemplateInput(absPath(MainTemplateFilename)); + std::string mainTemplateContent{ std::istreambuf_iterator{mainTemplateInput}, + std::istreambuf_iterator{} }; + + std::ifstream factoryTemplateInput(absPath(FactoryTemplateFilename)); + std::string factoryTemplateContent{ std::istreambuf_iterator{factoryTemplateInput}, + std::istreambuf_iterator{} }; -#ifdef JSON std::stringstream json; json << "["; @@ -95,23 +131,65 @@ void FactoryManager::writeDocumentation(const std::string& file, const std::stri json << "\"classes\": ["; ghoul::TemplateFactoryBase* factory = factoryInfo.factory.get(); - for (const std::string& c : factory->registeredClasses()) { - json << "\"" << c << "\","; + const std::vector& registeredClasses = factory->registeredClasses(); + for (const std::string& c : registeredClasses) { + json << "\"" << c << "\""; + if (&c != ®isteredClasses.back()) { + json << ","; + } } - json << "]},"; + json << "]}"; + if (&factoryInfo != &_factories.back()) { + json << ","; + } } json << "]"; // I did not check the output of this for correctness ---abock std::string jsonText = json.str(); -#else + std::ofstream f; f.exceptions(~std::ofstream::goodbit); f.open(file); + std::string jsonString = ""; + for (const char& c : json.str()) { + if (c == '\'') { + jsonString += "\\'"; + } else { + jsonString += c; + } + } + std::stringstream html; + html << "\n" + << "\n" + << "\t\n" + << "\t\t\n" + << "\t\t\n" + << "\t\n" + << "\t\n" + << "\t\tDocumentation\n" + << "\t\n" + << "\t\n" + << "\t\n" + << "\n"; + + f << html.str(); + + /* html << "\n" << "\t\n" << "\t\tFactories\n" @@ -142,10 +220,8 @@ void FactoryManager::writeDocumentation(const std::string& file, const std::stri html << "\t\n" << "\n" << ";"; - f << html.str(); -#endif - + */ } } diff --git a/src/util/time.cpp b/src/util/time.cpp index 62f5b66b0a..102faf22d1 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -179,8 +179,7 @@ scripting::LuaLibrary Time::luaLibrary() { &luascriptfunctions::time_setDeltaTime, "number", "Sets the amount of simulation time that happens " - "in one second of real time", - true + "in one second of real time" }, { "deltaTime", @@ -193,16 +192,14 @@ scripting::LuaLibrary Time::luaLibrary() { "setPause", &luascriptfunctions::time_setPause, "bool", - "Pauses the simulation time or restores the delta time", - true + "Pauses the simulation time or restores the delta time" }, { "togglePause", &luascriptfunctions::time_togglePause, "", "Toggles the pause function, i.e. temporarily setting the delta time to 0" - " and restoring it afterwards", - true + " and restoring it afterwards" }, { "setTime", @@ -211,8 +208,7 @@ scripting::LuaLibrary Time::luaLibrary() { "Sets the current simulation time to the " "specified value. If the parameter is a number, the value is the number " "of seconds past the J2000 epoch. If it is a string, it has to be a " - "valid ISO 8601 date string (YYYY-MM-DDTHH:MN:SS)", - true + "valid ISO 8601 date string (YYYY-MM-DDTHH:MN:SS)" }, { "currentTime", diff --git a/src/util/timemanager.cpp b/src/util/timemanager.cpp new file mode 100644 index 0000000000..c70ca0d934 --- /dev/null +++ b/src/util/timemanager.cpp @@ -0,0 +1,153 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2016 * +* * +* 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. * +****************************************************************************************/ + +#include +#include +#include + +namespace { + double SecondsOffTolerance = 0.1; +} + +namespace openspace { + +using datamessagestructures::TimeKeyframe; + +void TimeManager::preSynchronization(double dt) { + double now = OsEng.runTime(); + removeKeyframesBefore(_latestConsumedTimestamp); + if (_keyframes.size() == 0) { + Time::ref().advanceTime(dt); + } else { + consumeKeyframes(dt); + } +} + +void TimeManager::consumeKeyframes(double dt) { + double now = OsEng.runTime(); + TimeKeyframe kf; + kf._timestamp = now; + auto firstFutureKeyframe = std::lower_bound(_keyframes.begin(), _keyframes.end(), kf, &TimeManager::compareKeyframeTimes); + + bool consumingTimeJump = std::find_if(_keyframes.begin(), firstFutureKeyframe, [] (const TimeKeyframe& f) { + return f._requiresTimeJump; + }) != firstFutureKeyframe; + + Time& time = Time::ref(); + + if (firstFutureKeyframe == _keyframes.end()) { + // All keyframes are in the past. + // Consume the latest one. + TimeKeyframe& current = _keyframes.back(); + time.setTime(current._time, consumingTimeJump); + time.setDeltaTime(current._dt); + time.setPause(current._paused); + _latestConsumedTimestamp = current._timestamp; + } + else { + TimeKeyframe& next = *firstFutureKeyframe; + + if (firstFutureKeyframe != _keyframes.begin()) { + TimeKeyframe& latest = *(firstFutureKeyframe - 1); + // In case of unconsumed passed keyframes, let the last one + // determine whether the time should be paused or not. + // If there was a time jump or time is paused, apply it directly. + // Then consume the last keyframe. + + time.setPause(latest._paused); + time.setTimeJumped(consumingTimeJump); + time.setDeltaTime(latest._dt); + + if (consumingTimeJump || latest._paused) { + time.setTime(latest._time, consumingTimeJump); + } + _latestConsumedTimestamp = latest._timestamp; + } + + // Do not interpolate with time jumping keyframes. + // Instead, wait until their timestamp and apply them directly. + if (next._requiresTimeJump) { + Time::ref().advanceTime(dt); + return; + } + + if (time.paused()) { + return; + } + + double predictedTime = time.j2000Seconds() + time.deltaTime() * (next._timestamp - now); + bool withinTolerance = std::abs(predictedTime - next._time) < std::abs(next._dt * SecondsOffTolerance); + + if (next._dt == time.deltaTime() && withinTolerance) { + Time::ref().advanceTime(dt); + return; + } + + double t0 = now - dt; + double t1 = now; + double t2 = next._timestamp; + + double parameter = (t1 - t0) / (t2 - t0); + + double y0 = time.j2000Seconds(); + double yPrime0 = time.deltaTime(); + + double y2 = next._time; + double yPrime2 = next._dt; + + double y1 = (1 - parameter) * y0 + parameter * y2; + double y1Prime = (y1 - y0) / dt; + + time.setDeltaTime(y1Prime); + time.setTime(y1, false); + } +} + + +void TimeManager::addKeyframe(const TimeKeyframe& kf) { + if (kf._timestamp < OsEng.runTime()) { + return; + } + auto iter = std::upper_bound(_keyframes.begin(), _keyframes.end(), kf, &TimeManager::compareKeyframeTimes); + _keyframes.insert(iter, kf); +} + + +void TimeManager::removeKeyframesBefore(double timestamp) { + datamessagestructures::TimeKeyframe kf; + kf._timestamp = timestamp; + auto iter = std::upper_bound(_keyframes.begin(), _keyframes.end(), kf, &TimeManager::compareKeyframeTimes); + _keyframes.erase(_keyframes.begin(), iter); +} + +void TimeManager::clearKeyframes() { + _keyframes.clear(); +} + +bool TimeManager::compareKeyframeTimes(const TimeKeyframe& a, const TimeKeyframe& b) +{ + return a._timestamp < b._timestamp; +} + +} \ No newline at end of file diff --git a/src/util/timerange.cpp b/src/util/timerange.cpp new file mode 100644 index 0000000000..1df1c08661 --- /dev/null +++ b/src/util/timerange.cpp @@ -0,0 +1,140 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * 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. * + ****************************************************************************************/ + +#include + +#include +#include + +#include + +namespace { + const std::string KeyStart = "Start"; + const std::string KeyEnd = "End"; +} + +namespace openspace { + +openspace::Documentation TimeRange::Documentation() { + using namespace documentation; + return { + "Time Range", + "core_util_timerange", + { + { + KeyStart, + new StringAnnotationVerifier("A string representing a valid date"), + "The start date of the time range", + Optional::No + }, + { + KeyEnd, + new StringAnnotationVerifier("A string representing a valid date"), + "The end date of the time range", + Optional::No + } + }, + Exhaustive::Yes + }; +} + +TimeRange::TimeRange() + : TimeRange(std::numeric_limits::max(), -std::numeric_limits::max()) +{} + +TimeRange::TimeRange(double startTime, double endTime) + : start(startTime) + , end(endTime) +{} + +TimeRange::TimeRange(const ghoul::Dictionary& dict) { + if (!initializeFromDictionary(dict, *this)) { + throw std::runtime_error("Unable to read TimeRange from dictionary"); + } +} + +bool TimeRange::initializeFromDictionary(const ghoul::Dictionary& dict, TimeRange& timeRange) { + std::string startTimeStr; + std::string endTimeStr; + + bool success = true; + success &= dict.getValue(KeyStart, startTimeStr); + success &= dict.getValue(KeyEnd, endTimeStr); + if (success) { + // Parse to date. + // @TODO converting string to time stamp should not rely on Spice + timeRange.start = SpiceManager::ref().ephemerisTimeFromDate(startTimeStr); + timeRange.end = SpiceManager::ref().ephemerisTimeFromDate(endTimeStr); + return true; + } + else { + // Could not read TimeRange from Dict + return false; + } +} + +void TimeRange::include(double val){ + if (start > val) { + start = val; + } + if (end < val) { + end = val; + } +}; + +void TimeRange::include(const TimeRange& other) { + if (other.start < start) { + start = other.start; + } + + if (other.end > end) { + end = other.end; + } +} + +double TimeRange::duration() const { + return end - start; +} + +bool TimeRange::isDefined() const { + return start <= end; +} + +bool TimeRange::isEmpty() const { + return !isDefined(); +} + +bool TimeRange::inRange(double min, double max){ + return (min >= start && max <= end); +} + +bool TimeRange::includes(double val) const { + return (start <= val && val <= end); +} + +bool TimeRange::includes(const TimeRange& o) const { + return start <= o.start && o.end <= end; +} + +} // namespace openspace diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index c4bef6ff1d..04adf37437 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -291,8 +291,17 @@ endfunction () function (handle_option_tests) if (OPENSPACE_HAVE_TESTS) if (NOT TARGET gtest) - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + set(BUILD_GTEST ON CACHE BOOL "") + set(BUILD_GMOCK OFF CACHE BOOL "") + set(gtest_force_shared_crt ON CACHE BOOL "") + # set(BUILD_GMOCK OFF CACHE BOOL "") + # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) + # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) + # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) + # add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) set_property(TARGET gtest PROPERTY FOLDER "External") + set_property(TARGET gtest_main PROPERTY FOLDER "External") endif () file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) @@ -301,7 +310,7 @@ function (handle_option_tests) target_include_directories(OpenSpaceTest PUBLIC "${OPENSPACE_BASE_DIR}/include" "${OPENSPACE_BASE_DIR}/tests" - "${OPENSPACE_EXT_DIR}/ghoul/ext/gtest/include" + "${OPENSPACE_EXT_DIR}/ghoul/ext/googletest/googletest/include" ) target_link_libraries(OpenSpaceTest gtest libOpenSpace) @@ -316,7 +325,13 @@ function (handle_option_tests) endif (OPENSPACE_HAVE_TESTS) if (TARGET GhoulTest) if (NOT TARGET gtest) - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + set(BUILD_GTEST ON CACHE BOOL "") + set(BUILD_GMOCK OFF CACHE BOOL "") + set(gtest_force_shared_crt ON CACHE BOOL "") + # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) + # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) + # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) endif () set_property(TARGET gtest PROPERTY FOLDER "External") diff --git a/tests/gdal/TERRA_CR_B143_2016-04-12.wms b/tests/gdal/TERRA_CR_B143_2016-04-12.wms new file mode 100644 index 0000000000..974c7da85f --- /dev/null +++ b/tests/gdal/TERRA_CR_B143_2016-04-12.wms @@ -0,0 +1,8 @@ + + + http://map1.vis.earthdata.nasa.gov/twms-geo/twms.cgi? + MODIS TERRA tileset + 2016-04-12 + + 20 + diff --git a/tests/test_documentation.inl b/tests/test_documentation.inl index 35b6bca709..da672ca414 100644 --- a/tests/test_documentation.inl +++ b/tests/test_documentation.inl @@ -2308,6 +2308,73 @@ TEST_F(DocumentationTest, DoubleVector4Verifier) { EXPECT_EQ(TestResult::Offense::Reason::WrongType, negativeRes.offenses[0].reason); } +TEST_F(DocumentationTest, DeprecatedVerifier) { + using namespace openspace::documentation; + using namespace std::string_literals; + + Documentation doc { { + { "bool", new BoolDeprecatedVerifier }, + { "int" , new IntDeprecatedVerifier }, + { "double", new DoubleDeprecatedVerifier }, + { "string" , new StringDeprecatedVerifier }, + { "boolvec2", new DeprecatedVerifier }, + { "intvec2", new DeprecatedVerifier }, + { "doublevec2", new DeprecatedVerifier }, + { "boolvec3", new DeprecatedVerifier }, + { "intvec3", new DeprecatedVerifier }, + { "doublevec3", new DeprecatedVerifier }, + { "boolvec4", new DeprecatedVerifier }, + { "intvec4", new DeprecatedVerifier }, + { "doublevec4", new DeprecatedVerifier } + }}; + + ghoul::Dictionary positive { + { "bool", true }, + { "int", 1 }, + { "double", 2.0 }, + { "string" , ""s }, + { "boolvec2", glm::bvec2() }, + { "intvec2", glm::ivec2() }, + { "doublevec2", glm::dvec2() }, + { "boolvec3", glm::bvec3() }, + { "intvec3", glm::ivec3() }, + { "doublevec3", glm::dvec3() }, + { "boolvec4", glm::bvec4() }, + { "intvec4", glm::ivec4() }, + { "doublevec4", glm::dvec4() } + }; + TestResult positiveRes = testSpecification(doc, positive); + EXPECT_TRUE(positiveRes.success); + EXPECT_EQ(0, positiveRes.offenses.size()); + ASSERT_EQ(13, positiveRes.warnings.size()); + EXPECT_EQ("bool", positiveRes.warnings[0].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[0].reason); + EXPECT_EQ("boolvec2", positiveRes.warnings[1].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[1].reason); + EXPECT_EQ("boolvec3", positiveRes.warnings[2].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[2].reason); + EXPECT_EQ("boolvec4", positiveRes.warnings[3].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[3].reason); + EXPECT_EQ("double", positiveRes.warnings[4].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[4].reason); + EXPECT_EQ("doublevec2", positiveRes.warnings[5].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[5].reason); + EXPECT_EQ("doublevec3", positiveRes.warnings[6].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[6].reason); + EXPECT_EQ("doublevec4", positiveRes.warnings[7].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[7].reason); + EXPECT_EQ("int", positiveRes.warnings[8].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[8].reason); + EXPECT_EQ("intvec2", positiveRes.warnings[9].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[9].reason); + EXPECT_EQ("intvec3", positiveRes.warnings[10].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[10].reason); + EXPECT_EQ("intvec4", positiveRes.warnings[11].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[11].reason); + EXPECT_EQ("string", positiveRes.warnings[12].offender); + EXPECT_EQ(TestResult::Warning::Reason::Deprecated, positiveRes.warnings[12].reason); +} + TEST_F(DocumentationTest, VerifierTypePostConditions) { using namespace openspace::documentation; using namespace std::string_literals; @@ -2360,11 +2427,35 @@ TEST_F(DocumentationTest, VerifierTypePostConditions) { EXPECT_NE("", IntNotInRangeVerifier({ 0, 1 }).type()); EXPECT_NE("", DoubleNotInRangeVerifier({ 0.0, 1.0 }).type()); - EXPECT_NE("", BoolAnnotationVerifier("Annotation"s).type()); - EXPECT_NE("", IntAnnotationVerifier("Annotation"s).type()); - EXPECT_NE("", DoubleAnnotationVerifier("Annotation"s).type()); - EXPECT_NE("", StringAnnotationVerifier("Annotation"s).type()); - EXPECT_NE("", TableAnnotationVerifier("Annotation"s).type()); + EXPECT_NE("", BoolAnnotationVerifier("A"s).type()); + EXPECT_NE("", IntAnnotationVerifier("A"s).type()); + EXPECT_NE("", DoubleAnnotationVerifier("A"s).type()); + EXPECT_NE("", StringAnnotationVerifier("A"s).type()); + EXPECT_NE("", TableAnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + EXPECT_NE("", AnnotationVerifier("A"s).type()); + + EXPECT_NE("", BoolDeprecatedVerifier().type()); + EXPECT_NE("", IntDeprecatedVerifier().type()); + EXPECT_NE("", DoubleDeprecatedVerifier().type()); + EXPECT_NE("", StringDeprecatedVerifier().type()); + EXPECT_NE("", TableDeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); + EXPECT_NE("", DeprecatedVerifier().type()); EXPECT_NE("", ReferencingVerifier("identifier"s).type()); } @@ -2421,11 +2512,35 @@ TEST_F(DocumentationTest, VerifierDocumentationPostConditions) { EXPECT_NE("", IntNotInRangeVerifier({ 0, 1 }).documentation()); EXPECT_NE("", DoubleNotInRangeVerifier({ 0.0, 1.0 }).documentation()); - EXPECT_NE("", BoolAnnotationVerifier("Annotation"s).documentation()); - EXPECT_NE("", IntAnnotationVerifier("Annotation"s).documentation()); - EXPECT_NE("", DoubleAnnotationVerifier("Annotation"s).documentation()); - EXPECT_NE("", StringAnnotationVerifier("Annotation"s).documentation()); - EXPECT_NE("", TableAnnotationVerifier("Annotation"s).documentation()); + EXPECT_NE("", BoolAnnotationVerifier("A"s).documentation()); + EXPECT_NE("", IntAnnotationVerifier("A"s).documentation()); + EXPECT_NE("", DoubleAnnotationVerifier("A"s).documentation()); + EXPECT_NE("", StringAnnotationVerifier("A"s).documentation()); + EXPECT_NE("", TableAnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + EXPECT_NE("", AnnotationVerifier("A"s).documentation()); + + EXPECT_NE("", BoolDeprecatedVerifier().documentation()); + EXPECT_NE("", IntDeprecatedVerifier().documentation()); + EXPECT_NE("", DoubleDeprecatedVerifier().documentation()); + EXPECT_NE("", StringDeprecatedVerifier().documentation()); + EXPECT_NE("", TableDeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); + EXPECT_NE("", DeprecatedVerifier().documentation()); EXPECT_NE("", ReferencingVerifier("identifier"s).documentation()); diff --git a/tests/test_gdalwms.inl b/tests/test_gdalwms.inl index 82d597442b..fa082702d8 100644 --- a/tests/test_gdalwms.inl +++ b/tests/test_gdalwms.inl @@ -53,7 +53,7 @@ TEST_F(GdalWmsTest, Simple) { std::cout << res << std::endl; - std::string testFile = absPath("${SCENE}/debugglobe/map_service_configs/TERRA_CR_B143_2016-04-12.wms"); + std::string testFile = absPath("${TESTDIR}/gdal/TERRA_CR_B143_2016-04-12.wms"); poDataset = (GDALDataset *)GDALOpen(testFile.c_str(), GA_ReadOnly);