diff --git a/host/build.gradle b/host/build.gradle index e91980c2b..eae46015d 100644 --- a/host/build.gradle +++ b/host/build.gradle @@ -26,6 +26,10 @@ dependencies { exclude module: 'jsr311-api' } + //compile 'org.slf4j:log4j-over-slf4j:1.7.22' + //compile 'org.slf4j:jul-to-slf4j:1.7.22' + compile 'com.pi4j:pi4j-core:1.1' + compile 'net.java.dev.jna:jna:4.2.2' compile 'org.apache.xmlgraphics:xmlgraphics-commons:1.5' compile 'org.apache.xmlgraphics:batik-transcoder:1.8' compile 'com.sun.mail:javax.mail:1.5.5' @@ -48,12 +52,14 @@ dependencies { compile 'org.eclipse.jetty:jetty-server:9.3.7.v20160115' compile 'org.eclipse.jetty:jetty-servlet:9.3.7.v20160115' compile 'org.eclipse.jetty:jetty-rewrite:9.3.7.v20160115' + compile 'org.jboss.weld.servlet:weld-servlet-core:2.4.1.Final' compile 'org.eclipse.jetty.websocket:javax-websocket-server-impl:9.3.7.v20160115' compile 'com.jcraft:jsch:0.1.53' compile 'org.scream3r:jssc:2.8.0' - compile 'org.apache.logging.log4j:log4j-api:2.5' - compile 'org.apache.logging.log4j:log4j-core:2.5' + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8' + compile 'org.apache.logging.log4j:log4j-api:2.8' + compile 'org.apache.logging.log4j:log4j-core:2.8' compile 'org.apache.james:apache-mime4j:0.6.1' @@ -73,7 +79,7 @@ dependencies { compile 'org.powermock:powermock-module-junit4:1.6.4' compile 'org.powermock:powermock-api-mockito:1.6.4' - compile 'junit:junit:4.12' + compile 'junit:junit:4.12' compile fileTree(dir: 'libs', include: '**/*.jar') compile files('conf') diff --git a/host/conf/config.properties b/host/conf/config.properties index efd9bc5fd..68378b627 100644 --- a/host/conf/config.properties +++ b/host/conf/config.properties @@ -1,5 +1,4 @@ fakeserial=true -fakedisplay=true removeJobOnCompletion=false printerHostPort=9091 #hostGUI=resources @@ -29,6 +28,11 @@ printFileProcessor.org.area515.resinprinter.printphoto.ImagePrintFileProcessor=t printFileProcessor.org.area515.resinprinter.text.TextFilePrintFileProcessor=true printFileProcessor.org.area515.resinprinter.printphoto.SVGImagePrintFileProcessor=true +displayDevice.org.area515.resinprinter.display.dispmanx.RaspberryPiMainLCDScreen=true +displayDevice.org.area515.resinprinter.display.dispmanx.RaspberryPiForceTVScreen=true +displayDevice.org.area515.resinprinter.display.LastAvailableDisplay=true +displayDevice.org.area515.resinprinter.display.SimulatedDisplay=true + #Only enable this when there is at least one admin user!!! #useAuthentication=false UserManagementFeatureImplementation=org.area515.resinprinter.security.keystore.KeystoreLoginService @@ -57,6 +61,8 @@ NetworkManagerImplementation=org.area515.resinprinter.network.LinuxNetworkManage #connectToWifiSSIDCommand=netsh wlan connect name={1} #discoverSSIDCommand=powershell -Command "netsh wlan show profiles interface=Wi-Fi | %'{ $_.Split('':'')[1].trim(); }'" #discoverNetworkInterfaceCommand=blank for windows +#fakedisplay=true + #0=x(width), 1=y(height) imagingCommand=["raspistill", "-t", "1", "-n", "-w", "{0}", "-h", "{1}", "-o", "-"] @@ -110,4 +116,4 @@ username=username password=password smtpServer=smtp.googlemail.com smtpPort=587 -mail.smtp.starttls.enable=true +mail.smtp.starttls.enable=true \ No newline at end of file diff --git a/host/conf/eclipselog4j2.properties b/host/conf/eclipselog4j2.properties index e173fc2d8..e31df3788 100644 --- a/host/conf/eclipselog4j2.properties +++ b/host/conf/eclipselog4j2.properties @@ -16,7 +16,7 @@ appender.console.name=STDOUT appender.console.layout.type=PatternLayout appender.console.layout.pattern=[%t] %m%n -loggers=hex,serial,printer,media,slicer,scan,customizer,stlfileprocessor +loggers=hex,serial,printer,media,slicer,scan,customizer,stlfileprocessor,servlet #loggers=customizer,stlfileprocessor #We need to define each of the loggers that we listed in the previous line @@ -46,22 +46,25 @@ logger.slicer.additivity=false logger.slicer.appenderRefs=rolling logger.slicer.appenderRef.rolling.ref=STDOUT logger.scan.name=org.area515.resinprinter.slice.ScanlineFillPolygonWork -logger.scan.level=info +logger.scan.level=debug logger.scan.additivity=false logger.scan.appenderRefs=rolling logger.scan.appenderRef.rolling.ref=STDOUT - logger.customizer.name=org.area515.resinprinter.services.CustomizerService logger.customizer.level=debug logger.customizer.additivity=false logger.customizer.appenderRefs=consoleRef logger.customizer.appenderRef.consoleRef.ref=STDOUT - logger.stlfileprocessor.name=org.area515.resinprinter.job.STLFileProcessor logger.stlfileprocessor.level=debug logger.stlfileprocessor.additivity=false logger.stlfileprocessor.appenderRefs=consoleRef logger.stlfileprocessor.appenderRef.consoleRef.ref=STDOUT +logger.servlet.name=org.eclipse.jetty.servlet.ServletHandler +logger.servlet.level=debug +logger.servlet.additivity=false +logger.servlet.appenderRefs=consoleRef +logger.servlet.appenderRef.rolling.ref=STDOUT #Default logger rootLogger.level=info diff --git a/host/conf/log4j2.properties b/host/conf/log4j2.properties index f3de94de3..84b8489e9 100644 --- a/host/conf/log4j2.properties +++ b/host/conf/log4j2.properties @@ -26,7 +26,7 @@ appender.rolling.policies.size.size=20MB appender.rolling.strategy.type=DefaultRolloverStrategy appender.rolling.strategy.max=5 -loggers=hex,serial,printer,media,cleanupCWS,cleanupZip +loggers=hex,serial,printer,media,cleanupCWS,cleanupZip,printerDisplay,dispManX #We need to define each of the loggers that we listed in the previous line logger.media.name=org.area515.resinprinter.services.MediaService @@ -64,7 +64,21 @@ logger.cleanupCWS.level=debug logger.cleanupCWS.additivity=false logger.cleanupCWS.appenderRefs=rolling logger.cleanupCWS.appenderRef.rolling.ref=RollingFile - +logger.printerDisplay.name=org.area515.resinprinter.display.PrinterDisplayFrame +logger.printerDisplay.level=debug +logger.printerDisplay.additivity=false +logger.printerDisplay.appenderRefs=rolling +logger.printerDisplay.appenderRef.rolling.ref=RollingFile +logger.dispManX.name=org.area515.resinprinter.display.dispmanx.DispManXDevice +logger.dispManX.level=debug +logger.dispManX.additivity=false +logger.dispManX.appenderRefs=rolling +logger.dispManX.appenderRef.rolling.ref=RollingFile +logger.servlet.name=org.eclipse.jetty.servlet.ServletHandler +logger.servlet.level=debug +logger.servlet.additivity=false +logger.servlet.appenderRefs=rolling +logger.servlet.appenderRef.rolling.ref=RollingFile #Default logger rootLogger.level=info diff --git a/host/images/PhotonicSplash.xcf b/host/images/PhotonicSplash.xcf new file mode 100644 index 000000000..cd13eaddc Binary files /dev/null and b/host/images/PhotonicSplash.xcf differ diff --git a/host/jwrapper/Photonic3dClient.xml b/host/jwrapper/Photonic3dClient.xml new file mode 100644 index 000000000..c25474dbc --- /dev/null +++ b/host/jwrapper/Photonic3dClient.xml @@ -0,0 +1,41 @@ + +C:\Users\wgilster\Desktop\compiles\JWrapper\PhotonicJWrapperBuild +Photonic3D +C:\Users\wgilster\git\Creation-Workshop-Host\host\src\main\resources\PhotonicSplash.png +C:\Users\wgilster\git\Creation-Workshop-Host\host\resourcesnew\favicon\android-icon-48x48.png +NoInstall + + Photonic3D + C:\Users\wgilster\git\Creation-Workshop-Host\host\resourcesnew\favicon\android-icon-48x48.png + org.area515.resinprinter.client.Main + -w + PhotonicSplash.png + +en +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\win32\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\win64\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\linux\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\linuxx64\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\macos64\jre1.8.0_111.jre +true +None +true +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.4.5/6fb96728ee26edb19fe329d94f3bd4df1a97652a/jackson-core-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.jcraft/jsch/0.1.53/658b682d5c817b27ae795637dfec047c63d29935/jsch-0.1.53.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpclient/4.3.3/18f4247ff4572a074444572cee34647c43e7c9c7/httpclient-4.3.3.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpcore/4.3.2/31fbbff1ddbf98f3aa7377c94d33b0447c646b6e/httpcore-4.3.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-core/2.0.1/c588b1493c6f92f3e63755c9b763c740546f805a/cling-core-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-support/2.0.1/3be864b366ec15b075a936ecaeec731d8c62eba3/cling-support-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-http/1.1.0/463012d16bcb06a623d3b931a6cfe19e4beace63/seamless-http-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-swing/1.1.0/9ceae59cb0b977a4103d9a52b5e777d2e259678b/seamless-swing-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-util/1.1.0/ef025cdbb79c4a031148b20a6d59db89c72ea0a3/seamless-util-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-xml/1.1.0/497f05ee745c564e0ab5c26e66e60d4f7502fba3/seamless-xml-1.1.0.jar +C:\Users\wgilster\.gradle\caches\modules-2\files-2.1\commons-cli\commons-cli\1.2\2bf96b7aa8b611c177d329452af1dc933e14501c\commons-cli-1.2.jar +C:\Users\wgilster\Desktop\compiles\photonic3d.jar + +-splash:PhotonicSplash.png + + + + + diff --git a/host/jwrapper/PhotonicFull.xml b/host/jwrapper/PhotonicFull.xml new file mode 100644 index 000000000..3bea2fcb3 --- /dev/null +++ b/host/jwrapper/PhotonicFull.xml @@ -0,0 +1,142 @@ + +C:\Users\wgilster\Desktop\compiles\JWrapper\JWrapperBuild +Photonic3D +C:\Users\wgilster\git\Creation-Workshop-Host\host\images\photonic64.png +C:\Users\wgilster\git\Creation-Workshop-Host\host\images\photonic64.png +NoInstall + + Photonic3D + C:\Users\wgilster\git\Creation-Workshop-Host\host\images\photonic64.png + org.area515.resinprinter.client.Main + +en +C:\Users\wgilster\git\Creation-Workshop-Host\host\jwrapper\jwrapperJREs\win32\jre1.8.0_111 +C:\Users\wgilster\git\Creation-Workshop-Host\host\jwrapper\jwrapperJREs\win64\jre1.8.0_111 +C:\Users\wgilster\git\Creation-Workshop-Host\host\jwrapper\jwrapperJREs\linux\jre1.8.0_111 +C:\Users\wgilster\git\Creation-Workshop-Host\host\jwrapper\jwrapperJREs\linuxx64\jre1.8.0_111 +C:\Users\wgilster\git\Creation-Workshop-Host\host\jwrapper\jwrapperJREs\macos64\jre1.8.0_111.jre +true +None +true +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.4.5/b400d47efaf8ab612c03b904ae5bf26c6706ade6/jackson-annotations-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.4.5/6fb96728ee26edb19fe329d94f3bd4df1a97652a/jackson-core-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.4.5/c69c0cb613128c69d84a6a0304ddb9fce82e8242/jackson-databind-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.4.5/535f27c7e322abc54c9758b50df1594f31ee6524/jackson-dataformat-xml-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.4.5/918e84a84ee0b202bb74bab590119b01bbea9830/jackson-dataformat-yaml-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.datatype/jackson-datatype-joda/2.4.5/830f7e1a9d69674900d1fa066d6ba27112ec6ca9/jackson-datatype-joda-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/2.4.5/b5a04c9664913552b75820834e945424e32b244f/jackson-jaxrs-base-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/2.4.5/aefd5dd637402fd697c1e2b083535d153ffd66c0/jackson-jaxrs-json-provider-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.4.5/af5432247be7a0248a03b900687bb9a74a68ccfa/jackson-module-jaxb-annotations-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/annotations/2.0.1/9ef6656259841cebfb9fb0697bb122ada4485498/annotations-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/19.0/6ce200f6b23222af3d8abb6b6459e6c44f4bb0e9/guava-19.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.jcraft/jsch/0.1.53/658b682d5c817b27ae795637dfec047c63d29935/jsch-0.1.53.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.sun.istack/istack-commons-runtime/2.16/927eb6fbb003f40d536efd96e7cbcb7ea4c1c2d0/istack-commons-runtime-2.16.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.sun.mail/javax.mail/1.5.5/ffcd34b5de820f35bcc9303649cf6ab2c65ad44e/javax.mail-1.5.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.2.7/f327817188a7dce503ae3c667c5931077630b0a1/jaxb-core-2.2.7.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.2.7/a29e53347934e694c9551981cecd242fad05022b/jaxb-impl-2.2.7.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.sun.xml.fastinfoset/FastInfoset/1.2.12/e8c1c096162a146c2d84135c5036edf54c1b1d38/FastInfoset-1.2.12.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/commons-cli/commons-cli/1.2/2bf96b7aa8b611c177d329452af1dc933e14501c/commons-cli-1.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/commons-codec/commons-codec/1.6/b7f0fc8f61ecadeb3695f0b9464755eee44374d4/commons-codec-1.6.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/2.4/b1b6ea3b7e4aa4f492509a4952029cd8e48019ad/commons-io-2.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/commons-logging/commons-logging/1.2/4bfc12adfe4842bf07b657f0369c4cb522955686/commons-logging-1.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-annotations/1.5.8/48d3002e43bde443f19750ec5670d345e9cd8d62/swagger-annotations-1.5.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-core/1.5.8/545fe5b92dc4f7fc360fbda972fcc75ba8532044/swagger-core-1.5.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-jaxrs/1.5.8/15fdd3c3c08254c845ff1f2f5a345e10a51e1dad/swagger-jaxrs-1.5.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/io.swagger/swagger-models/1.5.8/36a5fbf513a2793a665660bfc579b054b67d349e/swagger-models-1.5.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.activation/activation/1.1/e6cb541461c2834bdea3eb920f1884d1eb508b50/activation-1.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.annotation/javax.annotation-api/1.2/479c1e06db31c432330183f5cae684163f186146/javax.annotation-api-1.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.servlet/javax.servlet-api/3.1.0/3cd63d075497751784b2fa84be59432f4905bf7c/javax.servlet-api-3.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.validation/validation-api/1.1.0.Final/8613ae82954779d518631e05daa73a6a954817d5/validation-api-1.1.0.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.websocket/javax.websocket-api/1.0/fc843b649d4a1dcb0497669d262befa3918c7ba8/javax.websocket-api-1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.xml.bind/jaxb-api/2.2.7/2f51c4bb4724ea408096ee9100ff2827e07e5b7c/jaxb-api-2.2.7.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/javax.xml.bind/jsr173_api/1.0/c79b8431c3104315c0aeaed7bf23d0ab0edbaa09/jsr173_api-1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/joda-time/joda-time/2.2/a5f29a7acaddea3f4af307e8cf2d0cc82645fd7d/joda-time-2.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/net.jcip/jcip-annotations/1.0/afba4942caaeaf46aab0b976afd57cc7c181467e/jcip-annotations-1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.2.1/66f13681add50ca9e4546ffabafaaac7645db3cf/commons-lang3-3.2.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.6.1/e4ba98f1d4b3c80ec46392f25e094a6a2e58fcbf/commons-math3-3.6.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpclient/4.3.3/18f4247ff4572a074444572cee34647c43e7c9c7/httpclient-4.3.3.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpcore/4.3.2/31fbbff1ddbf98f3aa7377c94d33b0447c646b6e/httpcore-4.3.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.james/apache-mime4j/0.6.1/e70f12e1ea268c09373a5d02290aad4f557addd6/apache-mime4j-0.6.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.5/e7fd981408caba8a0c0fb276413562468d260160/log4j-api-2.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-core/2.5/7ed845de1dfe070d43511fab321784e6c4118398/log4j-core-2.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-anim/1.8/68197dfa3643a906ba250025a03dc42e6efe2dec/batik-anim-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-awt-util/1.8/5cd7f97060cdfab0139e70504962d48ceee71ef2/batik-awt-util-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-bridge/1.8/4ab4110b0ed4650ef50d4a344f0ca5c027f3283a/batik-bridge-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-css/1.8/2b3f22cc65702a0821b7f0178d055282a1cdde59/batik-css-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-dom/1.8/4e696cf01cee52e8c4f86c842b5d8314e689209c/batik-dom-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-ext/1.8/8713f3238cfac337624a90c3ad7d45d7bc6fb1b5/batik-ext-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-gvt/1.8/fbde4cd3c43001c162446cf43093d09fda346e11/batik-gvt-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-parser/1.8/7e4e69a4aa5317861864db93da5f246214750484/batik-parser-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-script/1.8/5bda6a9d45065b184c83c46b64d8002b4e0ab7c7/batik-script-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-svg-dom/1.8/97c9d00d08c849066d2359b0f1124f0e82b952c2/batik-svg-dom-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-svggen/1.8/c4684e18303e931845df704f9b9f6995fd770789/batik-svggen-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-transcoder/1.8/f330b3e9946ff21ddf3ea6d4f58ae44145cfd362/batik-transcoder-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-util/1.8/35dcd204f397d6976290ca48ffa0011ba9b7ef43/batik-util-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/batik-xml/1.8/9bf0ee759fed1e3a2e4ad41819eac69ff4873732/batik-xml-1.8.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.xmlgraphics/xmlgraphics-commons/1.5/7fb5c2b2c18f0e87fbe9bded16429a5d7cc2dc2b/xmlgraphics-commons-1.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.codehaus.woodstox/stax2-api/3.1.4/ac19014b1e6a7c08aad07fe114af792676b685b7/stax2-api-3.1.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/javax-websocket-client-impl/9.3.7.v20160115/6064e1a1624f5ae77207178f9e3eb2d79c64ec08/javax-websocket-client-impl-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/javax-websocket-server-impl/9.3.7.v20160115/35174ae3b835c7c54fcaa50966e10da3c45500a7/javax-websocket-server-impl-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/websocket-api/9.3.7.v20160115/d9bd9f922c03490674c5d3b2fed3a01e28148818/websocket-api-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/websocket-client/9.3.7.v20160115/ead61f9a9ab66369292be4d91fb7480728cf4923/websocket-client-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/websocket-common/9.3.7.v20160115/1d491b04bd46cec33488f2b830136019300694f1/websocket-common-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/websocket-server/9.3.7.v20160115/813526a845502e070d96dbbe99d94680eddb91e9/websocket-server-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty.websocket/websocket-servlet/9.3.7.v20160115/daa68ddaf4c887ca9bd398166752d576954f1e20/websocket-servlet-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-annotations/9.3.7.v20160115/be95b93f571db1b5b255b20a087b5c12ea87b0cc/jetty-annotations-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-client/9.3.7.v20160115/c144f460967c41b16bd17eafd7707c054342c8e/jetty-client-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-http/9.3.7.v20160115/1ae96c106af283080f4eb468597788650fdc0597/jetty-http-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-io/9.3.7.v20160115/a37a88dda790f595f002102d746b5371a1c80150/jetty-io-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-jndi/9.3.7.v20160115/9492c89906ffadcb66b07c290e95eff35ebe9bac/jetty-jndi-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-plus/9.3.7.v20160115/be6f661fdd9469ee90e80a30c8cefad98db7cc8d/jetty-plus-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-rewrite/9.3.7.v20160115/95de020baae052fbe459de255055e8ccc1d5563a/jetty-rewrite-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-security/9.3.7.v20160115/2ab9d3ecf02ba8ca918a047e090a0cad351588a6/jetty-security-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-server/9.3.7.v20160115/59cf44c6d05c0179ccbb9ec06a46083bfb96bbf6/jetty-server-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-servlet/9.3.7.v20160115/6213334a9b8b533f6af047bcd5b9fa2a7150720d/jetty-servlet-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-util/9.3.7.v20160115/d97b3d3676d69df5e635c5576dc3c46e17294898/jetty-util-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-webapp/9.3.7.v20160115/8cc2923b3129992d6009b3a9c8bbe5f0b81e10c2/jetty-webapp-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-xml/9.3.7.v20160115/1a329f5304c019bb85c129b0df73a341f53e8899/jetty-xml-9.3.7.v20160115.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-core/2.0.1/c588b1493c6f92f3e63755c9b763c740546f805a/cling-core-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-support/2.0.1/3be864b366ec15b075a936ecaeec731d8c62eba3/cling-support-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.freemarker/freemarker/2.3.23/cd143cd8c02a9f838e3cb75a6dfe133750437f6d/freemarker-2.3.23.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.javassist/javassist/3.20.0-GA/a9cbcdfb7e9f86fbc74d3afae65f2248bfbf82a0/javassist-3.20.0-GA.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/jaxrs-api/3.0.9.Final/a723b3e3a0fded98577023caaeb0284728d13da/jaxrs-api-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/resteasy-client/3.0.9.Final/8e395dbc2a6d3e7aa875419d393bcdb279aeefa1/resteasy-client-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/resteasy-jackson2-provider/3.0.9.Final/25fc4302f8bb3fdfe1bb720ac3e41a5395048335/resteasy-jackson2-provider-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/resteasy-jaxb-provider/3.0.9.Final/2a578057808f9192653f07027c11f6dbf10a428c/resteasy-jaxb-provider-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/resteasy-jaxrs/3.0.9.Final/34ace9253b98b9aefef6d3423aed2d240aed1edc/resteasy-jaxrs-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.resteasy/resteasy-multipart-provider/3.0.9.Final/62e958b723c83b753a688aa29f8563b92fbacda1/resteasy-multipart-provider-3.0.9.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.jboss.spec.javax.annotation/jboss-annotations-api_1.1_spec/1.0.1.Final/8f9335f966885529d1254793182ff3df1c515eb8/jboss-annotations-api_1.1_spec-1.0.1.Final.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.mockito/mockito-core/1.10.19/e8546f5bef4e061d8dd73895b4e8f40e3fe6effe/mockito-core-1.10.19.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.objenesis/objenesis/2.1/87c0ea803b69252868d09308b4618f766f135a96/objenesis-2.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/5.0.1/7b7147a390a93a14d2edfdcf3f7b0e87a0939c3e/asm-commons-5.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/5.0.1/1b1e6e9d869acd704056d0a4223071a511c619e6/asm-tree-5.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/5.0.1/2fd56467a018aafe6ec6a73ccba520be4a7e1565/asm-5.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-api-mockito/1.6.4/fe12509b7e9e49d25131f4155145748a31e42e40/powermock-api-mockito-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-api-support/1.6.4/314daafb761541293595630e10a3699ebc07881d/powermock-api-support-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-core/1.6.4/85fb32e9ccba748d569fc36aef92e0b9e7f40b87/powermock-core-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-module-junit4-common/1.6.4/b0b578da443794ceb8224bd5f5f852aaf40f1b81/powermock-module-junit4-common-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-module-junit4/1.6.4/8692eb1d9bb8eb1310ffe8a20c2da7ee6d1b5994/powermock-module-junit4-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.powermock/powermock-reflect/1.6.4/5532f4e7c42db4bca4778bc9f1afcd4b0ee0b893/powermock-reflect-1.6.4.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.reflections/reflections/0.9.10/c7f4ea230aedc757ca1499ebcfb2953c81cf8b67/reflections-0.9.10.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.rxtx/rxtx/2.1.7/e96c946be1e6537378fd532d2742b523df2725a4/rxtx-2.1.7.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.scream3r/jssc/2.8.0/7e8d93052a8743a7937a6d46854057ac1ce876c1/jssc-2.8.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-http/1.1.0/463012d16bcb06a623d3b931a6cfe19e4beace63/seamless-http-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-swing/1.1.0/9ceae59cb0b977a4103d9a52b5e777d2e259678b/seamless-swing-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-util/1.1.0/ef025cdbb79c4a031148b20a6d59db89c72ea0a3/seamless-util-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-xml/1.1.0/497f05ee745c564e0ab5c26e66e60d4f7502fba3/seamless-xml-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.6.3/365f7c674cb7a4b38531353392128d9fb1e37c0e/slf4j-api-1.6.3.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.12/ebe66a6b88caab31d7a19571ad23656377523545/snakeyaml-1.12.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/se.sawano.java/alphanumeric-comparator/1.4.1/24e5642e40c00db96a0c0c077f774cc1f10f7546/alphanumeric-comparator-1.4.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/xalan/xalan/2.7.0/a33c0097f1c70b20fa7ded220ea317eb3500515e/xalan-2.7.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/xml-apis/xml-apis-ext/1.3.04/41a8b86b358e87f3f13cf46069721719105aff66/xml-apis-ext-1.3.04.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/xml-apis/xml-apis/2.0.2/3136ca936f64c9d68529f048c2618bd356bf85c9/xml-apis-2.0.2.jar +C:/Users/wgilster/git/Creation-Workshop-Host/host/libs/mp4parser/mp4parser.jar +C:\Users\wgilster\Desktop\compiles\photonic3d.jar + + + + + + diff --git a/host/jwrapper/PrintFlowClient.xml b/host/jwrapper/PrintFlowClient.xml new file mode 100644 index 000000000..bb45789f7 --- /dev/null +++ b/host/jwrapper/PrintFlowClient.xml @@ -0,0 +1,41 @@ + +C:\Users\wgilster\Desktop\compiles\JWrapper\PrintFlowJWrapperBuild +Photonic3D +C:\Users\wgilster\Desktop\compiles\SplashScreen1920x1080HighRes.jpg +C:\Users\wgilster\Desktop\compiles\IconPrintFlow48x48.png +NoInstall + + Photonic3D + C:\Users\wgilster\Desktop\compiles\IconPrintFlow48x48.png + org.area515.resinprinter.client.Main + -w + SplashScreen1920x1080HighRes.jpg + +en +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\win32\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\win64\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\linux\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\linuxx64\jre1.8.0_111 +C:\Users\wgilster\Desktop\compiles\JWrapper\jwrapperJREs\macos64\jre1.8.0_111.jre +true +None +true +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.4.5/6fb96728ee26edb19fe329d94f3bd4df1a97652a/jackson-core-2.4.5.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/com.jcraft/jsch/0.1.53/658b682d5c817b27ae795637dfec047c63d29935/jsch-0.1.53.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpclient/4.3.3/18f4247ff4572a074444572cee34647c43e7c9c7/httpclient-4.3.3.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.apache.httpcomponents/httpcore/4.3.2/31fbbff1ddbf98f3aa7377c94d33b0447c646b6e/httpcore-4.3.2.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-core/2.0.1/c588b1493c6f92f3e63755c9b763c740546f805a/cling-core-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.fourthline.cling/cling-support/2.0.1/3be864b366ec15b075a936ecaeec731d8c62eba3/cling-support-2.0.1.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-http/1.1.0/463012d16bcb06a623d3b931a6cfe19e4beace63/seamless-http-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-swing/1.1.0/9ceae59cb0b977a4103d9a52b5e777d2e259678b/seamless-swing-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-util/1.1.0/ef025cdbb79c4a031148b20a6d59db89c72ea0a3/seamless-util-1.1.0.jar +C:/Users/wgilster/.gradle/caches/modules-2/files-2.1/org.seamless/seamless-xml/1.1.0/497f05ee745c564e0ab5c26e66e60d4f7502fba3/seamless-xml-1.1.0.jar +C:\Users\wgilster\.gradle\caches\modules-2\files-2.1\commons-cli\commons-cli\1.2\2bf96b7aa8b611c177d329452af1dc933e14501c\commons-cli-1.2.jar +C:\Users\wgilster\Desktop\compiles\PrintFlowphotonic3d.jar + +-splash:SplashScreen1920x1080HighRes.jpg + + + + + diff --git a/host/launchers/CompilePhotonic3DClientToNative.launch b/host/launchers/CompilePhotonic3DClientToNative.launch new file mode 100644 index 000000000..4edd9870b --- /dev/null +++ b/host/launchers/CompilePhotonic3DClientToNative.launch @@ -0,0 +1,6 @@ + + + + + + diff --git a/host/launchers/CompilePrintFlowClientToNative.launch b/host/launchers/CompilePrintFlowClientToNative.launch new file mode 100644 index 000000000..e3742c70c --- /dev/null +++ b/host/launchers/CompilePrintFlowClientToNative.launch @@ -0,0 +1,6 @@ + + + + + + diff --git a/host/launchers/Photonic 3D Client.launch b/host/launchers/Photonic 3D Client.launch index 9a4fa15bf..06f7aa2ed 100644 --- a/host/launchers/Photonic 3D Client.launch +++ b/host/launchers/Photonic 3D Client.launch @@ -11,6 +11,7 @@ + diff --git a/host/launchers/Photonic 3D.launch b/host/launchers/Photonic 3D.launch index a2598e930..bcdd24950 100644 --- a/host/launchers/Photonic 3D.launch +++ b/host/launchers/Photonic 3D.launch @@ -12,5 +12,5 @@ - + diff --git a/host/resourcesnew/2dProperties.html b/host/resourcesnew/2dProperties.html index 1a924afbd..fbf0c81a5 100644 --- a/host/resourcesnew/2dProperties.html +++ b/host/resourcesnew/2dProperties.html @@ -57,7 +57,11 @@

Base Platform Calculator

diff --git a/host/resourcesnew/advancedProperties.html b/host/resourcesnew/advancedProperties.html index 6be586400..95dc81fe7 100644 --- a/host/resourcesnew/advancedProperties.html +++ b/host/resourcesnew/advancedProperties.html @@ -23,6 +23,21 @@

Override model normal with right hand rule

+
+
+ +

Printer response timeout (ms)

+ +
+
+
+
+ +

Restart serial on timeout

+ +
+
+ diff --git a/host/resourcesnew/bootcards/css/bootcards-desktop.min.css b/host/resourcesnew/bootcards/css/bootcards-desktop.min.css index 3f1d4cc6b..b5149c205 100644 --- a/host/resourcesnew/bootcards/css/bootcards-desktop.min.css +++ b/host/resourcesnew/bootcards/css/bootcards-desktop.min.css @@ -1,3 +1,3 @@ /* bootcards 1.1.2 2015-04-17 12:01 */ -@charset "UTF-8";.offcanvas,.offcanvas-list{position:fixed;top:0;bottom:0;width:200px;overflow-y:auto;z-index:1050;height:100%;-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.offcanvas-left{left:-200px}.offcanvas-left.active{-webkit-transform:translate3d(200px,0,0);-moz-transform:translate3d(200px,0,0);-o-transform:translate3d(200px,0,0);-ms-transform:translate3d(200px,0,0);transform:translate3d(200px,0,0)}.offcanvas-list{width:350px;left:-350px}.offcanvas-list.active{-webkit-transform:translate3d(350px,0,0);-moz-transform:translate3d(350px,0,0);-o-transform:translate3d(350px,0,0);-ms-transform:translate3d(350px,0,0);transform:translate3d(350px,0,0)}.offcanvas-list-title{position:fixed;background:rgba(247,247,247,.98);height:45px;font-size:17px;font-weight:500;line-height:44px;border-right:1px solid rgba(0,0,0,.2);border-bottom:1px solid rgba(0,0,0,.2)}.offcanvaslist-toggle{-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.offcanvaslist-toggle.active{-webkit-transform:translate3d(50px,0,0);-moz-transform:translate3d(50px,0,0);-o-transform:translate3d(50px,0,0);-ms-transform:translate3d(50px,0,0);transform:translate3d(50px,0,0)}.push-right{-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.push-right.active-left{-webkit-transform:translate3d(200px,0,0);-moz-transform:translate3d(200px,0,0);-o-transform:translate3d(200px,0,0);-ms-transform:translate3d(200px,0,0);transform:translate3d(200px,0,0)}.push-right.active-right{-webkit-transform:translate3d(-200px,0,0);-moz-transform:translate3d(-200px,0,0);-o-transform:translate3d(-200px,0,0);-ms-transform:translate3d(-200px,0,0);transform:translate3d(-200px,0,0)}.btn i{margin-right:5px}.btn.icon-only i,.btn.icononly i{margin-right:0}.list-group-item{margin-left:15px;padding-left:0}.list-group-item .list-group-item-text{line-height:18px;margin-bottom:5px;overflow:hidden}.list-group-item .list-group-item-heading:last-child,.list-group-item .list-group-item-text:last-child{margin-bottom:0}.list-group-item .row div>.list-group-item-heading:last-child,.list-group-item .row div>.list-group-item-text:last-child{margin-bottom:5px}.list-group-item .row div:last-child>.list-group-item-heading:last-child,.list-group-item .row div:last-child>.list-group-item-text:last-child{margin-bottom:0}.list-group-item img{height:40px;width:40px;margin-right:15px}.list-group-item i{opacity:.3;width:40px;text-align:center;margin-right:15px}a.list-group-item{cursor:pointer}a.list-group-item.active,a.list-group-item.active:focus,a.list-group-item.active:hover,a.list-group-item:active,a.list-group-item:hover{margin-left:0;padding-left:15px}.panel>.list-group .list-group-item:last-child{border-bottom:0}.bootcards-list-group-item-content{overflow:hidden}a.list-group-item:before{font-family:FontAwesome;content:'';position:absolute;right:15px;top:50%;font-size:14px;line-height:14px;margin-top:-7px;color:#ccc}.list-group.bootcards-no-indicators a.list-group-item:before{display:none}.list-group-item.bootcards-list-subheading{margin-left:0;padding-left:15px;font-weight:500;font-size:14px;z-index:6}a.list-group-item.bootcards-list-subheading{padding-left:40px}a.list-group-item.bootcards-list-subheading:before{font-family:FontAwesome;content:'';position:absolute;left:15px;top:50%;font-size:14px;line-height:14px;margin-top:-7px;color:#ccc}a.list-group-item.bootcards-list-subheading.collapsed:before{content:''}.bootcards-az-picker{position:fixed;width:25px;margin:0 0 0 -26px;padding:0;list-style:none;z-index:9;top:0;right:0;bottom:0;height:100%;background:#fff;padding-bottom:50px;padding-top:5px}.bootcards-az-picker li{font-size:11px;font-weight:500;text-align:center;padding:0;background:#fff;height:3.846%}.bootcards-az-picker a{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-moz-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-moz-box-pack:center;-ms-flex-pack:center;justify-content:center;height:100%}.bootcards-az-picker a:hover{text-decoration:none}@media only screen and (min-device-width:480px) and (max-device-width:767px) and (orientation:landscape){.bootcards-az-picker li{height:7.692%}.bootcards-az-picker li:nth-child(even){display:none}}.bootcards-list .form-group{position:relative;margin-bottom:0}@media (min-width:768px){.bootcards-list .form-group{margin-bottom:0}}.bootcards-list .search-form input,.bootcards-list form input{padding-left:32px}.bootcards-list .search-form .btn,.bootcards-list form .btn{color:#fff}.bootcards-list .search-form i.fa-search,.bootcards-list form i.fa-search{position:absolute;left:12px;top:10px;color:#999;font-size:14px}.bootcards-desktop-footer{display:none}.panel-default .panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-body>:last-child{margin-bottom:0}.panel-footer{overflow:hidden}.modal-footer small,.panel-footer small{color:#aaa;display:block;text-align:center;line-height:22px}.list-group+.panel-footer{border-top-width:1px}.btn label{margin:0;font-weight:inherit;cursor:pointer}.bootcards-clearinput{position:absolute;top:11px;color:#ccc;display:block;font-size:0}.bootcards-clearinput i{line-height:1;font-size:18px}.form-horizontal .form-group div div{padding:0}.bootcards-toggle{position:relative;display:block;width:74px;height:30px;background-color:#fff;border:2px solid #ddd;border-radius:20px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:background-color,border;-moz-transition-property:background-color,border;transition-property:background-color,border}.bootcards-toggle .bootcards-toggle-handle{position:absolute;top:-1px;left:-1px;z-index:2;width:28px;height:28px;background-color:#fff;border:1px solid #ddd;border-radius:100px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform,border,width;-moz-transition-property:-moz-transform,border,width;transition-property:transform,border,width}.bootcards-toggle:before{position:absolute;top:3px;right:11px;font-size:13px;color:#999;text-transform:uppercase;content:"Off"}.bootcards-toggle.active{background-color:#5cb85c;border:2px solid #5cb85c}.bootcards-toggle.active .bootcards-toggle-handle{border-color:#5cb85c;-webkit-transform:translate3d(44px,0,0);-ms-transform:translate3d(44px,0,0);transform:translate3d(44px,0,0)}.bootcards-toggle.active:before{right:auto;left:15px;color:#fff;content:"On"}.bootcards-toggle input[type=checkbox]{display:none}.bootcards-calendar{margin-top:-15px;background:#fff}.bootcards-calendar .fc-content td:first-child,.bootcards-calendar .fc-content th:first-child{border-left-width:0}.bootcards-calendar .fc-content td:last-child,.bootcards-calendar .fc-content th:last-child{border-right-width:0}.bootcards-calendar .fc-header-title h2{font-size:14px;text-transform:uppercase;color:#8f8f94;font-weight:400;margin:0;padding:8px 0;line-height:20px}.bootcards-calendar .fc-header-left,.bootcards-calendar .fc-header-right{padding:0 10px}.bootcards-calendar .fc-button .fc-icon{margin:0}.bootcards-calendar .fc-header .fc-button{background:0 0;box-shadow:none;border-color:transparent;margin:0;height:44px;line-height:40px;font-size:16px;text-shadow:none;padding:0}.bootcards-calendar .fc-header-left .fc-button{margin-right:10px}.bootcards-calendar .fc-header-right .fc-button{margin-left:10px}.bootcards-calendar .fc-icon-left-single-arrow:after,.bootcards-calendar .fc-icon-right-single-arrow:after{font-family:FontAwesome;font-size:18px;font-weight:400}.bootcards-calendar .fc-icon-left-single-arrow:after{content:''}.bootcards-calendar .fc-icon-right-single-arrow:after{content:''}.bootcards-calendar .fc-header .fc-button.fc-state-disabled{color:#AAA;opacity:1}.bootcards-calendar .fc-header .fc-button.fc-state-active{color:#AAA}.bootcards-calendar .fc-day-header{font-weight:400;font-size:12px;border-left-color:transparent;padding:5px 0;padding-right:3px}.bootcards-calendar .fc-event{background:#007aff;border-color:#007aff}.bootcards-calendar .fc-today{background:#FFF2F2}.table>tbody>tr>td:first-child,.table>tbody>tr>th:first-child,.table>tfoot>tr>td:first-child,.table>tfoot>tr>th:first-child,.table>thead>tr>td:first-child,.table>thead>tr>th:first-child{padding-left:15px}.table>tbody>tr>td:last-child,.table>tbody>tr>th:last-child,.table>tfoot>tr>td:last-child,.table>tfoot>tr>th:last-child,.table>thead>tr>td:last-child,.table>thead>tr>th:last-child{padding-right:15px}@font-face{font-family:icomoon;src:url(../fonts/icomoon.eot?-n2q9vw);src:url(../fonts/icomoon.eot?#iefix-n2q9vw) format("embedded-opentype"),url(../fonts/icomoon.woff?-n2q9vw) format("woff"),url(../fonts/icomoon.ttf?-n2q9vw) format("truetype"),url(../fonts/icomoon.svg?-n2q9vw#icomoon) format("svg");font-weight:400;font-style:normal}[class*=" icon-"],[class^=icon-]{font-family:icomoon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-file-pdf:before{content:"\e4e2"}.icon-file-word:before{content:"\e4e4"}.icon-file-excel:before{content:"\e4e5"}.icon-file-powerpoint:before{content:"\e4e7"}.icon-file:before{content:"\e08d"}.bootcards-file .list-group-item:first-child{position:relative;padding-left:74px}.bootcards-file .list-group-item:first-child i{font-size:64px;width:64px;position:absolute;left:0;top:10px}.bootcards-file .list-group-item :last-child{margin-bottom:0}.bootcards-chart .bootcards-chart-canvas{height:200px}.bootcards-summary .panel-body{padding:7px 25px}.bootcards-summary .panel-body>.row>div{padding:8px}.bootcards-summary-item{background:#f5f5f5;display:block;border-radius:4px;padding:25px 10px;text-align:center;position:relative;height:130px}@media (max-width:400px){.bootcards-summary-item{padding:15px 5px}}.bootcards-summary-item:hover{text-decoration:none;background:#eee}.bootcards-summary-item>i{color:#bbb;display:block;text-align:center;margin-bottom:5px}.bootcards-summary-item h4{margin:0 auto}.bootcards-summary-item .badge,.bootcards-summary-item .label{position:absolute;top:10px;right:10px}.bootcards-richtext>.panel-body{padding:25px;max-width:640px;margin:0 auto}.bootcards-richtext>.panel-body>:first-child{margin-top:0}@media (max-width:767px){.modal-dialog.modal-sm{margin:15px}}@media (min-width:768px){.modal-dialog.modal-sm{width:400px}}body{padding-top:80px;background:#f5f5f5}.navbar .navbar-brand{font-weight:700;color:#333}.navbar .btn i{vertical-align:0}.navbar .btn-back,.navbar .btn-menu,.navbar button[data-toggle=offcanvas]{display:none!important}@media (min-width:768px){body.has-bootcards-navbar-double{padding-top:110px}body.has-bootcards-navbar-double .bootcards-navbar-double .navbar-brand{line-height:50px}body.has-bootcards-navbar-double .bootcards-nav-secondary.navbar-nav>li>a{padding-top:5px;padding-bottom:5px}body.has-bootcards-navbar-double .bootcards-nav-primary,body.has-bootcards-navbar-double .bootcards-nav-secondary{margin-right:-15px;clear:right}body.has-bootcards-navbar-double .bootcards-nav-secondary,body.has-bootcards-navbar-double .bootcards-nav-secondary .dropdown-menu{font-size:12px}}.list-group label{font-weight:400}a.list-group-item.active:before{color:#e1edf7}.bootcards-list .panel-body>.search-form>.row>div:first-child,.bootcards-list .panel-body>form>.row>div:first-child{padding-right:0}.panel-title{margin:7px 0}.panel-content form{padding:15px}.modal-header{line-height:32px}.modal-title{text-align:center}.bootcards-clearinput{right:23px}.navbar-fixed-bottom{position:static;padding-top:20px;padding-bottom:20px;background:0 0}.navbar-fixed-bottom .btn-group{display:none}.navbar-fixed-bottom .bootcards-desktop-footer{display:block;text-align:center;color:#999}.bootcards-documentation-list>.panel{position:fixed}@media (min-width:768px){.bootcards-documentation-list>.panel{width:163px}}@media (min-width:992px){.bootcards-documentation-list>.panel{width:213px}}@media (min-width:1200px){.bootcards-documentation-list>.panel{width:263px}} \ No newline at end of file +@charset "UTF-8";.offcanvas,.offcanvas-list{position:fixed;top:0;bottom:0;width:200px;overflow-y:auto;z-index:1050;height:100%;-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.offcanvas-left{left:-200px}.offcanvas-left.active{-webkit-transform:translate3d(200px,0,0);-moz-transform:translate3d(200px,0,0);-o-transform:translate3d(200px,0,0);-ms-transform:translate3d(200px,0,0);transform:translate3d(200px,0,0)}.offcanvas-list{width:350px;left:-350px}.offcanvas-list.active{-webkit-transform:translate3d(350px,0,0);-moz-transform:translate3d(350px,0,0);-o-transform:translate3d(350px,0,0);-ms-transform:translate3d(350px,0,0);transform:translate3d(350px,0,0)}.offcanvas-list-title{position:fixed;background:rgba(247,247,247,.98);height:45px;font-size:17px;font-weight:500;line-height:44px;border-right:1px solid rgba(0,0,0,.2);border-bottom:1px solid rgba(0,0,0,.2)}.offcanvaslist-toggle{-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.offcanvaslist-toggle.active{-webkit-transform:translate3d(50px,0,0);-moz-transform:translate3d(50px,0,0);-o-transform:translate3d(50px,0,0);-ms-transform:translate3d(50px,0,0);transform:translate3d(50px,0,0)}.push-right{-webkit-transform:translate3d(0px,0,0);-moz-transform:translate3d(0px,0,0);-o-transform:translate3d(0px,0,0);-ms-transform:translate3d(0px,0,0);transform:translate3d(0px,0,0);-webkit-transition:.25s ease;-moz-transition:.25s ease;-o-transition:.25s ease;transition:.25s ease}.push-right.active-left{-webkit-transform:translate3d(200px,0,0);-moz-transform:translate3d(200px,0,0);-o-transform:translate3d(200px,0,0);-ms-transform:translate3d(200px,0,0);transform:translate3d(200px,0,0)}.push-right.active-right{-webkit-transform:translate3d(-200px,0,0);-moz-transform:translate3d(-200px,0,0);-o-transform:translate3d(-200px,0,0);-ms-transform:translate3d(-200px,0,0);transform:translate3d(-200px,0,0)}.btn i{margin-right:5px}.btn.icon-only i,.btn.icononly i{margin-right:0}.list-group-item{margin-left:15px;padding-left:0}.list-group-item .list-group-item-text{line-height:18px;margin-bottom:5px;overflow:hidden}.list-group-item .list-group-item-heading:last-child,.list-group-item .list-group-item-text:last-child{margin-bottom:0}.list-group-item .row div>.list-group-item-heading:last-child,.list-group-item .row div>.list-group-item-text:last-child{margin-bottom:5px}.list-group-item .row div:last-child>.list-group-item-heading:last-child,.list-group-item .row div:last-child>.list-group-item-text:last-child{margin-bottom:0}.list-group-item img{height:40px;width:40px;margin-right:15px}.list-group-item i{opacity:.3;width:40px;text-align:center;margin-right:15px;}a.list-group-item{cursor:pointer}a.list-group-item.active,a.list-group-item.active:focus,a.list-group-item.active:hover,a.list-group-item:active,a.list-group-item:hover{margin-left:0;padding-left:15px}.panel>.list-group .list-group-item:last-child{border-bottom:0}.bootcards-list-group-item-content{overflow:hidden}a.list-group-item:before{font-family:FontAwesome;content:'';position:absolute;right:15px;top:50%;font-size:14px;line-height:14px;margin-top:-7px;color:#ccc}.list-group.bootcards-no-indicators a.list-group-item:before{display:none}.list-group-item.bootcards-list-subheading{margin-left:0;padding-left:15px;font-weight:500;font-size:14px;z-index:6}a.list-group-item.bootcards-list-subheading{padding-left:40px}a.list-group-item.bootcards-list-subheading:before{font-family:FontAwesome;content:'';position:absolute;left:15px;top:50%;font-size:14px;line-height:14px;margin-top:-7px;color:#ccc}a.list-group-item.bootcards-list-subheading.collapsed:before{content:''}.bootcards-az-picker{position:fixed;width:25px;margin:0 0 0 -26px;padding:0;list-style:none;z-index:9;top:0;right:0;bottom:0;height:100%;background:#fff;padding-bottom:50px;padding-top:5px}.bootcards-az-picker li{font-size:11px;font-weight:500;text-align:center;padding:0;background:#fff;height:3.846%}.bootcards-az-picker a{display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-moz-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-moz-box-pack:center;-ms-flex-pack:center;justify-content:center;height:100%}.bootcards-az-picker a:hover{text-decoration:none}@media only screen and (min-device-width:480px) and (max-device-width:767px) and (orientation:landscape){.bootcards-az-picker li{height:7.692%}.bootcards-az-picker li:nth-child(even){display:none}}.bootcards-list .form-group{position:relative;margin-bottom:0}@media (min-width:768px){.bootcards-list .form-group{margin-bottom:0}}.bootcards-list .search-form input,.bootcards-list form input{padding-left:32px}.bootcards-list .search-form .btn,.bootcards-list form .btn{color:#fff}.bootcards-list .search-form i.fa-search,.bootcards-list form i.fa-search{position:absolute;left:12px;top:10px;color:#999;font-size:14px}.bootcards-desktop-footer{display:none}.panel-default .panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-body>:last-child{margin-bottom:0}.panel-footer{overflow:hidden}.modal-footer small,.panel-footer small{color:#aaa;display:block;text-align:center;line-height:22px}.list-group+.panel-footer{border-top-width:1px}.btn label{margin:0;font-weight:inherit;cursor:pointer}.bootcards-clearinput{position:absolute;top:11px;color:#ccc;display:block;font-size:0}.bootcards-clearinput i{line-height:1;font-size:18px}.form-horizontal .form-group div div{padding:0}.bootcards-toggle{position:relative;display:block;width:74px;height:30px;background-color:#fff;border:2px solid #ddd;border-radius:20px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:background-color,border;-moz-transition-property:background-color,border;transition-property:background-color,border}.bootcards-toggle .bootcards-toggle-handle{position:absolute;top:-1px;left:-1px;z-index:2;width:28px;height:28px;background-color:#fff;border:1px solid #ddd;border-radius:100px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform,border,width;-moz-transition-property:-moz-transform,border,width;transition-property:transform,border,width}.bootcards-toggle:before{position:absolute;top:3px;right:11px;font-size:13px;color:#999;text-transform:uppercase;content:"Off"}.bootcards-toggle.active{background-color:#5cb85c;border:2px solid #5cb85c}.bootcards-toggle.active .bootcards-toggle-handle{border-color:#5cb85c;-webkit-transform:translate3d(44px,0,0);-ms-transform:translate3d(44px,0,0);transform:translate3d(44px,0,0)}.bootcards-toggle.active:before{right:auto;left:15px;color:#fff;content:"On"}.bootcards-toggle input[type=checkbox]{display:none}.bootcards-calendar{margin-top:-15px;background:#fff}.bootcards-calendar .fc-content td:first-child,.bootcards-calendar .fc-content th:first-child{border-left-width:0}.bootcards-calendar .fc-content td:last-child,.bootcards-calendar .fc-content th:last-child{border-right-width:0}.bootcards-calendar .fc-header-title h2{font-size:14px;text-transform:uppercase;color:#8f8f94;font-weight:400;margin:0;padding:8px 0;line-height:20px}.bootcards-calendar .fc-header-left,.bootcards-calendar .fc-header-right{padding:0 10px}.bootcards-calendar .fc-button .fc-icon{margin:0}.bootcards-calendar .fc-header .fc-button{background:0 0;box-shadow:none;border-color:transparent;margin:0;height:44px;line-height:40px;font-size:16px;text-shadow:none;padding:0}.bootcards-calendar .fc-header-left .fc-button{margin-right:10px}.bootcards-calendar .fc-header-right .fc-button{margin-left:10px}.bootcards-calendar .fc-icon-left-single-arrow:after,.bootcards-calendar .fc-icon-right-single-arrow:after{font-family:FontAwesome;font-size:18px;font-weight:400}.bootcards-calendar .fc-icon-left-single-arrow:after{content:''}.bootcards-calendar .fc-icon-right-single-arrow:after{content:''}.bootcards-calendar .fc-header .fc-button.fc-state-disabled{color:#AAA;opacity:1}.bootcards-calendar .fc-header .fc-button.fc-state-active{color:#AAA}.bootcards-calendar .fc-day-header{font-weight:400;font-size:12px;border-left-color:transparent;padding:5px 0;padding-right:3px}.bootcards-calendar .fc-event{background:#007aff;border-color:#007aff}.bootcards-calendar .fc-today{background:#FFF2F2}.table>tbody>tr>td:first-child,.table>tbody>tr>th:first-child,.table>tfoot>tr>td:first-child,.table>tfoot>tr>th:first-child,.table>thead>tr>td:first-child,.table>thead>tr>th:first-child{padding-left:15px}.table>tbody>tr>td:last-child,.table>tbody>tr>th:last-child,.table>tfoot>tr>td:last-child,.table>tfoot>tr>th:last-child,.table>thead>tr>td:last-child,.table>thead>tr>th:last-child{padding-right:15px}@font-face{font-family:icomoon;src:url(../fonts/icomoon.eot?-n2q9vw);src:url(../fonts/icomoon.eot?#iefix-n2q9vw) format("embedded-opentype"),url(../fonts/icomoon.woff?-n2q9vw) format("woff"),url(../fonts/icomoon.ttf?-n2q9vw) format("truetype"),url(../fonts/icomoon.svg?-n2q9vw#icomoon) format("svg");font-weight:400;font-style:normal}[class*=" icon-"],[class^=icon-]{font-family:icomoon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-file-pdf:before{content:"\e4e2"}.icon-file-word:before{content:"\e4e4"}.icon-file-excel:before{content:"\e4e5"}.icon-file-powerpoint:before{content:"\e4e7"}.icon-file:before{content:"\e08d"}.bootcards-file .list-group-item:first-child{position:relative;padding-left:74px}.bootcards-file .list-group-item:first-child i{font-size:64px;width:64px;position:absolute;left:0;top:10px}.bootcards-file .list-group-item :last-child{margin-bottom:0}.bootcards-chart .bootcards-chart-canvas{height:200px}.bootcards-summary .panel-body{padding:7px 25px}.bootcards-summary .panel-body>.row>div{padding:8px}.bootcards-summary-item{background:#f5f5f5;display:block;border-radius:4px;padding:25px 10px;text-align:center;position:relative;height:130px}@media (max-width:400px){.bootcards-summary-item{padding:15px 5px}}.bootcards-summary-item:hover{text-decoration:none;background:#eee}.bootcards-summary-item>i{color:#bbb;display:block;text-align:center;margin-bottom:5px}.bootcards-summary-item h4{margin:0 auto}.bootcards-summary-item .badge,.bootcards-summary-item .label{position:absolute;top:10px;right:10px}.bootcards-richtext>.panel-body{padding:25px;max-width:640px;margin:0 auto}.bootcards-richtext>.panel-body>:first-child{margin-top:0}@media (max-width:767px){.modal-dialog.modal-sm{margin:15px}}@media (min-width:768px){.modal-dialog.modal-sm{width:400px}}body{padding-top:80px;background:#f5f5f5}.navbar .navbar-brand{font-weight:700;color:#333}.navbar .btn i{vertical-align:0}.navbar .btn-back,.navbar .btn-menu,.navbar button[data-toggle=offcanvas]{display:none!important}@media (min-width:768px){body.has-bootcards-navbar-double{padding-top:110px}body.has-bootcards-navbar-double .bootcards-navbar-double .navbar-brand{line-height:50px}body.has-bootcards-navbar-double .bootcards-nav-secondary.navbar-nav>li>a{padding-top:5px;padding-bottom:5px}body.has-bootcards-navbar-double .bootcards-nav-primary,body.has-bootcards-navbar-double .bootcards-nav-secondary{margin-right:-15px;clear:right}body.has-bootcards-navbar-double .bootcards-nav-secondary,body.has-bootcards-navbar-double .bootcards-nav-secondary .dropdown-menu{font-size:12px}}.list-group label{font-weight:400}a.list-group-item.active:before{color:#e1edf7}.bootcards-list .panel-body>.search-form>.row>div:first-child,.bootcards-list .panel-body>form>.row>div:first-child{padding-right:0}.panel-title{margin:7px 0}.panel-content form{padding:15px}.modal-header{line-height:32px}.modal-title{text-align:center}.bootcards-clearinput{right:23px}.navbar-fixed-bottom{position:static;padding-top:20px;padding-bottom:20px;background:0 0}.navbar-fixed-bottom .btn-group{display:none}.navbar-fixed-bottom .bootcards-desktop-footer{display:block;text-align:center;color:#999}.bootcards-documentation-list>.panel{position:fixed}@media (min-width:768px){.bootcards-documentation-list>.panel{width:163px}}@media (min-width:992px){.bootcards-documentation-list>.panel{width:213px}}@media (min-width:1200px){.bootcards-documentation-list>.panel{width:263px}} \ No newline at end of file diff --git a/host/resourcesnew/bootstrap/css/bootstrap.min.css b/host/resourcesnew/bootstrap/css/bootstrap.min.css index d65c66b1b..3f681f394 100644 --- a/host/resourcesnew/bootstrap/css/bootstrap.min.css +++ b/host/resourcesnew/bootstrap/css/bootstrap.min.css @@ -2,4 +2,4 @@ * Bootstrap v3.3.5 (http://getbootstrap.com) * Copyright 2011-2015 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:"\2a"}.glyphicon-plus:before{content:"\2b"}.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:thin dotted;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:thin dotted;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[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:14.33px;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:thin dotted;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,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,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,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,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,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,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,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,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,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,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,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,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-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-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-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:3;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:2;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{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{min-height:16.43px;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);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:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.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,.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,.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}} \ No newline at end of file + *//*! 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:"\2a"}.glyphicon-plus:before{content:"\2b"}.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:thin dotted;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:thin dotted;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[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:14.33px;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:thin dotted;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,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,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,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,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,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,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,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,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,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,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,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,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-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-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-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:3;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:2;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{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{min-height:16.43px;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);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:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.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,.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,.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}} \ No newline at end of file diff --git a/host/resourcesnew/cwh/js/cwhtestscript.js b/host/resourcesnew/cwh/js/cwhtestscript.js index a96bfc551..de5dd1e6e 100644 --- a/host/resourcesnew/cwh/js/cwhtestscript.js +++ b/host/resourcesnew/cwh/js/cwhtestscript.js @@ -1,15 +1,16 @@ -angular.module('cwh.testscript', []).directive('cwhTestscript', ['$window', '$animate', function($window, $animate) { +angular.module('cwh.testscript', []).directive('cwhTestscript', ['$window', '$animate', '$sce', function($window, $animate, $sce) { return { restrict: 'AE', //Use as attribute and element template: '' + 'Test' + '' + '' + - 'Help', + 'Help', + scope: {}, require: 'ngModel', link: function(scope, iElement, iAttrs, ngModelController) {//TODO: should be using formatters and parsers for this. - scope.helpURL = iAttrs.helpurl; + scope.helpURL = $sce.trustAsHtml(iAttrs.helpurl); iElement.find('textarea')[0].placeholder = iAttrs.placeholder == null?"":iAttrs.placeholder; ngModelController.$render = function() { @@ -19,7 +20,7 @@ angular.module('cwh.testscript', []).directive('cwhTestscript', ['$window', '$an ngModelController.$setViewValue(iElement.find('textarea')[0].value); } scope.helpClick = function() { - $window.location.href = scope.helpURL; + //$window.location.href = scope.helpURL; } scope.onFocus = function() { $animate.addClass(iElement.find('textarea')[0], 'cwh-text-large-area'); diff --git a/host/resourcesnew/cwh/js/index.js b/host/resourcesnew/cwh/js/index.js index 8d62d8c7d..4fcb3304b 100644 --- a/host/resourcesnew/cwh/js/index.js +++ b/host/resourcesnew/cwh/js/index.js @@ -171,9 +171,6 @@ }); $http.get('/services/settings/visibleCards').success(function(data) { $scope.visibleCards = data; - }); - $http.get('/services/settings/integerVersion').success(function(data) { - $scope.integerVersion = data; }); $http.get('/services/settings/releaseTagName').success(function(data) { $scope.releaseTagName = data; diff --git a/host/resourcesnew/cwh/js/printers.js b/host/resourcesnew/cwh/js/printers.js index 7bdad368a..6d7afa1ab 100644 --- a/host/resourcesnew/cwh/js/printers.js +++ b/host/resourcesnew/cwh/js/printers.js @@ -10,10 +10,12 @@ this.loadingProfilesMessage = "--- Loading slicing profiles from server ---" this.loadingMachineConfigMessage = "--- Loading machine configurations from server ---" this.autodirect = $location.search().autodirect; - function refreshSelectedPrinter(printerList) { + + //TODO: Instead of having this method we should understand how the selected printer gets out of sync and fix that + function refreshSelectedPrinterAndAutodirectIfNecessary(printerList) { var foundPrinter = false; if (printerList.length == 1 && printerList[0].started && controller.autodirect != 'disabled') { - controller.currentPrinter = printer; + controller.currentPrinter = printerList[0]; controller.gotoPrinterControls(); foundPrinter = true; } else { @@ -27,6 +29,7 @@ printersStarted += 1; currPrinter = printer; } + if (controller.currentPrinter != null && printer.configuration.name === controller.currentPrinter.configuration.name) { controller.currentPrinter = printer; foundPrinter = true; @@ -46,11 +49,11 @@ function refreshPrinters() { $http.get('/services/printers/list').success(function(data) { $scope.printers = data; - refreshSelectedPrinter(data); + refreshSelectedPrinterAndAutodirectIfNecessary(data); }); } - function executeActionAndRefreshPrinters(command, message, service, targetPrinter, postTargetPrinter) { + function executeActionAndRefreshPrinters(command, message, service, targetPrinter, postTargetPrinter, shouldRefreshPrinterList) { if (targetPrinter == null) { $scope.$emit("MachineResponse", {machineResponse: {command:command, message:message, successFunction:null, afterErrorFunction:null}}); return; @@ -59,6 +62,11 @@ if (postTargetPrinter) { $http.post(service, targetPrinter).then( function(response) { + if (shouldRefreshPrinterList) { + refreshPrinters(); + refreshSlicingProfiles(); + refreshMachineConfigurations(); + } }, function(response) { $scope.$emit("HTTPError", {status:response.status, statusText:response.data}); @@ -69,7 +77,7 @@ } else { $http.get(service + printerName).then( function(response) { - $scope.$emit("MachineResponse", {machineResponse: response.data, successFunction:refreshPrinters, afterErrorFunction:null}); + $scope.$emit("MachineResponse", {machineResponse: response.data, successFunction:shouldRefreshPrinterList?refreshPrinters:null, afterErrorFunction:null}); }, function(response) { $scope.$emit("HTTPError", {status:response.status, statusText:response.data}); @@ -86,12 +94,12 @@ openSavePrinterDialog(editTitle, false); } - $scope.savePrinter = function savePrinter(printer, renameProfiles) { - if (renameProfiles) { + $scope.savePrinter = function savePrinter(printer, isNewPrinter) { + if (isNewPrinter) {//Rename the profiles to what the user entered if this is a new printer controller.editPrinter.configuration.MachineConfigurationName = controller.editPrinter.configuration.name; controller.editPrinter.configuration.SlicingProfileName = controller.editPrinter.configuration.name; } - executeActionAndRefreshPrinters("Save Printer", "No printer selected to save.", '/services/printers/save', printer, true); + executeActionAndRefreshPrinters("Save Printer", "No printer selected to save.", '/services/printers/save', printer, true, isNewPrinter); controller.editPrinter = null; controller.openType = null; photonicUtils.clearPreviewExternalState(); @@ -164,16 +172,16 @@ this.startCurrentPrinter = function startCurrentPrinter() { $('#start-btn').attr('class', 'fa fa-refresh fa-spin'); - executeActionAndRefreshPrinters("Start Printer", "No printer selected to start.", '/services/printers/start/', controller.currentPrinter, false); + executeActionAndRefreshPrinters("Start Printer", "No printer selected to start.", '/services/printers/start/', controller.currentPrinter, false, true); } this.stopCurrentPrinter = function stopCurrentPrinter() { $('#stop-btn').attr('class', 'fa fa-refresh fa-spin'); - executeActionAndRefreshPrinters("Stop Printer", "No printer selected to Stop.", '/services/printers/stop/', controller.currentPrinter, false); + executeActionAndRefreshPrinters("Stop Printer", "No printer selected to Stop.", '/services/printers/stop/', controller.currentPrinter, false, true); } this.deleteCurrentPrinter = function deleteCurrentPrinter() { - executeActionAndRefreshPrinters("Delete Printer", "No printer selected to Delete.", '/services/printers/delete/', controller.currentPrinter, false); + executeActionAndRefreshPrinters("Delete Printer", "No printer selected to Delete.", '/services/printers/delete/', controller.currentPrinter, false, true); controller.currentPrinter = null; } @@ -220,17 +228,21 @@ controller.loadingFontsMessage = "Select a font..."; }); - $http.get('/services/machine/slicingProfiles/list').success( - function (data) { - controller.slicingProfiles = data; - controller.loadingProfilesMessage = "Select a slicing profile..."; - }); + function refreshSlicingProfiles() { + $http.get('/services/machine/slicingProfiles/list').success( + function (data) { + controller.slicingProfiles = data; + controller.loadingProfilesMessage = "Select a slicing profile..."; + }); + } - $http.get('/services/machine/machineConfigurations/list').success( - function (data) { - controller.machineConfigurations = data; - controller.loadingMachineConfigMessage = "Select a machine configuration..."; - }); + function refreshMachineConfigurations() { + $http.get('/services/machine/machineConfigurations/list').success( + function (data) { + controller.machineConfigurations = data; + controller.loadingMachineConfigMessage = "Select a machine configuration..."; + }); + } $http.get("https://api.github.com/repos/" + $scope.repo + "/contents/host/" + PRINTERS_DIRECTORY + "?ref=" + BRANCH).success( function (data) { @@ -242,7 +254,12 @@ photonicUtils.testScript(controller, scriptName, returnType, script); }; - controller.inkDetectors = [{name:"Visual Ink Detector", className:"org.area515.resinprinter.inkdetection.visual.VisualPrintMaterialDetector"}]; + controller.inkDetectors = [ + {name:"Visual Ink Detector", className:"org.area515.resinprinter.inkdetection.visual.VisualPrintMaterialDetector"}, + {name:"Digital GPIO Ink Detector", className:"org.area515.resinprinter.inkdetection.gpio.GpioDigitalPinInkDetector"} + ]; + refreshSlicingProfiles(); + refreshMachineConfigurations(); refreshPrinters(); }]) diff --git a/host/resourcesnew/cwh/js/uploadFile.js b/host/resourcesnew/cwh/js/uploadFile.js index 4a2d46f8a..fbf78fe33 100644 --- a/host/resourcesnew/cwh/js/uploadFile.js +++ b/host/resourcesnew/cwh/js/uploadFile.js @@ -52,16 +52,17 @@ }); $scope.fileToUpload.upload.then(function (response) { - //TODO: Upload complete should reload file list - if (!response.data.response) { - $rootScope.$emit("MachineResponse", {machineResponse: response.data, successFunction:null, afterErrorFunction:null}); + /*if (!response.data.response) { + $rootScope.$emit("MachineResponse", {machineResponse: {"command":"File Upload", "message":response.data}, successFunction:null, afterErrorFunction:null}); } else { $scope.fileToUpload.result = response.data; - } + }*/ }, function (response) { //TODO: Upload complete should reload file list - if (response.status > 0) - $scope.errorMsg = response.status + ': ' + response.data; + if (response.status != 200) { + $rootScope.$emit("MachineResponse", {machineResponse: {"command":"File Upload", "message":response.data}, successFunction:null, afterErrorFunction:null}); + //$scope.errorMsg = response.status + ': ' + response.data; + } } ); $scope.fileToUpload.upload.progress(function (evt) { diff --git a/host/resourcesnew/editCalculators.html b/host/resourcesnew/editCalculators.html index 8408e9cae..28fd6f0ec 100644 --- a/host/resourcesnew/editCalculators.html +++ b/host/resourcesnew/editCalculators.html @@ -16,7 +16,9 @@

Z Lift Distance (mm)

@@ -27,7 +29,9 @@

Lift Speed (mm/m)

@@ -38,7 +42,9 @@

Exposure Time (ms)

@@ -50,7 +56,12 @@

Projector Gradient (Paint)

diff --git a/host/resourcesnew/editGCodeTemplates.html b/host/resourcesnew/editGCodeTemplates.html index 6838a6a5b..57bdaf012 100644 --- a/host/resourcesnew/editGCodeTemplates.html +++ b/host/resourcesnew/editGCodeTemplates.html @@ -16,7 +16,10 @@

Header (Printer may require)

@@ -27,7 +30,10 @@

Pre-slice (Printer may require)

@@ -38,7 +44,10 @@

Z Lift sequence (Required)

@@ -49,7 +58,10 @@

Footer (Printer may require)

@@ -60,7 +72,10 @@

Shutter (Optional)

@@ -71,7 +86,10 @@

Set Z Lift Speed (Optional)

@@ -82,7 +100,10 @@

Set Z Lift Distance (Optional)

diff --git a/host/resourcesnew/fontawesome/css/font-awesome.min.css b/host/resourcesnew/fontawesome/css/font-awesome.min.css index 4ec922351..cce66c7fd 100644 --- a/host/resourcesnew/fontawesome/css/font-awesome.min.css +++ b/host/resourcesnew/fontawesome/css/font-awesome.min.css @@ -1,4 +1,4 @@ /*! * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.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} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad";}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.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} \ No newline at end of file diff --git a/host/resourcesnew/printables.html b/host/resourcesnew/printables.html index 05d4311ce..1177cae0f 100644 --- a/host/resourcesnew/printables.html +++ b/host/resourcesnew/printables.html @@ -282,18 +282,27 @@

Image Manipulation Calculator

- +

AffineTransform Calculator

diff --git a/host/resourcesnew/printer.html b/host/resourcesnew/printer.html index 8d770a09f..ec453db02 100644 --- a/host/resourcesnew/printer.html +++ b/host/resourcesnew/printer.html @@ -38,6 +38,12 @@

{{printersController.currentPrinter.configur

{{printersController.currentPrinter.configuration.AutoStart}}

+

{{printersController.currentPrinter.configuration.MachineConfigurationName}}

+
diff --git a/host/resourcesnew/settings.html b/host/resourcesnew/settings.html index a6c84f597..bb30418cf 100644 --- a/host/resourcesnew/settings.html +++ b/host/resourcesnew/settings.html @@ -11,7 +11,7 @@

Wifi Setup

Access Point

diff --git a/host/src/main/java/org/area515/resinprinter/client/Main.java b/host/src/main/java/org/area515/resinprinter/client/Main.java index 2f462e825..02d468189 100644 --- a/host/src/main/java/org/area515/resinprinter/client/Main.java +++ b/host/src/main/java/org/area515/resinprinter/client/Main.java @@ -1,27 +1,47 @@ package org.area515.resinprinter.client; import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; import java.awt.Desktop; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.SplashScreen; +import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; import java.net.SocketException; import java.net.URLEncoder; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; +import javax.swing.JWindow; +import javax.swing.SwingConstants; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -49,12 +69,96 @@ public class Main { public static final String PRINTER_TYPE = "3DPrinterHost"; public static final String PRINTERS_DIRECTORY = "printers"; public static final String BRANCH = "master"; - public static String REPO = "area515"; + public static String REPO = "area515/Photonic3D"; private static Set foundDevices = new HashSet(); private static long maxLengthToWait = 5000; private static long maxLengthToWaitForAll = 7000; + public static class StatusPanel extends JLabel { + private static final long serialVersionUID = 6639519775722442260L; + + private ScreenUpdater updater; + + public StatusPanel(String text, Icon icon, int horizontalAlignment) { + super(text, icon, horizontalAlignment); + } + + public void setScreenUpdater(ScreenUpdater updater) { + this.updater = updater; + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (updater != null) { + updater.updateGraphics((Graphics2D)g); + } + } + } + + public static class ScreenUpdater { + private Graphics2D graphics; + private SplashScreen screen; + private Rectangle outerBounds; + private String showText; + private StatusPanel statusPanel; + private Color textColor; + + private ScreenUpdater(SplashScreen splashScreen, Color textColor) { + this.graphics = splashScreen.createGraphics(); + this.screen = splashScreen; + this.outerBounds = graphics.getDeviceConfiguration().getBounds(); + this.textColor = textColor; + } + + private ScreenUpdater(StatusPanel statusPanel, Color textColor) { + this.outerBounds = statusPanel.getBounds(); + this.statusPanel = statusPanel; + this.statusPanel.setScreenUpdater(this); + this.textColor = textColor; + } + + public void showProgress(String showText) { + this.showText = showText; + if (graphics != null) { + graphics.setBackground(new Color(0, true)); + graphics.clearRect(0, 0, outerBounds.width, outerBounds.height); + + updateGraphics(graphics); + } + updateScreen(); + } + + public void updateGraphics(Graphics2D graphics) { + if (showText == null) { + return; + } + + graphics.setColor(textColor); + Rectangle2D bounds = graphics.getFontMetrics().getStringBounds(showText, graphics); + graphics.drawString(showText, (int)(outerBounds.width / 2 - bounds.getWidth() / 2), (int)bounds.getHeight()); + } + + public void close() { + if (statusPanel != null) { + javax.swing.SwingUtilities.getWindowAncestor(statusPanel).dispose(); + } + if (screen != null && screen.isVisible()) { + screen.close(); + } + } + + public void updateScreen() { + if (screen != null) { + screen.update(); + } + if (statusPanel != null) { + statusPanel.updateUI(); + } + } + } + @JsonIgnoreProperties(ignoreUnknown = true) public static class PrinterEntry { private String name; @@ -129,9 +233,9 @@ private static char[] getPassword(String title, String prompt) { return null; } - private static boolean findSuccessLine(String[] lines, String containsLine) { + private static boolean findSuccessLine(String[] lines, String matchesLine) { for (String line : lines) { - if (line.contains(containsLine)) { + if (line.matches(matchesLine)) { return true; } } @@ -241,7 +345,7 @@ public static boolean installPrinterProfile(Box installToBox) { // specify the host, protocol, and port HttpHost restTarget = new HttpHost("api.github.com", 443, "https"); HttpHost cwhTarget = new HttpHost(installToBox.getName(), 9091, "http"); - HttpGet getRequest = new HttpGet("/repos/" + REPO + "/Creation-Workshop-Host/contents/host/" + PRINTERS_DIRECTORY + "?ref=" + BRANCH); + HttpGet getRequest = new HttpGet("/repos/" + REPO + "/contents/host/" + PRINTERS_DIRECTORY + "?ref=" + BRANCH); PrinterEntry[] printers = null; try { @@ -304,10 +408,10 @@ public static boolean installPrinterProfile(Box installToBox) { public static boolean performInstall(Box box, String username, String oldPassword) throws IOException, JSchException { System.out.println("User chose install on:" + box); - final JOptionPane installOptionPane = new JOptionPane("Installing CWH...", JOptionPane.INFORMATION_MESSAGE, JOptionPane.CANCEL_OPTION, null, new String[]{"Cancel"}, "Cancel"); + final JOptionPane installOptionPane = new JOptionPane("Installing Photonic3D...", JOptionPane.INFORMATION_MESSAGE, JOptionPane.CANCEL_OPTION, null, new String[]{"Cancel"}, "Cancel"); JDialog installPane = null; installPane = new JDialog(); - installPane.setTitle("Installing CWH"); + installPane.setTitle("Installing Photonic3D"); installPane.setResizable(false); installPane.setAlwaysOnTop(true); installPane.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); @@ -334,8 +438,8 @@ public static boolean performInstall(Box box, String username, String oldPasswor } installOptionPane.setMessage("Downloading installation scripts..."); - String[] output = client.send("wget https://github.com/" + REPO + "/Creation-Workshop-Host/raw/master/host/bin/start.sh"); - if (!findSuccessLine(output, "start.sh' saved")) { + String[] output = client.send("wget https://github.com/" + REPO + "/raw/master/host/bin/start.sh"); + if (!findSuccessLine(output, "(?s:.*start.sh.*saved.*)")) { writeOutput(output); throw new IOException("This device can't seem to reach the internet."); } @@ -343,9 +447,9 @@ public static boolean performInstall(Box box, String username, String oldPasswor installOptionPane.setMessage("Performing installation..."); output = client.send("./start.sh"); - if (!findSuccessLine(output, "Starting printer host server")) { + if (!findSuccessLine(output, "(?s:.*Starting printer host server.*)")) { writeOutput(output); - throw new IOException("There was a problem installing CWH. Please refer to logs."); + throw new IOException("There was a problem installing Photonic3D. Please refer to logs."); } if (box.isRaspberryPi()) { @@ -371,6 +475,21 @@ public static boolean performInstall(Box box, String username, String oldPasswor } } + private static StatusPanel showSplashscreenIfJWrapperDoesntSupportIt(String splash) throws MalformedURLException { + StatusPanel panel = new StatusPanel("", new ImageIcon(Main.class.getClassLoader().getResource(splash)), SwingConstants.CENTER); + JFrame window = new JFrame(); + window.setUndecorated(true); + window.setResizable(false); + window.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + window.setFocusable(false); + window.getContentPane().add(panel); + window.pack(); + window.setVisible(true); + window.toFront(); + window.setLocationRelativeTo(null); + return panel; + } + /** * return 0 for printer found * returns -1 for user cancelled operation @@ -378,11 +497,55 @@ public static boolean performInstall(Box box, String username, String oldPasswor * * @param args */ - public static void main(String[] args) { - if (args.length > 0) { - REPO = args[0]; + public static void main(String[] args) throws MalformedURLException { + PosixParser parser = new PosixParser(); + Options options = new Options(); + options.addOption("h", "help", false, "Shows this help screen."); + options.addOption("r", "repo", true, "If the user chooses to install Photonic3d on a remote device, then Photonic3d will be installed from the following github repo"); + options.addOption("w", "jwrappersplash", true, "JWrapper doesn't support the Java splash screen so this client will build it's own and use the parameter sent to this method"); + options.addOption("t", "progresstextcolor", true, "Color [Red(0-255),Green(0-255),Blue(0-255),Alpha(0-255)] of text on splash screen to show printers as they are found on the network"); + CommandLine commands; + try { + commands = parser.parse(options, args); + } catch (ParseException e2) { + e2.printStackTrace(); + throw new IllegalArgumentException("Couldn't understand arguments sent in command line"); + } + + if (commands.hasOption("h")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java org.area515.resinprinter.client.Main [options]", options); + return; + } + + if (commands.hasOption("r")) { + REPO = commands.getOptionValue("r"); } + Color textColor = null; + if (commands.hasOption("t")) { + String colors[] = commands.getOptionValue("t").split(","); + if (colors.length != 4) { + System.out.println("TextColor must be in format r,g,b,a (0-255,0-255,0-255,0-255)"); + System.exit(-2); + } + textColor = new Color(Integer.valueOf(colors[0]),Integer.valueOf(colors[1]),Integer.valueOf(colors[2]),Integer.valueOf(colors[3])); + } else { + textColor = Color.WHITE; + } + + ScreenUpdater updater = null; + if (commands.hasOption("w")) { + StatusPanel manualSplash = showSplashscreenIfJWrapperDoesntSupportIt(commands.getOptionValue("w")); + updater = new ScreenUpdater(manualSplash, textColor); + } else { + SplashScreen splashScreen = SplashScreen.getSplashScreen(); + if (splashScreen != null) { + updater = new ScreenUpdater(splashScreen, textColor); + } + } + final ScreenUpdater finalUpdater = updater; + boolean installCompletedOnThisLoopIteration = false; boolean userHasbeenAskedToInstall = false; SubnetScanner scanner = new SubnetScanner(); @@ -419,10 +582,13 @@ public void run() { if (currentDevice.getType().getType().equals(PRINTER_TYPE)) { foundDevices.add(new PrintableDevice(currentDevice)); System.out.println("Found printer URL here:" + currentDevice.getDetails().getPresentationURI()); + if (finalUpdater != null) { + finalUpdater.showProgress("Found printer: " + currentDevice.getDisplayString()); + } } } - Thread.currentThread().sleep(300); + Thread.currentThread().sleep(200); } waitForURLFound.countDown(); @@ -447,13 +613,16 @@ public void run() { userHasbeenAskedToInstall = true; try { List boxes = scanner.waitForDevicesWithPossibleRemoteInstallCapability(); + if (finalUpdater != null) { + finalUpdater.close(); + } if (boxes.size() > 0) { Box box = (Box)JOptionPane.showInputDialog(null, - "I couldn't find CWH installed on your network.\n" + "I couldn't find Photonic3D installed on your network.\n" + "I did find place(s) where I might be able to install it.\n" - + "Choose any of the following locations to install CWH.\n" - + "Click 'Cancel' if you've already installed CWH.", - "Install CWH?", + + "Choose any of the following locations to install Photonic3D.\n" + + "Click 'Cancel' if you've already installed Photonic3D.", + "Install Photonic3D?", JOptionPane.QUESTION_MESSAGE, null, boxes.toArray(), @@ -470,7 +639,7 @@ public void run() { } } catch (JSchException | IOException e) { e.printStackTrace(); - JOptionPane.showConfirmDialog(null, "Unable To Install CWH", e.getMessage(), JOptionPane.ERROR); + JOptionPane.showConfirmDialog(null, e.getMessage(), "Unable To Install Photonic3D", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE); System.exit(-2); } } @@ -480,6 +649,9 @@ public void run() { } if (!installCompletedOnThisLoopIteration) { + if (finalUpdater != null) { + finalUpdater.close(); + } System.out.println("3d printer not found after waiting:" + maxLengthToWaitForAll); JOptionPane optionPane = new JOptionPane(); optionPane.setMessage("Couldn't find your printer on this network.\n" @@ -515,6 +687,9 @@ public void run() { searchPane.pack(); searchPane.setLocationRelativeTo(null); searchPane.setVisible(true); + if (finalUpdater != null) { + finalUpdater.close(); + } } } while (foundDevices.size() == 0); @@ -528,6 +703,9 @@ public void run() { if (foundDevices.size() == 1) { chosenPrinter = foundDevices.iterator().next(); } else { + if (finalUpdater != null) { + finalUpdater.close(); + } chosenPrinter = (PrintableDevice)JOptionPane.showInputDialog(null, "There were multiple 3d printers found on this network.\nWhich printer would you like to view?", "Choose a Printer", diff --git a/host/src/main/java/org/area515/resinprinter/discover/PhotonicDevice.java b/host/src/main/java/org/area515/resinprinter/discover/PhotonicDevice.java new file mode 100644 index 000000000..4221e5723 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/discover/PhotonicDevice.java @@ -0,0 +1,35 @@ +package org.area515.resinprinter.discover; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.fourthline.cling.model.Namespace; +import org.fourthline.cling.model.ValidationError; +import org.fourthline.cling.model.ValidationException; +import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.DeviceDetails; +import org.fourthline.cling.model.meta.DeviceIdentity; +import org.fourthline.cling.model.meta.Icon; +import org.fourthline.cling.model.meta.LocalDevice; +import org.fourthline.cling.model.meta.LocalService; +import org.fourthline.cling.model.resource.DeviceDescriptorResource; +import org.fourthline.cling.model.resource.IconResource; +import org.fourthline.cling.model.resource.Resource; +import org.fourthline.cling.model.resource.ServiceControlResource; +import org.fourthline.cling.model.resource.ServiceDescriptorResource; +import org.fourthline.cling.model.resource.ServiceEventSubscriptionResource; +import org.fourthline.cling.model.types.DeviceType; + +public class PhotonicDevice extends LocalDevice { + public PhotonicDevice(DeviceIdentity identity, DeviceType type, DeviceDetails details, + Icon[] icons, LocalService[] services) throws ValidationException { + super(identity, type, details, icons, services); + } + + @Override + public List validate() { + List errors = new ArrayList(); + return errors; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/discover/PhotonicNamespace.java b/host/src/main/java/org/area515/resinprinter/discover/PhotonicNamespace.java new file mode 100644 index 000000000..2ee437bb4 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/discover/PhotonicNamespace.java @@ -0,0 +1,13 @@ +package org.area515.resinprinter.discover; + +import java.net.URI; + +import org.fourthline.cling.model.Namespace; +import org.fourthline.cling.model.meta.Icon; + +public class PhotonicNamespace extends Namespace { + @Override + public URI getIconPath(Icon icon) { + return icon.getUri(); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/discover/PhotonicUpnpServiceConfiguration.java b/host/src/main/java/org/area515/resinprinter/discover/PhotonicUpnpServiceConfiguration.java new file mode 100644 index 000000000..b77733320 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/discover/PhotonicUpnpServiceConfiguration.java @@ -0,0 +1,15 @@ +package org.area515.resinprinter.discover; + +import org.fourthline.cling.DefaultUpnpServiceConfiguration; +import org.fourthline.cling.model.Namespace; + +public class PhotonicUpnpServiceConfiguration extends DefaultUpnpServiceConfiguration { + public PhotonicUpnpServiceConfiguration(int streamListenPort) { + super(streamListenPort); + } + + @Override + protected Namespace createNamespace() { + return new PhotonicNamespace(); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/discover/UPNPAdvertiser.java b/host/src/main/java/org/area515/resinprinter/discover/UPNPAdvertiser.java index da86dc5c1..fabd7876a 100644 --- a/host/src/main/java/org/area515/resinprinter/discover/UPNPAdvertiser.java +++ b/host/src/main/java/org/area515/resinprinter/discover/UPNPAdvertiser.java @@ -1,6 +1,14 @@ package org.area515.resinprinter.discover; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FilenameFilter; import java.net.URI; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,6 +24,7 @@ import org.fourthline.cling.model.message.header.STAllHeader; import org.fourthline.cling.model.meta.DeviceDetails; import org.fourthline.cling.model.meta.DeviceIdentity; +import org.fourthline.cling.model.meta.Icon; import org.fourthline.cling.model.meta.LocalDevice; import org.fourthline.cling.model.meta.LocalService; import org.fourthline.cling.model.meta.ManufacturerDetails; @@ -36,6 +45,7 @@ public class UPNPAdvertiser implements Feature { public static class UPNPSetup { private String deviceName = null; private String manufacturer = null; + private String deviceReleaseString = HostProperties.Instance().getReleaseTagName(); private int deviceVersion = HostProperties.Instance().getVersionNumber(); private String deviceType = Main.PRINTER_TYPE; private int upnpStreamPort = 5001; @@ -64,7 +74,7 @@ public UPNPSetup getSetup() { @Override public void start(URI webPresentationURI) { try { - UDN udn = UDN.uniqueSystemIdentifier(getSetup().deviceName + getSetup().deviceVersion + getSetup().manufacturer); + UDN udn = UDN.uniqueSystemIdentifier(getSetup().deviceName + getSetup().deviceReleaseString + getSetup().manufacturer); DeviceType type = new UDADeviceType(getSetup().deviceType, getSetup().deviceVersion); DeviceDetails details = new DeviceDetails(getSetup().deviceName, @@ -72,23 +82,46 @@ public void start(URI webPresentationURI) { new ModelDetails( getSetup().deviceName, getSetup().deviceName, - "v" + getSetup().deviceVersion), + "v" + getSetup().deviceReleaseString), webPresentationURI, new DLNADoc[] { new DLNADoc("DMS", DLNADoc.Version.V1_5), new DLNADoc("M-DMS", DLNADoc.Version.V1_5) }, null); - + + List icons = new ArrayList(); + File iconFiles[] = new File(HostProperties.Instance().getHostGUIDir(), "favicon").listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + name = name.toLowerCase(); + if (!name.endsWith(".jpg") && !name.endsWith(".gif") && !name.endsWith(".png")) { + return false; + } + + return name.contains("48") || name.contains("120"); + } + }); + for (File currentFile : iconFiles) { + String mimeType = Files.probeContentType(currentFile.toPath()); + BufferedImage image = ImageIO.read(currentFile); + icons.add(new Icon(mimeType, image.getWidth(), image.getHeight(), image.getColorModel().getPixelSize(), new URI(webPresentationURI.toString() + "/favicon/" + currentFile.getName()))); + } + LocalService contentManagerService = new AnnotationLocalServiceBinder().read(PrinterDirectoryService.class); PrinterHostSettingsServiceManager manager = new PrinterHostSettingsServiceManager(getSetup(), webPresentationURI, contentManagerService, PrinterDirectoryService.class); contentManagerService.setManager(manager);//new DefaultServiceManager(contentManagerService, PrinterDirectoryService.class)); LocalService connectionManagerService = new AnnotationLocalServiceBinder().read(AbstractPeeringConnectionManagerService.class); connectionManagerService.setManager(new DefaultServiceManager(connectionManagerService, AbstractPeeringConnectionManagerService.class)); + + PhotonicDevice printerServer = new PhotonicDevice( + new DeviceIdentity(udn), + type, + details, + icons.toArray(new Icon[icons.size()]), + new LocalService[]{contentManagerService, connectionManagerService}); - LocalDevice printerServer = new LocalDevice(new DeviceIdentity(udn), type, details, new LocalService[]{contentManagerService, connectionManagerService}); - - DefaultUpnpServiceConfiguration serviceConfiguration = new DefaultUpnpServiceConfiguration(getSetup().upnpStreamPort); + PhotonicUpnpServiceConfiguration serviceConfiguration = new PhotonicUpnpServiceConfiguration(getSetup().upnpStreamPort); upnpService = new UpnpServiceImpl(serviceConfiguration); if (!upnpService.getRouter().isEnabled()) { throw new IllegalArgumentException("It doesn't seem as though a network is available to publish this server, or an advertiser has already been started on this address."); @@ -97,7 +130,7 @@ public void start(URI webPresentationURI) { // Broadcast a search message for all devices ControlPoint controlPoint = upnpService.getControlPoint(); controlPoint.search(new STAllHeader()); - + // Add the bound local device to the registry upnpService.getRegistry().addDevice(printerServer); @@ -115,7 +148,7 @@ public void start(URI webPresentationURI) { } } } catch (Exception e) { - logger.error("Couldn't advertise URI:", webPresentationURI); + logger.error("Couldn't advertise URI:" + webPresentationURI, e); throw new RuntimeException("Couldn't advertise URI:" + webPresentationURI); } } diff --git a/host/src/main/java/org/area515/resinprinter/display/AlreadyAssignedException.java b/host/src/main/java/org/area515/resinprinter/display/AlreadyAssignedException.java index 8aea3ca3d..f58abd1c7 100644 --- a/host/src/main/java/org/area515/resinprinter/display/AlreadyAssignedException.java +++ b/host/src/main/java/org/area515/resinprinter/display/AlreadyAssignedException.java @@ -10,7 +10,7 @@ public class AlreadyAssignedException extends Exception { private static final long serialVersionUID = 5346661559747947463L; private Printer printer; - private GraphicsDevice graphicsDevice; + private String graphicsDevice; private SerialCommunicationsPort comPort; private PrintJob printJob; @@ -29,7 +29,7 @@ public AlreadyAssignedException(String message, SerialCommunicationsPort comPort this.comPort = comPort; } - public AlreadyAssignedException(String message, GraphicsDevice graphicsDevice) { + public AlreadyAssignedException(String message, String graphicsDevice) { super(message); this.graphicsDevice = graphicsDevice; } @@ -42,7 +42,7 @@ public Printer getPrinter() { return printer; } - public GraphicsDevice getGraphicsDevice() { + public String getGraphicsDeviceId() { return graphicsDevice; } diff --git a/host/src/main/java/org/area515/resinprinter/display/CustomNamedDisplayDevice.java b/host/src/main/java/org/area515/resinprinter/display/CustomNamedDisplayDevice.java deleted file mode 100644 index afea7a94f..000000000 --- a/host/src/main/java/org/area515/resinprinter/display/CustomNamedDisplayDevice.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.area515.resinprinter.display; - -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsDevice; -import java.awt.HeadlessException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - -public class CustomNamedDisplayDevice extends GraphicsDevice { - private String displayName; - - public CustomNamedDisplayDevice(String displayName) { - this.displayName = displayName; - } - - @Override - public int getType() { - return TYPE_IMAGE_BUFFER; - } - - @Override - public String getIDstring() { - return displayName; - } - - @Override - public GraphicsConfiguration[] getConfigurations() { - return null; - } - - @Override - public GraphicsConfiguration getDefaultConfiguration() { - GraphicsDevice[] devices; - try { - devices = DisplayManager.Instance().getGraphicsEnvironment().getScreenDevices(); - return devices[devices.length - 1].getDefaultConfiguration(); - } catch (HeadlessException | InappropriateDeviceException e) { - throw new IllegalArgumentException("Graphics environment not supported?", e); - } - } - - public String toString() { - return displayName; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((displayName == null) ? 0 : displayName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - CustomNamedDisplayDevice other = (CustomNamedDisplayDevice) obj; - if (displayName == null) { - if (other.displayName != null) - return false; - } else if (!displayName.equals(other.displayName)) - return false; - return true; - } -} diff --git a/host/src/main/java/org/area515/resinprinter/display/DisplayManager.java b/host/src/main/java/org/area515/resinprinter/display/DisplayManager.java index a58257834..6d0085d76 100644 --- a/host/src/main/java/org/area515/resinprinter/display/DisplayManager.java +++ b/host/src/main/java/org/area515/resinprinter/display/DisplayManager.java @@ -19,11 +19,9 @@ public class DisplayManager { private static final Logger logger = LogManager.getLogger(); private static DisplayManager INSTANCE = null; - public static final String LAST_AVAILABLE_DISPLAY = "Last available display"; - public static final String SIMULATED_DISPLAY = "Simulated display"; private GraphicsEnvironment ge = null; - private ConcurrentHashMap graphicsDevicesByPrinter = new ConcurrentHashMap(); + private ConcurrentHashMap displayIdsByPrinter = new ConcurrentHashMap(); private ConcurrentHashMap printersByDisplayIDString = new ConcurrentHashMap(); public static DisplayManager Instance() { @@ -36,58 +34,53 @@ public static DisplayManager Instance() { private DisplayManager(){ } - public void assignDisplay(Printer newPrinter, GraphicsDevice device) throws AlreadyAssignedException, InappropriateDeviceException { - if (device.getIDstring().equals(LAST_AVAILABLE_DISPLAY)) { - ArrayList devices = new ArrayList(); - devices.addAll(Arrays.asList(getGraphicsEnvironment().getScreenDevices())); - Collections.reverse(devices); - for (GraphicsDevice currentDevice : devices) { - if (!printersByDisplayIDString.containsKey(currentDevice.getIDstring())) { - device = currentDevice; - break; - } - } - - if (device.getIDstring().equals(LAST_AVAILABLE_DISPLAY)) { - throw new InappropriateDeviceException("No displays left to assign"); - } + public boolean isGraphicsDeviceDisplayAvailable(String displayId) { + try { + GraphicsOutputInterface display = getDisplayDevice(displayId); + return !printersByDisplayIDString.containsKey(display.getIDstring()); + } catch (InappropriateDeviceException e) { + throw new IllegalArgumentException("Couldn't getDisplayDevice for:" + displayId, e); } - - GraphicsDevice otherDevice = graphicsDevicesByPrinter.putIfAbsent(newPrinter, device); + } + + public void assignDisplay(Printer newPrinter, GraphicsOutputInterface device) throws AlreadyAssignedException, InappropriateDeviceException { + String nextIdString = device.buildIDString();//Note: Do NOT call getIDstring() since it's not appropriate + if (nextIdString == null) { + throw new InappropriateDeviceException(device + " didn't return an available display"); + } + String otherDevice = displayIdsByPrinter.putIfAbsent(newPrinter, nextIdString); if (otherDevice != null) { - throw new AlreadyAssignedException("Printer already assigned to:" + otherDevice.getIDstring(), otherDevice); + throw new AlreadyAssignedException("Printer already assigned to:" + otherDevice, otherDevice); } - Printer otherJob = printersByDisplayIDString.putIfAbsent(device.getIDstring(), newPrinter); + Printer otherJob = printersByDisplayIDString.putIfAbsent(nextIdString, newPrinter); if (otherJob != null) { - graphicsDevicesByPrinter.remove(newPrinter); + displayIdsByPrinter.remove(newPrinter); throw new AlreadyAssignedException("Display already assigned to:" + otherJob, otherJob); } - newPrinter.setGraphicsData(device); + newPrinter.initializeAndAssignGraphicsOutputInterface(device, nextIdString); newPrinter.showBlankImage(); logger.info("Display:{} assigned to Printer:{}", device, newPrinter); } - public List getDisplayDevices() { - List devices = new ArrayList(); + public List getDisplayDevices() { + List devices = new ArrayList(); try { - devices.addAll(Arrays.asList(getGraphicsEnvironment().getScreenDevices())); + for (GraphicsDevice device : getGraphicsEnvironment().getScreenDevices()) { + devices.add(new GraphicsDeviceOutputInterface(device.getIDstring(), device)); + } } catch (InappropriateDeviceException e) { logger.error("Continuing after error...", e); } - devices.add(new CustomNamedDisplayDevice(LAST_AVAILABLE_DISPLAY)); - if (HostProperties.Instance().getFakeDisplay()) { - devices.add(new CustomNamedDisplayDevice(SIMULATED_DISPLAY)); - } - + devices.addAll(HostProperties.Instance().getDisplayDevices()); return devices; } - public GraphicsDevice getDisplayDevice(String deviceId) throws InappropriateDeviceException { - GraphicsDevice newDevice = null; - for (GraphicsDevice currentDevice : getDisplayDevices()) { + public GraphicsOutputInterface getDisplayDevice(String deviceId) throws InappropriateDeviceException { + GraphicsOutputInterface newDevice = null; + for (GraphicsOutputInterface currentDevice : getDisplayDevices()) { if (currentDevice.getIDstring().equals(deviceId)) { newDevice = currentDevice; } @@ -96,8 +89,8 @@ public GraphicsDevice getDisplayDevice(String deviceId) throws InappropriateDevi return newDevice; } - public GraphicsDevice getDisplayDevice(int index) throws InappropriateDeviceException { - return getGraphicsEnvironment().getScreenDevices()[index]; + public GraphicsOutputInterface getDisplayDevice(int index) throws InappropriateDeviceException { + return getDisplayDevice(getGraphicsEnvironment().getScreenDevices()[index].getIDstring()); } GraphicsEnvironment getGraphicsEnvironment() throws InappropriateDeviceException { @@ -118,12 +111,19 @@ public void removeAssignment(Printer printer){ if (printer == null) return; - graphicsDevicesByPrinter.remove(printer); + String removalId = printer.getDisplayDeviceID(); + printer.disassociateDisplay(); - String deviceId = printer.getDisplayDeviceID(); - if (deviceId == null) - return; + String otherId = displayIdsByPrinter.remove(printer); + if (otherId != removalId && !otherId.equals(removalId)) { + logger.error("otherId:" + otherId + " different than printerDisplayId:" + removalId); + } - printersByDisplayIDString.remove(deviceId); + if (removalId != null) { + Printer otherPrinter = printersByDisplayIDString.remove(removalId); + if (!printer.equals(otherPrinter)) { + logger.error("otherPrinter:" + otherPrinter + " different than printer:" + printer); + } + } } } \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/GraphicsDeviceOutputInterface.java b/host/src/main/java/org/area515/resinprinter/display/GraphicsDeviceOutputInterface.java new file mode 100644 index 000000000..2595b0e9b --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/GraphicsDeviceOutputInterface.java @@ -0,0 +1,112 @@ +package org.area515.resinprinter.display; + +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.GraphicsDevice; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; + +import javax.swing.JFrame; + +public class GraphicsDeviceOutputInterface implements GraphicsOutputInterface { + private String displayName; + private GraphicsDevice device; + + public GraphicsDeviceOutputInterface(String displayName, GraphicsDevice device) { + this.displayName = displayName; + this.device = device; + } + + @Override + public String getIDstring() { + return displayName; + } + + public String toString() { + return displayName; + } + + public GraphicsDevice getGraphicsDevice() { + return device; + } + + @Override + public Rectangle getBoundary() { + return getGraphicsDevice().getDefaultConfiguration().getBounds(); + } + + @Override + public boolean isDisplayBusy() { + return false; + } + + @Override + public void resetSliceCount() { + throw new IllegalStateException("You should never call resetSliceCount from this class"); + } + + @Override + public void dispose() { + throw new IllegalStateException("You should never call dispose from this class"); + } + + @Override + public void showBlankImage() { + throw new IllegalStateException("You should never call showBlankImage from this class"); + } + + @Override + public void showCalibrationImage(int xPixels, int yPixels) { + throw new IllegalStateException("You should never call showCalibrationImage from this class"); + } + + @Override + public void showGridImage(int pixels) { + throw new IllegalStateException("You should never call showGridImage from this class"); + } + + @Override + public void showImage(BufferedImage image) { + throw new IllegalStateException("You should never call showImage from this class"); + } + + @Override + public String buildIDString() { + return displayName; + } + + @Override + public GraphicsOutputInterface initializeDisplay(String displayId) { + GraphicsDevice device; + try { + device = ((GraphicsDeviceOutputInterface)DisplayManager.Instance().getDisplayDevice(displayId)).device; + } catch (InappropriateDeviceException e) { + throw new IllegalArgumentException("Couldn't find displayId:" + displayId, e); + } + PrinterDisplayFrame refreshFrame = new PrinterDisplayFrame(device); + refreshFrame.setAlwaysOnTop(true); + refreshFrame.setUndecorated(true); + refreshFrame.setExtendedState(JFrame.MAXIMIZED_BOTH); + Dimension dim = device.getDefaultConfiguration().getBounds().getSize(); + refreshFrame.setMinimumSize(dim); + refreshFrame.setSize(dim); + refreshFrame.setVisible(true); + if (device.isFullScreenSupported()) { + device.setFullScreenWindow(refreshFrame);//TODO: Does projector not support full screen + } + //This can only be done with a real graphics device since it would reassign the printer Simulation + //OLD getConfiguration().getMachineConfig().setOSMonitorID(device.getDefaultConfiguration().getDevice().getIDstring()); + //TODO: we shut this off. Is that bad? + //device.getConfiguration().getMachineConfig().setOSMonitorID(device.getIDstring()); + + // hide mouse in full screen + Toolkit toolkit = Toolkit.getDefaultToolkit(); + Point hotSpot = new Point(0,0); + BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT); + Cursor invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor"); + refreshFrame.setCursor(invisibleCursor); + return refreshFrame; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/GraphicsOutputInterface.java b/host/src/main/java/org/area515/resinprinter/display/GraphicsOutputInterface.java new file mode 100644 index 000000000..d6fe13c45 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/GraphicsOutputInterface.java @@ -0,0 +1,58 @@ +package org.area515.resinprinter.display; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +public interface GraphicsOutputInterface { + public boolean isDisplayBusy(); //It's not necessary, but it's helpful to let the user know that the display might be busy in a gui... + public void resetSliceCount(); + public void dispose(); + public void showBlankImage(); + public void showCalibrationImage(int xPixels, int yPixels); + public void showGridImage(int pixels); + public void showImage(BufferedImage image); + public Rectangle getBoundary(); + public String getIDstring(); + public String buildIDString(); + public GraphicsOutputInterface initializeDisplay(String displayId); + + public static void showGrid(Graphics2D g2, Rectangle screenSize, int gridSquareSize) { + g2.setBackground(Color.black); + g2.clearRect(0, 0, screenSize.width, screenSize.height); + g2.setColor(Color.RED); + for (int x = 0; x < screenSize.width; x += gridSquareSize) { + g2.drawLine(x, 0, x, screenSize.height); + } + + for (int y = 0; y < screenSize.height; y += gridSquareSize) { + g2.drawLine(0, y, screenSize.width, y); + } + } + + public static void showCalibration(Graphics2D g2, Rectangle screenSize, int calibrationX, int calibrationY) { + g2.setBackground(Color.black); + g2.clearRect(0, 0, screenSize.width, screenSize.height); + g2.setColor(Color.RED); + int startingX = screenSize.width / 2 - calibrationX / 2; + int startingY = screenSize.height / 2 - calibrationY / 2; + int halfLengthOfDimLines = 50; + + //X Dimension lines + g2.drawLine(startingX , screenSize.height / 2 - halfLengthOfDimLines, startingX , screenSize.height / 2 + halfLengthOfDimLines); + g2.drawLine(startingX + calibrationX, screenSize.height / 2 - halfLengthOfDimLines, startingX + calibrationX, screenSize.height / 2 + halfLengthOfDimLines); + + //Y Dimension lines + g2.drawLine(screenSize.width / 2 - halfLengthOfDimLines, startingY , screenSize.width / 2 + halfLengthOfDimLines, startingY); + g2.drawLine(screenSize.width / 2 - halfLengthOfDimLines, startingY + calibrationY, screenSize.width / 2 + halfLengthOfDimLines, startingY + calibrationY); + + //Vertical line of cross + g2.drawLine(screenSize.width / 2, startingY, screenSize.width / 2, startingY + calibrationY); + + //Horizontal line of cross + g2.setStroke(new BasicStroke(5, 0, 0, 1.0f, new float[]{10, 10}, 2.0f)); + g2.drawLine(startingX, screenSize.height / 2, startingX + calibrationX, screenSize.height / 2); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/LastAvailableDisplay.java b/host/src/main/java/org/area515/resinprinter/display/LastAvailableDisplay.java new file mode 100644 index 000000000..2e3b7dbcd --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/LastAvailableDisplay.java @@ -0,0 +1,41 @@ +package org.area515.resinprinter.display; + +import java.awt.GraphicsDevice; +import java.awt.HeadlessException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +public class LastAvailableDisplay extends GraphicsDeviceOutputInterface { + public static final String NAME = "Last available display"; + + public LastAvailableDisplay() throws HeadlessException { + super(NAME, null); + } + + public GraphicsDevice getGraphicsDevice() { + try { + ArrayList devices = new ArrayList(); + devices.addAll(Arrays.asList(DisplayManager.Instance().getGraphicsEnvironment().getScreenDevices())); + Collections.reverse(devices); + for (GraphicsDevice currentDevice : devices) { + if (DisplayManager.Instance().isGraphicsDeviceDisplayAvailable(currentDevice.getIDstring())) { + return currentDevice; + } + } + + return null; + } catch (InappropriateDeviceException e) { + throw new IllegalArgumentException("Couldn't initialize graphics environment", e); + } + } + + @Override + public String buildIDString() { + GraphicsDevice lastDevice = getGraphicsDevice(); + if (lastDevice == null) { + return null; + } + return lastDevice.getIDstring(); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/PrinterDisplayFrame.java b/host/src/main/java/org/area515/resinprinter/display/PrinterDisplayFrame.java new file mode 100644 index 000000000..2488fc287 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/PrinterDisplayFrame.java @@ -0,0 +1,149 @@ +package org.area515.resinprinter.display; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsDevice; +import java.awt.HeadlessException; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import javax.swing.JFrame; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.area515.resinprinter.printer.Printer.DisplayState; +import org.area515.util.Log4jTimer; + +public class PrinterDisplayFrame extends JFrame implements GraphicsOutputInterface { + private static final long serialVersionUID = 5024551291098098753L; + private static final Logger logger = LogManager.getLogger(); + + private String IMAGE_REALIZE_TIMER = "Image Realize:"; + private DisplayState displayState = DisplayState.Blank; + private int gridSquareSize; + private int calibrationX; + private int calibrationY; + private BufferedImage displayImage; + private int sliceNumber; + private boolean isSimulatedDisplay; + private String displayId; + private GraphicsDevice device; + + public PrinterDisplayFrame(String displayId) throws HeadlessException { + super(); + this.displayId = displayId; + this.isSimulatedDisplay = true; + getRootPane().setBackground(Color.black); + getContentPane().setBackground(Color.black); + IMAGE_REALIZE_TIMER += hashCode(); + } + + public PrinterDisplayFrame(GraphicsDevice device) { + super(device.getDefaultConfiguration()); + this.device = device; + this.isSimulatedDisplay = false; + getRootPane().setBackground(Color.black); + getContentPane().setBackground(Color.black); + IMAGE_REALIZE_TIMER += hashCode(); + } + + public DisplayState getDisplayState() { + return displayState; + } + public void setDisplayState(DisplayState displayState) { + this.displayState = displayState; + } + + @Override + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + //we need to add this method back in because some UV light engines require it. + super.paint(g); + + Rectangle screenSize = getGraphicsConfiguration().getBounds(); + switch (displayState) { + case Blank : + g2.setBackground(Color.black); + g2.clearRect(0, 0, screenSize.width, screenSize.height); + logger.debug("Blank realized:{}", () -> Log4jTimer.completeGlobalTimer(IMAGE_REALIZE_TIMER)); + return; + case Grid : + GraphicsOutputInterface.showGrid(g2, screenSize, gridSquareSize); + logger.debug("Grid realized:{}", () -> Log4jTimer.completeGlobalTimer(IMAGE_REALIZE_TIMER)); + return; + case Calibration : + GraphicsOutputInterface.showCalibration(g2, screenSize, calibrationX, calibrationY); + logger.debug("Calibration realized:{}", () -> Log4jTimer.completeGlobalTimer(IMAGE_REALIZE_TIMER)); + return; + case CurrentSlice : + g2.drawImage(displayImage, null, screenSize.width / 2 - displayImage.getWidth() / 2, screenSize.height / 2 - displayImage.getHeight() / 2); + if (isSimulatedDisplay) { + g2.setColor(Color.RED); + g2.setFont(getFont()); + g2.drawString("Slice:" + sliceNumber, getInsets().left, getInsets().top + g2.getFontMetrics().getHeight()); + } + logger.debug("Image realized:{}", () -> Log4jTimer.completeGlobalTimer(IMAGE_REALIZE_TIMER)); + return; + } + + } + + public void resetSliceCount() { + sliceNumber = 0; + } + + public void showBlankImage() { + logger.debug("Blank assigned:{}", () -> Log4jTimer.startGlobalTimer(IMAGE_REALIZE_TIMER)); + setDisplayState(DisplayState.Blank); + repaint(); + } + + public void showCalibrationImage(int xPixels, int yPixels) { + logger.debug("Calibration assigned:{}", () -> Log4jTimer.startGlobalTimer(IMAGE_REALIZE_TIMER)); + setDisplayState(DisplayState.Calibration); + calibrationX = xPixels; + calibrationY = yPixels; + repaint(); + } + + public void showGridImage(int pixels) { + logger.debug("Grid assigned:{}", () -> Log4jTimer.startGlobalTimer(IMAGE_REALIZE_TIMER)); + setDisplayState(DisplayState.Grid); + gridSquareSize = pixels; + repaint(); + } + + public void showImage(BufferedImage image) { + logger.debug("Image assigned:{}", () -> Log4jTimer.startGlobalTimer(IMAGE_REALIZE_TIMER)); + sliceNumber++; + setDisplayState(DisplayState.CurrentSlice); + displayImage = image; + repaint(); + } + + @Override + public Rectangle getBoundary() { + return getGraphicsConfiguration().getBounds(); + } + + @Override + public boolean isDisplayBusy() { + return false; + } + + @Override + public String getIDstring() { + return displayId; + } + + @Override + public String buildIDString() { + throw new IllegalStateException("You should never call buildIDString from this class"); + } + + @Override + public GraphicsOutputInterface initializeDisplay(String displayId) { + throw new IllegalStateException("You should never call initializeDisplay from this class"); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/SimulatedDisplay.java b/host/src/main/java/org/area515/resinprinter/display/SimulatedDisplay.java new file mode 100644 index 000000000..0034d3032 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/SimulatedDisplay.java @@ -0,0 +1,40 @@ +package org.area515.resinprinter.display; + +import java.awt.Dimension; +import java.awt.GraphicsDevice; +import java.awt.HeadlessException; + +import javax.swing.JFrame; + +public class SimulatedDisplay extends GraphicsDeviceOutputInterface { + public static final String NAME = "Simulated display"; + public int displayIndex; + + public SimulatedDisplay() { + super(NAME, null); + } + + @Override + public GraphicsDevice getGraphicsDevice() { + try { + return DisplayManager.Instance().getGraphicsEnvironment().getScreenDevices()[0]; + } catch (HeadlessException | InappropriateDeviceException e) { + throw new IllegalArgumentException("Couldn't get screen devices from display manager.", e); + } + } + + @Override + public String buildIDString() { + return NAME + ":" + displayIndex++; + } + + @Override + public GraphicsOutputInterface initializeDisplay(String displayId) { + PrinterDisplayFrame refreshFrame = new PrinterDisplayFrame(displayId); + refreshFrame.setTitle(displayId); + refreshFrame.setVisible(true); + refreshFrame.setExtendedState(JFrame.MAXIMIZED_BOTH); + refreshFrame.setMinimumSize(new Dimension(500, 500)); + return refreshFrame; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/ALPHA.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/ALPHA.java new file mode 100644 index 000000000..895ac6c63 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/ALPHA.java @@ -0,0 +1,19 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum ALPHA { + DISPMANX_FLAGS_ALPHA_FROM_SOURCE(0), + DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS(1), + DISPMANX_FLAGS_ALPHA_FIXED_NON_ZERO(2), + DISPMANX_FLAGS_ALPHA_FIXED_EXCEED_0X07(3), + DISPMANX_FLAGS_ALPHA_PREMULT(1 << 16), + DISPMANX_FLAGS_ALPHA_MIX(1 << 17); + private int flag = 0; + + ALPHA(int flag) { + this.flag = flag; + } + + public int getFlag() { + return flag; + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManX.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManX.java new file mode 100644 index 000000000..4207c707a --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManX.java @@ -0,0 +1,38 @@ +package org.area515.resinprinter.display.dispmanx; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; + +public interface DispManX extends Library { + public DispManX INSTANCE = (DispManX)Native.loadLibrary("bcm_host", DispManX.class); + + public int bcm_host_init(); + public int graphics_get_display_size(int screenIndex, IntByReference width, IntByReference height); + public int vc_dispmanx_display_open(int screenIndex); + public int vc_dispmanx_resource_create(int resourceType, int width, int height, IntByReference imagePointer); + public int vc_dispmanx_rect_set(VC_RECT_T.ByReference rectangleToCreate, int offsetX, int offsetY, int width, int height); + public int vc_dispmanx_update_start(int priority); + public int vc_dispmanx_element_add( + int updateHandle, + int displayHandle, + int layer, + VC_RECT_T.ByReference destinationRectangle, + int sourceResourceHandle, + VC_RECT_T.ByReference sourceRectangle, + int protectionMode, + VC_DISPMANX_ALPHA_T.ByReference alpha, + int clamp, + int imageTransformation); + public int vc_dispmanx_update_submit_sync(int updateHandle); + public int vc_dispmanx_resource_write_data( + int resourceHandle, + int resourceType, + int pitch, + Pointer imageConvertedFromBufferedImage, + VC_RECT_T.ByReference copyRectangle); + public int vc_dispmanx_element_remove(int updateHandle, int elementHandle); + public int vc_dispmanx_resource_delete(int resourceHandle); + public int vc_dispmanx_display_close(int displayHandle); +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManXDevice.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManXDevice.java new file mode 100644 index 000000000..c8dc26463 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/DispManXDevice.java @@ -0,0 +1,345 @@ +package org.area515.resinprinter.display.dispmanx; + +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.area515.resinprinter.display.GraphicsDeviceOutputInterface; +import org.area515.resinprinter.display.GraphicsOutputInterface; +import org.area515.resinprinter.display.InappropriateDeviceException; +import org.area515.util.Log4jTimer; + +import com.sun.jna.Memory; +import com.sun.jna.ptr.IntByReference; + +public class DispManXDevice implements GraphicsOutputInterface { + private static final String IMAGE_REALIZE_TIMER = "Image Realize"; + private static final Logger logger = LogManager.getLogger(); + private static boolean BCM_INIT = false; + private static ReentrantLock BCM_LOCK = new ReentrantLock(true); + + private ReentrantLock displayLock = new ReentrantLock(true); + private ReentrantLock activityLock = new ReentrantLock(true); + private Rectangle bounds = new Rectangle(); + private SCREEN screen; + private VC_DISPMANX_ALPHA_T.ByReference alpha; + private int displayHandle; + private boolean screenInitialized = false; + private String displayName; + + //For dispmanx + private int imageResourceHandle; + private int imageElementHandle; + //For Image + private Memory imagePixels; + private int imageWidth; + private int imageHeight; + //For Calibration and Grid + private Memory calibrationAndGridPixels; + private BufferedImage calibrationAndGridImage; + + public DispManXDevice(String displayName, SCREEN screen) throws InappropriateDeviceException { + this.displayName = displayName; + this.screen = screen; + + //Call a harmless method to ensure that Dispmanx lib is initialized + VC_RECT_T.ByReference sourceRect = new VC_RECT_T.ByReference(); + DispManX.INSTANCE.vc_dispmanx_rect_set(sourceRect, 0, 0, 0, 0); + } + + private static void bcmHostInit() { + if (BCM_INIT) { + return; + } + + BCM_LOCK.lock(); + try { + if (BCM_INIT) { + return; + } + + logger.info("initialize bcm host"); + int returnCode = DispManX.INSTANCE.bcm_host_init(); + if (returnCode != 0) { + throw new IllegalArgumentException("bcm_host_init failed with:" + returnCode); + } + BCM_INIT = true; + } finally { + BCM_LOCK.unlock(); + } + } + + private void initializeScreen() { + if (screenInitialized) { + return; + } + + displayLock.lock(); + try { + if (screenInitialized) { + return; + } + + logger.info("initialize screen"); + bcmHostInit(); + + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + int returnCode = DispManX.INSTANCE.graphics_get_display_size(screen.getId(), width, height); + if (returnCode != 0) { + throw new IllegalArgumentException("graphics_get_display_size failed with:" + returnCode); + } + bounds.setBounds(0, 0, width.getValue(), height.getValue()); + + displayHandle = DispManX.INSTANCE.vc_dispmanx_display_open(screen.getId()); + if (displayHandle == 0) { + throw new IllegalArgumentException("vc_dispmanx_display_open failed with:" + returnCode); + } + + VC_DISPMANX_ALPHA_T.ByReference alpha = new VC_DISPMANX_ALPHA_T.ByReference(); + alpha.flags = ALPHA.DISPMANX_FLAGS_ALPHA_FROM_SOURCE.getFlag() | ALPHA.DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS.getFlag(); + alpha.opacity = 255; + screenInitialized = true; + } finally { + displayLock.unlock(); + } + } + + @Override + public void dispose() { + displayLock.lock(); + try { + logger.info("dispose screen"); + removeAllElementsFromScreen(); + logger.info("vc_dispmanx_display_close result:" + DispManX.INSTANCE.vc_dispmanx_display_close(displayHandle)); + calibrationAndGridPixels = null; + imagePixels = null; + calibrationAndGridImage = null; + imageWidth = 0; + imageHeight = 0; + screenInitialized = false; + } finally { + displayLock.unlock(); + } + } + + public static int getPitch( int x, int y ) { + return ((x + (y)-1) & ~((y)-1)); + } + + private Memory loadBitmapRGB565(BufferedImage image, Memory destPixels, IntByReference width, IntByReference height, IntByReference pitchByRef) { + int bytesPerPixel = 2; + int pitch = getPitch(bytesPerPixel * image.getWidth(), 32); + pitchByRef.setValue(pitch); + if (destPixels == null) { + destPixels = new Memory(pitch * image.getHeight()); + } + + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + int rgb = image.getRGB(x, y); + destPixels.setShort((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, (short)(((rgb & 0xf80000) >>> 8) | ((rgb & 0xfc00) >>> 5) | (rgb & 0xf8 >>> 3))); + } + } + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + private Memory loadBitmapARGB8888(BufferedImage image, Memory destPixels, IntByReference width, IntByReference height, IntByReference pitchByRef) { + int bytesPerPixel = 4; + int pitch = getPitch(bytesPerPixel * image.getWidth(), 32); + pitchByRef.setValue(pitch); + if (destPixels == null) { + destPixels = new Memory(pitch * image.getHeight()); + } + + logger.debug("loadBitmapARGB8888 alg started:{}", () -> Log4jTimer.startTimer(IMAGE_REALIZE_TIMER)); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + destPixels.setInt((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, image.getRGB(x, y)); + } + } + logger.debug("loadBitmapARGB8888 alg complete:{}", () -> Log4jTimer.completeTimer(IMAGE_REALIZE_TIMER)); + + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + @Override + public void showBlankImage() { + initializeScreen(); + removeAllElementsFromScreen(); + } + + private void removeAllElementsFromScreen() { + logger.info("screen cleanup started"); + int updateHandle = DispManX.INSTANCE.vc_dispmanx_update_start( 0 ); + if (updateHandle == 0) { + logger.info("vc_dispmanx_update_start failed"); + } else { + logger.debug("image vc_dispmanx_element_remove result:" + DispManX.INSTANCE.vc_dispmanx_element_remove(updateHandle, imageElementHandle)); + logger.debug("vc_dispmanx_update_submit_sync result:" + DispManX.INSTANCE.vc_dispmanx_update_submit_sync(updateHandle)); + logger.debug("image vc_dispmanx_resource_delete result:" + DispManX.INSTANCE.vc_dispmanx_resource_delete(imageResourceHandle)); + } + } + + private void initializeCalibrationAndGridImage() { + if (calibrationAndGridImage != null) { + return; + } + + calibrationAndGridImage = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_ARGB); + } + + @Override + public void showCalibrationImage(int xPixels, int yPixels) { + logger.debug("Calibration assigned:{}", () -> Log4jTimer.startTimer(IMAGE_REALIZE_TIMER)); + showBlankImage(); + initializeCalibrationAndGridImage(); + Graphics2D graphics = (Graphics2D)calibrationAndGridImage.createGraphics(); + GraphicsOutputInterface.showCalibration(graphics, bounds, xPixels, yPixels); + graphics.dispose(); + calibrationAndGridPixels = showImage(calibrationAndGridPixels, calibrationAndGridImage); + logger.debug("Calibration realized:{}", () -> Log4jTimer.completeTimer(IMAGE_REALIZE_TIMER)); + } + + @Override + public void showGridImage(int pixels) { + logger.debug("Grid assigned:{}", () -> Log4jTimer.startTimer(IMAGE_REALIZE_TIMER)); + showBlankImage(); + initializeCalibrationAndGridImage(); + Graphics2D graphics = (Graphics2D)calibrationAndGridImage.createGraphics(); + GraphicsOutputInterface.showGrid(graphics, bounds, pixels); + graphics.dispose(); + + calibrationAndGridPixels = showImage(calibrationAndGridPixels, calibrationAndGridImage); + logger.debug("Grid realized:{}", () -> Log4jTimer.completeTimer(IMAGE_REALIZE_TIMER)); + } + + private Memory showImage(Memory memory, BufferedImage image) { + activityLock.lock(); + try { + showBlankImage();//delete the old resources because we are creating new ones... + + IntByReference imageWidth = new IntByReference(); + IntByReference imageHeight = new IntByReference(); + IntByReference imagePitch = new IntByReference(); + + memory = loadBitmapARGB8888(image, memory, imageWidth, imageHeight, imagePitch); + VC_RECT_T.ByReference sourceRect = new VC_RECT_T.ByReference(); + DispManX.INSTANCE.vc_dispmanx_rect_set(sourceRect, 0, 0, imageWidth.getValue()<<16, imageHeight.getValue()<<16);//Shifting by 16 is a zoom factor of zero + + IntByReference nativeImageReference = new IntByReference(); + imageResourceHandle = DispManX.INSTANCE.vc_dispmanx_resource_create( + VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), + imageWidth.getValue(), + imageHeight.getValue(), + nativeImageReference); + if (imageResourceHandle == 0) { + throw new IllegalArgumentException("Couldn't create resourceHandle for dispmanx"); + } + + VC_RECT_T.ByReference sizeRect = new VC_RECT_T.ByReference(); + DispManX.INSTANCE.vc_dispmanx_rect_set(sizeRect, 0, 0, imageWidth.getValue(), imageHeight.getValue()); + int returnCode = DispManX.INSTANCE.vc_dispmanx_resource_write_data( + imageResourceHandle, + VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), + imagePitch.getValue() , + memory, + sizeRect); + if (returnCode != 0) { + throw new IllegalArgumentException("Couldn't vc_dispmanx_resource_write_data for dispmanx:" + returnCode); + } + + int updateHandle = DispManX.INSTANCE.vc_dispmanx_update_start(0); //This method should be called create update + if (updateHandle == 0) { + throw new IllegalArgumentException("Couldn't vc_dispmanx_update_start for dispmanx"); + } + + VC_RECT_T.ByReference destinationRect = new VC_RECT_T.ByReference(); + DispManX.INSTANCE.vc_dispmanx_rect_set( + destinationRect, + (bounds.width - imageWidth.getValue()) / 2, + (bounds.height - imageHeight.getValue()) / 2, + imageWidth.getValue(), + imageHeight.getValue()); + imageElementHandle = DispManX.INSTANCE.vc_dispmanx_element_add( //Creates and adds the element to the current screen update + updateHandle, + displayHandle, + 1, + destinationRect, + imageResourceHandle, + sourceRect, + PROTECTION.DISPMANX_PROTECTION_NONE.getcConst(), + alpha, + 0, + VC_IMAGE_TRANSFORM_T.VC_IMAGE_ROT0.getcConst()); + if (updateHandle == 0) { + throw new IllegalArgumentException("Couldn't vc_dispmanx_element_add for dispmanx"); + } + + returnCode = DispManX.INSTANCE.vc_dispmanx_update_submit_sync(updateHandle);//Wait for the update to complete + if (returnCode != 0) { + throw new IllegalArgumentException("Couldn't vc_dispmanx_update_submit_sync for dispmanx:" + returnCode); + } + + return memory; + } finally { + activityLock.unlock(); + } + } + + @Override + public void showImage(BufferedImage image) { + logger.debug("Image assigned:{}", () -> Log4jTimer.startTimer(IMAGE_REALIZE_TIMER)); + if (image.getWidth() == imageWidth && image.getHeight() == imageHeight) { + imagePixels = showImage(imagePixels, image); + } else { + imagePixels = showImage(null, image); + } + imageWidth = image.getWidth(); + imageHeight = image.getHeight(); + logger.debug("Image realized:{}", () -> Log4jTimer.completeTimer(IMAGE_REALIZE_TIMER)); + } + + @Override + public void resetSliceCount() { + //Since this isn't used for debugging we don't do anything + } + + @Override + public Rectangle getBoundary() { + initializeScreen(); + return bounds; + } + + @Override + public boolean isDisplayBusy() { + return activityLock.isLocked(); + } + + @Override + public String getIDstring() { + return displayName; + } + + @Override + public String buildIDString() { + return displayName; + } + + @Override + public GraphicsOutputInterface initializeDisplay(String displayId) { + return this; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/NativeMemoryBackedBufferedImage.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/NativeMemoryBackedBufferedImage.java new file mode 100644 index 000000000..087488a52 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/NativeMemoryBackedBufferedImage.java @@ -0,0 +1,76 @@ +package org.area515.resinprinter.display.dispmanx; + +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.ByteBuffer; +import java.util.Hashtable; + +import com.sun.jna.Memory; + +public class NativeMemoryBackedBufferedImage extends BufferedImage { + private Memory memory; + + public NativeMemoryBackedBufferedImage(Memory memory, ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { + super(cm, raster, isRasterPremultiplied, properties); + this.memory = memory; + } + + public Memory getMemory() { + return memory; + } + + public int getPitch() { + return ((4 * getWidth() + (getHeight())-1) & ~((getHeight())-1)); + } + + private static int getPitch(int x, int y) { + return ((x + (y)-1) & ~((y)-1)); + } + + public static NativeMemoryBackedBufferedImage newInstance(int width, int height) { + final int pitch = getPitch(4 * width, 32); + Memory pixelMemory = new Memory(pitch * height); + final ByteBuffer buffer = pixelMemory.getByteBuffer(0, pixelMemory.size()); + DataBuffer nativeScreenBuffer = new DataBuffer(DataBuffer.TYPE_INT, width * height) { + @Override + public int getElem(int bank, int i) { + //TODO: COLORS ARE TOTALLY WACKED! + //TODO: ALPHA IS BROKEN + //TODO: (y*(pitch / bytesPerPixel) + x) * bytesPerPixel + return (buffer.get(i * 4) << 24) | (buffer.get(i * 4 + 1) << 16) | (buffer.get(i * 4 + 2) << 8) | (buffer.get(i * 4 + 3)); + } + + @Override + public void setElem(int bank, int i, int val) { + //TODO: COLORS ARE TOTALLY WACKED! + //TODO: ALPHA IS BROKEN + //TODO: (y*(pitch / bytesPerPixel) + x) * bytesPerPixel + buffer.put(i * 4 + 0, (byte)((val | 0xFF000000) >> 24)); + buffer.put(i * 4 + 1, (byte)((val | 0xFF0000) >> 16)); + buffer.put(i * 4 + 2, (byte)((val | 0xFF00) >> 8)); + buffer.put(i * 4 + 3, (byte)(val | 0xFF)); + } + }; + + SampleModel argb = new SinglePixelPackedSampleModel( + DataBuffer.TYPE_INT, + width, + height, + new int[] { 0xFF0000, 0xFF00, 0xFF, 0xFF000000 }); + + WritableRaster raster = new WritableRaster(argb, nativeScreenBuffer, new Point()){}; + + return new NativeMemoryBackedBufferedImage( + pixelMemory, + new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000), + raster, + false, + null); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/PROTECTION.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/PROTECTION.java new file mode 100644 index 000000000..749ec84ff --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/PROTECTION.java @@ -0,0 +1,16 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum PROTECTION { + DISPMANX_PROTECTION_MAX(0x0f), + DISPMANX_PROTECTION_NONE(0), + DISPMANX_PROTECTION_HDCP(11); + private int cConst; + + PROTECTION(int cConst) { + this.cConst = cConst; + } + + public int getcConst() { + return cConst; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiForceTVScreen.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiForceTVScreen.java new file mode 100644 index 000000000..027921c8f --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiForceTVScreen.java @@ -0,0 +1,9 @@ +package org.area515.resinprinter.display.dispmanx; + +import org.area515.resinprinter.display.InappropriateDeviceException; + +public class RaspberryPiForceTVScreen extends DispManXDevice { + public RaspberryPiForceTVScreen() throws InappropriateDeviceException { + super("Raspberry Pi Force TV", SCREEN.DISPMANX_ID_FORCE_TV); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiMainLCDScreen.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiMainLCDScreen.java new file mode 100644 index 000000000..c379aa1e6 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/RaspberryPiMainLCDScreen.java @@ -0,0 +1,9 @@ +package org.area515.resinprinter.display.dispmanx; + +import org.area515.resinprinter.display.InappropriateDeviceException; + +public class RaspberryPiMainLCDScreen extends DispManXDevice { + public RaspberryPiMainLCDScreen() throws InappropriateDeviceException { + super("Raspberry Pi Main LCD", SCREEN.DISPMANX_ID_MAIN_LCD); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/SCREEN.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/SCREEN.java new file mode 100644 index 000000000..562fca838 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/SCREEN.java @@ -0,0 +1,20 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum SCREEN { + DISPMANX_ID_MAIN_LCD(0), + DISPMANX_ID_AUX_LCD(1), + DISPMANX_ID_HDMI(2), + DISPMANX_ID_SDTV(3), + DISPMANX_ID_FORCE_LCD(4), + DISPMANX_ID_FORCE_TV(5), + DISPMANX_ID_FORCE_OTHER(6); + private int id; + + SCREEN(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/TRANSFORM.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/TRANSFORM.java new file mode 100644 index 000000000..3800f2d5b --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/TRANSFORM.java @@ -0,0 +1,16 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum TRANSFORM { + TRANSFORM_HFLIP(1<<0), + TRANSFORM_VFLIP(1<<1), + TRANSFORM_TRANSPOSE (1<<2); + private int cConst; + + TRANSFORM(int cConst) { + this.cConst = cConst; + } + + public int getcConst() { + return cConst; + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_DISPMANX_ALPHA_T.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_DISPMANX_ALPHA_T.java new file mode 100644 index 000000000..47843ba7e --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_DISPMANX_ALPHA_T.java @@ -0,0 +1,19 @@ +package org.area515.resinprinter.display.dispmanx; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public class VC_DISPMANX_ALPHA_T extends Structure { + public static class ByReference extends VC_DISPMANX_ALPHA_T implements Structure.ByReference {} + + public int flags; + public int opacity; + public int mask; + + @Override + protected List getFieldOrder() { + return Arrays.asList("flags", "opacity", "mask"); + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TRANSFORM_T.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TRANSFORM_T.java new file mode 100644 index 000000000..974b435b9 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TRANSFORM_T.java @@ -0,0 +1,21 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum VC_IMAGE_TRANSFORM_T { + VC_IMAGE_ROT0(0), + VC_IMAGE_MIRROR_ROT0(TRANSFORM.TRANSFORM_HFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT180(TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_ROT180(TRANSFORM.TRANSFORM_HFLIP.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT90(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()), + VC_IMAGE_ROT270(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_HFLIP.getcConst()), + VC_IMAGE_ROT90(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT270(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_HFLIP.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()); + private int cConst; + + VC_IMAGE_TRANSFORM_T(int cConst) { + this.cConst = cConst; + } + + public int getcConst() { + return cConst; + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TYPE_T.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TYPE_T.java new file mode 100644 index 000000000..95adc3d09 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_IMAGE_TYPE_T.java @@ -0,0 +1,79 @@ +package org.area515.resinprinter.display.dispmanx; + +public enum VC_IMAGE_TYPE_T { + VC_IMAGE_MIN(0), //bounds for error checking + + VC_IMAGE_RGB565(1), + VC_IMAGE_1BPP(2), + VC_IMAGE_YUV420(3), + VC_IMAGE_48BPP(4), + VC_IMAGE_RGB888(5), + VC_IMAGE_8BPP(6), + VC_IMAGE_4BPP(7), // 4bpp palettised image + VC_IMAGE_3D32(8), /* A separated format of 16 colour/light shorts followed by 16 z values */ + VC_IMAGE_3D32B(9), /* 16 colours followed by 16 z values */ + VC_IMAGE_3D32MAT(10), /* A separated format of 16 material/colour/light shorts followed by 16 z values */ + VC_IMAGE_RGB2X9(11), /* 32 bit format containing 18 bits of 6.6.6 RGB, 9 bits per short */ + VC_IMAGE_RGB666(12), /* 32-bit format holding 18 bits of 6.6.6 RGB */ + VC_IMAGE_PAL4_OBSOLETE(13), // 4bpp palettised image with embedded palette + VC_IMAGE_PAL8_OBSOLETE(14), // 8bpp palettised image with embedded palette + VC_IMAGE_RGBA32(15), /* RGB888 with an alpha byte after each pixel */ /* xxx: isn't it BEFORE each pixel? */ + VC_IMAGE_YUV422(16), /* a line of Y (32-byte padded), a line of U (16-byte padded), and a line of V (16-byte padded) */ + VC_IMAGE_RGBA565(17), /* RGB565 with a transparent patch */ + VC_IMAGE_RGBA16(18), /* Compressed (4444) version of RGBA32 */ + VC_IMAGE_YUV_UV(19), /* VCIII codec format */ + VC_IMAGE_TF_RGBA32(20), /* VCIII T-format RGBA8888 */ + VC_IMAGE_TF_RGBX32(21), /* VCIII T-format RGBx8888 */ + VC_IMAGE_TF_FLOAT(22), /* VCIII T-format float */ + VC_IMAGE_TF_RGBA16(23), /* VCIII T-format RGBA4444 */ + VC_IMAGE_TF_RGBA5551(24), /* VCIII T-format RGB5551 */ + VC_IMAGE_TF_RGB565(25), /* VCIII T-format RGB565 */ + VC_IMAGE_TF_YA88(26), /* VCIII T-format 8-bit luma and 8-bit alpha */ + VC_IMAGE_TF_BYTE(27), /* VCIII T-format 8 bit generic sample */ + VC_IMAGE_TF_PAL8(28), /* VCIII T-format 8-bit palette */ + VC_IMAGE_TF_PAL4(29), /* VCIII T-format 4-bit palette */ + VC_IMAGE_TF_ETC1(30), /* VCIII T-format Ericsson Texture Compressed */ + VC_IMAGE_BGR888(31), /* RGB888 with R & B swapped */ + VC_IMAGE_BGR888_NP(32), /* RGB888 with R & B swapped, but with no pitch, i.e. no padding after each row of pixels */ + VC_IMAGE_BAYER(33), /* Bayer image, extra defines which variant is being used */ + VC_IMAGE_CODEC(34), /* General wrapper for codec images e.g. JPEG from camera */ + VC_IMAGE_YUV_UV32(35), /* VCIII codec format */ + VC_IMAGE_TF_Y8(36), /* VCIII T-format 8-bit luma */ + VC_IMAGE_TF_A8(37), /* VCIII T-format 8-bit alpha */ + VC_IMAGE_TF_SHORT(38),/* VCIII T-format 16-bit generic sample */ + VC_IMAGE_TF_1BPP(39), /* VCIII T-format 1bpp black/white */ + VC_IMAGE_OPENGL(40), + VC_IMAGE_YUV444I(41), /* VCIII-B0 HVS YUV 4:4:4 interleaved samples */ + VC_IMAGE_YUV422PLANAR(42), /* Y, U, & V planes separately (VC_IMAGE_YUV422 has them interleaved on a per line basis) */ + VC_IMAGE_ARGB8888(43), /* 32bpp with 8bit alpha at MS byte, with R, G, B (LS byte) */ + VC_IMAGE_XRGB8888(44), /* 32bpp with 8bit unused at MS byte, with R, G, B (LS byte) */ + + VC_IMAGE_YUV422YUYV(45), /* interleaved 8 bit samples of Y, U, Y, V */ + VC_IMAGE_YUV422YVYU(46), /* interleaved 8 bit samples of Y, V, Y, U */ + VC_IMAGE_YUV422UYVY(47), /* interleaved 8 bit samples of U, Y, V, Y */ + VC_IMAGE_YUV422VYUY(48), /* interleaved 8 bit samples of V, Y, U, Y */ + + VC_IMAGE_RGBX32(49), /* 32bpp like RGBA32 but with unused alpha */ + VC_IMAGE_RGBX8888(50), /* 32bpp, corresponding to RGBA with unused alpha */ + VC_IMAGE_BGRX8888(51), /* 32bpp, corresponding to BGRA with unused alpha */ + + VC_IMAGE_YUV420SP(52), /* Y as a plane, then UV byte interleaved in plane with with same pitch, half height */ + + VC_IMAGE_YUV444PLANAR(53), /* Y, U, & V planes separately 4:4:4 */ + + VC_IMAGE_TF_U8(54), /* T-format 8-bit U - same as TF_Y8 buf from U plane */ + VC_IMAGE_TF_V8(55), /* T-format 8-bit U - same as TF_Y8 buf from V plane */ + + VC_IMAGE_MAX(56), //bounds for error checking + VC_IMAGE_FORCE_ENUM_16BIT(0xffff); + + private int cIndex; + + VC_IMAGE_TYPE_T(int cIndex) { + this.cIndex = cIndex; + } + + public int getcIndex() { + return cIndex; + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_RECT_T.java b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_RECT_T.java new file mode 100644 index 000000000..e6041f7d2 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/display/dispmanx/VC_RECT_T.java @@ -0,0 +1,19 @@ +package org.area515.resinprinter.display.dispmanx; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Structure; + +public class VC_RECT_T extends Structure { + public static class ByReference extends VC_RECT_T implements Structure.ByReference {} + public int x; + public int y; + public int width; + public int height; + + @Override + protected List getFieldOrder() { + return Arrays.asList("x", "y", "width", "height"); + } +} \ No newline at end of file diff --git a/host/src/main/java/org/area515/resinprinter/gcode/GCodeControl.java b/host/src/main/java/org/area515/resinprinter/gcode/GCodeControl.java index 065c2c7d5..5692bbf5c 100644 --- a/host/src/main/java/org/area515/resinprinter/gcode/GCodeControl.java +++ b/host/src/main/java/org/area515/resinprinter/gcode/GCodeControl.java @@ -7,6 +7,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.area515.resinprinter.display.AlreadyAssignedException; import org.area515.resinprinter.display.InappropriateDeviceException; import org.area515.resinprinter.job.JobStatus; import org.area515.resinprinter.job.PrintJob; @@ -22,31 +23,33 @@ public abstract class GCodeControl { public static Logger logger = LogManager.getLogger(); - public int SUGGESTED_TIMEOUT_FOR_ONE_GCODE = 1000 * 60 * 2;//2 minutes + private int SUGGESTED_TIMEOUT_FOR_ONE_GCODE = 1000 * 60 * 2;//2 minutes private Pattern GCODE_RESPONSE_PATTERN = Pattern.compile("(?i)(?:(o?k|e?rror:|a?larm:)(.*)|]*)>|\\[?([^]]*)\\])\r?\n"); private Printer printer; private ReentrantLock gCodeLock = new ReentrantLock(); private StringBuilder builder = new StringBuilder(); private int parseLocation = 0; + private int gcodeTimeout; + private boolean restartSerialOnTimeout; public GCodeControl(Printer printer) { this.printer = printer; - //Don't do this it MUST be lazy loaded! - //this.port = printer.getSerialPort(); + this.gcodeTimeout = printer.getConfiguration().getMachineConfig().getPrinterResponseTimeoutMillis() != null?printer.getConfiguration().getMachineConfig().getPrinterResponseTimeoutMillis():SUGGESTED_TIMEOUT_FOR_ONE_GCODE; + this.restartSerialOnTimeout = printer.getConfiguration().getMachineConfig().getRestartSerialOnTimeout() != null?printer.getConfiguration().getMachineConfig().getRestartSerialOnTimeout():false; } private Printer getPrinter() { return printer; } - private PrinterResponse readUntilOkOrStoppedPrinting(Printer printer) throws IOException { + private PrinterResponse readUntilOkOrStoppedPrinting(boolean exitIfPrintInactive) throws IOException { PrinterResponse line = null; StringBuilder responseBuilder = new StringBuilder(); ParseState state = null; Matcher matcher = null; do { - state = IOUtilities.readLine(printer, getPrinter().getPrinterFirmwareSerialPort(), builder, parseLocation, SUGGESTED_TIMEOUT_FOR_ONE_GCODE, IOUtilities.CPU_LIMITING_DELAY); + state = IOUtilities.readLine(exitIfPrintInactive?getPrinter():null, getPrinter().getPrinterFirmwareSerialPort(), builder, parseLocation, gcodeTimeout, IOUtilities.CPU_LIMITING_DELAY); parseLocation = state.parseLocation; if (state.currentLine != null) { if (line == null) { @@ -60,6 +63,13 @@ private PrinterResponse readUntilOkOrStoppedPrinting(Printer printer) throws IOE logger.info("lineRead: {}", state.currentLine); } while (matcher != null && !matcher.matches()); + if (state.timeout && restartSerialOnTimeout) { + try { + getPrinter().getPrinterFirmwareSerialPort().restartCommunications(); + } catch (AlreadyAssignedException | InappropriateDeviceException e) { + throw new IOException("Problems restarting serial port:" + getPrinter().getPrinterFirmwareSerialPort(), e); + } + } return line; } @@ -84,7 +94,7 @@ String sendGcodeAndRespectPrinter(PrintJob printJob, String cmd) throws IOExcept for (int attempt = 0; mustAttempt; attempt++) { logger.info("Write {}: {}", attempt, cmd); getPrinter().getPrinterFirmwareSerialPort().write(cmd.getBytes()); - PrinterResponse response = readUntilOkOrStoppedPrinting(printer); + PrinterResponse response = readUntilOkOrStoppedPrinting(true); if (response == null) { return "";//I think this should be null, but I'm preserving backwards compatibility } @@ -92,13 +102,14 @@ String sendGcodeAndRespectPrinter(PrintJob printJob, String cmd) throws IOExcept if (isPausableError(response.getLastLineMatcher(), printJob)) { attempt++; printJob.setErrorDescription(response.getLastLineMatcher().group(2)); - printer.setStatus(JobStatus.PausedWithWarning); - NotificationManager.jobChanged(printer, printJob); + logger.info("Received error from printer:" + response.getLastLineMatcher().group(2)); + getPrinter().setStatus(JobStatus.PausedWithWarning); + NotificationManager.jobChanged(getPrinter(), printJob); //Allow the user to manipulate the printer while paused gCodeLock.unlock(); try { - mustAttempt = printer.waitForPauseIfRequired(); + mustAttempt = getPrinter().waitForPauseIfRequired(); } finally { gCodeLock.lock(); } @@ -124,7 +135,7 @@ public String sendGcode(String cmd) { logger.info("Write: {}", cmd); getPrinter().getPrinterFirmwareSerialPort().write(cmd.getBytes()); - PrinterResponse response = readUntilOkOrStoppedPrinting(null); + PrinterResponse response = readUntilOkOrStoppedPrinting(false); if (response == null) { return ""; } @@ -205,7 +216,7 @@ private void parseCommentCommand(String comment) { } } - public String executeGCodeWithTemplating(PrintJob printJob, String gcodes) throws InappropriateDeviceException { + public String executeGCodeWithTemplating(PrintJob printJob, String gcodes, boolean stopSendingGCodeWhenPrintInactive) throws InappropriateDeviceException { Pattern gCodePattern = Pattern.compile("\\s*([^;]*)\\s*(;.*)?", Pattern.CASE_INSENSITIVE); try { if (gcodes == null || gcodes.trim().isEmpty()) { @@ -219,7 +230,7 @@ public String executeGCodeWithTemplating(PrintJob printJob, String gcodes) throw } for (String gcode : gcodes.split("[\r]?\n")) { - if (!printJob.getPrinter().isPrintActive()) { + if (stopSendingGCodeWhenPrintInactive && !printJob.getPrinter().isPrintActive()) { break; } diff --git a/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetector.java b/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetector.java index 45964c931..7002a1e1e 100644 --- a/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetector.java +++ b/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetector.java @@ -5,6 +5,7 @@ import org.area515.resinprinter.printer.Printer; public interface PrintMaterialDetector { + public void initializeDetector(PrintMaterialDetectorSettings settings); /** * This method is executed synchronously with the printing process to ensure the print material is settled to a * point where it can be measured properly. For example, a VisualPrintMaterialDetector would want to take a diff --git a/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetectorSettings.java b/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetectorSettings.java new file mode 100644 index 000000000..e7682b47c --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/inkdetection/PrintMaterialDetectorSettings.java @@ -0,0 +1,32 @@ +package org.area515.resinprinter.inkdetection; + +import java.util.HashMap; + +import javax.xml.bind.annotation.XmlAnyElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlValue; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +public class PrintMaterialDetectorSettings { + private HashMap settings; + + @JsonAnyGetter + public HashMap getSettings() { + return settings; + } + + public void setSettings(HashMap settings) { + this.settings = settings; + } + + @JsonAnySetter + public void putSettings(String key, String value) { + if (settings == null) { + settings = new HashMap<>(); + } + + settings.put(key, value); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/inkdetection/gpio/GpioDigitalPinInkDetector.java b/host/src/main/java/org/area515/resinprinter/inkdetection/gpio/GpioDigitalPinInkDetector.java new file mode 100644 index 000000000..0d4a5bb44 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/inkdetection/gpio/GpioDigitalPinInkDetector.java @@ -0,0 +1,61 @@ +package org.area515.resinprinter.inkdetection.gpio; + +import java.io.IOException; + +import org.area515.resinprinter.inkdetection.PrintMaterialDetector; +import org.area515.resinprinter.inkdetection.PrintMaterialDetectorSettings; +import org.area515.resinprinter.printer.Printer; + +import com.pi4j.io.gpio.GpioController; +import com.pi4j.io.gpio.GpioFactory; +import com.pi4j.io.gpio.GpioPinDigitalInput; +import com.pi4j.io.gpio.Pin; +import com.pi4j.io.gpio.PinState; +import com.pi4j.io.gpio.RaspiPin; +import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent; +import com.pi4j.io.gpio.event.GpioPinListenerDigital; + +public class GpioDigitalPinInkDetector implements PrintMaterialDetector, GpioPinListenerDigital { + private GpioController gpio; + private GpioPinDigitalInput inputPin; + protected volatile float remainingResin; + private boolean pinLowIsLow; + + @Override + public void startMeasurement(Printer printer) { + //Do nothing since we are event driven and don't have any CPU resources + } + + @Override + public float getPercentageOfPrintMaterialRemaining(Printer printer) throws IOException { + return remainingResin; + } + + @Override + public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) { + remainingResin = (pinLowIsLow && event.getState() == PinState.LOW) || + (!pinLowIsLow && event.getState() == PinState.HIGH)?0:Float.MAX_VALUE; + } + + @Override + public void initializeDetector(PrintMaterialDetectorSettings settings) { + Pin rPin = null; + if (settings != null) { + Object pin = settings.getSettings().get("Pin"); + if (pin == null) { + rPin = RaspiPin.getPinByAddress(0); + } else if (pin instanceof String) { + rPin = RaspiPin.getPinByAddress(Integer.parseInt((String)pin)); + } else if (pin instanceof Number) { + rPin = RaspiPin.getPinByAddress(((Number)pin).intValue()); + } + } else { + rPin = RaspiPin.getPinByAddress(0); + } + + gpio = GpioFactory.getInstance(); + inputPin = gpio.provisionDigitalInputPin(rPin, "PrintMaterialDetector"); + gpio.setShutdownOptions(true, PinState.LOW, inputPin); + inputPin.addListener(this); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/inkdetection/visual/VisualPrintMaterialDetector.java b/host/src/main/java/org/area515/resinprinter/inkdetection/visual/VisualPrintMaterialDetector.java index 757311930..4d0c271b7 100644 --- a/host/src/main/java/org/area515/resinprinter/inkdetection/visual/VisualPrintMaterialDetector.java +++ b/host/src/main/java/org/area515/resinprinter/inkdetection/visual/VisualPrintMaterialDetector.java @@ -18,6 +18,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.area515.resinprinter.inkdetection.PrintMaterialDetector; +import org.area515.resinprinter.inkdetection.PrintMaterialDetectorSettings; import org.area515.resinprinter.printer.Printer; import org.area515.resinprinter.server.Main; import org.area515.resinprinter.services.MediaService; @@ -173,4 +174,9 @@ float getPrintMaterialRemainingFromPhoto( BufferedImage edgesImage = detector.getEdgesImage(); return getPrintMaterialRemainingFromEdgeImage(edgesImage, houghCircleDetection, houghLineDetection); } + + @Override + public void initializeDetector(PrintMaterialDetectorSettings settings) { + //No custom settings right now. + } } diff --git a/host/src/main/java/org/area515/resinprinter/job/AbstractPrintFileProcessor.java b/host/src/main/java/org/area515/resinprinter/job/AbstractPrintFileProcessor.java index 1e9447aec..18eac24d0 100644 --- a/host/src/main/java/org/area515/resinprinter/job/AbstractPrintFileProcessor.java +++ b/host/src/main/java/org/area515/resinprinter/job/AbstractPrintFileProcessor.java @@ -91,6 +91,9 @@ public DataAid(PrintJob printJob) throws JobManagerException { } CustomizerService.INSTANCE.addOrUpdateCustomizer(customizer); } + if (customizer.getNextStep() == null) { + customizer.setNextStep(PrinterStep.PerformHeader); + } if (customizer.getZScale() == null) { customizer.setZScale(1.0); } @@ -194,7 +197,7 @@ public void performHeader(DataAid aid) throws InappropriateDeviceException, IOEx if (aid.slicingProfile.getgCodeHeader() != null && aid.slicingProfile.getgCodeHeader().trim().length() > 0 && aid.customizer.getNextStep() == PrinterStep.PerformHeader) { - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeHeader()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeHeader(), true); moveToNextPrinterStep(aid.customizer, PrinterStep.PerformPreSlice); } @@ -234,7 +237,7 @@ public JobStatus performPreSlice(DataAid aid, List errors) throws Inap if (aid.slicingProfile.getgCodePreslice() != null && aid.slicingProfile.getgCodePreslice().trim().length() > 0 && aid.customizer.getNextStep() == PrinterStep.PerformPreSlice) { - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodePreslice()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodePreslice(), true); } moveToNextPrinterStep(aid.customizer, PrinterStep.PerformExposure); @@ -267,12 +270,12 @@ public JobStatus printImageAndPerformPostProcessing(DataAid aid, BufferedImage s } } - logger.info("ExposureStart:{}", ()->Log4jTimer.startTimer(EXPOSURE_TIMER)); aid.printer.showImage(sliceImage); - + logger.info("ExposureStart:{}", ()->Log4jTimer.startTimer(EXPOSURE_TIMER)); + if (aid.slicingProfile.getgCodeShutter() != null && aid.slicingProfile.getgCodeShutter().trim().length() > 0) { aid.printer.setShutterOpen(true); - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeShutter()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeShutter(), true); } //Sleep for the amount of time that we are exposing the resin. @@ -280,7 +283,7 @@ public JobStatus printImageAndPerformPostProcessing(DataAid aid, BufferedImage s if (aid.slicingProfile.getgCodeShutter() != null && aid.slicingProfile.getgCodeShutter().trim().length() > 0) { aid.printer.setShutterOpen(false); - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeShutter()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeShutter(), true); } //Blank the screen @@ -308,14 +311,14 @@ public JobStatus printImageAndPerformPostProcessing(DataAid aid, BufferedImage s } } if (aid.slicingProfile.getZLiftDistanceGCode() != null && aid.slicingProfile.getZLiftDistanceGCode().trim().length() > 0) { - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getZLiftDistanceGCode()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getZLiftDistanceGCode(), true); } if (aid.slicingProfile.getZLiftSpeedGCode() != null && aid.slicingProfile.getZLiftSpeedGCode().trim().length() > 0) { - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getZLiftSpeedGCode()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getZLiftSpeedGCode(), true); } //Perform the lift gcode manipulation - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeLift()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeLift(), true); Double buildArea = getBuildAreaMM(aid.printJob); // Log slice settings (in JSON for extraction and processing) @@ -344,7 +347,7 @@ public JobStatus performFooter(DataAid aid) throws IOException, InappropriateDev } if (aid.slicingProfile.getgCodeFooter() != null && aid.slicingProfile.getgCodeFooter().trim().length() > 0) { - aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeFooter()); + aid.printer.getGCodeControl().executeGCodeWithTemplating(aid.printJob, aid.slicingProfile.getgCodeFooter(), true); } if (aid.printer.isProjectorPowerControlSupported()) { diff --git a/host/src/main/java/org/area515/resinprinter/job/CreationWorkshopSceneFileProcessor.java b/host/src/main/java/org/area515/resinprinter/job/CreationWorkshopSceneFileProcessor.java index d9bace6a5..ee2c87e16 100644 --- a/host/src/main/java/org/area515/resinprinter/job/CreationWorkshopSceneFileProcessor.java +++ b/host/src/main/java/org/area515/resinprinter/job/CreationWorkshopSceneFileProcessor.java @@ -15,7 +15,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipEntry; @@ -204,7 +203,7 @@ public JobStatus processFile(final PrintJob printJob) throws Exception { // print out comments //logger.info("Ignored line:{}", currentLine); - printer.getGCodeControl().executeGCodeWithTemplating(printJob, currentLine); + printer.getGCodeControl().executeGCodeWithTemplating(printJob, currentLine, true); } return printer.isPrintActive()?JobStatus.Completed:printer.getStatus(); @@ -229,10 +228,6 @@ public JobStatus processFile(final PrintJob printJob) throws Exception { } } - public static File buildExtractionDirectoryForJob(String archive, UUID jobId) { - return Paths.get(HostProperties.Instance().getWorkingDir().toString(), archive, jobId.toString()).toFile(); - } - public static File buildExtractionDirectory(String archive) { return Paths.get(HostProperties.Instance().getWorkingDir().toString(), archive).toFile(); } diff --git a/host/src/main/java/org/area515/resinprinter/job/InkDetector.java b/host/src/main/java/org/area515/resinprinter/job/InkDetector.java index be3c172b1..d6118da45 100644 --- a/host/src/main/java/org/area515/resinprinter/job/InkDetector.java +++ b/host/src/main/java/org/area515/resinprinter/job/InkDetector.java @@ -7,11 +7,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.area515.resinprinter.inkdetection.PrintMaterialDetector; +import org.area515.resinprinter.inkdetection.PrintMaterialDetectorSettings; import org.area515.resinprinter.notification.NotificationManager; import org.area515.resinprinter.printer.Printer; import org.area515.resinprinter.server.Main; public class InkDetector { + public static final String DETECTION_ERROR = "Error occurred while performing ink detection"; private static final Logger logger = LogManager.getLogger(); private Printer printer; private PrintJob printJob; @@ -32,12 +34,13 @@ public Boolean call() throws Exception { return false; } catch (IOException e) { - logger.error("Error occurred while performing visual detection", e); + logger.error(DETECTION_ERROR, e); if (hasAlreadyPausedWithError) { return false; } hasAlreadyPausedWithError = true; + printJob.setErrorDescription(DETECTION_ERROR); printer.setStatus(JobStatus.PausedWithWarning); NotificationManager.jobChanged(printer, printJob); throw e; @@ -45,10 +48,11 @@ public Boolean call() throws Exception { } } - public InkDetector(Printer printer, PrintJob job, PrintMaterialDetector detector, float percentageConsideredEmpty) { + public InkDetector(Printer printer, PrintJob job, PrintMaterialDetector detector, PrintMaterialDetectorSettings settings, float percentageConsideredEmpty) { this.printer = printer; this.detector = detector; this.printMaterialRemainingForEmpty = percentageConsideredEmpty; + detector.initializeDetector(settings); } public float performMeasurement() throws IOException { diff --git a/host/src/main/java/org/area515/resinprinter/job/PrintJob.java b/host/src/main/java/org/area515/resinprinter/job/PrintJob.java index af6ad2bdd..e320d7983 100644 --- a/host/src/main/java/org/area515/resinprinter/job/PrintJob.java +++ b/host/src/main/java/org/area515/resinprinter/job/PrintJob.java @@ -208,7 +208,7 @@ public void overrideZLiftDistance(double zLiftDistance) throws InappropriateDevi try { overrideZLiftDistance = true; this.zLiftDistance = zLiftDistance; - printer.getGCodeControl().executeGCodeWithTemplating(this, printer.getConfiguration().getSlicingProfile().getZLiftDistanceGCode()); + printer.getGCodeControl().executeGCodeWithTemplating(this, printer.getConfiguration().getSlicingProfile().getZLiftDistanceGCode(), true); } catch (InappropriateDeviceException e) { throw e; } @@ -244,7 +244,7 @@ public void overrideZLiftSpeed(double zLiftSpeed) throws InappropriateDeviceExce try { this.overrideZLiftSpeed = true; this.zLiftSpeed = zLiftSpeed; - printer.getGCodeControl().executeGCodeWithTemplating(this, printer.getConfiguration().getSlicingProfile().getZLiftSpeedGCode()); + printer.getGCodeControl().executeGCodeWithTemplating(this, printer.getConfiguration().getSlicingProfile().getZLiftSpeedGCode(), true); } catch (InappropriateDeviceException e) { throw e; } diff --git a/host/src/main/java/org/area515/resinprinter/job/STLFileProcessor.java b/host/src/main/java/org/area515/resinprinter/job/STLFileProcessor.java index 3aaef3e2a..0199bc71b 100644 --- a/host/src/main/java/org/area515/resinprinter/job/STLFileProcessor.java +++ b/host/src/main/java/org/area515/resinprinter/job/STLFileProcessor.java @@ -23,6 +23,7 @@ public class STLFileProcessor extends AbstractPrintFileProcessor, Set> implements Previewable { public static String STL_OVERHEAD = "stlOverhead"; + public static final String TOO_LARGE = "This file is too large for Photonic3D to load:"; private static final Logger logger = LogManager.getLogger(); @@ -127,7 +128,7 @@ public BufferedImage renderPreviewImage(DataAid aid) throws SliceHandlingExcepti dataAid.sliceHeight / 2, true, overrideNormals, - new CloseOffMend());//*/ + new CloseOffMend()); dataAid.slicer.loadFile(new FileInputStream(dataAid.printJob.getJobFile()), null, null); dataAid.printJob.setTotalSlices(dataAid.slicer.getZMaxIndex() - dataAid.slicer.getZMinIndex()); //Get the slicer queued up for the first image; @@ -137,6 +138,8 @@ public BufferedImage renderPreviewImage(DataAid aid) throws SliceHandlingExcepti return renderer.call().getPrintableImage(); } catch (IOException | JobManagerException e) { throw new SliceHandlingException(e); + } catch (OutOfMemoryError e) { + throw new SliceHandlingException(TOO_LARGE + aid.printJob.getJobFile(), e); } } diff --git a/host/src/main/java/org/area515/resinprinter/job/render/CurrentImageRenderer.java b/host/src/main/java/org/area515/resinprinter/job/render/CurrentImageRenderer.java index 727048b0a..b3f8f31ed 100644 --- a/host/src/main/java/org/area515/resinprinter/job/render/CurrentImageRenderer.java +++ b/host/src/main/java/org/area515/resinprinter/job/render/CurrentImageRenderer.java @@ -29,29 +29,6 @@ public BufferedImage buildImage(int renderedWidth, int renderedHeight) { return new BufferedImage(renderedWidth, renderedHeight, BufferedImage.TYPE_4BYTE_ABGR); } - /*public BufferedImage buildLargestImageBetweenPrinterAndRenderedImage(int renderedWidth, int renderedHeight) { - int actualWidth = renderedWidth > aid.xResolution?renderedWidth:aid.xResolution; - int actualHeight = renderedHeight > aid.yResolution?renderedHeight:aid.yResolution; - return new BufferedImage(actualWidth, actualHeight, BufferedImage.TYPE_4BYTE_ABGR); - } - - public BufferedImage buildLargestImageBetweenPrinterAndRenderedImage(BufferedImage renderedImage) { - if (renderedImage.getWidth() < aid.xResolution || - renderedImage.getHeight() < aid.yResolution) { - BufferedImage newSize = buildLargestImageBetweenPrinterAndRenderedImage(renderedImage.getWidth(), renderedImage.getHeight()); - Graphics graphics = newSize.getGraphics(); - graphics.setColor(Color.black); - graphics.fillRect(0, 0, newSize.getWidth(), newSize.getHeight()); - graphics.setColor(Color.white); - int centerX = aid.xResolution / 2; - int centerY = aid.yResolution / 2; - graphics.drawImage(renderedImage, centerX - (newSize.getWidth() / 2), centerY - (newSize.getHeight() / 2), null); - return newSize; - } - - return renderedImage; - }*/ - public RenderedData call() throws JobManagerException { long startTime = System.currentTimeMillis(); Lock lock = aid.cache.getSpecificLock(imageIndexToBuild); @@ -65,7 +42,7 @@ public RenderedData call() throws JobManagerException { if (!aid.optimizeWithPreviewMode) { long pixelArea = computePixelArea(image); imageData.setArea((double)pixelArea); - logger.info("Loaded {} with {} non-black pixels in {}ms", imageIndexToBuild, pixelArea, System.currentTimeMillis()-startTime); + logger.info("Loaded {} with {} non-black pixels in {}ms", imageIndexToBuild, pixelArea, System.currentTimeMillis()-startTime); } return imageData; } catch (ScriptException e) { diff --git a/host/src/main/java/org/area515/resinprinter/network/HexUnescaper.java b/host/src/main/java/org/area515/resinprinter/network/HexUnescaper.java new file mode 100644 index 000000000..8fd682546 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/network/HexUnescaper.java @@ -0,0 +1,45 @@ +package org.area515.resinprinter.network; +import java.io.IOException; +import java.io.Writer; + +import org.apache.commons.lang3.text.translate.CharSequenceTranslator; + +/** + * Translates escaped HEX values of the form \\x\d\d back to + * Unicode. It supports multiple 'x' characters and will work with or + * without the +. + * + */ +public class HexUnescaper extends CharSequenceTranslator { + @Override + public int translate(final CharSequence input, final int index, final Writer out) throws IOException { + if (input.charAt(index) == '\\' && index + 1 < input.length() && input.charAt(index + 1) == 'x') { + // consume optional additional 'x' chars + int i = 2; + while (index + i < input.length() && input.charAt(index + i) == 'x') { + i++; + } + + if (index + i < input.length() && input.charAt(index + i) == '+') { + i++; + } + + if (index + i + 2 <= input.length()) { + // Get 2 hex digits + final CharSequence hex = input.subSequence(index + i, index + i + 2); + + try { + final int value = Integer.parseInt(hex.toString(), 16); + out.write((char) value); + } catch (final NumberFormatException nfe) { + throw new IllegalArgumentException("Unable to parse hex value: " + hex, nfe); + } + return i + 2; + } else { + throw new IllegalArgumentException("Less than 2 hex digits in hex value: '" + input.subSequence(index, input.length()) + + "' due to end of CharSequence"); + } + } + return 0; + } +} diff --git a/host/src/main/java/org/area515/resinprinter/network/LinuxNetworkManager.java b/host/src/main/java/org/area515/resinprinter/network/LinuxNetworkManager.java index e3270304f..e06108791 100644 --- a/host/src/main/java/org/area515/resinprinter/network/LinuxNetworkManager.java +++ b/host/src/main/java/org/area515/resinprinter/network/LinuxNetworkManager.java @@ -6,6 +6,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.text.translate.AggregateTranslator; +import org.apache.commons.lang3.text.translate.CharSequenceTranslator; +import org.apache.commons.lang3.text.translate.EntityArrays; +import org.apache.commons.lang3.text.translate.LookupTranslator; +import org.apache.commons.lang3.text.translate.NumericEntityUnescaper; +import org.apache.commons.lang3.text.translate.UnicodeUnescaper; import org.area515.util.IOUtilities; import org.area515.util.IOUtilities.ParseAction; import org.area515.util.IOUtilities.SearchStyle; @@ -13,6 +20,25 @@ public class LinuxNetworkManager implements NetworkManager { public static final String WIFI_REGEX = "\\s*([A-Fa-f0-9:]+)\\s+(-?\\d+)\\s+(-?\\d+)\\s+([\\[\\]\\+\\-\\w]+)\\t(.+)"; + public static final CharSequenceTranslator UNESCAPE_UNIX = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), + new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), + new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()), + new NumericEntityUnescaper(), //☺ + //new OctalUnescaper(), // .between('\1', '\377'), + new UnicodeUnescaper(), //\u0044 + new HexUnescaper(), //\x45 + //new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()), + new LookupTranslator( + new String[][] { + {"\\\\", "\\"}, + {"\\\"", "\""}, + {"\\'", "'"}, + {"\\", ""} + }) + ); + private void buildWirelessInfo(String nicName, NetInterface netFace) { Pattern networkEncryptionClass = Pattern.compile("\\[([\\+\\-\\w]+)\\]"); @@ -31,7 +57,10 @@ private void buildWirelessInfo(String nicName, NetInterface netFace) { WirelessNetwork currentWireless = new WirelessNetwork(); netFace.getWirelessNetworks().add(currentWireless); - currentWireless.setSsid(lines[4]); + currentWireless.setSsid(UNESCAPE_UNIX.translate(lines[4])); + if (currentWireless.getSsid().startsWith("\u0000")) { + currentWireless.setHidden(true); + } currentWireless.setParentInterfaceName(netFace.getName()); Matcher matcher = networkEncryptionClass.matcher(lines[3]); while (matcher.find()) { diff --git a/host/src/main/java/org/area515/resinprinter/network/WirelessNetwork.java b/host/src/main/java/org/area515/resinprinter/network/WirelessNetwork.java index db4ea03a0..31462849f 100644 --- a/host/src/main/java/org/area515/resinprinter/network/WirelessNetwork.java +++ b/host/src/main/java/org/area515/resinprinter/network/WirelessNetwork.java @@ -5,6 +5,7 @@ public class WirelessNetwork { private String ssid; + private boolean hidden; private List supportedWirelessEncryption = new ArrayList(); private String parentInterfaceName; private String password; @@ -18,6 +19,13 @@ public void setSsid(String ssid) { this.ssid = ssid; } + public boolean isHidden() { + return hidden; + } + public void setHidden(boolean hidden) { + this.hidden = hidden; + } + public List getSupportedWirelessEncryption() { return supportedWirelessEncryption; } diff --git a/host/src/main/java/org/area515/resinprinter/notification/PauseOnErrorNotifier.java b/host/src/main/java/org/area515/resinprinter/notification/PauseOnErrorNotifier.java index f604a8dda..8690916b1 100644 --- a/host/src/main/java/org/area515/resinprinter/notification/PauseOnErrorNotifier.java +++ b/host/src/main/java/org/area515/resinprinter/notification/PauseOnErrorNotifier.java @@ -35,6 +35,7 @@ public void fileUploadComplete(File fileUploaded) { @Override public void geometryError(PrintJob job, List error) { + job.setErrorDescription("Your 3D file has improper geometry."); job.getPrinter().setStatus(JobStatus.PausedWithWarning); } diff --git a/host/src/main/java/org/area515/resinprinter/printer/MachineConfig.java b/host/src/main/java/org/area515/resinprinter/printer/MachineConfig.java index 468f8b76e..759b7a128 100644 --- a/host/src/main/java/org/area515/resinprinter/printer/MachineConfig.java +++ b/host/src/main/java/org/area515/resinprinter/printer/MachineConfig.java @@ -20,7 +20,7 @@ public static class DisplayedControls { public static class CorrectionMask { } - + public static class MotorsDriverConfig { @XmlElement(name="DriverType") private String driverType = "eGENERIC"; @@ -131,8 +131,12 @@ public void setComPortSettings(ComPortSettings comPortSettings) { private MonitorDriverConfig monitorDriverConfig; @XmlElement(name="PauseOnPrinterResponseRegEx") private String pauseOnPrinterResponseRegEx; + @XmlElement(name="PrinterResponseTimeoutMillis") + private Integer printerResponseTimeoutMillis; @XmlElement(name="OverrideModelNormalsWithRightHandRule") private Boolean overrideModelNormalsWithRightHandRule; + @XmlElement(name="RestartSerialOnTimeout") + private Boolean restartSerialOnTimeout; private String name; @XmlTransient @@ -230,6 +234,23 @@ public void setOverrideModelNormalsWithRightHandRule(Boolean overrideModelNormal this.overrideModelNormalsWithRightHandRule = overrideModelNormalsWithRightHandRule; } + @XmlTransient + public Integer getPrinterResponseTimeoutMillis() { + return printerResponseTimeoutMillis; + } + public void setPrinterResponseTimeoutMillis(Integer printerResponseTimeoutMillis) { + this.printerResponseTimeoutMillis = printerResponseTimeoutMillis; + } + + @XmlTransient + public Boolean getRestartSerialOnTimeout() { + return restartSerialOnTimeout; + } + public void setRestartSerialOnTimeout(Boolean restartSerialOnTimeout) { + this.restartSerialOnTimeout = restartSerialOnTimeout; + } + + @XmlTransient public String getOSMonitorID() { return monitorDriverConfig.osMonitorID; diff --git a/host/src/main/java/org/area515/resinprinter/printer/Named.java b/host/src/main/java/org/area515/resinprinter/printer/Named.java index 68f453053..156e180a2 100644 --- a/host/src/main/java/org/area515/resinprinter/printer/Named.java +++ b/host/src/main/java/org/area515/resinprinter/printer/Named.java @@ -2,4 +2,5 @@ public interface Named { public void setName(String name); + public String getName(); } diff --git a/host/src/main/java/org/area515/resinprinter/printer/Printer.java b/host/src/main/java/org/area515/resinprinter/printer/Printer.java index e77e830f2..394f4bb3f 100644 --- a/host/src/main/java/org/area515/resinprinter/printer/Printer.java +++ b/host/src/main/java/org/area515/resinprinter/printer/Printer.java @@ -1,29 +1,16 @@ package org.area515.resinprinter.printer; -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Frame; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GraphicsDevice; -import java.awt.Insets; -import java.awt.Point; import java.awt.Rectangle; -import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import javax.swing.JFrame; import javax.xml.bind.annotation.XmlTransient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.area515.resinprinter.display.DisplayManager; +import org.area515.resinprinter.display.GraphicsOutputInterface; import org.area515.resinprinter.display.InappropriateDeviceException; import org.area515.resinprinter.gcode.GCodeControl; import org.area515.resinprinter.job.JobStatus; @@ -38,19 +25,12 @@ public class Printer { private PrinterConfiguration configuration; //For Display - private Frame refreshFrame; - private DisplayState displayState = DisplayState.Blank; - private int gridSquareSize; - private Point calibrationXY; - private BufferedImage displayImage; + private GraphicsOutputInterface refreshFrame; private boolean started; private boolean shutterOpen; private Integer bulbHours; - private String displayDeviceID; private long currentSlicePauseTime; - private int sliceNumber; - private Font defaultFont; - private Insets frameInsets; + private String displayDeviceID; //For Serial Ports private SerialCommunicationsPort printerFirmwareSerialPort; @@ -145,10 +125,10 @@ public void setStatus(JobStatus status) { if (this.status != null && this.status.isPaused()) { jobContinued.signalAll(); } - + logger.info("Moving from status:" + this.status + " to status:" + status); this.status = status; if (!status.isPrintInProgress()) { - sliceNumber = 0; + refreshFrame.resetSliceCount(); } } finally { statusLock.unlock(); @@ -194,92 +174,11 @@ public JobStatus togglePause() { } } - public void setGraphicsData(final GraphicsDevice device) { - refreshFrame = new JFrame() { - private static final long serialVersionUID = 5024551291098098753L; - - @Override - public void paint(Graphics g) { - //super.paint(g); - - Rectangle screenSize = refreshFrame.getGraphicsConfiguration().getBounds(); - Graphics2D g2 = (Graphics2D)g; - switch (displayState) { - case Blank : - g2.setBackground(Color.black); - g2.clearRect(0, 0, screenSize.width, screenSize.height); - return; - case Grid : - g2.setBackground(Color.black); - g2.clearRect(0, 0, screenSize.width, screenSize.height); - g2.setColor(Color.RED); - for (int x = 0; x < screenSize.width; x += gridSquareSize) { - g2.drawLine(x, 0, x, screenSize.height); - } - - for (int y = 0; y < screenSize.height; y += gridSquareSize) { - g2.drawLine(0, y, screenSize.width, y); - } - return; - case Calibration : - g2.setBackground(Color.black); - g2.clearRect(0, 0, screenSize.width, screenSize.height); - g2.setColor(Color.RED); - int startingX = screenSize.width / 2 - calibrationXY.x / 2; - int startingY = screenSize.height / 2 - calibrationXY.y / 2; - int halfLengthOfDimLines = 50; - - //X Dimension lines - g2.drawLine(startingX , screenSize.height / 2 - halfLengthOfDimLines, startingX , screenSize.height / 2 + halfLengthOfDimLines); - g2.drawLine(startingX + calibrationXY.x, screenSize.height / 2 - halfLengthOfDimLines, startingX + calibrationXY.x, screenSize.height / 2 + halfLengthOfDimLines); - - //Y Dimension lines - g2.drawLine(screenSize.width / 2 - halfLengthOfDimLines, startingY , screenSize.width / 2 + halfLengthOfDimLines, startingY); - g2.drawLine(screenSize.width / 2 - halfLengthOfDimLines, startingY + calibrationXY.y, screenSize.width / 2 + halfLengthOfDimLines, startingY + calibrationXY.y); - - //Vertical line of cross - g2.drawLine(screenSize.width / 2, startingY, screenSize.width / 2, startingY + calibrationXY.y); - - //Horizontal line of cross - g2.setStroke(new BasicStroke(5, 0, 0, 1.0f, new float[]{10, 10}, 2.0f)); - g2.drawLine(startingX, screenSize.height / 2, startingX + calibrationXY.x, screenSize.height / 2); - return; - case CurrentSlice : - g2.drawImage(displayImage, null, screenSize.width / 2 - displayImage.getWidth() / 2, screenSize.height / 2 - displayImage.getHeight() / 2); - if (device.getIDstring().equalsIgnoreCase(DisplayManager.SIMULATED_DISPLAY)) { - g2.setColor(Color.RED); - g2.setFont(defaultFont); - g2.drawString("Slice:" + sliceNumber, frameInsets.left, frameInsets.top + g2.getFontMetrics().getHeight()); - } - return; - } - } - }; - - if (device.getIDstring().equalsIgnoreCase(DisplayManager.SIMULATED_DISPLAY)) { - refreshFrame.setTitle("Printer Simulation"); - defaultFont = refreshFrame.getFont(); - refreshFrame.setVisible(true); - refreshFrame.setExtendedState(JFrame.MAXIMIZED_BOTH); - refreshFrame.setMinimumSize(new Dimension(500, 500)); - frameInsets = refreshFrame.getInsets(); - } else { - refreshFrame.setUndecorated(true); - device.setFullScreenWindow(refreshFrame); - //This can only be done with a real graphics device since it would reassign the printer Simulation - //OLD getConfiguration().getMachineConfig().setOSMonitorID(device.getDefaultConfiguration().getDevice().getIDstring()); - getConfiguration().getMachineConfig().setOSMonitorID(device.getIDstring()); - - // hide mouse in full screen - Toolkit toolkit = Toolkit.getDefaultToolkit(); - Point hotSpot = new Point(0,0); - BufferedImage cursorImage = new BufferedImage(1, 1, BufferedImage.TRANSLUCENT); - Cursor invisibleCursor = toolkit.createCustomCursor(cursorImage, hotSpot, "InvisibleCursor"); - refreshFrame.setCursor(invisibleCursor); - } - - this.displayDeviceID = device.getIDstring(); - Rectangle screenSize = refreshFrame.getGraphicsConfiguration().getBounds(); + public void initializeAndAssignGraphicsOutputInterface(final GraphicsOutputInterface device, final String displayDeviceID) { + this.displayDeviceID = displayDeviceID; + this.refreshFrame = device.initializeDisplay(displayDeviceID); + + Rectangle screenSize = refreshFrame.getBoundary(); getConfiguration().getMachineConfig().getMonitorDriverConfig().setDLP_X_Res(screenSize.width); getConfiguration().getMachineConfig().getMonitorDriverConfig().setDLP_Y_Res(screenSize.height); } @@ -287,31 +186,33 @@ public void paint(Graphics g) { public String getDisplayDeviceID() { return displayDeviceID; } - - public void showBlankImage() { - displayState = DisplayState.Blank; - refreshFrame.repaint(); + + public void showBlankImage() { + refreshFrame.showBlankImage(); } public void showCalibrationImage(int xPixels, int yPixels) { - displayState = DisplayState.Calibration; - calibrationXY = new Point(xPixels, yPixels); - refreshFrame.repaint(); + refreshFrame.showCalibrationImage(xPixels, yPixels); } public void showGridImage(int pixels) { - displayState = DisplayState.Grid; - gridSquareSize = pixels; - refreshFrame.repaint(); + refreshFrame.showGridImage(pixels); } public void showImage(BufferedImage image) { - sliceNumber++; - displayState = DisplayState.CurrentSlice; - displayImage = image; - refreshFrame.repaint(); + refreshFrame.showImage(image); } + @JsonIgnore + @XmlTransient + public boolean isDisplayBusy() { + if (refreshFrame == null) { + return false; + } + + return refreshFrame.isDisplayBusy(); + } + @JsonIgnore @XmlTransient public boolean isProjectorPowerControlSupported() { @@ -428,10 +329,15 @@ public void close() { if (refreshFrame != null) { refreshFrame.dispose(); } - bulbHours = null; started = false; } + public void disassociateDisplay() { + this.bulbHours = null; + this.refreshFrame = null; + this.displayDeviceID = null; + } + @Override public int hashCode() { final int prime = 31; diff --git a/host/src/main/java/org/area515/resinprinter/printer/PrinterManager.java b/host/src/main/java/org/area515/resinprinter/printer/PrinterManager.java index 42061ea2b..deea1c928 100644 --- a/host/src/main/java/org/area515/resinprinter/printer/PrinterManager.java +++ b/host/src/main/java/org/area515/resinprinter/printer/PrinterManager.java @@ -1,7 +1,6 @@ package org.area515.resinprinter.printer; -import java.awt.GraphicsDevice; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -12,6 +11,7 @@ import org.apache.logging.log4j.Logger; import org.area515.resinprinter.display.AlreadyAssignedException; import org.area515.resinprinter.display.DisplayManager; +import org.area515.resinprinter.display.GraphicsOutputInterface; import org.area515.resinprinter.display.InappropriateDeviceException; import org.area515.resinprinter.job.JobManagerException; import org.area515.resinprinter.job.PrintJob; @@ -117,7 +117,7 @@ public Printer startPrinter(PrinterConfiguration currentConfiguration) throws Jo try { printer = new Printer(currentConfiguration); String monitorId = currentConfiguration.getMachineConfig().getOSMonitorID(); - GraphicsDevice graphicsDevice = null; + GraphicsOutputInterface graphicsDevice = null; if (monitorId != null) { graphicsDevice = DisplayManager.Instance().getDisplayDevice(currentConfiguration.getMachineConfig().getOSMonitorID()); } else { @@ -157,24 +157,26 @@ public Printer startPrinter(PrinterConfiguration currentConfiguration) throws Jo logger.info("Printer started:{}", printer); return printer; } catch (JobManagerException | AlreadyAssignedException | InappropriateDeviceException e) { - logger.error("Error starting printer:" + currentConfiguration, e); - DisplayManager.Instance().removeAssignment(printer); - SerialManager.Instance().removeAssignments(printer); - if (printer != null) { - printer.close(); - } + handleError(printer, currentConfiguration, e); throw e; - } catch (Exception e) { - logger.error("Error starting printer:" + currentConfiguration, e); - DisplayManager.Instance().removeAssignment(printer); - SerialManager.Instance().removeAssignments(printer); + } catch (Throwable e) { + handleError(printer, currentConfiguration, e); + throw new InappropriateDeviceException("Internal error on server", e); + } finally { + printerLock.unlock(); + } + } + + private void handleError(Printer printer, PrinterConfiguration currentConfiguration, Throwable e) { + logger.error("Error starting printer:" + currentConfiguration, e); + try { if (printer != null) { printer.close(); } - throw new InappropriateDeviceException("Internal error on server"); } finally { - printerLock.unlock(); - } + DisplayManager.Instance().removeAssignment(printer); + SerialManager.Instance().removeAssignments(printer); + } } public void assignPrinter(PrintJob newJob, Printer printer) throws AlreadyAssignedException { diff --git a/host/src/main/java/org/area515/resinprinter/printer/SlicingProfile.java b/host/src/main/java/org/area515/resinprinter/printer/SlicingProfile.java index da87103a7..57f71b154 100644 --- a/host/src/main/java/org/area515/resinprinter/printer/SlicingProfile.java +++ b/host/src/main/java/org/area515/resinprinter/printer/SlicingProfile.java @@ -9,6 +9,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.area515.resinprinter.inkdetection.PrintMaterialDetector; +import org.area515.resinprinter.inkdetection.PrintMaterialDetectorSettings; import org.area515.resinprinter.job.InkDetector; import org.area515.resinprinter.job.PrintJob; import org.area515.util.TemplateEngine; @@ -131,6 +132,16 @@ public static class InkConfig { @XmlElement(name="ResinPriceL") private double resinPriceL; private InkDetector detector; + @XmlElement(name="PrintMaterialDetectorSettings") + private PrintMaterialDetectorSettings printMaterialDetectorSettings; + + @XmlTransient + public PrintMaterialDetectorSettings getPrintMaterialDetectorSettings() { + return printMaterialDetectorSettings; + } + public void setPrintMaterialDetectorSettings(PrintMaterialDetectorSettings printMaterialDetectorSettings) { + this.printMaterialDetectorSettings = printMaterialDetectorSettings; + } @XmlTransient public String getName() { @@ -207,7 +218,7 @@ public InkDetector getInkDetector(PrintJob printJob) { } try { - this.detector = new InkDetector(printJob.getPrinter(), printJob, ((Class)Class.forName(detectorClass)).newInstance(), percentageConsideredEmpty); + this.detector = new InkDetector(printJob.getPrinter(), printJob, ((Class)Class.forName(detectorClass)).newInstance(), getPrintMaterialDetectorSettings(), percentageConsideredEmpty); return this.detector; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { logger.info("Failed to load PrintMaterialDetector:{}", detector); diff --git a/host/src/main/java/org/area515/resinprinter/serial/ConsoleCommPort.java b/host/src/main/java/org/area515/resinprinter/serial/ConsoleCommPort.java index 93a46fdfc..ec2d7fa85 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/ConsoleCommPort.java +++ b/host/src/main/java/org/area515/resinprinter/serial/ConsoleCommPort.java @@ -7,11 +7,16 @@ public class ConsoleCommPort implements SerialCommunicationsPort { private static final Logger logger = LogManager.getLogger(); public static final String GCODE_RESPONSE_SIMULATION = "GCode response simulation"; + private static int consoleNumber = 0; private String name = GCODE_RESPONSE_SIMULATION; private int readCount; private int timeout; + public ConsoleCommPort() { + this.name = this.name + ":" + consoleNumber++; + } + @Override public void open(String printerName, int timeout, ComPortSettings settings) { readCount = 0; @@ -56,6 +61,10 @@ public byte[] read() { return "ok\n".getBytes(); } + @Override + public void restartCommunications() { + } + public String toString() { return name; } diff --git a/host/src/main/java/org/area515/resinprinter/serial/CustomCommPort.java b/host/src/main/java/org/area515/resinprinter/serial/CustomCommPort.java index 7a5f7b837..1684102b6 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/CustomCommPort.java +++ b/host/src/main/java/org/area515/resinprinter/serial/CustomCommPort.java @@ -41,6 +41,10 @@ public byte[] read() throws IOException { //Do nothing return null; } + + @Override + public void restartCommunications() { + } public String toString() { return name; diff --git a/host/src/main/java/org/area515/resinprinter/serial/JSSCCommPort.java b/host/src/main/java/org/area515/resinprinter/serial/JSSCCommPort.java index fe1081fa5..90ec01c55 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/JSSCCommPort.java +++ b/host/src/main/java/org/area515/resinprinter/serial/JSSCCommPort.java @@ -15,7 +15,9 @@ public class JSSCCommPort implements SerialCommunicationsPort { private static final Logger logger = LogManager.getLogger(); private SerialPort port; - private String cwhName; + private String name; + private ComPortSettings settings; + private int timeout; @Override public void open(String controllingDevice, int timeout, @@ -40,7 +42,9 @@ public void open(String controllingDevice, int timeout, throw new InappropriateDeviceException("Speed hasn't been configured for this device(" + settings.getPortName() + ")."); } - port = new SerialPort(settings.getPortName()); + this.timeout = timeout; + this.settings = new ComPortSettings(settings); + this.port = new SerialPort(settings.getPortName()); int parity = 0; if (settings.getParity().equalsIgnoreCase("EVEN")) { parity = SerialPort.PARITY_EVEN; @@ -98,12 +102,12 @@ public void close() { @Override public void setName(String name) { - cwhName = name; + this.name = name; } @Override public String getName() { - return cwhName; + return name; } @Override @@ -111,7 +115,7 @@ public void write(byte[] gcode) throws IOException { try { port.writeBytes(gcode); } catch (SerialPortException e) { - throw new IOException("Couldn't write gcode to " + cwhName, e); + throw new IOException("Couldn't write gcode to " + name, e); } } @@ -128,15 +132,37 @@ public byte[] read() throws IOException { } } + private void printDiagnostic() { + try { + logger.info("Printing diagnostic for:" + this.settings); + logger.info("open:" + port.isOpened()); + logger.info("CTS:" + port.isCTS()); + logger.info("DSR:" + port.isDSR()); + logger.info("RING:" + port.isRING()); + logger.info("RLSD:" + port.isRLSD()); + logger.info("flow:" + port.getFlowControlMode()); + } catch (SerialPortException e) { + logger.error("Couldn't print diagnostic", e); + } + } + + @Override + public void restartCommunications() throws AlreadyAssignedException, InappropriateDeviceException { + printDiagnostic(); + close(); + open(name, timeout, settings); + printDiagnostic(); + } + public String toString() { - return cwhName; + return name; } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((cwhName == null) ? 0 : cwhName.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @@ -149,10 +175,10 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; JSSCCommPort other = (JSSCCommPort) obj; - if (cwhName == null) { - if (other.cwhName != null) + if (name == null) { + if (other.name != null) return false; - } else if (!cwhName.equals(other.cwhName)) + } else if (!name.equals(other.name)) return false; return true; } diff --git a/host/src/main/java/org/area515/resinprinter/serial/RXTXCommPort.java b/host/src/main/java/org/area515/resinprinter/serial/RXTXCommPort.java index 979059b64..7b34e65c7 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/RXTXCommPort.java +++ b/host/src/main/java/org/area515/resinprinter/serial/RXTXCommPort.java @@ -21,7 +21,9 @@ public abstract class RXTXCommPort implements SerialCommunicationsPort { protected InputStream inputStream; private OutputStream outputStream; private SerialPort serialPort; - + private ComPortSettings settings; + private int timeout; + @Override public void open(String printerName, int timeout, ComPortSettings settings) throws AlreadyAssignedException, InappropriateDeviceException { if (settings == null) { @@ -43,6 +45,8 @@ public void open(String printerName, int timeout, ComPortSettings settings) thro throw new InappropriateDeviceException("Speed hasn't been configured for this device(" + settings.getPortName() + ")."); } + this.settings = settings; + this.timeout = timeout; String portName = settings.getPortName(); try { CommPortIdentifier identifier = CommPortIdentifier.getPortIdentifier(portName); @@ -117,6 +121,12 @@ public void write(byte[] gcode) throws IOException { outputStream.write(gcode); } + @Override + public void restartCommunications() throws AlreadyAssignedException, InappropriateDeviceException { + close(); + open(name, timeout, settings); + } + public String toString() { return name; } diff --git a/host/src/main/java/org/area515/resinprinter/serial/SerialCommunicationsPort.java b/host/src/main/java/org/area515/resinprinter/serial/SerialCommunicationsPort.java index 6a86a9e13..d3b30c8a7 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/SerialCommunicationsPort.java +++ b/host/src/main/java/org/area515/resinprinter/serial/SerialCommunicationsPort.java @@ -13,4 +13,5 @@ public interface SerialCommunicationsPort { public String getName(); public void write(byte[] data) throws IOException; public byte[] read() throws IOException; + public void restartCommunications() throws AlreadyAssignedException, InappropriateDeviceException; } diff --git a/host/src/main/java/org/area515/resinprinter/serial/SerialManager.java b/host/src/main/java/org/area515/resinprinter/serial/SerialManager.java index 25960f111..a74a1f668 100644 --- a/host/src/main/java/org/area515/resinprinter/serial/SerialManager.java +++ b/host/src/main/java/org/area515/resinprinter/serial/SerialManager.java @@ -3,8 +3,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -176,6 +174,10 @@ private DetectedResources detectResourcesAndAssignPort(Printer printer, SerialCo identifier = null; String[] identifiers = SerialManager.Instance().getPortNames(); for (String currentIdentifier : identifiers) { + if (currentIdentifier.equals(ConsoleCommPort.GCODE_RESPONSE_SIMULATION)) { + continue; + } + logger.debug("Autodetection trying against serial device:{}", currentIdentifier); //Auto detection will continue to override settings that haven't been set on the command line @@ -343,7 +345,7 @@ public List getSerialDevices() { return idents; } - + /** * This should be called when you stop using the port. * This will prevent port locking on platforms like Linux. diff --git a/host/src/main/java/org/area515/resinprinter/server/ApplicationConfig.java b/host/src/main/java/org/area515/resinprinter/server/ApplicationConfig.java index b01fc3995..000a4be6c 100644 --- a/host/src/main/java/org/area515/resinprinter/server/ApplicationConfig.java +++ b/host/src/main/java/org/area515/resinprinter/server/ApplicationConfig.java @@ -31,7 +31,7 @@ public class ApplicationConfig extends Application{ public ApplicationConfig() { BeanConfig beanConfig = new BeanConfig(); beanConfig.setTitle("Photonic3D REST API"); - beanConfig.setVersion("0.0." + HostProperties.Instance().getVersionNumber()); + beanConfig.setVersion(HostProperties.Instance().getReleaseTagName()); beanConfig.setSchemes(new String[]{"http"}); //beanConfig.setHost("localhost:9091"); beanConfig.setBasePath("/services"); @@ -52,7 +52,7 @@ public ApplicationConfig() { singletons.add(RemoteService.INSTANCE); } - public JacksonJaxbJsonProvider buildJacksonJaxbJsonProvider() { + public static JacksonJaxbJsonProvider buildJacksonJaxbJsonProvider() { ObjectMapper mapper = new ObjectMapper(); //mapper.enable(SerializationFeature.INDENT_OUTPUT); AnnotationIntrospector pair = AnnotationIntrospector.pair( diff --git a/host/src/main/java/org/area515/resinprinter/server/HostProperties.java b/host/src/main/java/org/area515/resinprinter/server/HostProperties.java index 2bc2992f4..cf5f61b27 100644 --- a/host/src/main/java/org/area515/resinprinter/server/HostProperties.java +++ b/host/src/main/java/org/area515/resinprinter/server/HostProperties.java @@ -34,6 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.area515.resinprinter.display.AlreadyAssignedException; +import org.area515.resinprinter.display.GraphicsOutputInterface; import org.area515.resinprinter.display.InappropriateDeviceException; import org.area515.resinprinter.job.PrintFileProcessor; import org.area515.resinprinter.network.LinuxNetworkManager; @@ -74,7 +75,6 @@ public class HostProperties { private File printDir; private String hostGUI; private boolean fakeSerial = false; - private boolean fakedisplay = false; private boolean removeJobOnCompletion = true; private boolean forceCalibrationOnFirstUse = false; private boolean limitLiveStreamToOneCPU = false; @@ -82,6 +82,7 @@ public class HostProperties { private List> featureClasses = new ArrayList>(); private List> notificationClasses = new ArrayList>(); private List printFileProcessors = new ArrayList(); + private List displayDevices = new ArrayList(); private Class serialPortClass; private Class networkManagerClass; @@ -187,10 +188,9 @@ private HostProperties() { uploadDirString = configurationProperties.getProperty("uploaddir"); fakeSerial = new Boolean(configurationProperties.getProperty("fakeserial", "false")); - fakedisplay = new Boolean(configurationProperties.getProperty("fakedisplay", "false")); hostGUI = configurationProperties.getProperty("hostGUI", "resources"); visibleCards = Arrays.asList(configurationProperties.getProperty("visibleCards", "printers,printJobs,printables,users,settings").split(",")); - + //This loads features for (Entry currentProperty : configurationProperties.entrySet()) { String currentPropertyString = currentProperty.getKey() + ""; @@ -199,8 +199,8 @@ private HostProperties() { if ("true".equalsIgnoreCase(currentProperty.getValue() + "")) { try { featureClasses.add((Class)Class.forName(currentPropertyString)); - } catch (ClassNotFoundException e) { - logger.error("Failed to load feature:{}", currentPropertyString); + } catch (NoClassDefFoundError | UnsatisfiedLinkError | ClassNotFoundException e) { + logger.error("Failed to load Feature:" + currentPropertyString, e); } } } @@ -214,8 +214,8 @@ private HostProperties() { if ("true".equalsIgnoreCase(currentProperty.getValue() + "")) { try { notificationClasses.add((Class)Class.forName(currentPropertyString)); - } catch (ClassNotFoundException e) { - logger.error("Failed to load notifier:{}", currentPropertyString); + } catch (NoClassDefFoundError | UnsatisfiedLinkError | ClassNotFoundException e) { + logger.error("Failed to load Notifier:" + currentPropertyString, e); } } } @@ -230,8 +230,24 @@ private HostProperties() { try { PrintFileProcessor processor = ((Class)Class.forName(currentPropertyString)).newInstance(); printFileProcessors.add(processor); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { - logger.error("Failed to load PrintFileProcessor:{}", currentPropertyString); + } catch (NoClassDefFoundError | UnsatisfiedLinkError | ClassNotFoundException | InstantiationException | IllegalAccessException e) { + logger.error("Failed to load PrintFileProcessor:" + currentPropertyString, e); + } + } + } + } + + //This loads displayDevices + for (Entry currentProperty : configurationProperties.entrySet()) { + String currentPropertyString = currentProperty.getKey() + ""; + if (currentPropertyString.startsWith("displayDevice.")) { + currentPropertyString = currentPropertyString.replace("displayDevice.", ""); + if ("true".equalsIgnoreCase(currentProperty.getValue() + "")) { + try { + GraphicsOutputInterface device = ((Class)Class.forName(currentPropertyString)).newInstance(); + displayDevices.add(device); + } catch (NoClassDefFoundError | UnsatisfiedLinkError | ClassNotFoundException | InstantiationException | IllegalAccessException e) { + logger.error("Failed to load DisplayDevice:" + currentPropertyString, e); } } } @@ -425,7 +441,16 @@ public String getSecurityRealmName() { } public int getVersionNumber() { - return versionNumber; + if (releaseTagName == null) { + return 0; + } + + String release = releaseTagName.replaceAll("[^\\d]", ""); + if (release.length() == 0) { + return 0; + } + + return Integer.valueOf(release); } public String getReleaseTagName() { @@ -440,7 +465,7 @@ public int getPrinterHostPort() { return printerHostPort; } - public String hostGUI() { + public String getHostGUIDir() { return hostGUI; } @@ -460,10 +485,6 @@ public boolean getFakeSerial(){ return fakeSerial; } - public boolean getFakeDisplay(){ - return fakedisplay; - } - public String getSSLKeypairPassword() { return sslKeypairPassword; } @@ -504,6 +525,10 @@ public List getPrintFileProcessors() { return printFileProcessors; } + public List getDisplayDevices() { + return displayDevices; + } + public boolean isUseSSL() { return useSSL; } @@ -595,6 +620,7 @@ public CwhEmailSettings loadEmailSettings() { Boolean.valueOf(properties.getProperty("mail.smtp.starttls.enable"))); return settings; } + public void saveEmailSettings(CwhEmailSettings settings) { Properties emailProperties = new Properties(); StringBuilder toEmails = new StringBuilder(); diff --git a/host/src/main/java/org/area515/resinprinter/server/Main.java b/host/src/main/java/org/area515/resinprinter/server/Main.java index 5598881f7..3a73da3e1 100644 --- a/host/src/main/java/org/area515/resinprinter/server/Main.java +++ b/host/src/main/java/org/area515/resinprinter/server/Main.java @@ -118,7 +118,7 @@ public static void main(String[] args) throws Exception { // In this example it is the current directory but it can be configured to anything that the jvm has access to. resource_handler.setDirectoriesListed(true); resource_handler.setWelcomeFiles(new String[]{ "index.html" }); - resource_handler.setResourceBase(HostProperties.Instance().hostGUI()); + resource_handler.setResourceBase(HostProperties.Instance().getHostGUIDir()); //Angular is pretty messed up when it comes to link rewriting: https://github.com/angular/angular.js/issues/4608 //I can't believe we need to server side changes to fix this!!! https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode diff --git a/host/src/main/java/org/area515/resinprinter/services/CustomizerService.java b/host/src/main/java/org/area515/resinprinter/services/CustomizerService.java index 6950886de..edcee30d0 100644 --- a/host/src/main/java/org/area515/resinprinter/services/CustomizerService.java +++ b/host/src/main/java/org/area515/resinprinter/services/CustomizerService.java @@ -151,12 +151,16 @@ public void projectImage(@PathParam("customizerName") String customizerName) thr public StreamingOutput renderFirstSliceImage(@PathParam("customizerName") String customizerName) throws NoPrinterFoundException, SliceHandlingException { Customizer customizer = customizersByName.getIfPresent(customizerName); if (customizer == null) { - throw new IllegalArgumentException("Customizer is missing"); + throw new IllegalArgumentException("Customizer is missing for:" + customizerName); } File file = new File(HostProperties.Instance().getUploadDir(), customizer.getPrintableName() + "." + customizer.getPrintableExtension()); PrintFileProcessor processor = PrintFileFilter.INSTANCE.findAssociatedPrintProcessor(file); if (!(processor instanceof Previewable)) { + if (processor == null) { + throw new IllegalArgumentException("Couldn't find file processor for file:" + file); + } + throw new IllegalArgumentException(processor.getFriendlyName() + " files don't support image preview."); } diff --git a/host/src/main/java/org/area515/resinprinter/services/MachineService.java b/host/src/main/java/org/area515/resinprinter/services/MachineService.java index 7af98703e..3b093a06f 100644 --- a/host/src/main/java/org/area515/resinprinter/services/MachineService.java +++ b/host/src/main/java/org/area515/resinprinter/services/MachineService.java @@ -41,6 +41,7 @@ import javax.mail.Transport; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -53,11 +54,15 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.area515.resinprinter.display.DisplayManager; +import org.area515.resinprinter.display.GraphicsOutputInterface; import org.area515.resinprinter.job.PrintFileProcessor; import org.area515.resinprinter.network.NetInterface; import org.area515.resinprinter.network.NetworkManager; @@ -209,7 +214,8 @@ public Boolean call() throws Exception { return false; } } - + + //TODO: getWirelessStrength @ApiOperation(value="Retrieves all of the supported file types that are returned from the each of the org.area515.resinprinter.job.PrintFileProcessor.getFileExtensions()." + SwaggerMetadata.PRINT_FILE_PROCESSOR) @ApiResponses(value = { @@ -256,7 +262,7 @@ public Response uploadFont(InputStream istream) { } } - @ApiOperation(value="Upload TrueType fonts to be used with 2D file processing.") + @ApiOperation(value="Enumerates the list of available fonts to be used with 2D file printing.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) @@ -385,11 +391,11 @@ public Response stageOfflineInstall(MultipartFormDataInput input) { @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @GET - @Path("wirelessNetworks/list") - @Produces(MediaType.APPLICATION_JSON) - public List getWirelessNetworks() { - Class managerClass = HostProperties.Instance().getNetworkManagerClass(); + @GET + @Path("wirelessNetworks/list") + @Produces(MediaType.APPLICATION_JSON) + public List getWirelessNetworks() { + Class managerClass = HostProperties.Instance().getNetworkManagerClass(); try { NetworkManager networkManager = managerClass.newInstance(); List interfaces = networkManager.getNetworkInterfaces(); @@ -406,16 +412,16 @@ public List getWirelessNetworks() { logger.error("Error retrieving wireless networks", e); return null; } - } + } @ApiOperation(value = "Connects to the supplied wireless SSID using the provided passphrase and Wireless settings.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @PUT - @Path("wirelessConnect") - @Consumes(MediaType.APPLICATION_JSON) - public void connectToWifiSSID(WirelessNetwork network) { + @PUT + @Path("wirelessConnect") + @Consumes(MediaType.APPLICATION_JSON) + public void connectToWifiSSID(WirelessNetwork network) { Class managerClass = HostProperties.Instance().getNetworkManagerClass(); try { NetworkManager networkManager = managerClass.newInstance(); @@ -423,16 +429,16 @@ public void connectToWifiSSID(WirelessNetwork network) { } catch (InstantiationException | IllegalAccessException e) { logger.error("Error connecting to WifiSSID:" + network.getSsid(), e); } - } + } @ApiOperation(value = "Enumerates the list of serial ports available on the Photonic 3D host.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @GET - @Path("serialPorts/list") - @Produces(MediaType.APPLICATION_JSON) - public List getSerialPorts() { + @GET + @Path("serialPorts/list") + @Produces(MediaType.APPLICATION_JSON) + public List getSerialPorts() { List identifiers = SerialManager.Instance().getSerialDevices(); List identifierStrings = new ArrayList(); for (SerialCommunicationsPort current : identifiers) { @@ -440,42 +446,98 @@ public List getSerialPorts() { } return identifierStrings; - } + } @ApiOperation(value = "Enumerates the list of graphics displays that are available on the Photonic 3D host.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @GET - @Path("graphicsDisplays/list") - @Produces(MediaType.APPLICATION_JSON) - public List getDisplays() { - List devices = DisplayManager.Instance().getDisplayDevices(); + @GET + @Path("graphicsDisplays/list") + @Produces(MediaType.APPLICATION_JSON) + public List getDisplays() { + List devices = DisplayManager.Instance().getDisplayDevices(); List deviceStrings = new ArrayList(); - for (GraphicsDevice current : devices) { + for (GraphicsOutputInterface current : devices) { deviceStrings.add(current.getIDstring()); } return deviceStrings; - } - - @ApiOperation(value = "Enumerates the list of machine configurations that are available on the Photonic 3D host.") - @ApiResponses(value = { - @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), - @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @GET - @Path("machineConfigurations/list") - public List getMachineConfigurations() { - return HostProperties.Instance().getConfigurations(HostProperties.Instance().MACHINE_DIR, HostProperties.MACHINE_EXTENSION, MachineConfig.class); - } + } + + + @ApiOperation(value = "Enumerates the list of machine configurations that are available on the Photonic 3D host.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), + @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) + @GET + @Path("machineConfigurations/list") + public List getMachineConfigurations() { + return HostProperties.Instance().getConfigurations(HostProperties.Instance().MACHINE_DIR, HostProperties.MACHINE_EXTENSION, MachineConfig.class); + } + + @ApiOperation(value = "Save a machine configuration to the machine config directory of Photonic 3D host.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), + @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("machineConfigurations") + public void saveMachineConfiguration(MachineConfig machineConfig) throws JAXBException { + File machineFile = new File(HostProperties.Instance().MACHINE_DIR, machineConfig.getName() + HostProperties.MACHINE_EXTENSION); + JAXBContext jaxbContext = JAXBContext.newInstance(MachineConfig.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal(machineConfig, machineFile); + } + + @ApiOperation(value = "Deletes a machine configuration(by name) from the machine config directory of Photonic 3D host.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), + @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) + @DELETE + @Path("machineConfigurations/{machineConfigurationName}") + public void deleteMachineConfiguration(@PathParam("machineConfigurationName") String machineConfig) throws JAXBException { + File machineFile = new File(HostProperties.Instance().MACHINE_DIR, machineConfig + HostProperties.MACHINE_EXTENSION); + machineFile.delete(); + } + + + @ApiOperation(value = "Enumerates the list of slicing profiles that are available on the Photonic 3D host.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) - @GET - @Path("slicingProfiles/list") - public List getSlicingProfiles() { - return HostProperties.Instance().getConfigurations(HostProperties.Instance().PROFILES_DIR, HostProperties.PROFILES_EXTENSION, SlicingProfile.class); - } + @GET + @Path("slicingProfiles/list") + public List getSlicingProfiles() { + return HostProperties.Instance().getConfigurations(HostProperties.Instance().PROFILES_DIR, HostProperties.PROFILES_EXTENSION, SlicingProfile.class); + } + + @ApiOperation(value = "Save a slicing profile to the slicing profile directory of Photonic 3D host.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), + @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("slicingProfiles") + public void saveSlicingProfile(SlicingProfile slicingProfile) throws JAXBException { + File profileFile = new File(HostProperties.Instance().PROFILES_DIR, slicingProfile.getName() + HostProperties.PROFILES_EXTENSION); + JAXBContext jaxbContext = JAXBContext.newInstance(SlicingProfile.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal(slicingProfile, profileFile); + } + + @ApiOperation(value = "Deletes a slicing profile from the slicing profile directory of Photonic 3D host.") + @ApiResponses(value = { + @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), + @ApiResponse(code = 500, message = SwaggerMetadata.UNEXPECTED_ERROR)}) + @DELETE + @Path("slicingProfiles/{slicingProfileName}") + public void deleteSlicingProfile(@PathParam("slicingProfileName") String profile) throws JAXBException { + File profileFile = new File(HostProperties.Instance().PROFILES_DIR, profile + HostProperties.PROFILES_EXTENSION); + profileFile.delete(); + } } diff --git a/host/src/main/java/org/area515/resinprinter/services/PrintableService.java b/host/src/main/java/org/area515/resinprinter/services/PrintableService.java index e3212c823..63b265490 100644 --- a/host/src/main/java/org/area515/resinprinter/services/PrintableService.java +++ b/host/src/main/java/org/area515/resinprinter/services/PrintableService.java @@ -68,6 +68,7 @@ public class PrintableService { public static PrintableService INSTANCE = new PrintableService(); public static final String UNKNOWN_FILE = "I don't know how do deal with a file of this type:"; public static final String NO_FILE = "You didn't attempt to upload a file, or the filename was Blank."; + public static final String NO_FILE_MIME = "No file specified in multipart mime!"; private PrintableService() { } @@ -76,16 +77,16 @@ public static Response uploadFile(String fileName, InputStream istream, File par File newUploadFile = new File(parentDirectory, fileName); try { if (!saveFile(istream, newUploadFile.getAbsoluteFile())) { - return Response.status(Status.BAD_REQUEST).entity(UNKNOWN_FILE + fileName).build(); + return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN_TYPE).entity(UNKNOWN_FILE + fileName).build(); } } catch (IOException e) { String output = "Error while uploading file: " + newUploadFile; logger.error(output, e); - return Response.status(Status.INTERNAL_SERVER_ERROR).entity(output).build(); + return Response.status(Status.INTERNAL_SERVER_ERROR).type(MediaType.TEXT_PLAIN_TYPE).entity(output).build(); } String output = "File saved to location: " + newUploadFile; - return Response.status(Status.OK).entity(output).build(); + return Response.status(Status.OK).type(MediaType.TEXT_PLAIN_TYPE).entity(output).build(); } public static Response uploadFile(MultipartFormDataInput input, File parentDirectory) { @@ -94,8 +95,8 @@ public static Response uploadFile(MultipartFormDataInput input, File parentDirec List inPart = formParts.get("file"); if (inPart == null) { - logger.info("No file specified in multipart mime!"); - return Response.status(500).build(); + logger.info(NO_FILE_MIME); + return Response.status(Status.INTERNAL_SERVER_ERROR).type(MediaType.TEXT_PLAIN_TYPE).entity(NO_FILE_MIME).build(); } File newUploadFile = null; @@ -108,7 +109,7 @@ public static Response uploadFile(MultipartFormDataInput input, File parentDirec // If the filename was blank we aren't interested in the file. if (fileName == null || fileName.isEmpty()) { - return Response.status(Status.BAD_REQUEST).entity(NO_FILE).build(); + return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN_TYPE).entity(NO_FILE).build(); } // Handle the body of that part with an InputStream @@ -118,18 +119,18 @@ public static Response uploadFile(MultipartFormDataInput input, File parentDirec newUploadFile = new File(parentDirectory, fileName); if (!saveFile(istream, newUploadFile.getAbsoluteFile())) { - return Response.status(Status.BAD_REQUEST).entity(UNKNOWN_FILE + fileName).build(); + return Response.status(Status.BAD_REQUEST).type(MediaType.TEXT_PLAIN_TYPE).entity(UNKNOWN_FILE + fileName).build(); } } catch (IOException e) { String output = "Error while uploading file: " + newUploadFile; logger.error(output, e); - return Response.status(Status.INTERNAL_SERVER_ERROR).entity(output).build(); + return Response.status(Status.INTERNAL_SERVER_ERROR).type(MediaType.TEXT_PLAIN_TYPE).entity(output).build(); } } String output = "File saved to location: " + newUploadFile; - return Response.status(Status.OK).entity(output).build(); + return Response.status(Status.OK).type(MediaType.TEXT_PLAIN_TYPE).entity(output).build(); } @ApiOperation(value="Upload a printable file using multipart/form-data. " diff --git a/host/src/main/java/org/area515/resinprinter/services/PrinterService.java b/host/src/main/java/org/area515/resinprinter/services/PrinterService.java index b7bd5d993..143c11da4 100644 --- a/host/src/main/java/org/area515/resinprinter/services/PrinterService.java +++ b/host/src/main/java/org/area515/resinprinter/services/PrinterService.java @@ -7,7 +7,6 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; -import java.awt.GraphicsDevice; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.File; @@ -36,7 +35,10 @@ import org.apache.logging.log4j.Logger; import org.area515.resinprinter.display.AlreadyAssignedException; import org.area515.resinprinter.display.DisplayManager; +import org.area515.resinprinter.display.GraphicsOutputInterface; import org.area515.resinprinter.display.InappropriateDeviceException; +import org.area515.resinprinter.display.LastAvailableDisplay; +import org.area515.resinprinter.display.SimulatedDisplay; import org.area515.resinprinter.exception.NoPrinterFoundException; import org.area515.resinprinter.job.Customizer; import org.area515.resinprinter.job.InkDetector; @@ -83,7 +85,7 @@ private MachineResponse openShutter(String printerName, boolean shutter) throws String shutterGCode = printer.getConfiguration().getSlicingProfile().getgCodeShutter(); if (shutterGCode != null && shutterGCode.trim().length() > 0) { printer.setShutterOpen(shutter); - return new MachineResponse(name, true, printer.getGCodeControl().executeGCodeWithTemplating(job, printer.getConfiguration().getSlicingProfile().getgCodeShutter())); + return new MachineResponse(name, true, printer.getGCodeControl().executeGCodeWithTemplating(job, printer.getConfiguration().getSlicingProfile().getgCodeShutter(), false)); } return new MachineResponse(name, false, "This printer doesn't support a shutter."); @@ -307,7 +309,7 @@ public Printer createTemplatePrinter() throws InappropriateDeviceException { //TODO: Return a nice unused name for this printer instead of the hardcoded value below PrinterConfiguration configuration = createTemplatePrinter( "CWH Template Printer", //"mUVe 1 DLP (Testing)", - DisplayManager.SIMULATED_DISPLAY, + SimulatedDisplay.NAME, ConsoleCommPort.GCODE_RESPONSE_SIMULATION, 134, 75, 185); configuration.getSlicingProfile().getSelectedInkConfig().setNumberOfFirstLayers(10); @@ -415,13 +417,13 @@ PrinterConfiguration createTemplatePrinter(String printername, String displayId, slicingProfile.setLiftFeedRate(50); slicingProfile.setDirection(BuildDirection.Bottom_Up); try { - if (DisplayManager.SIMULATED_DISPLAY.equals(displayId)) { + if (SimulatedDisplay.NAME.equals(displayId)) { monitor.setDLP_X_Res(1920); monitor.setDLP_Y_Res(1080); } else { - GraphicsDevice device = DisplayManager.Instance().getDisplayDevice(DisplayManager.LAST_AVAILABLE_DISPLAY); - monitor.setDLP_X_Res(device.getDefaultConfiguration().getBounds().getWidth()); - monitor.setDLP_Y_Res(device.getDefaultConfiguration().getBounds().getHeight()); + GraphicsOutputInterface device = DisplayManager.Instance().getDisplayDevice(LastAvailableDisplay.NAME); + monitor.setDLP_X_Res(device.getBoundary().getWidth()); + monitor.setDLP_Y_Res(device.getBoundary().getHeight()); } machineConfig.setxRenderSize((int)monitor.getDLP_X_Res()); machineConfig.setyRenderSize((int)monitor.getDLP_Y_Res()); @@ -480,6 +482,10 @@ public MachineResponse showGridScreen(@PathParam("printername") String printerNa throw new InappropriateDeviceException("Printer:" + printerName + " not started"); } + if (currentPrinter.isDisplayBusy()) { + throw new InappropriateDeviceException("Printer:" + printerName + " display is busy, try again later."); + } + currentPrinter.showGridImage(pixels); return new MachineResponse("gridscreenshown", true, "Showed calibration screen on:" + printerName); } catch (InappropriateDeviceException e) { @@ -503,6 +509,10 @@ public MachineResponse showCalibrationScreen(@PathParam("printername") String pr throw new InappropriateDeviceException("Printer:" + printerName + " not started"); } + if (currentPrinter.isDisplayBusy()) { + throw new InappropriateDeviceException("Printer:" + printerName + " display is busy, try again later."); + } + logger.info("Showing calibration screen for xPixels:{} yPixels:{}", xPixels, yPixels); currentPrinter.showCalibrationImage(xPixels, yPixels); return new MachineResponse("calibrationscreenshown", true, "Showed calibration screen on:" + printerName); @@ -531,14 +541,14 @@ public MachineResponse calibrate(@PathParam("printername") String printerName, @ currentConfiguration.getSlicingProfile().setDotsPermmX(xPixelsPerMM); currentConfiguration.getSlicingProfile().setDotsPermmY(yPixelsPerMM); Printer printer = PrinterService.INSTANCE.getPrinter(printerName); - GraphicsDevice device = null; + GraphicsOutputInterface device = null; if (printer.isStarted()) { device = DisplayManager.Instance().getDisplayDevice(printer.getDisplayDeviceID()); } else { device = DisplayManager.Instance().getDisplayDevice(currentConfiguration.getMachineConfig().getOSMonitorID()); } - currentConfiguration.getSlicingProfile().setxResolution(device.getDefaultConfiguration().getBounds().width); - currentConfiguration.getSlicingProfile().setyResolution(device.getDefaultConfiguration().getBounds().height); + currentConfiguration.getSlicingProfile().setxResolution(device.getBoundary().width); + currentConfiguration.getSlicingProfile().setyResolution(device.getBoundary().height); currentConfiguration.setCalibrated(true); logger.info("Calibrated printer to xPixelsPerMM:{} yPixelsPerMM:{}", xPixelsPerMM, yPixelsPerMM); @@ -567,6 +577,10 @@ public MachineResponse showBlankScreen(@PathParam("printername") String printerN throw new InappropriateDeviceException("Printer:" + printerName + " not started"); } + if (currentPrinter.isDisplayBusy()) { + throw new InappropriateDeviceException("Printer:" + printerName + " display is busy, try again later."); + } + currentPrinter.showBlankImage(); return new MachineResponse("blankscreenshown", true, "Showed blank screen on:" + printerName); } catch (InappropriateDeviceException e) { @@ -802,6 +816,9 @@ public MachineResponse print(@PathParam("fileName") String fileName, @PathParam( @Produces(MediaType.APPLICATION_JSON) public MachineResponse printWithCustomizer(@PathParam("customizerName") String customizerName) { Customizer customizer = CustomizerService.INSTANCE.getCustomizer(customizerName, null); + if (customizer == null) { + return new MachineResponse("startJob", false, "Customizer:" + customizerName + " not found"); + } return startPrintJob(customizer.getPrinterName(), customizer.getPrintableName() + "." + customizer.getPrintableExtension(), customizer); } diff --git a/host/src/main/java/org/area515/resinprinter/services/SettingsService.java b/host/src/main/java/org/area515/resinprinter/services/SettingsService.java index 4ebf22217..047911fac 100644 --- a/host/src/main/java/org/area515/resinprinter/services/SettingsService.java +++ b/host/src/main/java/org/area515/resinprinter/services/SettingsService.java @@ -43,6 +43,7 @@ public List getVisibleCards() { return HostProperties.Instance().getVisibleCards(); } + @Deprecated @ApiOperation(value="Returns the integer based version number of Photonic3D found in the build.number file.") @ApiResponses(value = { @ApiResponse(code = 200, message = SwaggerMetadata.SUCCESS), diff --git a/host/src/main/java/org/area515/resinprinter/slice/SliceBrowser.java b/host/src/main/java/org/area515/resinprinter/slice/SliceBrowser.java index c4b6dec3d..596616e35 100644 --- a/host/src/main/java/org/area515/resinprinter/slice/SliceBrowser.java +++ b/host/src/main/java/org/area515/resinprinter/slice/SliceBrowser.java @@ -60,7 +60,8 @@ public class SliceBrowser extends JSplitPane { //781;//"C:\\Users\\wgilster\\Documents\\Fat_Guy_Statue.stl"; good //"C:\\Users\\wgilster\\git\\Creation-Workshop-Host\\host\\src\\test\\resources\\org\\area515\\resinprinter\\slice\\CornerBracket_2.stl" - private String firstFile = "C:\\Users\\wgilster\\Documents\\fdhgg.stl";//1,200,670 + //private String firstFile = "C:\\Users\\wgilster\\Documents\\fdhgg.stl";//1,200,670 + private String firstFile = "C:\\Users\\wgilster\\uploaddir\\johnny-test pieces 1.stl"; // private String firstFile = "C:\\Users\\wgilster\\Documents\\NonManifoldBox.stl";//-19 // private String firstFile = "C:\\Users\\wgilster\\Documents\\Fat_Guy_Statue.stl"; // private String firstFile = "C:\\Users\\wgilster\\AppData\\Local\\Temp\\uploaddir\\CornerBracket_2.stl";//95 @@ -484,6 +485,7 @@ public void actionPerformed(ActionEvent e) { });*/ final JButton findNextTriangle = new JButton("FTONS");//Find triangles on next slice + findNextTriangle.setToolTipText("Find watched triangles on next slice"); findNextTriangle.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -494,6 +496,7 @@ public void actionPerformed(ActionEvent e) { }); final JButton findPreviousTriangle = new JButton("FTOPS");//Find triangles on previous slice + findPreviousTriangle.setToolTipText("Find watched triangles on previous slice"); findPreviousTriangle.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -502,6 +505,23 @@ public void actionPerformed(ActionEvent e) { } }); + final JButton testTriangleEqualButton = new JButton("Test Equal"); + testTriangleEqualButton.setToolTipText("Find watched triangles on previous slice"); + testTriangleEqualButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + List triangles = sliceBrowserListener.getSelectedTriangles(); + for (int t = 0; t < triangles.size() - 1; t++) { + System.out.print("Triangle:" + t + " = " + (t+1)); + if (triangles.get(t).equals(triangles.get(t+1))) { + System.out.println(" equals"); + } else { + System.out.println(" not equal"); + } + } + } + }); + final JButton clearYWatches = new JButton("Clear Y watches");//Find triangles on previous slice clearYWatches.addActionListener(new ActionListener() { @Override @@ -511,6 +531,7 @@ public void actionPerformed(ActionEvent e) { }); final JButton runWatches = new JButton("Run watch");//Find triangles on previous slice + runWatches.setToolTipText("Find watched triangles on this slice"); runWatches.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -554,6 +575,10 @@ public void actionPerformed(ActionEvent e) { cons = new GridBagConstraints(); cons.gridx = 4; cons.gridy = 1; + bottomPanel.add(testTriangleEqualButton, cons); + cons = new GridBagConstraints(); + cons.gridx = 5; + cons.gridy = 1; bottomPanel.add(runWatches, cons); window.add(zSliceBar, BorderLayout.EAST); window.add(browserPanel, BorderLayout.CENTER); diff --git a/host/src/main/java/org/area515/resinprinter/sms/AlertAPITest.java b/host/src/main/java/org/area515/resinprinter/sms/AlertAPITest.java new file mode 100644 index 000000000..747334637 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/sms/AlertAPITest.java @@ -0,0 +1,133 @@ +package org.area515.resinprinter.sms; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class AlertAPITest { + public static class HttpClientUtils { + + private final static Map instances = new HashMap<>(); + + private final ThreadSafeClientConnManager threadSafeClientConnManager; + + private final int connectionTimeout; + private final int soTimeout; + + private HttpClientUtils(int connectionTimeout, int soTimeout) { + this.connectionTimeout = connectionTimeout; + this.soTimeout = soTimeout; + + this.threadSafeClientConnManager = new ThreadSafeClientConnManager(); + this.threadSafeClientConnManager.setDefaultMaxPerRoute(200); + this.threadSafeClientConnManager.setMaxTotal(200); + } + + /** + * Return an existing or instantiate a new HttpClient factory instance with explicitly specified connection and read timeout values + * + * @param connectionTimeout the timeout value in milliseconds to use when establishing a new http socket + * @param soTimeout the timeout value in milliseconds to wait for a http response before closing the socket + * + * @return HttpClientUtils an instance of the HttpClient factory primed with the requested timeout values + */ + public static HttpClientUtils getInstance(int connectionTimeout, int soTimeout) { + String key = "c-" + connectionTimeout + "-so-" + soTimeout; + HttpClientUtils instance = instances.get(key); + if (instance == null) { + instance = new HttpClientUtils(connectionTimeout, soTimeout); + instances.put(key, instance); + } + return instance; + } + + /** + * Instantiate a new HttpClient instance that uses the timeout values associated with this factory instance + * + * @return HttpClient a new HttpClient instance + */ + public HttpClient getNewHttpClient() { + HttpParams httpClientParams = new BasicHttpParams(); + HttpProtocolParams.setUserAgent(httpClientParams, "nexmo-java/2.0.0-prerelease"); + HttpProtocolParams.setContentCharset(httpClientParams, "UTF-8"); + HttpProtocolParams.setHttpElementCharset(httpClientParams, "UTF-8"); + HttpConnectionParams.setConnectionTimeout(httpClientParams, this.connectionTimeout); + HttpConnectionParams.setSoTimeout(httpClientParams, this.soTimeout); + HttpConnectionParams.setStaleCheckingEnabled(httpClientParams, true); + HttpConnectionParams.setTcpNoDelay(httpClientParams, true); + + return new DefaultHttpClient(this.threadSafeClientConnManager, httpClientParams); + } + } + + public static class ShortCode { + @JsonProperty("api_key") + private String api_key; + @JsonProperty("api_secret") + private String api_secret; + @JsonProperty("to") + private String to; + @JsonProperty("template") + private int template; + + //This is a custom parameter for your template + @JsonProperty("body") + private String body; + } + + public static class ResponseMessage { + @JsonProperty("status") + private String status; + @JsonProperty("message-id") + private String messageId; + @JsonProperty("to") + private String to; + @JsonProperty("client-ref") + private String clientRef; + @JsonProperty("remaining-balance") + private String remainingBalance; + @JsonProperty("message-price") + private String messagePrice; + @JsonProperty("network") + private String network; + @JsonProperty("error-message") + private String errorMessage; + } + + public static class ShortCodeResponse { + @JsonProperty("message-count") + private String messageCount; + @JsonProperty("messages") + private List messages; + } + + public static void main(String[] args) throws Exception { + ShortCode code = new ShortCode(); + //TODO: Setup short code request here... + + HttpClientUtils utils = HttpClientUtils.getInstance(5000, 30000); + HttpClient client = utils.getNewHttpClient(); + HttpPost httpPost = new HttpPost("https://rest.nexmo.com/sc/us/alert/json"); + httpPost.setHeader("Accept", "application/json"); + httpPost.setHeader("Content-type", "application/json"); + ObjectMapper mapper = new ObjectMapper(); + httpPost.setEntity(new StringEntity(mapper.writeValueAsString(code))); + HttpResponse httpResponse = client.execute(httpPost); + ShortCodeResponse response = mapper.readValue(httpResponse.getEntity().getContent(), ShortCodeResponse.class); + System.out.println(response); + } +} diff --git a/host/src/main/java/org/area515/resinprinter/twodim/PlatformImageRenderer.java b/host/src/main/java/org/area515/resinprinter/twodim/PlatformImageRenderer.java index 8f686d184..64a555158 100644 --- a/host/src/main/java/org/area515/resinprinter/twodim/PlatformImageRenderer.java +++ b/host/src/main/java/org/area515/resinprinter/twodim/PlatformImageRenderer.java @@ -48,7 +48,7 @@ public BufferedImage renderImage(BufferedImage imageToDisplay) throws JobManager Map overrides = new HashMap<>(); overrides.put("totalPlatformSlices", totalPlatformSlices); try { - TemplateEngine.runScriptInImagingContext(imageToDisplay, data.getPreTransformedImage(), aid.printJob, aid.printer, aid.scriptEngine, overrides, platformScript, "2D Platform rendering script", true); + TemplateEngine.runScriptInImagingContext(imageToDisplay, data.getPreTransformedImage(), aid.printJob, aid.printer, aid.scriptEngine, overrides, platformScript, "2D Platform rendering script", false); } catch (ScriptException e) { throw new JobManagerException("Failed to execute script", e); } diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/AlwaysTrueValueMatcher.java b/host/src/main/java/org/area515/resinprinter/util/cron/AlwaysTrueValueMatcher.java new file mode 100644 index 000000000..083c4be26 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/AlwaysTrueValueMatcher.java @@ -0,0 +1,35 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +/** + * This ValueMatcher always returns true! + * + * @author Carlo Pelliccia + */ +class AlwaysTrueValueMatcher implements ValueMatcher { + + /** + * Always true! + */ + public boolean match(int value) { + return true; + } + +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/CronFeature.java b/host/src/main/java/org/area515/resinprinter/util/cron/CronFeature.java new file mode 100644 index 000000000..7809fa7ab --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/CronFeature.java @@ -0,0 +1,89 @@ +package org.area515.resinprinter.util.cron; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.area515.resinprinter.plugin.Feature; +import org.area515.resinprinter.server.Main; + +public class CronFeature implements Feature { + private static final Logger logger = LogManager.getLogger(); + + private List taskList = new ArrayList(); + + public static class CronTask implements Callable { + private String taskName; + private Runnable runnable; + private Callable callable; + private CronPredictor predictor; + private ScheduledFuture future; + private boolean canceledTask; + + public CronTask(String taskName, Runnable runnable, SchedulingPattern pattern) { + this.taskName = taskName; + this.runnable = runnable; + this.predictor = new CronPredictor(pattern); + } + + public CronTask(String taskName, Callable callable, SchedulingPattern pattern) { + this.taskName = taskName; + this.callable = callable; + this.predictor = new CronPredictor(pattern); + } + + public synchronized void scheduleNextRun() throws RejectedExecutionException { + if (canceledTask) { + throw new RejectedExecutionException("Task cannot be executed again since it has been cancelled."); + } + future = Main.GLOBAL_EXECUTOR.schedule(this, predictor.nextMatchingTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + public synchronized boolean cancel() { + canceledTask = true; + return future.cancel(true); + } + + @Override + public Object call() throws Exception { + Object returnObject; + try { + if (runnable != null) { + runnable.run(); + returnObject = Void.TYPE; + } else if (callable != null){ + returnObject = callable.call(); + } else { + throw new RejectedExecutionException("Must implement either a runnable or a callable."); + } + scheduleNextRun(); + return returnObject; + } catch (RejectedExecutionException e) { + logger.error("I will NEVER attempt to execute this task again:" + taskName, e); + throw e; + } catch (Exception e) { + logger.error("I will attempt to execute this task again:" + taskName, e); + throw e; + } catch (Throwable t) { + logger.error("Can't recover, I will NEVER attempt to execute this task again:" + taskName, t); + throw t; + } + } + } + + @Override + public void start(URI uri) throws Exception { + + } + + @Override + public void stop() { + + } +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/CronPredictor.java b/host/src/main/java/org/area515/resinprinter/util/cron/CronPredictor.java new file mode 100644 index 000000000..f1bc42034 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/CronPredictor.java @@ -0,0 +1,335 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + *

+ * A predictor is able to predict when a scheduling pattern will be matched. + *

+ *

+ * Suppose you want to know when the scheduler will execute a task scheduled + * with the pattern 0 3 * jan-jun,sep-dec mon-fri. You can predict the + * next n execution of the task using a Predictor instance: + *

+ * + *
+ * String pattern = "0 3 * jan-jun,sep-dec mon-fri";
+ * Predictor p = new Predictor(pattern);
+ * for (int i = 0; i < n; i++) {
+ * 	System.out.println(p.nextMatchingDate());
+ * }
+ * 
+ * + * @author Carlo Pelliccia + * @since 1.1 + */ +public class CronPredictor { + + /** + * The scheduling pattern on which the predictor works. + */ + private SchedulingPattern schedulingPattern; + + /** + * The start time for the next prediction. + */ + private long time; + + /** + * The time zone for the prediction. + */ + private TimeZone timeZone = TimeZone.getDefault(); + + /** + * It builds a predictor with the given scheduling pattern and start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @param start + * The start time of the prediction. + * @throws InvalidPatternException + * In the given scheduling pattern isn't valid. + */ + public CronPredictor(String schedulingPattern, long start) + throws InvalidPatternException { + this.schedulingPattern = new SchedulingPattern(schedulingPattern); + this.time = (start / (1000 * 60)) * 1000 * 60; + } + + /** + * It builds a predictor with the given scheduling pattern and start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @param start + * The start time of the prediction. + * @throws InvalidPatternException + * In the given scheduling pattern isn't valid. + */ + public CronPredictor(String schedulingPattern, Date start) + throws InvalidPatternException { + this(schedulingPattern, start.getTime()); + } + + /** + * It builds a predictor with the given scheduling pattern and the current + * system time as the prediction start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @throws InvalidPatternException + * In the given scheduling pattern isn't valid. + */ + public CronPredictor(String schedulingPattern) throws InvalidPatternException { + this(schedulingPattern, System.currentTimeMillis()); + } + + /** + * It builds a predictor with the given scheduling pattern and start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @param start + * The start time of the prediction. + * @since 2.0 + */ + public CronPredictor(SchedulingPattern schedulingPattern, long start) { + this.schedulingPattern = schedulingPattern; + this.time = (start / (1000 * 60)) * 1000 * 60; + } + + /** + * It builds a predictor with the given scheduling pattern and start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @param start + * The start time of the prediction. + * @since 2.0 + */ + public CronPredictor(SchedulingPattern schedulingPattern, Date start) { + this(schedulingPattern, start.getTime()); + } + + /** + * It builds a predictor with the given scheduling pattern and the current + * system time as the prediction start time. + * + * @param schedulingPattern + * The pattern on which the prediction will be based. + * @since 2.0 + */ + public CronPredictor(SchedulingPattern schedulingPattern) { + this(schedulingPattern, System.currentTimeMillis()); + } + + /** + * Sets the time zone for predictions. + * + * @param timeZone + * The time zone for predictions. + * @since 2.2.5 + */ + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + } + + /** + * It returns the next matching moment as a millis value. + * + * @return The next matching moment as a millis value. + */ + public synchronized long nextMatchingTime() { + // Go a minute ahead. + time += 60000; + // Is it matching? + if (schedulingPattern.match(time)) { + return time; + } + // Go through the matcher groups. + int size = schedulingPattern.matcherSize; + long[] times = new long[size]; + for (int k = 0; k < size; k++) { + // Ok, split the time! + GregorianCalendar c = new GregorianCalendar(); + c.setTimeInMillis(time); + c.setTimeZone(timeZone); + int minute = c.get(Calendar.MINUTE); + int hour = c.get(Calendar.HOUR_OF_DAY); + int dayOfMonth = c.get(Calendar.DAY_OF_MONTH); + int month = c.get(Calendar.MONTH); + int year = c.get(Calendar.YEAR); + // Gets the matchers. + ValueMatcher minuteMatcher = (ValueMatcher) schedulingPattern.minuteMatchers.get(k); + ValueMatcher hourMatcher = (ValueMatcher) schedulingPattern.hourMatchers.get(k); + ValueMatcher dayOfMonthMatcher = (ValueMatcher) schedulingPattern.dayOfMonthMatchers.get(k); + ValueMatcher dayOfWeekMatcher = (ValueMatcher) schedulingPattern.dayOfWeekMatchers.get(k); + ValueMatcher monthMatcher = (ValueMatcher) schedulingPattern.monthMatchers.get(k); + for (;;) { // day of week + for (;;) { // month + for (;;) { // day of month + for (;;) { // hour + for (;;) { // minutes + if (minuteMatcher.match(minute)) { + break; + } else { + minute++; + if (minute > 59) { + minute = 0; + hour++; + } + } + } + if (hour > 23) { + hour = 0; + dayOfMonth++; + } + if (hourMatcher.match(hour)) { + break; + } else { + hour++; + minute = 0; + } + } + if (dayOfMonth > 31) { + dayOfMonth = 1; + month++; + } + if (month > Calendar.DECEMBER) { + month = Calendar.JANUARY; + year++; + } + if (dayOfMonthMatcher instanceof DayOfMonthValueMatcher) { + DayOfMonthValueMatcher aux = (DayOfMonthValueMatcher) dayOfMonthMatcher; + if (aux.match(dayOfMonth, month + 1, c.isLeapYear(year))) { + break; + } else { + dayOfMonth++; + hour = 0; + minute = 0; + } + } else if (dayOfMonthMatcher.match(dayOfMonth)) { + break; + } else { + dayOfMonth++; + hour = 0; + minute = 0; + } + } + if (monthMatcher.match(month + 1)) { + break; + } else { + month++; + dayOfMonth = 1; + hour = 0; + minute = 0; + } + } + // Is this ok? + c = new GregorianCalendar(); + c.setTimeZone(timeZone); + c.set(Calendar.MINUTE, minute); + c.set(Calendar.HOUR_OF_DAY, hour); + c.set(Calendar.DAY_OF_MONTH, dayOfMonth); + c.set(Calendar.MONTH, month); + c.set(Calendar.YEAR, year); + // Day-of-month/month/year compatibility check. + int oldDayOfMonth = dayOfMonth; + int oldMonth = month; + int oldYear = year; + dayOfMonth = c.get(Calendar.DAY_OF_MONTH); + month = c.get(Calendar.MONTH); + year = c.get(Calendar.YEAR); + if (month != oldMonth || dayOfMonth != oldDayOfMonth + || year != oldYear) { + // Take another spin! + continue; + } + // Day of week. + int dayOfWeek = c.get(Calendar.DAY_OF_WEEK); + if (dayOfWeekMatcher.match(dayOfWeek - 1)) { + break; + } else { + dayOfMonth++; + hour = 0; + minute = 0; + if (dayOfMonth > 31) { + dayOfMonth = 1; + month++; + if (month > Calendar.DECEMBER) { + month = Calendar.JANUARY; + year++; + } + } + } + } + // Seems it matches! + times[k] = (c.getTimeInMillis() / (1000 * 60)) * 1000 * 60; + } + // Which one? + long min = Long.MAX_VALUE; + for (int k = 0; k < size; k++) { + if (times[k] < min) { + min = times[k]; + } + } + // Updates the object current time value. + time = min; + // Here it is. + return time; + } + + /** + * It returns the next matching moment as a {@link Date} object. + * + * @return The next matching moment as a {@link Date} object. + */ + public synchronized Date nextMatchingDate() { + return new Date(nextMatchingTime()); + } + + public static void main(String[] args) { + Date currentDate = new Date(); + CronPredictor cron = new CronPredictor(currentDate.getMinutes() + " * * * *", currentDate); + System.out.println(currentDate + "->" + cron.nextMatchingDate()); + + Calendar cal = Calendar.getInstance(); + /*cal.set(2014, 6, 15, 8, 8); + cron = new CronPredictor("* * * 6 *", cal.getTime()); + System.out.println(cal.getTime() + "->" + cron.nextMatchingDate()); + + cal.set(2014, 6, 15, 8, 8); + cron = new CronPredictor("* * * 7 *", cal.getTime()); + System.out.println(cal.getTime() + "->" + cron.nextMatchingDate()); + + cal.set(2014, 6, 15, 8, 8); + cron = new CronPredictor("* * * 8 *", cal.getTime()); + System.out.println(cal.getTime() + "->" + cron.nextMatchingDate());*/ + + cal.set(2014, 6, 15, 8, 8); + cron = new CronPredictor("* 1 * * *", cal.getTime()); + System.out.println(cal.getTime() + "->" + cron.nextMatchingDate()); + + } +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/DayOfMonthValueMatcher.java b/host/src/main/java/org/area515/resinprinter/util/cron/DayOfMonthValueMatcher.java new file mode 100644 index 000000000..e040e0ed4 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/DayOfMonthValueMatcher.java @@ -0,0 +1,65 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +import java.util.ArrayList; + +/** + *

+ * A ValueMatcher whose rules are in a plain array of integer values. When asked + * to validate a value, this ValueMatcher checks if it is in the array and, if + * not, checks whether the last-day-of-month setting applies. + *

+ * + * @author Paul Fernley + */ +class DayOfMonthValueMatcher extends IntArrayValueMatcher { + + private static final int[] lastDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + /** + * Builds the ValueMatcher. + * + * @param integers + * An ArrayList of Integer elements, one for every value accepted + * by the matcher. The match() method will return true only if + * its parameter will be one of this list or the + * last-day-of-month setting applies. + */ + public DayOfMonthValueMatcher(ArrayList integers) { + super(integers); + } + + /** + * Returns true if the given value is included in the matcher list or the + * last-day-of-month setting applies. + */ + public boolean match(int value, int month, boolean isLeapYear) { + return (super.match(value) || (value > 27 && match(32) && isLastDayOfMonth(value, month, isLeapYear))); + } + + public boolean isLastDayOfMonth(int value, int month, boolean isLeapYear) { + if (isLeapYear && month == 2) { + return value == 29; + } else { + return value == lastDays[month - 1]; + } + } + +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/IntArrayValueMatcher.java b/host/src/main/java/org/area515/resinprinter/util/cron/IntArrayValueMatcher.java new file mode 100644 index 000000000..f9201a985 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/IntArrayValueMatcher.java @@ -0,0 +1,70 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +import java.util.ArrayList; + +/** + *

+ * A ValueMatcher whose rules are in a plain array of integer values. When asked + * to validate a value, this ValueMatcher checks if it is in the array. + *

+ * + * @author Carlo Pelliccia + */ +class IntArrayValueMatcher implements ValueMatcher { + + /** + * The accepted values. + */ + private int[] values; + + /** + * Builds the ValueMatcher. + * + * @param integers + * An ArrayList of Integer elements, one for every value accepted + * by the matcher. The match() method will return true only if + * its parameter will be one of this list. + */ + public IntArrayValueMatcher(ArrayList integers) { + int size = integers.size(); + values = new int[size]; + for (int i = 0; i < size; i++) { + try { + values[i] = ((Integer) integers.get(i)).intValue(); + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + } + } + + /** + * Returns true if the given value is included in the matcher list. + */ + public boolean match(int value) { + for (int i = 0; i < values.length; i++) { + if (values[i] == value) { + return true; + } + } + return false; + } + +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/InvalidPatternException.java b/host/src/main/java/org/area515/resinprinter/util/cron/InvalidPatternException.java new file mode 100644 index 000000000..a3ac4e46b --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/InvalidPatternException.java @@ -0,0 +1,46 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +/** + *

+ * This kind of exception is thrown if an invalid scheduling pattern is + * encountered by the scheduler. + *

+ * + * @author Carlo Pelliccia + */ +public class InvalidPatternException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Package-reserved construction. + */ + InvalidPatternException() { + } + + /** + * Package-reserved construction. + */ + InvalidPatternException(String message) { + super(message); + } + +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/SchedulingPattern.java b/host/src/main/java/org/area515/resinprinter/util/cron/SchedulingPattern.java new file mode 100644 index 000000000..015bbcb41 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/SchedulingPattern.java @@ -0,0 +1,737 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** + *

+ * A UNIX crontab-like pattern is a string split in five space separated parts. + * Each part is intented as: + *

+ *
    + *
  1. Minutes sub-pattern. During which minutes of the hour + * should the task been launched? The values range is from 0 to 59.
  2. + *
  3. Hours sub-pattern. During which hours of the day should + * the task been launched? The values range is from 0 to 23.
  4. + *
  5. Days of month sub-pattern. During which days of the + * month should the task been launched? The values range is from 1 to 31. The + * special value L can be used to recognize the last day of month.
  6. + *
  7. Months sub-pattern. During which months of the year + * should the task been launched? The values range is from 1 (January) to 12 + * (December), otherwise this sub-pattern allows the aliases "jan", + * "feb", "mar", "apr", "may", + * "jun", "jul", "aug", "sep", + * "oct", "nov" and "dec".
  8. + *
  9. Days of week sub-pattern. During which days of the week + * should the task been launched? The values range is from 0 (Sunday) to 6 + * (Saturday), otherwise this sub-pattern allows the aliases "sun", + * "mon", "tue", "wed", "thu", + * "fri" and "sat".
  10. + *
+ *

+ * The star wildcard character is also admitted, indicating "every minute + * of the hour", "every hour of the day", "every day of the + * month", "every month of the year" and "every day of the + * week", according to the sub-pattern in which it is used. + *

+ *

+ * Once the scheduler is started, a task will be launched when the five parts in + * its scheduling pattern will be true at the same time. + *

+ *

+ * Some examples: + *

+ *

+ * 5 * * * *
+ * This pattern causes a task to be launched once every hour, at the begin of + * the fifth minute (00:05, 01:05, 02:05 etc.). + *

+ *

+ * * * * * *
+ * This pattern causes a task to be launched every minute. + *

+ *

+ * * 12 * * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour + * of Monday. + *

+ *

+ * * 12 16 * Mon
+ * This pattern causes a task to be launched every minute during the 12th hour + * of Monday, 16th, but only if the day is the 16th of the month. + *

+ *

+ * Every sub-pattern can contain two or more comma separated values. + *

+ *

+ * 59 11 * * 1,2,3,4,5
+ * This pattern causes a task to be launched at 11:59AM on Monday, Tuesday, + * Wednesday, Thursday and Friday. + *

+ *

+ * Values intervals are admitted and defined using the minus character. + *

+ *

+ * 59 11 * * 1-5
+ * This pattern is equivalent to the previous one. + *

+ *

+ * The slash character can be used to identify step values within a range. It + * can be used both in the form */c and a-b/c. The + * subpattern is matched every c values of the range + * 0,maxvalue or a-b. + *

+ *

+ * */5 * * * *
+ * This pattern causes a task to be launched every 5 minutes (0:00, 0:05, 0:10, + * 0:15 and so on). + *

+ *

+ * 3-18/5 * * * *
+ * This pattern causes a task to be launched every 5 minutes starting from the + * third minute of the hour, up to the 18th (0:03, 0:08, 0:13, 0:18, 1:03, 1:08 + * and so on). + *

+ *

+ * */15 9-17 * * *
+ * This pattern causes a task to be launched every 15 minutes between the 9th + * and 17th hour of the day (9:00, 9:15, 9:30, 9:45 and so on... note that the + * last execution will be at 17:45). + *

+ *

+ * All the fresh described syntax rules can be used together. + *

+ *

+ * * 12 10-16/2 * *
+ * This pattern causes a task to be launched every minute during the 12th hour + * of the day, but only if the day is the 10th, the 12th, the 14th or the 16th + * of the month. + *

+ *

+ * * 12 1-15,17,20-25 * *
+ * This pattern causes a task to be launched every minute during the 12th hour + * of the day, but the day of the month must be between the 1st and the 15th, + * the 20th and the 25, or at least it must be the 17th. + *

+ *

+ * Finally cron4j lets you combine more scheduling patterns into one, with the + * pipe character: + *

+ *

+ * 0 5 * * *|8 10 * * *|22 17 * * *
+ * This pattern causes a task to be launched every day at 05:00, 10:08 and + * 17:22. + *

+ * + * @author Carlo Pelliccia + * @since 2.0 + */ +public class SchedulingPattern { + + /** + * The parser for the minute values. + */ + private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser(); + + /** + * The parser for the hour values. + */ + private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser(); + + /** + * The parser for the day of month values. + */ + private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser(); + + /** + * The parser for the month values. + */ + private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser(); + + /** + * The parser for the day of week values. + */ + private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser(); + + /** + * Validates a string as a scheduling pattern. + * + * @param schedulingPattern + * The pattern to validate. + * @return true if the given string represents a valid scheduling pattern; + * false otherwise. + */ + public static boolean validate(String schedulingPattern) { + try { + new SchedulingPattern(schedulingPattern); + } catch (InvalidPatternException e) { + return false; + } + return true; + } + + /** + * The pattern as a string. + */ + private String asString; + + /** + * The ValueMatcher list for the "minute" field. + */ + protected ArrayList minuteMatchers = new ArrayList(); + + /** + * The ValueMatcher list for the "hour" field. + */ + protected ArrayList hourMatchers = new ArrayList(); + + /** + * The ValueMatcher list for the "day of month" field. + */ + protected ArrayList dayOfMonthMatchers = new ArrayList(); + + /** + * The ValueMatcher list for the "month" field. + */ + protected ArrayList monthMatchers = new ArrayList(); + + /** + * The ValueMatcher list for the "day of week" field. + */ + protected ArrayList dayOfWeekMatchers = new ArrayList(); + + /** + * How many matcher groups in this pattern? + */ + protected int matcherSize = 0; + + /** + * Builds a SchedulingPattern parsing it from a string. + * + * @param pattern + * The pattern as a crontab-like string. + * @throws InvalidPatternException + * If the supplied string is not a valid pattern. + */ + public SchedulingPattern(String pattern) throws InvalidPatternException { + this.asString = pattern; + StringTokenizer st1 = new StringTokenizer(pattern, "|"); + if (st1.countTokens() < 1) { + throw new InvalidPatternException("invalid pattern: \"" + pattern + "\""); + } + while (st1.hasMoreTokens()) { + String localPattern = st1.nextToken(); + StringTokenizer st2 = new StringTokenizer(localPattern, " \t"); + if (st2.countTokens() != 5) { + throw new InvalidPatternException("invalid pattern: \"" + localPattern + "\""); + } + try { + minuteMatchers.add(buildValueMatcher(st2.nextToken(), MINUTE_VALUE_PARSER)); + } catch (Exception e) { + throw new InvalidPatternException("invalid pattern \"" + + localPattern + "\". Error parsing minutes field: " + + e.getMessage() + "."); + } + try { + hourMatchers.add(buildValueMatcher(st2.nextToken(), HOUR_VALUE_PARSER)); + } catch (Exception e) { + throw new InvalidPatternException("invalid pattern \"" + + localPattern + "\". Error parsing hours field: " + + e.getMessage() + "."); + } + try { + dayOfMonthMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_MONTH_VALUE_PARSER)); + } catch (Exception e) { + throw new InvalidPatternException("invalid pattern \"" + + localPattern + + "\". Error parsing days of month field: " + + e.getMessage() + "."); + } + try { + monthMatchers.add(buildValueMatcher(st2.nextToken(), MONTH_VALUE_PARSER)); + } catch (Exception e) { + throw new InvalidPatternException("invalid pattern \"" + + localPattern + "\". Error parsing months field: " + + e.getMessage() + "."); + } + try { + dayOfWeekMatchers.add(buildValueMatcher(st2.nextToken(), DAY_OF_WEEK_VALUE_PARSER)); + } catch (Exception e) { + throw new InvalidPatternException("invalid pattern \"" + + localPattern + + "\". Error parsing days of week field: " + + e.getMessage() + "."); + } + matcherSize++; + } + } + + /** + * A ValueMatcher utility builder. + * + * @param str + * The pattern part for the ValueMatcher creation. + * @param parser + * The parser used to parse the values. + * @return The requested ValueMatcher. + * @throws Exception + * If the supplied pattern part is not valid. + */ + private ValueMatcher buildValueMatcher(String str, ValueParser parser) + throws Exception { + if (str.length() == 1 && str.equals("*")) { + return new AlwaysTrueValueMatcher(); + } + ArrayList values = new ArrayList(); + StringTokenizer st = new StringTokenizer(str, ","); + while (st.hasMoreTokens()) { + String element = st.nextToken(); + ArrayList local; + try { + local = parseListElement(element, parser); + } catch (Exception e) { + throw new Exception("invalid field \"" + str + + "\", invalid element \"" + element + "\", " + + e.getMessage()); + } + for (Iterator i = local.iterator(); i.hasNext();) { + Object value = i.next(); + if (!values.contains(value)) { + values.add(value); + } + } + } + if (values.size() == 0) { + throw new Exception("invalid field \"" + str + "\""); + } + if (parser == DAY_OF_MONTH_VALUE_PARSER) { + return new DayOfMonthValueMatcher(values); + } else { + return new IntArrayValueMatcher(values); + } + } + + /** + * Parses an element of a list of values of the pattern. + * + * @param str + * The element string. + * @param parser + * The parser used to parse the values. + * @return A list of integers representing the allowed values. + * @throws Exception + * If the supplied pattern part is not valid. + */ + private ArrayList parseListElement(String str, ValueParser parser) + throws Exception { + StringTokenizer st = new StringTokenizer(str, "/"); + int size = st.countTokens(); + if (size < 1 || size > 2) { + throw new Exception("syntax error"); + } + ArrayList values; + try { + values = parseRange(st.nextToken(), parser); + } catch (Exception e) { + throw new Exception("invalid range, " + e.getMessage()); + } + if (size == 2) { + String dStr = st.nextToken(); + int div; + try { + div = Integer.parseInt(dStr); + } catch (NumberFormatException e) { + throw new Exception("invalid divisor \"" + dStr + "\""); + } + if (div < 1) { + throw new Exception("non positive divisor \"" + div + "\""); + } + ArrayList values2 = new ArrayList(); + for (int i = 0; i < values.size(); i += div) { + values2.add(values.get(i)); + } + return values2; + } else { + return values; + } + } + + /** + * Parses a range of values. + * + * @param str + * The range string. + * @param parser + * The parser used to parse the values. + * @return A list of integers representing the allowed values. + * @throws Exception + * If the supplied pattern part is not valid. + */ + private ArrayList parseRange(String str, ValueParser parser) + throws Exception { + if (str.equals("*")) { + int min = parser.getMinValue(); + int max = parser.getMaxValue(); + ArrayList values = new ArrayList(); + for (int i = min; i <= max; i++) { + values.add(new Integer(i)); + } + return values; + } + StringTokenizer st = new StringTokenizer(str, "-"); + int size = st.countTokens(); + if (size < 1 || size > 2) { + throw new Exception("syntax error"); + } + String v1Str = st.nextToken(); + int v1; + try { + v1 = parser.parse(v1Str); + } catch (Exception e) { + throw new Exception("invalid value \"" + v1Str + "\", " + + e.getMessage()); + } + if (size == 1) { + ArrayList values = new ArrayList(); + values.add(new Integer(v1)); + return values; + } else { + String v2Str = st.nextToken(); + int v2; + try { + v2 = parser.parse(v2Str); + } catch (Exception e) { + throw new Exception("invalid value \"" + v2Str + "\", " + + e.getMessage()); + } + ArrayList values = new ArrayList(); + if (v1 < v2) { + for (int i = v1; i <= v2; i++) { + values.add(new Integer(i)); + } + } else if (v1 > v2) { + int min = parser.getMinValue(); + int max = parser.getMaxValue(); + for (int i = v1; i <= max; i++) { + values.add(new Integer(i)); + } + for (int i = min; i <= v2; i++) { + values.add(new Integer(i)); + } + } else { + // v1 == v2 + values.add(new Integer(v1)); + } + return values; + } + } + + /** + * This methods returns true if the given timestamp (expressed as a UNIX-era + * millis value) matches the pattern, according to the given time zone. + * + * @param timezone + * A time zone. + * @param millis + * The timestamp, as a UNIX-era millis value. + * @return true if the given timestamp matches the pattern. + */ + public boolean match(TimeZone timezone, long millis) { + GregorianCalendar gc = new GregorianCalendar(); + gc.setTimeInMillis(millis); + gc.setTimeZone(timezone); + int minute = gc.get(Calendar.MINUTE); + int hour = gc.get(Calendar.HOUR_OF_DAY); + int dayOfMonth = gc.get(Calendar.DAY_OF_MONTH); + int month = gc.get(Calendar.MONTH) + 1; + int dayOfWeek = gc.get(Calendar.DAY_OF_WEEK) - 1; + int year = gc.get(Calendar.YEAR); + for (int i = 0; i < matcherSize; i++) { + ValueMatcher minuteMatcher = (ValueMatcher) minuteMatchers.get(i); + ValueMatcher hourMatcher = (ValueMatcher) hourMatchers.get(i); + ValueMatcher dayOfMonthMatcher = (ValueMatcher) dayOfMonthMatchers.get(i); + ValueMatcher monthMatcher = (ValueMatcher) monthMatchers.get(i); + ValueMatcher dayOfWeekMatcher = (ValueMatcher) dayOfWeekMatchers.get(i); + boolean eval = minuteMatcher.match(minute) + && hourMatcher.match(hour) + && ((dayOfMonthMatcher instanceof DayOfMonthValueMatcher) ? ((DayOfMonthValueMatcher) dayOfMonthMatcher) + .match(dayOfMonth, month, gc.isLeapYear(year)) + : dayOfMonthMatcher.match(dayOfMonth)) + && monthMatcher.match(month) + && dayOfWeekMatcher.match(dayOfWeek); + if (eval) { + return true; + } + } + return false; + } + + /** + * This methods returns true if the given timestamp (expressed as a UNIX-era + * millis value) matches the pattern, according to the system default time + * zone. + * + * @param millis + * The timestamp, as a UNIX-era millis value. + * @return true if the given timestamp matches the pattern. + */ + public boolean match(long millis) { + return match(TimeZone.getDefault(), millis); + } + + /** + * Returns the pattern as a string. + * + * @return The pattern as a string. + */ + public String toString() { + return asString; + } + + /** + * This utility method changes an alias to an int value. + * + * @param value + * The value. + * @param aliases + * The aliases list. + * @param offset + * The offset appplied to the aliases list indices. + * @return The parsed value. + * @throws Exception + * If the expressed values doesn't match any alias. + */ + private static int parseAlias(String value, String[] aliases, int offset) + throws Exception { + for (int i = 0; i < aliases.length; i++) { + if (aliases[i].equalsIgnoreCase(value)) { + return offset + i; + } + } + throw new Exception("invalid alias \"" + value + "\""); + } + + /** + * Definition for a value parser. + */ + private static interface ValueParser { + + /** + * Attempts to parse a value. + * + * @param value + * The value. + * @return The parsed value. + * @throws Exception + * If the value can't be parsed. + */ + public int parse(String value) throws Exception; + + /** + * Returns the minimum value accepred by the parser. + * + * @return The minimum value accepred by the parser. + */ + public int getMinValue(); + + /** + * Returns the maximum value accepred by the parser. + * + * @return The maximum value accepred by the parser. + */ + public int getMaxValue(); + + } + + /** + * A simple value parser. + */ + private static class SimpleValueParser implements ValueParser { + + /** + * The minimum allowed value. + */ + protected int minValue; + + /** + * The maximum allowed value. + */ + protected int maxValue; + + /** + * Builds the value parser. + * + * @param minValue + * The minimum allowed value. + * @param maxValue + * The maximum allowed value. + */ + public SimpleValueParser(int minValue, int maxValue) { + this.minValue = minValue; + this.maxValue = maxValue; + } + + public int parse(String value) throws Exception { + int i; + try { + i = Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new Exception("invalid integer value"); + } + if (i < minValue || i > maxValue) { + throw new Exception("value out of range"); + } + return i; + } + + public int getMinValue() { + return minValue; + } + + public int getMaxValue() { + return maxValue; + } + + } + + /** + * The minutes value parser. + */ + private static class MinuteValueParser extends SimpleValueParser { + + /** + * Builds the value parser. + */ + public MinuteValueParser() { + super(0, 59); + } + + } + + /** + * The hours value parser. + */ + private static class HourValueParser extends SimpleValueParser { + + /** + * Builds the value parser. + */ + public HourValueParser() { + super(0, 23); + } + + } + + /** + * The days of month value parser. + */ + private static class DayOfMonthValueParser extends SimpleValueParser { + + /** + * Builds the value parser. + */ + public DayOfMonthValueParser() { + super(1, 31); + } + + /** + * Added to support last-day-of-month. + * + * @param value + * The value to be parsed + * @return the integer day of the month or 32 for last day of the month + * @throws Exception + * if the input value is invalid + */ + public int parse(String value) throws Exception { + if (value.equalsIgnoreCase("L")) { + return 32; + } else { + return super.parse(value); + } + } + + } + + /** + * The value parser for the months field. + */ + private static class MonthValueParser extends SimpleValueParser { + + /** + * Months aliases. + */ + private static String[] ALIASES = { "jan", "feb", "mar", "apr", "may", + "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + + /** + * Builds the months value parser. + */ + public MonthValueParser() { + super(1, 12); + } + + public int parse(String value) throws Exception { + try { + // try as a simple value + return super.parse(value); + } catch (Exception e) { + // try as an alias + return parseAlias(value, ALIASES, 1); + } + } + + } + + /** + * The value parser for the months field. + */ + private static class DayOfWeekValueParser extends SimpleValueParser { + + /** + * Days of week aliases. + */ + private static String[] ALIASES = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; + + /** + * Builds the months value parser. + */ + public DayOfWeekValueParser() { + super(0, 7); + } + + public int parse(String value) throws Exception { + try { + // try as a simple value + return super.parse(value) % 7; + } catch (Exception e) { + // try as an alias + return parseAlias(value, ALIASES, 0); + } + } + + } + +} diff --git a/host/src/main/java/org/area515/resinprinter/util/cron/ValueMatcher.java b/host/src/main/java/org/area515/resinprinter/util/cron/ValueMatcher.java new file mode 100644 index 000000000..ee6f7fc64 --- /dev/null +++ b/host/src/main/java/org/area515/resinprinter/util/cron/ValueMatcher.java @@ -0,0 +1,41 @@ +/* + * cron4j - A pure Java cron-like scheduler + * + * Copyright (C) 2007-2010 Carlo Pelliccia (www.sauronsoftware.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version + * 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License 2.1 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License version 2.1 along with this program. + * If not, see . + */ +package org.area515.resinprinter.util.cron; + +/** + *

+ * This interface describes the ValueMatcher behavior. A ValueMatcher is an + * object that validate an integer value against a set of rules. + *

+ * + * @author Carlo Pelliccia + */ +interface ValueMatcher { + + /** + * Validate the given integer value against a set of rules. + * + * @param value + * The value. + * @return true if the given value matches the rules of the ValueMatcher, + * false otherwise. + */ + public boolean match(int value); + +} diff --git a/host/src/main/java/org/area515/util/IOUtilities.java b/host/src/main/java/org/area515/util/IOUtilities.java index 79d8ee56d..fac6ebf84 100644 --- a/host/src/main/java/org/area515/util/IOUtilities.java +++ b/host/src/main/java/org/area515/util/IOUtilities.java @@ -37,6 +37,7 @@ public static enum SearchStyle { public static class ParseState { public int parseLocation; public String currentLine; + public boolean timeout; } public static class ParseAction { @@ -247,6 +248,7 @@ public static ParseState readLine(Printer printer, SerialCommunicationsPort seri ParseState state = new ParseState(); state.currentLine = null; state.parseLocation = parseLocation; + state.timeout = true; return state; } diff --git a/host/src/main/java/org/area515/util/Log4jTimer.java b/host/src/main/java/org/area515/util/Log4jTimer.java index 0e7dc8fba..ba5530d8d 100644 --- a/host/src/main/java/org/area515/util/Log4jTimer.java +++ b/host/src/main/java/org/area515/util/Log4jTimer.java @@ -1,20 +1,48 @@ package org.area515.util; +import java.util.HashMap; +import java.util.Map; + import org.apache.logging.log4j.ThreadContext; public class Log4jTimer { + public static Map GLOBAL = new HashMap<>(); + public static long startTimer(String timerName) { long newTime = System.currentTimeMillis(); ThreadContext.put(timerName, newTime + ""); return newTime; } + public static long startGlobalTimer(String timerName) { + long newTime = System.currentTimeMillis(); + GLOBAL.put(timerName, newTime + ""); + return newTime; + } + public static long splitTimer(String timerName) { long newTime = System.currentTimeMillis(); - long timeTaken = newTime - Long.parseLong(ThreadContext.get(timerName)); + String value = ThreadContext.get(timerName); + if (value == null) { + return startTimer(timerName); + } + + long timeTaken = newTime - Long.parseLong(value); ThreadContext.put(timerName, newTime + ""); return timeTaken; } + + public static long splitGlobalTimer(String timerName) { + long newTime = System.currentTimeMillis(); + String value = GLOBAL.get(timerName); + if (value == null) { + return startGlobalTimer(timerName); + } + + long timeTaken = newTime - Long.parseLong(value); + GLOBAL.put(timerName, newTime + ""); + return timeTaken; + } public static long completeTimer(String timerName) { long newTime = System.currentTimeMillis(); @@ -27,4 +55,16 @@ public static long completeTimer(String timerName) { ThreadContext.remove(timerName); return timeTaken; } + + public static long completeGlobalTimer(String timerName) { + long newTime = System.currentTimeMillis(); + String value = GLOBAL.get(timerName); + if (value == null) { + return -1; + } + + long timeTaken = newTime - Long.parseLong(value); + GLOBAL.remove(timerName); + return timeTaken; + } } diff --git a/host/src/main/java/org/area515/util/TemplateEngine.java b/host/src/main/java/org/area515/util/TemplateEngine.java index 07007ea2e..57957e2fa 100644 --- a/host/src/main/java/org/area515/util/TemplateEngine.java +++ b/host/src/main/java/org/area515/util/TemplateEngine.java @@ -18,11 +18,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.area515.resinprinter.job.AbstractPrintFileProcessor.DataAid; -import org.area515.resinprinter.job.JobManagerException; import org.area515.resinprinter.job.PrintJob; import org.area515.resinprinter.printer.Printer; -import org.area515.resinprinter.printer.SlicingProfile.TwoDimensionalSettings; import org.area515.resinprinter.server.HostProperties; import freemarker.cache.StringTemplateLoader; diff --git a/host/src/main/resources/PhotonicSplash.png b/host/src/main/resources/PhotonicSplash.png new file mode 100644 index 000000000..0754eee46 Binary files /dev/null and b/host/src/main/resources/PhotonicSplash.png differ diff --git a/host/src/test/java/org/area515/resinprinter/display/DispManXDirectMemory.java b/host/src/test/java/org/area515/resinprinter/display/DispManXDirectMemory.java new file mode 100644 index 000000000..f62003ea1 --- /dev/null +++ b/host/src/test/java/org/area515/resinprinter/display/DispManXDirectMemory.java @@ -0,0 +1,108 @@ +package org.area515.resinprinter.display; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.io.IOException; + +import org.area515.resinprinter.display.dispmanx.ALPHA; +import org.area515.resinprinter.display.dispmanx.DispManX; +import org.area515.resinprinter.display.dispmanx.NativeMemoryBackedBufferedImage; +import org.area515.resinprinter.display.dispmanx.PROTECTION; +import org.area515.resinprinter.display.dispmanx.VC_DISPMANX_ALPHA_T; +import org.area515.resinprinter.display.dispmanx.VC_IMAGE_TRANSFORM_T; +import org.area515.resinprinter.display.dispmanx.VC_IMAGE_TYPE_T; +import org.area515.resinprinter.display.dispmanx.VC_RECT_T; + +import com.sun.jna.ptr.IntByReference; + +public class DispManXDirectMemory { + public static void res( String str, int val ) { + if ( val != 0 ) { + System.out.printf( "%s: %08x\n", str, val ); + } else { + System.out.println(str + " is 0"); + } + } + + //sudo vi DispManXDirectMemory.java + //sudo javac -cp lib/*:. DispManXDirectMemory.java + //sudo java -cp lib/*:. DispManXDirectMemory 2 10 + public static void main(String[] args) throws IOException, InterruptedException { + DispManX dispMan = DispManX.INSTANCE; + System.out.println("BCM Initialized:" + dispMan.bcm_host_init()); + + int screen = Integer.parseInt( args[0] ); + int time = Integer.parseInt( args[1] ); + //IntByReference pitch = new IntByReference(); + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + res( "get display size", dispMan.graphics_get_display_size( screen, width, height ) ); + System.out.printf( "display %d: %d x %d\n", screen, width.getValue(), height.getValue() ); + int display = dispMan.vc_dispmanx_display_open( screen ); + + NativeMemoryBackedBufferedImage data = NativeMemoryBackedBufferedImage.newInstance(width.getValue(), height.getValue()); + int pitch = data.getPitch(); + Graphics2D g = (Graphics2D)data.createGraphics(); + g.drawOval(0, 0, width.getValue(), height.getValue()); + g.setColor(Color.green); + g.setFont(g.getFont().deriveFont((float)30)); + System.out.println(g.getFont()); + g.drawString("Hello this is a test", width.getValue() / 2, 50); + System.out.printf( "bitmap: %d x %d pitch->%d\n", width.getValue(), height.getValue(), pitch); + + VC_RECT_T.ByReference copyRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference sourceRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference destinationRect = new VC_RECT_T.ByReference(); + + res( "rect set", dispMan.vc_dispmanx_rect_set( copyRect, 0, 0, width.getValue(), height.getValue() ) ); + //This seems to be some form of a zoom factor + res( "rect set", dispMan.vc_dispmanx_rect_set( sourceRect, 0, 0, width.getValue()<<16, height.getValue()<<16 ) ); + res( "rect set", dispMan.vc_dispmanx_rect_set( destinationRect, 0, 0, width.getValue(), height.getValue() ) ); + + IntByReference ref = new IntByReference(); + int resourceHandle = dispMan.vc_dispmanx_resource_create( + VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), + width.getValue(), + height.getValue(), + ref ); + res( "resource write data", dispMan.vc_dispmanx_resource_write_data( + resourceHandle, + VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), + pitch, + data.getMemory(), + destinationRect ) + ); + + System.out.println("copyRect:" + copyRect.width + ", " + copyRect.height); + System.out.println("sourceRect:" + sourceRect.width + ", " + sourceRect.height); + System.out.println("destinationRect:" + destinationRect.width + ", " + destinationRect.height); + + int update = dispMan.vc_dispmanx_update_start( 0 ); + VC_DISPMANX_ALPHA_T.ByReference alpha = new VC_DISPMANX_ALPHA_T.ByReference(); + alpha.flags = ALPHA.DISPMANX_FLAGS_ALPHA_FROM_SOURCE.getFlag() | ALPHA.DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS.getFlag(); + alpha.opacity = 255; + + + int element = dispMan.vc_dispmanx_element_add( + update, + display, + 2010, + destinationRect, + resourceHandle, + sourceRect, + PROTECTION.DISPMANX_PROTECTION_NONE.getcConst(), + alpha, + 0, + VC_IMAGE_TRANSFORM_T.VC_IMAGE_ROT0.getcConst() ); + + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + + Thread.sleep( time * 1000 ); + update = dispMan.vc_dispmanx_update_start( 0 ); + + res( "element remove", dispMan.vc_dispmanx_element_remove( update, element ) ); + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + res( "resource delete", dispMan.vc_dispmanx_resource_delete( resourceHandle ) ); + res( "display close", dispMan.vc_dispmanx_display_close( display ) ); + } +} diff --git a/host/src/test/java/org/area515/resinprinter/display/DispManXLoadImage.java b/host/src/test/java/org/area515/resinprinter/display/DispManXLoadImage.java new file mode 100644 index 000000000..48f91b75a --- /dev/null +++ b/host/src/test/java/org/area515/resinprinter/display/DispManXLoadImage.java @@ -0,0 +1,192 @@ +package org.area515.resinprinter.display; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.area515.resinprinter.display.dispmanx.ALPHA; +import org.area515.resinprinter.display.dispmanx.DispManX; +import org.area515.resinprinter.display.dispmanx.PROTECTION; +import org.area515.resinprinter.display.dispmanx.SCREEN; +import org.area515.resinprinter.display.dispmanx.VC_DISPMANX_ALPHA_T; +import org.area515.resinprinter.display.dispmanx.VC_IMAGE_TRANSFORM_T; +import org.area515.resinprinter.display.dispmanx.VC_IMAGE_TYPE_T; +import org.area515.resinprinter.display.dispmanx.VC_RECT_T; + +import com.sun.jna.Memory; +import com.sun.jna.ptr.IntByReference; + +public class DispManXLoadImage { + public static void displayInfo( int id, String name ) { + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + int res = DispManX.INSTANCE.graphics_get_display_size( id, width, height ); + if ( res >= 0 ) { + System.out.printf( "\t%d %s: %dx%d\n", id, name, width.getValue(), height.getValue() ); + } else { + System.out.println("graphics_get_display_size returned:" + res); + } + } + + public static void usage() { + System.out.printf( "usage: org.area515.resinprinter.display.DispManXTest \n"); + displayInfo( SCREEN.DISPMANX_ID_MAIN_LCD.getId(), "Main LCD" ); + displayInfo( SCREEN.DISPMANX_ID_AUX_LCD.getId(), "AUX LCD" ); + displayInfo( SCREEN.DISPMANX_ID_HDMI.getId(), "HDMI" ); + displayInfo( SCREEN.DISPMANX_ID_SDTV.getId(), "SDTV" ); + displayInfo( SCREEN.DISPMANX_ID_FORCE_LCD.getId(), "Force LCD" ); + displayInfo( SCREEN.DISPMANX_ID_FORCE_TV.getId(), "Force TV" ); + displayInfo( SCREEN.DISPMANX_ID_FORCE_OTHER.getId(), "Force other" ); + System.exit( 1 ); + } + + public static void info( int screen ) { + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + int res = DispManX.INSTANCE.graphics_get_display_size( screen, width, height ); + if (res >= 0) { + System.out.printf( "%d, %d\n", width, height ); + } else { + System.out.println("graphics_get_display_size returned:" + res); + } + System.exit( 1 ); + } + + public static void res( String str, int val ) { + if ( val != 0 ) { + System.out.printf( "%s: %08x\n", str, val ); + } else { + System.out.println(str + " is 0"); + } + } + + public static int getPitch( int x, int y ) { + return ((x + (y)-1) & ~((y)-1));//y*((x + y-1)/y); + } + + public static Memory loadBitmapRGB565(String fileName, IntByReference width, IntByReference height, IntByReference pitchByRef) throws IOException { + int bytesPerPixel = 2; + BufferedImage image = ImageIO.read(new File(fileName)); + int pitch = getPitch( bytesPerPixel * image.getWidth(), 32 ); + pitchByRef.setValue(pitch); + Memory destPixels = new Memory(pitch * image.getHeight()); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + int rgb = image.getRGB(x, y); + destPixels.setShort((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, (short)(((rgb & 0xf80000) >>> 8) | ((rgb & 0xfc00) >>> 5) | (rgb & 0xf8 >>> 3))); + } + } + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + public static Memory loadBitmapARGB8888(String fileName, IntByReference width, IntByReference height, IntByReference pitchByRef) throws IOException { + int bytesPerPixel = 4; + BufferedImage image = ImageIO.read(new File(fileName)); + int pitch = getPitch( bytesPerPixel * image.getWidth(), 32 ); + pitchByRef.setValue(pitch); + long start = System.currentTimeMillis(); + Memory destPixels = new Memory(pitch * image.getHeight()); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + destPixels.setInt((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, image.getRGB(x, y)); + } + } + System.out.println(System.currentTimeMillis() - start); + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + //sudo vi DispManXLoadImage.java + //sudo javac -cp lib/*:. DispManXLoadImage.java + //sudo java -cp lib/*:. DispManXLoadImage 2 10 resourcesnew/favicon/apple-icon-144x144.png + public static void main(String[] args) throws IOException, InterruptedException { + DispManX dispMan = DispManX.INSTANCE; + System.out.println("BCM Initialized:" + dispMan.bcm_host_init()); + if ( args.length < 3 ) { + usage(); + return; + } + + int screen = Integer.parseInt( args[0] ); + /*if (screen == 2 || screen == 3 || screen == 5) { + IntByReference vchiHandle = new IntByReference(); + PointerByReference connections = new PointerByReference(); + + dispMan.vcos_init(); + res("vchi_initialise", dispMan.vchi_initialise(vchiHandle)); + res("vchi_connect", dispMan.vchi_connect(null, 0, vchiHandle.getValue())); + res("vc_vchi_tv_init", dispMan.vc_vchi_tv_init(vchiHandle, connections.getPointer(), 1)); + res("vc_tv_hdmi_power_on_preferred", dispMan.vc_tv_hdmi_power_on_preferred()); + }*/ + + int time = Integer.parseInt( args[1] ); + IntByReference pitch = new IntByReference(); + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + res( "get display size", dispMan.graphics_get_display_size( screen, width, height ) ); + System.out.printf( "display %d: %d x %d\n", screen, width.getValue(), height.getValue() ); + int display = dispMan.vc_dispmanx_display_open( screen ); + Memory bitmap = loadBitmapRGB565( args[2], width, height, pitch ); + System.out.printf( "bitmap: %d x %d pitch->%d\n", width.getValue(), height.getValue(), pitch.getValue()); + + VC_RECT_T.ByReference copyRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference sourceRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference destinationRect = new VC_RECT_T.ByReference(); + + res( "rect set", dispMan.vc_dispmanx_rect_set( copyRect, 0, 0, width.getValue(), height.getValue() ) ); + //This seems to be some form of a zoom factor + res( "rect set", dispMan.vc_dispmanx_rect_set( sourceRect, 0, 0, width.getValue()<<16, height.getValue()<<16 ) ); + res( "rect set", dispMan.vc_dispmanx_rect_set( destinationRect, 0, 0, width.getValue(), height.getValue() ) ); + + IntByReference ref = new IntByReference(); + int resourceHandle = dispMan.vc_dispmanx_resource_create( + VC_IMAGE_TYPE_T.VC_IMAGE_RGB565.getcIndex(), + width.getValue(), + height.getValue(), + ref ); + res( "resource write data", dispMan.vc_dispmanx_resource_write_data( + resourceHandle, + VC_IMAGE_TYPE_T.VC_IMAGE_RGB565.getcIndex(), + pitch.getValue() , + bitmap, + destinationRect ) + ); + + System.out.println("copyRect:" + copyRect.width + ", " + copyRect.height); + System.out.println("sourceRect:" + sourceRect.width + ", " + sourceRect.height); + System.out.println("destinationRect:" + destinationRect.width + ", " + destinationRect.height); + + int update = dispMan.vc_dispmanx_update_start( 0 ); + VC_DISPMANX_ALPHA_T.ByReference alpha = new VC_DISPMANX_ALPHA_T.ByReference(); + alpha.flags = ALPHA.DISPMANX_FLAGS_ALPHA_FROM_SOURCE.getFlag() | ALPHA.DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS.getFlag(); + alpha.opacity = 255; + + + int element = dispMan.vc_dispmanx_element_add( + update, + display, + 2010, + destinationRect, + resourceHandle, + sourceRect, + PROTECTION.DISPMANX_PROTECTION_NONE.getcConst(), + alpha, + 0, + VC_IMAGE_TRANSFORM_T.VC_IMAGE_ROT0.getcConst() ); + + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + + Thread.sleep( time * 1000 ); + update = dispMan.vc_dispmanx_update_start( 0 ); + + res( "element remove", dispMan.vc_dispmanx_element_remove( update, element ) ); + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + res( "resource delete", dispMan.vc_dispmanx_resource_delete( resourceHandle ) ); + res( "display close", dispMan.vc_dispmanx_display_close( display ) ); + } +} diff --git a/host/src/test/java/org/area515/resinprinter/display/DispManXTestSelfContained.java b/host/src/test/java/org/area515/resinprinter/display/DispManXTestSelfContained.java new file mode 100644 index 000000000..13eab8614 --- /dev/null +++ b/host/src/test/java/org/area515/resinprinter/display/DispManXTestSelfContained.java @@ -0,0 +1,372 @@ +package org.area515.resinprinter.display; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import javax.imageio.ImageIO; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; + +public class DispManXTestSelfContained { + public static class VC_DISPMANX_ALPHA_T extends Structure { + public static class ByReference extends VC_DISPMANX_ALPHA_T implements Structure.ByReference {} + + public int flags; + public int opacity; + public int mask; + + @Override + protected List getFieldOrder() { + return Arrays.asList("flags", "opacity", "mask"); + } + } + + public static class VC_RECT_T extends Structure { + public static class ByReference extends VC_RECT_T implements Structure.ByReference {} + public int x; + public int y; + public int width; + public int height; + + @Override + protected List getFieldOrder() { + return Arrays.asList("x", "y", "width", "height"); + } + } + + public enum ALPHA { + DISPMANX_FLAGS_ALPHA_FROM_SOURCE(0), + DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS(1), + DISPMANX_FLAGS_ALPHA_FIXED_NON_ZERO(2), + DISPMANX_FLAGS_ALPHA_FIXED_EXCEED_0X07(3), + DISPMANX_FLAGS_ALPHA_PREMULT(1 << 16), + DISPMANX_FLAGS_ALPHA_MIX(1 << 17); + private int flag = 0; + + ALPHA(int flag) { + this.flag = flag; + } + + public int getFlag() { + return flag; + } + } + + public enum TRANSFORM { + TRANSFORM_HFLIP(1<<0), + TRANSFORM_VFLIP(1<<1), + TRANSFORM_TRANSPOSE (1<<2); + private int cConst; + + TRANSFORM(int cConst) { + this.cConst = cConst; + } + + public int getcConst() { + return cConst; + } + } + + public enum VC_IMAGE_TRANSFORM_T { + VC_IMAGE_ROT0(0), + VC_IMAGE_MIRROR_ROT0(TRANSFORM.TRANSFORM_HFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT180(TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_ROT180(TRANSFORM.TRANSFORM_HFLIP.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT90(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()), + VC_IMAGE_ROT270(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_HFLIP.getcConst()), + VC_IMAGE_ROT90(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()), + VC_IMAGE_MIRROR_ROT270(TRANSFORM.TRANSFORM_TRANSPOSE.getcConst()|TRANSFORM.TRANSFORM_HFLIP.getcConst()|TRANSFORM.TRANSFORM_VFLIP.getcConst()); + private int cConst; + + VC_IMAGE_TRANSFORM_T(int cConst) { + this.cConst = cConst; + } + + public int getcConst() { + return cConst; + } + } + + public enum VC_IMAGE_TYPE_T { + VC_IMAGE_MIN(0), //bounds for error checking + + VC_IMAGE_RGB565(1), + VC_IMAGE_1BPP(2), + VC_IMAGE_YUV420(3), + VC_IMAGE_48BPP(4), + VC_IMAGE_RGB888(5), + VC_IMAGE_8BPP(6), + VC_IMAGE_4BPP(7), // 4bpp palettised image + VC_IMAGE_3D32(8), /* A separated format of 16 colour/light shorts followed by 16 z values */ + VC_IMAGE_3D32B(9), /* 16 colours followed by 16 z values */ + VC_IMAGE_3D32MAT(10), /* A separated format of 16 material/colour/light shorts followed by 16 z values */ + VC_IMAGE_RGB2X9(11), /* 32 bit format containing 18 bits of 6.6.6 RGB, 9 bits per short */ + VC_IMAGE_RGB666(12), /* 32-bit format holding 18 bits of 6.6.6 RGB */ + VC_IMAGE_PAL4_OBSOLETE(13), // 4bpp palettised image with embedded palette + VC_IMAGE_PAL8_OBSOLETE(14), // 8bpp palettised image with embedded palette + VC_IMAGE_RGBA32(15), /* RGB888 with an alpha byte after each pixel */ /* xxx: isn't it BEFORE each pixel? */ + VC_IMAGE_YUV422(16), /* a line of Y (32-byte padded), a line of U (16-byte padded), and a line of V (16-byte padded) */ + VC_IMAGE_RGBA565(17), /* RGB565 with a transparent patch */ + VC_IMAGE_RGBA16(18), /* Compressed (4444) version of RGBA32 */ + VC_IMAGE_YUV_UV(19), /* VCIII codec format */ + VC_IMAGE_TF_RGBA32(20), /* VCIII T-format RGBA8888 */ + VC_IMAGE_TF_RGBX32(21), /* VCIII T-format RGBx8888 */ + VC_IMAGE_TF_FLOAT(22), /* VCIII T-format float */ + VC_IMAGE_TF_RGBA16(23), /* VCIII T-format RGBA4444 */ + VC_IMAGE_TF_RGBA5551(24), /* VCIII T-format RGB5551 */ + VC_IMAGE_TF_RGB565(25), /* VCIII T-format RGB565 */ + VC_IMAGE_TF_YA88(26), /* VCIII T-format 8-bit luma and 8-bit alpha */ + VC_IMAGE_TF_BYTE(27), /* VCIII T-format 8 bit generic sample */ + VC_IMAGE_TF_PAL8(28), /* VCIII T-format 8-bit palette */ + VC_IMAGE_TF_PAL4(29), /* VCIII T-format 4-bit palette */ + VC_IMAGE_TF_ETC1(30), /* VCIII T-format Ericsson Texture Compressed */ + VC_IMAGE_BGR888(31), /* RGB888 with R & B swapped */ + VC_IMAGE_BGR888_NP(32), /* RGB888 with R & B swapped, but with no pitch, i.e. no padding after each row of pixels */ + VC_IMAGE_BAYER(33), /* Bayer image, extra defines which variant is being used */ + VC_IMAGE_CODEC(34), /* General wrapper for codec images e.g. JPEG from camera */ + VC_IMAGE_YUV_UV32(35), /* VCIII codec format */ + VC_IMAGE_TF_Y8(36), /* VCIII T-format 8-bit luma */ + VC_IMAGE_TF_A8(37), /* VCIII T-format 8-bit alpha */ + VC_IMAGE_TF_SHORT(38),/* VCIII T-format 16-bit generic sample */ + VC_IMAGE_TF_1BPP(39), /* VCIII T-format 1bpp black/white */ + VC_IMAGE_OPENGL(40), + VC_IMAGE_YUV444I(41), /* VCIII-B0 HVS YUV 4:4:4 interleaved samples */ + VC_IMAGE_YUV422PLANAR(42), /* Y, U, & V planes separately (VC_IMAGE_YUV422 has them interleaved on a per line basis) */ + VC_IMAGE_ARGB8888(43), /* 32bpp with 8bit alpha at MS byte, with R, G, B (LS byte) */ + VC_IMAGE_XRGB8888(44), /* 32bpp with 8bit unused at MS byte, with R, G, B (LS byte) */ + + VC_IMAGE_YUV422YUYV(45), /* interleaved 8 bit samples of Y, U, Y, V */ + VC_IMAGE_YUV422YVYU(46), /* interleaved 8 bit samples of Y, V, Y, U */ + VC_IMAGE_YUV422UYVY(47), /* interleaved 8 bit samples of U, Y, V, Y */ + VC_IMAGE_YUV422VYUY(48), /* interleaved 8 bit samples of V, Y, U, Y */ + + VC_IMAGE_RGBX32(49), /* 32bpp like RGBA32 but with unused alpha */ + VC_IMAGE_RGBX8888(50), /* 32bpp, corresponding to RGBA with unused alpha */ + VC_IMAGE_BGRX8888(51), /* 32bpp, corresponding to BGRA with unused alpha */ + + VC_IMAGE_YUV420SP(52), /* Y as a plane, then UV byte interleaved in plane with with same pitch, half height */ + + VC_IMAGE_YUV444PLANAR(53), /* Y, U, & V planes separately 4:4:4 */ + + VC_IMAGE_TF_U8(54), /* T-format 8-bit U - same as TF_Y8 buf from U plane */ + VC_IMAGE_TF_V8(55), /* T-format 8-bit U - same as TF_Y8 buf from V plane */ + + VC_IMAGE_MAX(56), //bounds for error checking + VC_IMAGE_FORCE_ENUM_16BIT(0xffff); + + private int cIndex; + + VC_IMAGE_TYPE_T(int cIndex) { + this.cIndex = cIndex; + } + + public int getcIndex() { + return cIndex; + } + } + public interface DispManX extends Library { + public DispManX INSTANCE = (DispManX)Native.loadLibrary("bcm_host", DispManX.class); + + public static final int DISPMANX_ID_MAIN_LCD = 0; + public static final int DISPMANX_ID_AUX_LCD = 1; + public static final int DISPMANX_ID_HDMI = 2; + public static final int DISPMANX_ID_SDTV = 3; + public static final int DISPMANX_ID_FORCE_LCD = 4; + public static final int DISPMANX_ID_FORCE_TV = 5; + public static final int DISPMANX_ID_FORCE_OTHER = 6; + + public static final int DISPMANX_PROTECTION_MAX = 0x0f; + public static final int DISPMANX_PROTECTION_NONE = 0; + public static final int DISPMANX_PROTECTION_HDCP = 11; + + public int bcm_host_init(); + public int graphics_get_display_size(int screenIndex, IntByReference width, IntByReference height); + public int vc_dispmanx_display_open(int screenIndex); + public int vc_dispmanx_resource_create(int resourceType, int width, int height, IntByReference imagePointer); + public int vc_dispmanx_rect_set(VC_RECT_T.ByReference rectangleToCreate, int offsetX, int offsetY, int width, int height); + public int vc_dispmanx_update_start(int priority);//Constant of 10 + public int vc_dispmanx_element_add( + int updateHandle, + int displayHandle, + int layer, // 2000 + VC_RECT_T.ByReference destinationRectangle, + int sourceResourceHandle, + VC_RECT_T.ByReference sourceRectangle, + int protectionMode, // DISPMANX_PROTECTION_NONE + VC_DISPMANX_ALPHA_T.ByReference alpha, // { DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0 }; + int clamp, // 0 + int imageTransformation); // VC_IMAGE_ROT0 + public int vc_dispmanx_update_submit_sync(int updateHandle); + public int vc_dispmanx_resource_write_data( + int resourceHandle, + int resourceType, // VC_IMAGE_RGB565 + int pitch, + Pointer imageConvertedFromBufferedImage, + VC_RECT_T.ByReference copyRectangle); + public int vc_dispmanx_element_remove(int updateHandle, int elementHandle); + public int vc_dispmanx_resource_delete(int resourceHandle); + public int vc_dispmanx_display_close(int displayHandle); + } + + public static void displayInfo( int id, String name ) { + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + int res = DispManX.INSTANCE.graphics_get_display_size( id, width, height ); + if ( res >= 0 ) { + System.out.printf( "\t%d %s: %dx%d\n", id, name, width.getValue(), height.getValue() ); + } else { + System.out.println("graphics_get_display_size returned:" + res); + } + } + + public static void usage() { + System.out.printf( "usage: org.area515.resinprinter.display.DispManXTest \n"); + displayInfo( DispManX.DISPMANX_ID_MAIN_LCD, "Main LCD" ); + displayInfo( DispManX.DISPMANX_ID_AUX_LCD, "AUX LCD" ); + displayInfo( DispManX.DISPMANX_ID_HDMI, "HDMI" ); + displayInfo( DispManX.DISPMANX_ID_SDTV, "SDTV" ); + displayInfo( DispManX.DISPMANX_ID_FORCE_LCD, "Force LCD" ); + displayInfo( DispManX.DISPMANX_ID_FORCE_TV, "Force TV" ); + displayInfo( DispManX.DISPMANX_ID_FORCE_OTHER, "Force other" ); + System.exit( 1 ); + } + + public static void info( int screen ) { + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + int res = DispManX.INSTANCE.graphics_get_display_size( screen, width, height ); + if (res >= 0) { + System.out.printf( "%d, %d\n", width, height ); + } else { + System.out.println("graphics_get_display_size returned:" + res); + } + System.exit( 1 ); + } + + public static void res( String str, int val ) { + if ( val != 0 ) { + System.out.printf( "%s: %08x\n", str, val ); + } else { + System.out.println(str + " is 0"); + } + } + + public static int getPitch( int x, int y ) { + return ((x + (y)-1) & ~((y)-1));//y*((x + y-1)/y); + } + + public static Memory loadBitmapRGB565(String fileName, IntByReference width, IntByReference height, IntByReference pitchByRef) throws IOException { + int bytesPerPixel = 2; + BufferedImage image = ImageIO.read(new File(fileName)); + int pitch = getPitch( bytesPerPixel * image.getWidth(), 32 ); + pitchByRef.setValue(pitch); + Memory destPixels = new Memory(pitch * image.getHeight()); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + int rgb = image.getRGB(x, y); + destPixels.setShort((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, (short)(((rgb & 0xf80000) >>> 8) | ((rgb & 0xfc00) >>> 5) | (rgb & 0xf8 >>> 3))); + } + } + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + public static Memory loadBitmapARGB8888(String fileName, IntByReference width, IntByReference height, IntByReference pitchByRef) throws IOException { + int bytesPerPixel = 4; + BufferedImage image = ImageIO.read(new File(fileName)); + int pitch = getPitch( bytesPerPixel * image.getWidth(), 32 ); + pitchByRef.setValue(pitch); + long start = System.currentTimeMillis(); + Memory destPixels = new Memory(pitch * image.getHeight()); + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + destPixels.setInt((y*(pitch / bytesPerPixel) + x) * bytesPerPixel, image.getRGB(x, y)); + } + } + System.out.println(System.currentTimeMillis() - start); + width.setValue(image.getWidth()); + height.setValue(image.getHeight()); + return destPixels; + } + + //sudo vi DispManXTest.java + //sudo javac -cp lib/*:. DispManXTest.java + //sudo java -cp lib/*:. DispManXTest 2 10 resourcesnew/favicon/apple-icon-144x144.png + public static void main(String[] args) throws IOException, InterruptedException { + DispManX dispMan = DispManX.INSTANCE; + System.out.println("Initialized:" + dispMan.bcm_host_init()); + if ( args.length < 3 ) { + usage(); + return; + } + + int screen = Integer.parseInt( args[0] ); + int time = Integer.parseInt( args[1] ); + IntByReference pitch = new IntByReference(); + IntByReference width = new IntByReference(); + IntByReference height = new IntByReference(); + res( "get display size", dispMan.graphics_get_display_size( screen, width, height ) ); + System.out.printf( "display %d: %d x %d\n", screen, width.getValue(), height.getValue() ); + int display = dispMan.vc_dispmanx_display_open( screen ); + Memory bitmap = loadBitmapARGB8888( args[2], width, height, pitch ); + System.out.printf( "bitmap: %d x %d pitch->%d\n", width.getValue(), height.getValue(), pitch.getValue()); + + IntByReference ref = new IntByReference(); + int resourceHandle = dispMan.vc_dispmanx_resource_create( VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), width.getValue(), height.getValue(), ref ); + + VC_RECT_T.ByReference copyRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference sourceRect = new VC_RECT_T.ByReference(); + VC_RECT_T.ByReference destinationRect = new VC_RECT_T.ByReference(); + + res( "rect set", dispMan.vc_dispmanx_rect_set( copyRect, 0, 0, width.getValue(), height.getValue() ) ); + //This seems to be some form of a zoom factor + res( "rect set", dispMan.vc_dispmanx_rect_set( sourceRect, 0, 0, width.getValue()<<16, height.getValue()<<16 ) ); + res( "rect set", dispMan.vc_dispmanx_rect_set( destinationRect, 0, 0, width.getValue(), height.getValue() ) ); + + System.out.println("copyRect:" + copyRect.width + ", " + copyRect.height); + System.out.println("sourceRect:" + sourceRect.width + ", " + sourceRect.height); + System.out.println("destinationRect:" + destinationRect.width + ", " + destinationRect.height); + + int update = dispMan.vc_dispmanx_update_start( 0 ); + VC_DISPMANX_ALPHA_T.ByReference alpha = new VC_DISPMANX_ALPHA_T.ByReference(); + alpha.flags = ALPHA.DISPMANX_FLAGS_ALPHA_FROM_SOURCE.getFlag() | ALPHA.DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS.getFlag(); + alpha.opacity = 255; + int element = dispMan.vc_dispmanx_element_add( + update, + display, + 2010, + destinationRect, + resourceHandle, + sourceRect, + DispManX.DISPMANX_PROTECTION_NONE, + alpha, + 0, + VC_IMAGE_TRANSFORM_T.VC_IMAGE_ROT0.getcConst() ); + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + + res( "resource write data", dispMan.vc_dispmanx_resource_write_data( + resourceHandle, + VC_IMAGE_TYPE_T.VC_IMAGE_ARGB8888.getcIndex(), + pitch.getValue() , + bitmap, + destinationRect ) + ); + + Thread.sleep( time * 1000 ); + update = dispMan.vc_dispmanx_update_start( 0 ); + + res( "element remove", dispMan.vc_dispmanx_element_remove( update, element ) ); + res( "submit", dispMan.vc_dispmanx_update_submit_sync( update ) ); + res( "resource delete", dispMan.vc_dispmanx_resource_delete( resourceHandle ) ); + res( "display close", dispMan.vc_dispmanx_display_close( display ) ); + } +} diff --git a/host/src/test/java/org/area515/resinprinter/gcode/TestGCodeTemplating.java b/host/src/test/java/org/area515/resinprinter/gcode/TestGCodeTemplating.java index 798d6b954..4af7baf94 100644 --- a/host/src/test/java/org/area515/resinprinter/gcode/TestGCodeTemplating.java +++ b/host/src/test/java/org/area515/resinprinter/gcode/TestGCodeTemplating.java @@ -17,8 +17,8 @@ public class TestGCodeTemplating { public void testEmptyGCode() throws Exception { AbstractPrintFileProcessor processor = Mockito.mock(AbstractPrintFileProcessor.class, Mockito.CALLS_REAL_METHODS); PrintJob printJob = AbstractPrintFileProcessorTest.createTestPrintJob(processor); - Assert.assertNull(printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, null)); - Assert.assertNull(printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, " ")); + Assert.assertNull(printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, null, true)); + Assert.assertNull(printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, " ", true)); } @Test @@ -51,7 +51,7 @@ public String answer(InvocationOnMock invocation) throws Throwable { return (String)"ok"; } }); - printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, gcodes); + printJob.getPrinter().getGCodeControl().executeGCodeWithTemplating(printJob, gcodes, true); } } diff --git a/host/src/test/java/org/area515/resinprinter/job/AbstractPrintFileProcessorTest.java b/host/src/test/java/org/area515/resinprinter/job/AbstractPrintFileProcessorTest.java index 6471bc33d..a3fd1abba 100644 --- a/host/src/test/java/org/area515/resinprinter/job/AbstractPrintFileProcessorTest.java +++ b/host/src/test/java/org/area515/resinprinter/job/AbstractPrintFileProcessorTest.java @@ -59,7 +59,7 @@ public static PrintJob createTestPrintJob(PrintFileProcessor processor) throws I Mockito.when(slicingProfile.getDirection()).thenReturn(BuildDirection.Bottom_Up); Mockito.when(printer.getGCodeControl()).thenReturn(gCode); Mockito.when(slicingProfile.getgCodeLift()).thenReturn("Lift z"); - Mockito.doCallRealMethod().when(gCode).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString()); + Mockito.doCallRealMethod().when(gCode).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString(), Mockito.anyBoolean()); Mockito.when(printer.getConfiguration().getMachineConfig()).thenReturn(machine); Mockito.when(printer.getConfiguration().getMachineConfig().getMonitorDriverConfig()).thenReturn(monitorConfig); return printJob; @@ -127,7 +127,7 @@ public void unsupportedBuildAreaDoesntBreakLiftDistanceCalculator() throws Excep DataAid aid = processor.initializeJobCacheWithDataAid(printJob); aid.customizer.setNextStep(PrinterStep.PerformExposure); processor.printImageAndPerformPostProcessing(aid, image); - Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString()); + Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString(), Mockito.anyBoolean()); } @Test @@ -140,7 +140,7 @@ public void getExceptionWhenWeReturnGarbageForLiftDistanceCalculator() throws Ex try { aid.customizer.setNextStep(PrinterStep.PerformExposure); processor.printImageAndPerformPostProcessing(aid, image); - Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString()); + Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString(), true); } catch (IllegalArgumentException e) { Assert.assertEquals("The result of your lift distance script needs to evaluate to an instance of java.lang.Number", e.getMessage()); } @@ -156,7 +156,7 @@ public void noNullPointerWhenWeReturnNull() throws Exception { try { aid.customizer.setNextStep(PrinterStep.PerformExposure); processor.printImageAndPerformPostProcessing(aid, image); - Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString()); + Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(1)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString(), Mockito.anyBoolean()); } catch (IllegalArgumentException e) { Assert.assertEquals("The result of your lift distance script needs to evaluate to an instance of java.lang.Number", e.getMessage()); } @@ -234,6 +234,6 @@ public String answer(InvocationOnMock invocation) throws Throwable { aid.customizer.setNextStep(PrinterStep.PerformExposure); processor.printImageAndPerformPostProcessing(aid, image); //The two executes are for getZLiftDistanceGCode and the life gcode itself - Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(2)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString()); + Mockito.verify(printJob.getPrinter().getGCodeControl(), Mockito.times(2)).executeGCodeWithTemplating(Mockito.any(PrintJob.class), Mockito.anyString(), Mockito.anyBoolean()); } } diff --git a/host/src/test/java/org/area515/resinprinter/network/LinuxNetworkManagerTest.java b/host/src/test/java/org/area515/resinprinter/network/LinuxNetworkManagerTest.java index 81271c9f4..d225b16ba 100644 --- a/host/src/test/java/org/area515/resinprinter/network/LinuxNetworkManagerTest.java +++ b/host/src/test/java/org/area515/resinprinter/network/LinuxNetworkManagerTest.java @@ -13,15 +13,16 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PowerMockIgnore({"javax.management.*"}) public class LinuxNetworkManagerTest { - public static String SCAN_WIFI_DATA = "wpa_cli v1.0\nCopyright (c) 2004-2012, Jouni Malinen and contributors\n\nThis program is free software. You can distribute it and/or modify it\nunder the terms of the GNU General Public License version 2.\n\nAlternatively, this software may be distributed under the terms of the\nBSD license. See README and COPYING for more details.\n\n\n\n\nInteractive mode\n\n>OK\n>\r<3>CTRL-EVENT-SCAN-RESULTS \n>bssid / frequency / signal level / flags / ssid\n03:15:2a:0c:93:15 2437 -69 [WEP][ESS]\tSomeNetwork\n11:51:a6:71:51:55 2412 92 [WPA-PSK-TKIP+CCMP][WPA2-PSK-TKIP+CCMP][WPS][ESS]\tCenturyLink9999\nac:95:17:92:60:20 2437 26 [WPA2-PSK-CCMP][WPS][ESS]\tSomeHouse\n>"; + public static String SCAN_WIFI_DATA = "wpa_cli v1.0\nCopyright (c) 2004-2012, Jouni Malinen and contributors\n\nThis program is free software. You can distribute it and/or modify it\nunder the terms of the GNU General Public License version 2.\n\nAlternatively, this software may be distributed under the terms of the\nBSD license. See README and COPYING for more details.\n\n\n\n\nInteractive mode\n\n>OK\n>\r<3>CTRL-EVENT-SCAN-RESULTS \n>bssid / frequency / signal level / flags / ssid\n03:15:2a:0c:93:15 2437 -69 [WEP][ESS]\tSomeNetwork\n11:51:a6:71:51:55 2412 92 [WPA-PSK-TKIP+CCMP][WPA2-PSK-TKIP+CCMP][WPS][ESS]\t\\x00\nac:95:17:92:60:20 2437 26 [WPA2-PSK-CCMP][WPS][ESS]\t☺\\u0044\\\\\\x45\\\\u0044Test\n>"; + @Test @PrepareForTest(IOUtilities.class) public void getNetworks() throws IOException { @@ -70,7 +71,8 @@ public Process answer(InvocationOnMock invocation) throws Throwable { Assert.assertEquals(lanName, interfaces.get(0).getName()); Assert.assertEquals(3, interfaces.get(0).getWirelessNetworks().size()); Assert.assertEquals("SomeNetwork", interfaces.get(0).getWirelessNetworks().get(0).getSsid()); - Assert.assertEquals("CenturyLink9999", interfaces.get(0).getWirelessNetworks().get(1).getSsid()); - Assert.assertEquals("SomeHouse", interfaces.get(0).getWirelessNetworks().get(2).getSsid()); + Assert.assertEquals("\u0000", interfaces.get(0).getWirelessNetworks().get(1).getSsid()); + Assert.assertEquals(true, interfaces.get(0).getWirelessNetworks().get(1).isHidden()); + Assert.assertEquals("\u263AD\\E\\u0044Test", interfaces.get(0).getWirelessNetworks().get(2).getSsid()); } } diff --git a/host/src/test/java/org/area515/resinprinter/security/keystore/RendezvousExchange.java b/host/src/test/java/org/area515/resinprinter/security/keystore/RendezvousExchange.java index a015bc31a..a6e2ea75a 100644 --- a/host/src/test/java/org/area515/resinprinter/security/keystore/RendezvousExchange.java +++ b/host/src/test/java/org/area515/resinprinter/security/keystore/RendezvousExchange.java @@ -145,6 +145,10 @@ public void messageExchange() throws Exception { Assert.assertEquals(testMessage, new String(dataFrom2)); //=================== + //TODO: This local execution of the concurrency test failed on: 12/8/2016 + //The I believe this occurred because there was a timeout waiting for a response from method of: org.area515.resinprinter.security.keystore.RendezvousExchange.HttpRequestRunnable + //Why did we timeout? + runOneWayConcurrencyTest(server1, user1.getUserId(), user2.getUserId()); //=================== diff --git a/host/src/test/java/org/area515/resinprinter/services/MachineServiceTest.java b/host/src/test/java/org/area515/resinprinter/services/MachineServiceTest.java new file mode 100644 index 000000000..2facc6aa2 --- /dev/null +++ b/host/src/test/java/org/area515/resinprinter/services/MachineServiceTest.java @@ -0,0 +1,100 @@ +package org.area515.resinprinter.services; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ws.rs.core.MediaType; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import org.area515.resinprinter.printer.MachineConfig; +import org.area515.resinprinter.printer.Named; +import org.area515.resinprinter.printer.Printer; +import org.area515.resinprinter.printer.PrinterConfiguration; +import org.area515.resinprinter.printer.SlicingProfile; +import org.area515.resinprinter.printer.SlicingProfile.InkConfig; +import org.area515.resinprinter.server.ApplicationConfig; +import org.junit.Assert; +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; + +public class MachineServiceTest { + public static boolean isFound(List named, String name) { + boolean fileFound = false; + for (Named currentConfig : named) { + if (currentConfig.getName().equals(name)) { + fileFound = true; + break; + } + } + + return fileFound; + } + + public Printer readConfiguration() throws JsonParseException, JsonMappingException, IOException { + JacksonJaxbJsonProvider provider = ApplicationConfig.buildJacksonJaxbJsonProvider(); + Object printer = provider.readFrom(Object.class, Printer.class, null, MediaType.APPLICATION_JSON_TYPE, null, MachineServiceTest.class.getResourceAsStream("jsonToXMLTest.json")); + return (Printer)printer; + } + + private void assertPrinterProperties(PrinterConfiguration config) { + SlicingProfile slicingProfile = config.getSlicingProfile(); + InkConfig inkConfig = slicingProfile.getSelectedInkConfig(); + Map settings = slicingProfile.getInkConfigs().iterator().next().getPrintMaterialDetectorSettings().getSettings(); + + Assert.assertEquals("Firm Amber 50 Microns", inkConfig.getName()); + Assert.assertEquals(2, settings.size()); + Assert.assertEquals("AnotherValue", settings.get("AnotherSetting")); + Assert.assertEquals("GoodValue", settings.get("GoodSetting")); + Assert.assertEquals(10L, slicingProfile.getInkConfigs().size()); + Assert.assertEquals(2L, (long)slicingProfile.getSelectedInkConfigIndex()); + } + + @Test + public void createListDeleteNewMachineConfig() throws JAXBException { + String configName = UUID.randomUUID() + ""; + MachineConfig config = new MachineConfig(); + config.setName(configName); + MachineService.INSTANCE.saveMachineConfiguration(config); + Assert.assertTrue(isFound(MachineService.INSTANCE.getMachineConfigurations(), configName)); + MachineService.INSTANCE.deleteMachineConfiguration(configName); + Assert.assertFalse(isFound(MachineService.INSTANCE.getMachineConfigurations(), configName)); + } + + @Test + public void createListDeleteNewSlicingProfileConfig() throws JAXBException { + String configName = UUID.randomUUID() + ""; + SlicingProfile config = new SlicingProfile(); + config.setName(configName); + MachineService.INSTANCE.saveSlicingProfile(config); + Assert.assertTrue(isFound(MachineService.INSTANCE.getSlicingProfiles(), configName)); + MachineService.INSTANCE.deleteSlicingProfile(configName); + Assert.assertFalse(isFound(MachineService.INSTANCE.getSlicingProfiles(), configName)); + } + + @Test + public void roundTripJSONToObjectToXMLToObject() throws JsonParseException, JsonMappingException, IOException, JAXBException { + Printer printer = readConfiguration(); + PrinterConfiguration configuration = printer.getConfiguration(); + assertPrinterProperties(configuration); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + JAXBContext jaxbContext = JAXBContext.newInstance(PrinterConfiguration.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal(configuration, output); + + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + configuration = (PrinterConfiguration)jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(output.toByteArray())); + assertPrinterProperties(configuration); + } +} diff --git a/host/src/test/java/org/area515/resinprinter/test/FullTestSuite.java b/host/src/test/java/org/area515/resinprinter/test/FullTestSuite.java index 8f84ba7bf..11e458b46 100644 --- a/host/src/test/java/org/area515/resinprinter/test/FullTestSuite.java +++ b/host/src/test/java/org/area515/resinprinter/test/FullTestSuite.java @@ -15,6 +15,7 @@ import org.area515.resinprinter.security.KeystoreSecurityTest; import org.area515.resinprinter.security.SerializeMessageAsJson; import org.area515.resinprinter.security.keystore.RendezvousExchange; +import org.area515.resinprinter.services.MachineServiceTest; import org.area515.resinprinter.services.TestScriptAndTemplating; import org.area515.resinprinter.slice.CheckSlicePoints; import org.area515.resinprinter.stl.ZSlicingGeometry; @@ -43,6 +44,7 @@ TestGCodeTemplating.class, TestByteSession.class, TestCustomizer.class, + MachineServiceTest.class, }) public class FullTestSuite { diff --git a/host/src/test/java/org/area515/util/IOUtilitiesTest.java b/host/src/test/java/org/area515/util/IOUtilitiesTest.java index 3bc8ccbe2..3b84c9eb4 100644 --- a/host/src/test/java/org/area515/util/IOUtilitiesTest.java +++ b/host/src/test/java/org/area515/util/IOUtilitiesTest.java @@ -136,8 +136,8 @@ public void testNativeCommandCommunication() throws IOException { List dataReturned = IOUtilities.communicateWithNativeCommand(actions, "^>|\n", true, null, "wlan0"); Assert.assertEquals("SomeNetwork", dataReturned.get(0)[4]); - Assert.assertEquals("CenturyLink9999", dataReturned.get(1)[4]); - Assert.assertEquals("SomeHouse", dataReturned.get(2)[4]); + Assert.assertEquals("\\x00", dataReturned.get(1)[4]); + Assert.assertEquals("☺\\u0044\\\\\\x45\\\\u0044Test", dataReturned.get(2)[4]); } @Test public void inputStreamReadLineTest() throws IOException { diff --git a/host/src/test/resources/org/area515/resinprinter/services/jsonToXMLTest.json b/host/src/test/resources/org/area515/resinprinter/services/jsonToXMLTest.json new file mode 100644 index 000000000..b6f14688e --- /dev/null +++ b/host/src/test/resources/org/area515/resinprinter/services/jsonToXMLTest.json @@ -0,0 +1,189 @@ +{ + "configuration": { + "name": "LC HR", + "machineConfig": { + "name": "LC HR", + "FileVersion": 0, + "PlatformXSize": 160, + "PlatformYSize": 120, + "PlatformZSize": 200, + "MaxXFeedRate": 0, + "MaxYFeedRate": 0, + "MaxZFeedRate": 0, + "XRenderSize": 2048, + "YRenderSize": 1536, + "MotorsDriverConfig": { + "DriverType": "Photocentric", + "ComPortSettings": { + "PortName": "Autodetect 3d printer firmware", + "Speed": 115200, + "Databits": 8, + "Parity": "None", + "Stopbits": "One", + "Handshake": "None" + } + }, + "MonitorDriverConfig": { + "DLP_X_Res": 2048, + "DLP_Y_Res": 1536, + "MonitorID": "DISPLAY1", + "OSMonitorID": "Photocentric Custom Display", + "DisplayCommEnabled": false, + "ComPortSettings": { + "Handshake": "None" + }, + "MonitorTop": 12, + "MonitorLeft": 11, + "MonitorRight": 11, + "MonitorBottom": 12, + "UseMask": false + }, + "PauseOnPrinterResponseRegEx": ".*door.*open.*" + }, + "slicingProfile": { + "gCodeHeader": "G91;\nM17;\nG1 Z10 F50;\nG1 Z-10 F50;", + "gCodeFooter": "M18", + "gCodePreslice": null, + "gCodeLift": "G1 Z${ZLiftDist} F${ZLiftRate};\nG1 Z-${(ZLiftDist - LayerThickness)} F180;\nM17;\n; 1500;", + "gCodeShutter": null, + "name": "Daylight Resin Hard - Red 25 micron", + "zliftDistanceGCode": null, + "zliftSpeedGCode": null, + "selectedInkConfigIndex": 0, + "DotsPermmX": 10.3937007874, + "DotsPermmY": 10.3937007874, + "XResolution": 2048, + "YResolution": 1536, + "BlankTime": 0, + "PlatformTemp": 0, + "ExportSVG": 0, + "Export": false, + "ExportPNG": false, + "Direction": "Bottom_Up", + "LiftDistance": 5, + "SlideTiltValue": 0, + "AntiAliasing": true, + "UseMainLiftGCode": false, + "AntiAliasingValue": 10, + "LiftFeedRate": 50, + "LiftRetractRate": 0, + "FlipX": false, + "FlipY": true, + "ZLiftDistanceCalculator": "var minLift = 4.5;\nvar value = 8.0;\nif ($CURSLICE > $NumFirstLayers) {\nvalue = minLift + 0.0015*Math.pow($buildAreaMM,1);\n}\nvalue", + "ZLiftSpeedCalculator": "var value = 50.0;\nif ($CURSLICE > $NumFirstLayers) {\nvalue = 100.0 - 0.02 * Math.pow($buildAreaMM,1);\n}\nvalue", + "ExposureTimeCalculator": "var value = $FirstLayerTime;\nif ($CURSLICE > $NumFirstLayers) {\n\tvalue = $LayerTime\n}\nvalue", + "SelectedInk": "Firm Amber 50 Microns", + "MinTestExposure": 0, + "TestExposureStep": 0, + "InkConfig": [ + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "PrintMaterialDetector": "org.area515.resinprinter.inkdetection.visual.VisualPrintMaterialDetector", + "PrintMaterialDetectorSettings": { + "GoodSetting": "GoodValue", + "AnotherSetting": "AnotherValue" + }, + "Name": "Castable 50 Microns", + "SliceHeight": 0.05, + "LayerTime": 20000, + "FirstLayerTime": 150000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Castable 100 Microns", + "SliceHeight": 0.1, + "LayerTime": 25000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Firm Amber 50 Microns", + "SliceHeight": 0.05, + "LayerTime": 16000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Firm Amber 100 Microns", + "SliceHeight": 0.1, + "LayerTime": 20000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Firm Color 50 Microns", + "SliceHeight": 0.05, + "LayerTime": 20000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Firm Color 100 Microns", + "SliceHeight": 0.1, + "LayerTime": 25000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Hard Amber 50 Microns", + "SliceHeight": 0.05, + "LayerTime": 16000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Hard Amber 100 Microns", + "SliceHeight": 0.1, + "LayerTime": 20000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Hard Color 50 Microns", + "SliceHeight": 0.05, + "LayerTime": 20000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + }, + { + "PercentageOfPrintMaterialConsideredEmpty": 10, + "Name": "Hard Color 100 Microns", + "SliceHeight": 0.1, + "LayerTime": 25000, + "FirstLayerTime": 200000, + "NumberofBottomLayers": 4, + "ResinPriceL": 45 + } + ] + }, + "MachineConfigurationName": "Photocentric 10", + "SlicingProfileName": "Daylight Resin Hard - Red 25 micron", + "AutoStart": true, + "Calibrated": false + }, + "started": true, + "shutterOpen": false, + "displayDeviceID": "Photocentric Custom Display", + "currentSlicePauseTime": 0, + "status": "Ready", + "printPaused": false, + "cachedBulbHours": null, + "printInProgress": false + } \ No newline at end of file