summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--INFO1
-rw-r--r--docs/jenkins-job-builder/opnfv-jjb-usage.rst16
-rw-r--r--jjb-sandbox/releng/releng-sandbox-jobs.yml1
-rwxr-xr-xjjb/3rd_party_ci/create-apex-vms.sh10
-rwxr-xr-xjjb/3rd_party_ci/download-netvirt-artifact.sh27
-rwxr-xr-xjjb/3rd_party_ci/functest-netvirt.sh7
-rwxr-xr-xjjb/3rd_party_ci/install-netvirt.sh40
-rw-r--r--jjb/3rd_party_ci/odl-netvirt.yml76
-rwxr-xr-xjjb/3rd_party_ci/postprocess-netvirt.sh10
-rwxr-xr-xjjb/apex/apex-build.sh3
-rwxr-xr-xjjb/apex/apex-deploy.sh65
-rw-r--r--jjb/apex/apex-snapshot-create.sh100
-rw-r--r--jjb/apex/apex-snapshot-deploy.sh165
-rwxr-xr-xjjb/apex/apex-upload-artifact.sh19
-rw-r--r--jjb/apex/apex.yml423
-rw-r--r--jjb/armband/armband-ci-jobs.yml4
-rwxr-xr-xjjb/armband/armband-deploy.sh7
-rwxr-xr-xjjb/armband/armband-download-artifact.sh2
-rw-r--r--jjb/armband/armband-project-jobs.yml4
-rw-r--r--jjb/armband/armband-verify-jobs.yml22
-rwxr-xr-xjjb/armband/build.sh1
-rwxr-xr-xjjb/armband/upload-artifacts.sh2
-rw-r--r--jjb/availability/availability.yml1
-rw-r--r--jjb/barometer/barometer.yml5
-rw-r--r--jjb/bottlenecks/bottlenecks-ci-jobs.yml62
-rw-r--r--jjb/bottlenecks/bottlenecks-cleanup.sh111
-rw-r--r--jjb/bottlenecks/bottlenecks-project-jobs.yml6
-rw-r--r--jjb/bottlenecks/bottlenecks-run-suite.sh65
-rw-r--r--jjb/compass4nfv/compass-ci-jobs.yml54
-rw-r--r--jjb/compass4nfv/compass-deploy.sh5
-rw-r--r--jjb/compass4nfv/compass-dovetail-jobs.yml208
-rw-r--r--jjb/compass4nfv/compass-project-jobs.yml4
-rw-r--r--jjb/compass4nfv/compass-verify-jobs.yml18
-rw-r--r--jjb/conductor/conductor.yml1
-rw-r--r--jjb/copper/copper.yml8
-rw-r--r--jjb/cperf/cperf-ci-jobs.yml2
-rw-r--r--jjb/daisy4nfv/daisy-daily-jobs.yml199
-rwxr-xr-xjjb/daisy4nfv/daisy-deploy.sh63
-rw-r--r--jjb/daisy4nfv/daisy-project-jobs.yml232
-rwxr-xr-xjjb/daisy4nfv/daisy4nfv-build.sh1
-rwxr-xr-xjjb/daisy4nfv/daisy4nfv-download-artifact.sh23
-rw-r--r--jjb/daisy4nfv/daisy4nfv-merge-jobs.yml160
-rw-r--r--jjb/daisy4nfv/daisy4nfv-verify-jobs.yml134
-rwxr-xr-xjjb/daisy4nfv/daisy4nfv-virtual-deploy.sh29
-rw-r--r--jjb/doctor/doctor.yml134
-rw-r--r--jjb/domino/domino.yml1
-rw-r--r--jjb/dovetail/dovetail-artifacts-upload.yml2
-rw-r--r--jjb/dovetail/dovetail-ci-jobs.yml48
-rw-r--r--jjb/dovetail/dovetail-project-jobs.yml2
-rw-r--r--jjb/dovetail/dovetail-weekly-jobs.yml135
-rw-r--r--jjb/dpacc/dpacc.yml1
-rw-r--r--jjb/escalator/escalator.yml16
-rwxr-xr-xjjb/fuel/fuel-basic-exp.sh18
-rwxr-xr-xjjb/fuel/fuel-build-exp.sh10
-rw-r--r--jjb/fuel/fuel-daily-jobs.yml405
-rwxr-xr-xjjb/fuel/fuel-deploy-exp.sh10
-rwxr-xr-xjjb/fuel/fuel-deploy.sh6
-rwxr-xr-xjjb/fuel/fuel-download-artifact.sh2
-rwxr-xr-xjjb/fuel/fuel-plugin-build.sh10
-rwxr-xr-xjjb/fuel/fuel-plugin-test.sh10
-rw-r--r--jjb/fuel/fuel-plugin-verify-jobs.yml236
-rw-r--r--jjb/fuel/fuel-project-jobs.yml15
-rwxr-xr-xjjb/fuel/fuel-smoke-test-exp.sh10
-rwxr-xr-xjjb/fuel/fuel-upload-artifact.sh2
-rw-r--r--jjb/fuel/fuel-verify-jobs-experimental.yml255
-rw-r--r--jjb/fuel/fuel-verify-jobs.yml16
-rw-r--r--jjb/fuel/fuel-weekly-jobs.yml210
-rwxr-xr-xjjb/functest/functest-cleanup.sh12
-rw-r--r--jjb/functest/functest-daily-jobs.yml (renamed from jjb/functest/functest-ci-jobs.yml)47
-rw-r--r--jjb/functest/functest-exit.sh5
-rwxr-xr-xjjb/functest/functest-loop.sh6
-rw-r--r--jjb/functest/functest-project-jobs.yml1
-rwxr-xr-xjjb/functest/functest-suite.sh26
-rw-r--r--jjb/functest/functest-weekly-jobs.yml124
-rwxr-xr-xjjb/functest/set-functest-env.sh61
-rw-r--r--jjb/global/installer-params.yml27
-rw-r--r--jjb/global/releng-defaults.yml9
-rw-r--r--jjb/global/releng-macros.yml114
-rw-r--r--jjb/global/slave-params.yml162
-rw-r--r--jjb/infra/bifrost-cleanup-job.yml136
-rwxr-xr-xjjb/infra/bifrost-upload-logs.sh29
-rw-r--r--jjb/infra/bifrost-verify-jobs.yml31
-rwxr-xr-xjjb/infra/bifrost-verify.sh65
-rw-r--r--jjb/ipv6/ipv6.yml1
-rw-r--r--jjb/joid/joid-daily-jobs.yml152
-rw-r--r--jjb/joid/joid-deploy.sh97
-rw-r--r--jjb/joid/joid-verify-jobs.yml10
-rwxr-xr-xjjb/kvmfornfv/kvmfornfv-upload-artifact.sh34
-rw-r--r--jjb/kvmfornfv/kvmfornfv.yml50
-rw-r--r--jjb/models/models.yml67
-rw-r--r--jjb/moon/moon.yml1
-rwxr-xr-xjjb/multisite/fuel-deploy-for-multisite.sh124
-rw-r--r--jjb/multisite/multisite-daily-jobs.yml140
-rw-r--r--jjb/multisite/multisite-verify-jobs.yml68
-rw-r--r--jjb/multisite/multisite.yml149
-rw-r--r--jjb/netready/netready.yml2
-rw-r--r--jjb/octopus/octopus.yml1
-rw-r--r--jjb/onosfw/onosfw.yml3
-rw-r--r--jjb/openretriever/openretriever-project.yml62
-rw-r--r--jjb/opera/opera-daily-jobs.yml16
-rw-r--r--jjb/opera/opera-project-jobs.yml4
-rw-r--r--jjb/opera/opera-verify-jobs.yml3
-rw-r--r--jjb/opnfvdocs/docs-post-rtd.sh7
-rw-r--r--jjb/opnfvdocs/docs-rtd.yaml88
-rw-r--r--jjb/opnfvdocs/opnfvdocs.yml15
-rw-r--r--jjb/opnfvdocs/project.cfg1
-rw-r--r--jjb/ovsnfv/ovsnfv.yml3
-rw-r--r--jjb/parser/parser.yml7
-rw-r--r--jjb/pharos/pharos.yml1
-rw-r--r--jjb/prediction/prediction.yml1
-rw-r--r--jjb/promise/promise.yml1
-rw-r--r--jjb/qtip/helpers/cleanup-deploy.sh (renamed from jjb/qtip/qtip-cleanup.sh)0
-rw-r--r--jjb/qtip/helpers/validate-deploy.sh (renamed from jjb/qtip/qtip-daily-ci.sh)7
-rw-r--r--jjb/qtip/helpers/validate-setup.sh24
-rw-r--r--jjb/qtip/qtip-ci-jobs.yml101
-rw-r--r--jjb/qtip/qtip-validate-jobs.yml142
-rw-r--r--jjb/qtip/qtip-verify-jobs.yml (renamed from jjb/qtip/qtip-project-jobs.yml)14
-rw-r--r--jjb/releng/artifact-cleanup.yml1
-rw-r--r--jjb/releng/opnfv-docker-arm.yml77
-rw-r--r--jjb/releng/opnfv-docker.sh29
-rw-r--r--jjb/releng/opnfv-docker.yml30
-rw-r--r--jjb/releng/opnfv-docs.yml4
-rw-r--r--jjb/releng/opnfv-lint.yml6
-rw-r--r--jjb/releng/releng-ci-jobs.yml3
-rw-r--r--jjb/releng/testapi-automate.yml226
-rw-r--r--jjb/releng/testapi-backup-mongodb.sh31
-rw-r--r--jjb/releng/testapi-docker-deploy.sh81
-rw-r--r--jjb/releng/testapi-docker-update.sh18
-rw-r--r--jjb/securityaudit/opnfv-security-audit.yml1
-rw-r--r--jjb/snaps/snaps.yml62
-rw-r--r--jjb/storperf/storperf.yml10
-rw-r--r--jjb/ves/ves.yml68
-rw-r--r--jjb/vnf_forwarding_graph/vnf_forwarding_graph.yml1
-rw-r--r--jjb/vswitchperf/vswitchperf.yml9
-rw-r--r--jjb/yardstick/yardstick-ci-jobs.yml17
-rwxr-xr-xjjb/yardstick/yardstick-daily.sh4
-rw-r--r--jjb/yardstick/yardstick-project-jobs.yml18
-rw-r--r--modules/opnfv/deployment/__init__.py (renamed from modules/opnfv/installer_adapters/__init__.py)0
-rw-r--r--modules/opnfv/deployment/apex/__init__.py (renamed from modules/opnfv/installer_adapters/apex/__init__.py)0
-rw-r--r--modules/opnfv/deployment/apex/adapter.py100
-rw-r--r--modules/opnfv/deployment/example.py36
-rw-r--r--modules/opnfv/deployment/factory.py45
-rw-r--r--modules/opnfv/deployment/fuel/__init__.py (renamed from modules/opnfv/installer_adapters/compass/__init__.py)0
-rw-r--r--modules/opnfv/deployment/fuel/adapter.py199
-rw-r--r--modules/opnfv/deployment/manager.py386
-rw-r--r--modules/opnfv/installer_adapters/InstallerHandler.py81
-rw-r--r--modules/opnfv/installer_adapters/apex/ApexAdapter.py32
-rw-r--r--modules/opnfv/installer_adapters/compass/CompassAdapter.py32
-rw-r--r--modules/opnfv/installer_adapters/daisy/DaisyAdapter.py32
-rw-r--r--modules/opnfv/installer_adapters/fuel/FuelAdapter.py236
-rw-r--r--modules/opnfv/installer_adapters/fuel/example.py22
-rw-r--r--modules/opnfv/installer_adapters/joid/JoidAdapter.py32
-rw-r--r--modules/opnfv/installer_adapters/joid/__init__.py0
-rw-r--r--modules/opnfv/utils/Credentials.py2
-rw-r--r--modules/opnfv/utils/constants.py1
-rw-r--r--modules/opnfv/utils/opnfv_logger.py (renamed from modules/opnfv/utils/OPNFVLogger.py)0
-rw-r--r--modules/opnfv/utils/ovs_logger.py27
-rw-r--r--modules/opnfv/utils/ssh_utils.py (renamed from modules/opnfv/utils/SSHUtils.py)40
-rw-r--r--prototypes/bifrost/playbooks/test-bifrost-infracloud.yaml2
-rwxr-xr-xprototypes/bifrost/scripts/destroy-env.sh35
-rwxr-xr-xprototypes/bifrost/scripts/test-bifrost-deployment.sh16
-rw-r--r--prototypes/openstack-ansible/README.md48
-rw-r--r--prototypes/openstack-ansible/file/cinder.yml13
-rw-r--r--prototypes/openstack-ansible/file/exports12
-rw-r--r--prototypes/openstack-ansible/file/modules8
-rw-r--r--prototypes/openstack-ansible/file/openstack_user_config.yml278
-rw-r--r--prototypes/openstack-ansible/file/user_variables.yml27
-rw-r--r--prototypes/openstack-ansible/playbooks/inventory11
-rw-r--r--prototypes/openstack-ansible/playbooks/jumphost_configuration.yml53
-rw-r--r--prototypes/openstack-ansible/playbooks/targethost_configuration.yml61
-rwxr-xr-xprototypes/openstack-ansible/scripts/osa_deploy.sh82
-rw-r--r--prototypes/openstack-ansible/template/bifrost/compute.interface.j286
-rw-r--r--prototypes/openstack-ansible/template/bifrost/controller.interface.j271
-rw-r--r--prototypes/openstack-ansible/var/ubuntu.yml6
-rwxr-xr-xutils/fetch_os_creds.sh32
-rwxr-xr-xutils/jenkins-jnlp-connect.sh15
-rwxr-xr-xutils/lab-reconfiguration/reconfigUcsNet.py146
-rw-r--r--utils/opnfv-artifacts.py79
-rw-r--r--utils/push-test-logs.sh14
-rw-r--r--utils/test/dashboard/dashboard/common/elastic_access.py2
-rw-r--r--utils/test/dashboard/dashboard/conf/testcases.py2
-rw-r--r--utils/test/dashboard/dashboard/elastic2kibana/utility.py3
-rw-r--r--utils/test/dashboard/dashboard/functest/format.py22
-rw-r--r--utils/test/dashboard/dashboard/mongo2elastic/main.py22
-rw-r--r--utils/test/dashboard/kibana_cleanup.py17
-rw-r--r--utils/test/declaration/addtestcase.php40
-rw-r--r--utils/test/declaration/index.php221
-rw-r--r--utils/test/declaration/testcases.php36
-rw-r--r--utils/test/reporting/api/handlers/landing.py167
-rwxr-xr-xutils/test/reporting/api/install.sh3
-rwxr-xr-xutils/test/reporting/docker/reporting.sh16
-rw-r--r--utils/test/reporting/docker/supervisor.conf6
-rwxr-xr-xutils/test/reporting/functest/reporting-status.py25
-rwxr-xr-xutils/test/reporting/functest/reporting-tempest.py8
-rwxr-xr-xutils/test/reporting/functest/reporting-vims.py4
-rw-r--r--utils/test/reporting/functest/testCase.py42
-rw-r--r--utils/test/reporting/html/danube.html11
-rw-r--r--utils/test/reporting/img/storperf.jpgbin0 -> 29732 bytes
-rwxr-xr-xutils/test/reporting/pages/angular.sh10
-rw-r--r--utils/test/reporting/pages/app/images/green.pngbin0 -> 5064 bytes
-rw-r--r--utils/test/reporting/pages/app/images/green@2x.pngbin0 -> 7708 bytes
-rw-r--r--utils/test/reporting/pages/app/index.html50
-rw-r--r--utils/test/reporting/pages/app/scripts/app.config.js (renamed from modules/opnfv/installer_adapters/daisy/__init__.py)0
-rw-r--r--utils/test/reporting/pages/app/scripts/app.js17
-rw-r--r--utils/test/reporting/pages/app/scripts/config.js11
-rw-r--r--utils/test/reporting/pages/app/scripts/config.router.js2
-rw-r--r--utils/test/reporting/pages/app/scripts/controllers/main.controller.js32
-rw-r--r--utils/test/reporting/pages/app/scripts/controllers/table.controller.js593
-rw-r--r--utils/test/reporting/pages/app/scripts/data.json76
-rw-r--r--utils/test/reporting/pages/app/scripts/factory/table.factory.js20
-rw-r--r--utils/test/reporting/pages/app/styles/custome.css72
-rw-r--r--utils/test/reporting/pages/app/styles/fonts/glyphicons-halflings-regular.svg2
-rw-r--r--utils/test/reporting/pages/app/views/commons/table.html92
-rw-r--r--utils/test/reporting/pages/app/views/main.html296
-rw-r--r--utils/test/reporting/pages/bower.json75
-rw-r--r--utils/test/reporting/pages/test/karma.conf.js1
-rw-r--r--utils/test/reporting/reporting.yaml29
-rw-r--r--utils/test/reporting/storperf/reporting-status.py145
-rw-r--r--utils/test/reporting/storperf/template/index-status-tmpl.html111
-rw-r--r--utils/test/reporting/utils/reporting_utils.py48
-rw-r--r--utils/test/reporting/utils/scenarioResult.py (renamed from utils/test/reporting/yardstick/scenarioResult.py)6
-rw-r--r--utils/test/reporting/yardstick/reporting-status.py2
-rw-r--r--utils/test/testapi/.coveragerc27
-rw-r--r--utils/test/testapi/docker/Dockerfile7
-rwxr-xr-xutils/test/testapi/docker/prepare-env.sh5
-rw-r--r--utils/test/testapi/etc/config.ini1
-rw-r--r--utils/test/testapi/htmlize/doc-build.sh21
-rw-r--r--utils/test/testapi/htmlize/finish.sh17
-rw-r--r--utils/test/testapi/htmlize/htmlize.py11
-rw-r--r--utils/test/testapi/htmlize/prepare.sh28
-rw-r--r--utils/test/testapi/htmlize/push-doc-artifact.sh6
-rw-r--r--utils/test/testapi/opnfv_testapi/cmd/server.py34
-rw-r--r--utils/test/testapi/opnfv_testapi/common/config.py37
-rw-r--r--utils/test/testapi/opnfv_testapi/common/constants.py1
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/handlers.py104
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/models.py214
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/pod_handlers.py18
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/project_handlers.py22
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/result_handlers.py38
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py236
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/scenario_models.py132
-rw-r--r--utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py24
-rw-r--r--utils/test/testapi/opnfv_testapi/router/url_mappings.py39
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/__init__.py (renamed from modules/opnfv/installer_adapters/fuel/__init__.py)0
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/noparam.ini16
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/normal.ini17
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/nosection.ini11
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/notboolean.ini17
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/notint.ini17
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/common/test_config.py36
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/fake_pymongo.py69
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/scenario-c1.json (renamed from utils/test/testapi/opnfv_testapi/tests/unit/scenario-create.json)2
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/scenario-c2.json73
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_base.py44
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_fake_pymongo.py18
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_pod.py35
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_project.py58
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_result.py122
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_scenario.py295
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_testcase.py85
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_token.py118
-rw-r--r--utils/test/testapi/opnfv_testapi/tests/unit/test_version.py8
-rwxr-xr-xutils/test/testapi/run_test.sh42
-rw-r--r--utils/test/testapi/test-requirements.txt10
-rw-r--r--utils/test/testapi/tox.ini41
-rw-r--r--utils/test/testapi/update/templates/utils.py2
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/README.md12
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/app.js79
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/bin/www9
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/package.json18
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/.DS_Storebin0 -> 6148 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/LICENSE21
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/README.md49
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.css8582
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.min.css16
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/.DS_Storebin0 -> 6148 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.eotbin0 -> 20966 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.ttfbin0 -> 127744 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woffbin0 -> 62876 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff2bin0 -> 49976 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.eotbin0 -> 20940 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.ttfbin0 -> 126792 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woffbin0 -> 62316 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff2bin0 -> 49380 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.eotbin0 -> 21364 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.ttfbin0 -> 127488 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woffbin0 -> 62980 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff2bin0 -> 50224 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.eotbin0 -> 21320 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.ttfbin0 -> 126072 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woffbin0 -> 61736 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff2bin0 -> 49236 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.eotbin0 -> 21659 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.ttfbin0 -> 127584 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woffbin0 -> 61628 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff2bin0 -> 48524 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.js8031
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.min.js10
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/images/3rd_party/commits.pngbin0 -> 1437 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/images/logo.pngbin0 -> 12138 bytes
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/javascripts/global.js27
-rwxr-xr-xutils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/3rd_party/bootstrap.css6757
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/search_projects.css132
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/style.css318
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/routes/index.js18
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/routes/search_projects.js19
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/views/error.jade12
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/views/index.jade122
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/views/layout.jade53
-rw-r--r--utils/test/vnfcatalogue/VNF_Catalogue/views/search_projects.jade75
-rw-r--r--utils/test/vnfcatalogue/helpers/README.md22
-rw-r--r--utils/test/vnfcatalogue/helpers/migrate.js78
-rw-r--r--utils/test/vnfcatalogue/helpers/schema.js51
314 files changed, 33992 insertions, 4318 deletions
diff --git a/.gitignore b/.gitignore
index 024dfac4b..0aa7b8c09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,12 @@ wheels/
.venv/
venv/
ENV/
+node_modules/
+.coverage
+=1.3.1
+cover/
+coverage.xml
+nosetests.xml
+testapi_venv/
+.cache
+.tox
diff --git a/INFO b/INFO
index 626637fcc..069d3d0b5 100644
--- a/INFO
+++ b/INFO
@@ -22,6 +22,7 @@ Mei Mei (Huawei, meimei@huawei.com)
Trevor Bramwell (Linux Foundation, tbramwell@linuxfoundation.org)
Serena Feng (ZTE, feng.xiaowei@zte.com.cn)
Yolanda Robla Mota (Red Hat, yroblamo@redhat.com)
+Markos Chandras (SUSE, mchandras@suse.de)
Link to TSC approval of the project: http://ircbot.wl.linuxfoundation.org/meetings/opnfv-meeting/2015/opnfv-meeting.2015-07-14-14.00.html
Link to TSC voting for removal of Victor Laza as committer: http://meetbot.opnfv.org/meetings/opnfv-meeting/2016/opnfv-meeting.2016-02-16-14.59.html
diff --git a/docs/jenkins-job-builder/opnfv-jjb-usage.rst b/docs/jenkins-job-builder/opnfv-jjb-usage.rst
index 73b31b20a..52dbdebe5 100644
--- a/docs/jenkins-job-builder/opnfv-jjb-usage.rst
+++ b/docs/jenkins-job-builder/opnfv-jjb-usage.rst
@@ -39,11 +39,24 @@ Job Types
* Trigger: **remerge**
+* Experimental Job
+
+ * Trigger: **check-experimental**
+
The verify and merge jobs are retriggerable in Gerrit by simply leaving
a comment with one of the keywords listed above.
This is useful in case you need to re-run one of those jobs in case
if build issues or something changed with the environment.
+The experimental jobs are not triggered automatically. You need to leave
+a comment with the keyword list above to trigger it manually. It is useful
+for trying out experimental features.
+
+Note that, experimental jobs `skip vote`_ for verified status, which means
+it will reset the verified status to 0. If you want to keep the verified
+status, use **recheck-experimental** in commit message to trigger both
+verify and experimental jobs.
+
You can add below persons as reviewers to your patch in order to get it
reviewed and submitted.
@@ -57,6 +70,7 @@ reviewed and submitted.
* jose.lausuch@ericsson.com
* koffirodrigue@gmail.com
* r-mibu@cq.jp.nec.com
+* tbramwell@linuxfoundation.org
Or Add the group releng-contributors
@@ -67,3 +81,5 @@ in `releng-jobs.yaml`_.
.. _releng-jobs.yaml:
https://gerrit.opnfv.org/gerrit/gitweb?p=releng.git;a=blob;f=jjb/releng-jobs.yaml;
+.. _skip vote:
+ https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger#GerritTrigger-SkipVote
diff --git a/jjb-sandbox/releng/releng-sandbox-jobs.yml b/jjb-sandbox/releng/releng-sandbox-jobs.yml
index adefe363e..97fea8992 100644
--- a/jjb-sandbox/releng/releng-sandbox-jobs.yml
+++ b/jjb-sandbox/releng/releng-sandbox-jobs.yml
@@ -13,7 +13,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: 'master'
scm:
- git-scm-gerrit
diff --git a/jjb/3rd_party_ci/create-apex-vms.sh b/jjb/3rd_party_ci/create-apex-vms.sh
index 3f5dbd1c4..0744ac89a 100755
--- a/jjb/3rd_party_ci/create-apex-vms.sh
+++ b/jjb/3rd_party_ci/create-apex-vms.sh
@@ -1,12 +1,8 @@
#!/bin/bash
-set -e
+set -o errexit
+set -o nounset
+set -o pipefail
-if [ -z ${WORKSPACE} ]; then
- echo "WORKSPACE is unset. Please do so."
- exit 1
-fi
-# wipe the WORKSPACE
-/bin/rm -rf $WORKSPACE/*
# clone opnfv sdnvpn repo
git clone https://gerrit.opnfv.org/gerrit/p/sdnvpn.git $WORKSPACE/sdnvpn
diff --git a/jjb/3rd_party_ci/download-netvirt-artifact.sh b/jjb/3rd_party_ci/download-netvirt-artifact.sh
index be2d4059a..6aea01d2a 100755
--- a/jjb/3rd_party_ci/download-netvirt-artifact.sh
+++ b/jjb/3rd_party_ci/download-netvirt-artifact.sh
@@ -1,27 +1,30 @@
#!/bin/bash
-set -e
+set -o errexit
+set -o nounset
+set -o pipefail
-if [ -z ${WORKSPACE} ]; then
- echo "WORKSPACE is unset. Please do so."
- exit 1
-fi
-# wipe the WORKSPACE
-/bin/rm -rf $WORKSPACE/*
+ODL_ZIP=distribution-karaf-0.6.0-SNAPSHOT.zip
echo "Attempting to fetch the artifact location from ODL Jenkins"
CHANGE_DETAILS_URL="https://git.opendaylight.org/gerrit/changes/netvirt~master~$GERRIT_CHANGE_ID/detail"
# due to limitation with the Jenkins Gerrit Trigger, we need to use Gerrit REST API to get the change details
-ODL_JOB_URL=$(curl -s $CHANGE_DETAILS_URL | grep netvirt-patch-test-current-carbon | tail -1 | \
- sed 's/\\n//g' | awk '{print $6}')
-NETVIRT_ARTIFACT_URL="${ODL_JOB_URL}org.opendaylight.integration\$distribution-karaf/artifact/org.opendaylight.integration/distribution-karaf/0.6.0-SNAPSHOT/distribution-karaf-0.6.0-SNAPSHOT.tar.gz"
+ODL_BUILD_JOB_NUM=$(curl -s $CHANGE_DETAILS_URL | grep -Eo 'netvirt-distribution-check-carbon/[0-9]+' | tail -1 | grep -Eo [0-9]+)
+
+NETVIRT_ARTIFACT_URL="https://jenkins.opendaylight.org/releng/job/netvirt-distribution-check-carbon/${ODL_BUILD_JOB_NUM}/artifact/${ODL_ZIP}"
echo -e "URL to artifact is\n\t$NETVIRT_ARTIFACT_URL"
echo "Downloading the artifact. This could take time..."
-wget -q -O $NETVIRT_ARTIFACT $NETVIRT_ARTIFACT_URL
+wget -q -O $ODL_ZIP $NETVIRT_ARTIFACT_URL
if [[ $? -ne 0 ]]; then
echo "The artifact does not exist! Probably removed due to ODL Jenkins artifact retention policy."
echo "Rerun netvirt-patch-test-current-carbon to get artifact rebuilt."
exit 1
fi
+
+#TODO(trozet) remove this once odl-pipeline accepts zip files
+echo "Converting artifact zip to tar.gz"
+unzip $ODL_ZIP
+tar czf /tmp/${NETVIRT_ARTIFACT} $(echo $ODL_ZIP | sed -n 's/\.zip//p')
+
echo "Download complete"
-ls -al $NETVIRT_ARTIFACT
+ls -al /tmp/${NETVIRT_ARTIFACT}
diff --git a/jjb/3rd_party_ci/functest-netvirt.sh b/jjb/3rd_party_ci/functest-netvirt.sh
deleted file mode 100755
index adffaf42d..000000000
--- a/jjb/3rd_party_ci/functest-netvirt.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-set -e
-
-# wipe the WORKSPACE
-/bin/rm -rf $WORKSPACE/*
-
-echo "Hello World"
diff --git a/jjb/3rd_party_ci/install-netvirt.sh b/jjb/3rd_party_ci/install-netvirt.sh
index f111d4847..ed1a12bc8 100755
--- a/jjb/3rd_party_ci/install-netvirt.sh
+++ b/jjb/3rd_party_ci/install-netvirt.sh
@@ -1,15 +1,33 @@
#!/bin/bash
-set -e
+set -o errexit
+set -o nounset
+set -o pipefail
-if [ -z ${WORKSPACE} ]; then
- echo "WORKSPACE is unset. Please do so."
- exit 1
-fi
-# wipe the WORKSPACE
-/bin/rm -rf $WORKSPACE/*
+SNAP_CACHE=$HOME/snap_cache
# clone opnfv sdnvpn repo
git clone https://gerrit.opnfv.org/gerrit/p/sdnvpn.git $WORKSPACE/sdnvpn
-. $WORKSPACE/sdnvpn/odl-pipeline/odl-pipeline-common.sh
-pushd $LIB
-./odl_reinstaller.sh --cloner-info $CLONER_INFO --odl-artifact $NETVIRT_ARTIFACT
-popd \ No newline at end of file
+
+if [ ! -f "/tmp/${NETVIRT_ARTIFACT}" ]; then
+ echo "ERROR: /tmp/${NETVIRT_ARTIFACT} specified as NetVirt Artifact, but file does not exist"
+ exit 1
+fi
+
+if [ ! -f "${SNAP_CACHE}/node.yaml" ]; then
+ echo "ERROR: node.yaml pod config missing in ${SNAP_CACHE}"
+ exit 1
+fi
+
+if [ ! -f "${SNAP_CACHE}/id_rsa" ]; then
+ echo "ERROR: id_rsa ssh creds missing in ${SNAP_CACHE}"
+ exit 1
+fi
+
+# TODO (trozet) snapshot should have already been unpacked into cache folder
+# but we really should check the cache here, and not use a single cache folder
+# for when we support multiple jobs on a single slave
+pushd sdnvpn/odl-pipeline/lib > /dev/null
+# FIXME (trozet) remove this once permissions are fixed in sdnvpn repo
+chmod +x odl_reinstaller.sh
+./odl_reinstaller.sh --pod-config ${SNAP_CACHE}/node.yaml \
+ --odl-artifact /tmp/${NETVIRT_ARTIFACT} --ssh-key-file ${SNAP_CACHE}/id_rsa
+popd > /dev/null
diff --git a/jjb/3rd_party_ci/odl-netvirt.yml b/jjb/3rd_party_ci/odl-netvirt.yml
index ca1936156..470e4335e 100644
--- a/jjb/3rd_party_ci/odl-netvirt.yml
+++ b/jjb/3rd_party_ci/odl-netvirt.yml
@@ -17,13 +17,11 @@
#####################################
phase:
- 'create-apex-vms':
- slave-label: 'ericsson-virtual5'
+ slave-label: 'odl-netvirt-virtual-intel'
- 'install-netvirt':
- slave-label: 'odl-netvirt-virtual'
- - 'functest':
- slave-label: 'odl-netvirt-virtual'
+ slave-label: 'odl-netvirt-virtual-intel'
- 'postprocess':
- slave-label: 'odl-netvirt-virtual'
+ slave-label: 'odl-netvirt-virtual-intel'
#####################################
# jobs
#####################################
@@ -43,30 +41,38 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 5
max-per-node: 1
option: 'project'
+ scm:
+ - git:
+ url: https://gerrit.opnfv.org/gerrit/apex
+ branches:
+ - 'origin/master'
+ timeout: 15
+ wipe-workspace: true
+
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: NETVIRT_ARTIFACT
- default: $WORKSPACE/distribution-karaf.tar.gz
- - 'odl-netvirt-virtual-defaults'
+ default: distribution-karaf.tar.gz
+ - 'odl-netvirt-virtual-intel-defaults'
triggers:
- gerrit:
server-name: 'git.opendaylight.org'
trigger-on:
- - comment-added-contains-event:
- comment-contains-value: 'https://jenkins.opendaylight.org/releng/job/netvirt-patch-test-current-carbon/.*?/ : SUCCESS'
- - comment-added-contains-event:
- comment-contains-value: 'https://jenkins.opendaylight.org/releng/job/netvirt-patch-test-current-carbon/.*?/ : UNSTABLE'
+ # - comment-added-contains-event:
+ # comment-contains-value: 'https://jenkins.opendaylight.org/releng/job/netvirt-patch-test-current-carbon/.*?/ : SUCCESS'
+ # - comment-added-contains-event:
+ # comment-contains-value: 'https://jenkins.opendaylight.org/releng/job/netvirt-patch-test-current-carbon/.*?/ : UNSTABLE'
- comment-added-contains-event:
comment-contains-value: 'opnfv-test'
projects:
@@ -87,7 +93,7 @@
- name: 'odl-netvirt-verify-virtual-create-apex-vms-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_ID=$GERRIT_CHANGE_ID
@@ -95,7 +101,7 @@
GERRIT_PATCHSET_REVISION=$GERRIT_PATCHSET_REVISION
NETVIRT_ARTIFACT=$NETVIRT_ARTIFACT
APEX_ENV_NUMBER=$APEX_ENV_NUMBER
- node-parameters: false
+ node-parameters: true
kill-phase-on: FAILURE
abort-all-job: true
- multijob:
@@ -105,7 +111,7 @@
- name: 'odl-netvirt-verify-virtual-install-netvirt-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_ID=$GERRIT_CHANGE_ID
@@ -121,9 +127,9 @@
projects:
- name: 'functest-netvirt-virtual-suite-{stream}'
predefined-parameters: |
- FUNCTEST_SUITE_NAME=vping_userdata,bgpvpn
- RC_FILE_PATH=/home/jenkins/cloner-info/overcloudrc
- DEPLOY_SCENARIO=os-odl_l2-bgpvpn-noha
+ DEPLOY_SCENARIO=os-odl_l3-nofeature-ha
+ FUNCTEST_SUITE_NAME=tempest_smoke_serial
+ RC_FILE_PATH=$HOME/cloner-info/overcloudrc
node-parameters: true
kill-phase-on: FAILURE
abort-all-job: false
@@ -134,7 +140,7 @@
- name: 'odl-netvirt-verify-virtual-postprocess-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_ID=$GERRIT_CHANGE_ID
@@ -143,7 +149,7 @@
NETVIRT_ARTIFACT=$NETVIRT_ARTIFACT
node-parameters: true
kill-phase-on: FAILURE
- abort-all-job: true
+ abort-all-job: false
- job-template:
name: 'odl-netvirt-verify-virtual-{phase}-{stream}'
@@ -153,6 +159,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 5
@@ -161,8 +168,9 @@
- build-blocker:
use-build-blocker: true
blocking-jobs:
- - 'odl-netvirt-verify-virtual-install-.*'
- - 'odl-netvirt-verify-virtual-functest-.*'
+ - 'odl-netvirt-verify-virtual-create-apex-vms-.*'
+ - 'odl-netvirt-verify-virtual-install-netvirt-.*'
+ - 'functest-netvirt-virtual-suite-.*'
- 'odl-netvirt-verify-virtual-postprocess-.*'
block-level: 'NODE'
@@ -172,13 +180,28 @@
timeout: 360
fail: true
+ scm:
+ - git:
+ url: https://gerrit.opnfv.org/gerrit/apex
+ branches:
+ - 'origin/master'
+ timeout: 15
+ wipe-workspace: true
+
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- '{installer}-defaults'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-odl_l2-bgpvpn-noha'
+ description: 'Scenario to deploy and test'
+ - string:
+ name: GS_URL
+ default: artifacts.opnfv.org/apex
+ description: "URL to Google Storage with snapshot artifacts."
builders:
- description-setter:
@@ -191,7 +214,7 @@
name: 'netvirt-verify-create-apex-vms-builder'
builders:
- shell:
- !include-raw: ./create-apex-vms.sh
+ !include-raw: ../apex/apex-snapshot-deploy.sh
- builder:
name: 'netvirt-verify-install-netvirt-builder'
builders:
@@ -200,11 +223,6 @@
- shell:
!include-raw: ./install-netvirt.sh
- builder:
- name: 'netvirt-verify-functest-builder'
- builders:
- - shell:
- !include-raw: ./functest-netvirt.sh
-- builder:
name: 'netvirt-verify-postprocess-builder'
builders:
- shell:
diff --git a/jjb/3rd_party_ci/postprocess-netvirt.sh b/jjb/3rd_party_ci/postprocess-netvirt.sh
index 5baf378a9..796514259 100755
--- a/jjb/3rd_party_ci/postprocess-netvirt.sh
+++ b/jjb/3rd_party_ci/postprocess-netvirt.sh
@@ -1,12 +1,8 @@
#!/bin/bash
-set -e
+set -o errexit
+set -o nounset
+set -o pipefail
-if [ -z ${WORKSPACE} ]; then
- echo "WORKSPACE is unset. Please do so."
- exit 1
-fi
-# wipe the WORKSPACE
-/bin/rm -rf $WORKSPACE/*
# clone opnfv sdnvpn repo
git clone https://gerrit.opnfv.org/gerrit/p/sdnvpn.git $WORKSPACE/sdnvpn
. $WORKSPACE/sdnvpn/odl-pipeline/odl-pipeline-common.sh
diff --git a/jjb/apex/apex-build.sh b/jjb/apex/apex-build.sh
index ee1dfb5d3..220d02435 100755
--- a/jjb/apex/apex-build.sh
+++ b/jjb/apex/apex-build.sh
@@ -12,6 +12,9 @@ echo
if echo $BUILD_TAG | grep "apex-verify" 1> /dev/null; then
export OPNFV_ARTIFACT_VERSION=dev${BUILD_NUMBER}
export BUILD_ARGS="-r $OPNFV_ARTIFACT_VERSION -c $CACHE_DIRECTORY"
+elif echo $BUILD_TAG | grep "csit" 1> /dev/null; then
+ export OPNFV_ARTIFACT_VERSION=csit${BUILD_NUMBER}
+ export BUILD_ARGS="-r $OPNFV_ARTIFACT_VERSION -c $CACHE_DIRECTORY"
elif [ "$ARTIFACT_VERSION" == "daily" ]; then
export OPNFV_ARTIFACT_VERSION=$(date -u +"%Y-%m-%d")
export BUILD_ARGS="-r $OPNFV_ARTIFACT_VERSION -c $CACHE_DIRECTORY --iso"
diff --git a/jjb/apex/apex-deploy.sh b/jjb/apex/apex-deploy.sh
index 8d5c4cb13..c91e3ee82 100755
--- a/jjb/apex/apex-deploy.sh
+++ b/jjb/apex/apex-deploy.sh
@@ -3,7 +3,7 @@ set -o errexit
set -o nounset
set -o pipefail
-APEX_PKGS="common undercloud onos"
+APEX_PKGS="common undercloud" # removed onos for danube
IPV6_FLAG=False
# log info to console
@@ -15,7 +15,7 @@ if ! rpm -q wget > /dev/null; then
sudo yum -y install wget
fi
-if [[ $BUILD_DIRECTORY == *verify* ]]; then
+if [[ "$BUILD_DIRECTORY" == *verify* || "$BUILD_DIRECTORY" == *promote* ]]; then
# Build is from a verify, use local build artifacts (not RPMs)
cd $WORKSPACE/../${BUILD_DIRECTORY}
WORKSPACE=$(pwd)
@@ -62,10 +62,25 @@ fi
if [ -z "$DEPLOY_SCENARIO" ]; then
echo "Deploy scenario not set!"
exit 1
+elif [[ "$DEPLOY_SCENARIO" == *gate* ]]; then
+ echo "Detecting Gating scenario..."
+ if [ -z "$GERRIT_EVENT_COMMENT_TEXT" ]; then
+ echo "ERROR: Gate job triggered without comment!"
+ exit 1
+ else
+ DEPLOY_SCENARIO=$(echo ${GERRIT_EVENT_COMMENT_TEXT} | grep start-gate-scenario | grep -Eo 'os-.*$')
+ if [ -z "$DEPLOY_SCENARIO" ]; then
+ echo "ERROR: Unable to detect scenario in Gerrit Comment!"
+ echo "Format of comment to trigger gate should be 'start-gate-scenario: <scenario>'"
+ exit 1
+ else
+ echo "Gate scenario detected: ${DEPLOY_SCENARIO}"
+ fi
+ fi
fi
-# use local build for verify
-if [[ "$BUILD_DIRECTORY" == *verify* ]]; then
+# use local build for verify and promote
+if [[ "$BUILD_DIRECTORY" == *verify* || "$BUILD_DIRECTORY" == *promote* ]]; then
if [ ! -e "${WORKSPACE}/build/lib" ]; then
ln -s ${WORKSPACE}/lib ${WORKSPACE}/build/lib
fi
@@ -144,7 +159,7 @@ if [ "$OPNFV_CLEAN" == 'yes' ]; then
else
clean_opts=''
fi
- if [[ "$BUILD_DIRECTORY" == *verify* ]]; then
+ if [[ "$BUILD_DIRECTORY" == *verify* || "$BUILD_DIRECTORY" == *promote* ]]; then
sudo CONFIG=${CONFIG} LIB=${LIB} ./clean.sh ${clean_opts}
else
sudo CONFIG=${CONFIG} LIB=${LIB} opnfv-clean ${clean_opts}
@@ -166,21 +181,19 @@ fi
if [[ "$JOB_NAME" == *virtual* ]]; then
# settings for virtual deployment
- if [ "$IPV6_FLAG" == "True" ]; then
- NETWORK_FILE="${NETWORK_SETTINGS_DIR}/network_settings_v6.yaml"
- else
- NETWORK_FILE="${NETWORK_SETTINGS_DIR}/network_settings.yaml"
- fi
DEPLOY_CMD="${DEPLOY_CMD} -v"
+ if [[ "${DEPLOY_SCENARIO}" =~ fdio|ovs ]]; then
+ DEPLOY_CMD="${DEPLOY_CMD} --virtual-default-ram 14 --virtual-compute-ram 8"
+ fi
+ if [[ "$JOB_NAME" == *csit* ]]; then
+ DEPLOY_CMD="${DEPLOY_CMD} -e csit-environment.yaml"
+ fi
+ if [[ "$JOB_NAME" == *promote* ]]; then
+ DEPLOY_CMD="${DEPLOY_CMD} --virtual-computes 2"
+ fi
else
# settings for bare metal deployment
- if [ "$IPV6_FLAG" == "True" ]; then
- NETWORK_FILE="/root/network/network_settings_v6.yaml"
- elif [[ "$JOB_NAME" == *master* ]]; then
- NETWORK_FILE="/root/network/network_settings-master.yaml"
- else
- NETWORK_FILE="/root/network/network_settings.yaml"
- fi
+ NETWORK_SETTINGS_DIR="/root/network"
INVENTORY_FILE="/root/inventory/pod_settings.yaml"
if ! sudo test -e "$INVENTORY_FILE"; then
@@ -191,6 +204,14 @@ else
DEPLOY_CMD="${DEPLOY_CMD} -i ${INVENTORY_FILE}"
fi
+if [ "$IPV6_FLAG" == "True" ]; then
+ NETWORK_FILE="${NETWORK_SETTINGS_DIR}/network_settings_v6.yaml"
+elif echo ${DEPLOY_SCENARIO} | grep fdio; then
+ NETWORK_FILE="${NETWORK_SETTINGS_DIR}/network_settings_vpp.yaml"
+else
+ NETWORK_FILE="${NETWORK_SETTINGS_DIR}/network_settings.yaml"
+fi
+
# Check that network settings file exists
if ! sudo test -e "$NETWORK_FILE"; then
echo "ERROR: Required settings file missing: Network Settings file ${NETWORK_FILE}"
@@ -200,6 +221,16 @@ fi
# start deployment
sudo ${DEPLOY_CMD} -d ${DEPLOY_FILE} -n ${NETWORK_FILE} --debug
+if [[ "$JOB_NAME" == *csit* ]]; then
+ echo "CSIT job: setting host route for floating ip routing"
+ # csit route to allow docker container to reach floating ips
+ UNDERCLOUD=$(sudo virsh domifaddr undercloud | grep -Eo "[0-9\.]+{3}[0-9]+")
+ if sudo route | grep 192.168.37.128 > /dev/null; then
+ sudo route del -net 192.168.37.128 netmask 255.255.255.128
+ fi
+ sudo route add -net 192.168.37.128 netmask 255.255.255.128 gw ${UNDERCLOUD}
+fi
+
echo
echo "--------------------------------------------------------"
echo "Done!"
diff --git a/jjb/apex/apex-snapshot-create.sh b/jjb/apex/apex-snapshot-create.sh
new file mode 100644
index 000000000..b2a39449e
--- /dev/null
+++ b/jjb/apex/apex-snapshot-create.sh
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+##############################################################################
+# Copyright (c) 2016 Tim Rozet (Red Hat) and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error)
+SNAP_TYPE=$(echo ${JOB_NAME} | sed -n 's/^apex-\(.\+\)-promote.*$/\1/p')
+
+echo "Creating Apex snapshot..."
+echo "-------------------------"
+echo
+
+# create tmp directory
+tmp_dir=$(pwd)/.tmp
+mkdir -p ${tmp_dir}
+
+# TODO(trozet) remove this after fix goes in for tripleo_inspector to copy these
+pushd ${tmp_dir} > /dev/null
+echo "Copying overcloudrc and ssh key from Undercloud..."
+# Store overcloudrc
+UNDERCLOUD=$(sudo virsh domifaddr undercloud | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
+sudo scp ${SSH_OPTIONS[@]} stack@${UNDERCLOUD}:overcloudrc ./
+# Copy out ssh key of stack from undercloud
+sudo scp ${SSH_OPTIONS[@]} stack@${UNDERCLOUD}:.ssh/id_rsa ./
+popd > /dev/null
+
+echo "Gathering introspection information"
+git clone https://gerrit.opnfv.org/gerrit/sdnvpn.git
+pushd sdnvpn/odl-pipeline/lib > /dev/null
+sudo ./tripleo_introspector.sh --out-file ${tmp_dir}/node.yaml
+popd > /dev/null
+sudo rm -rf sdnvpn
+
+echo "Shutting down nodes"
+# Shut down nodes
+nodes=$(sudo virsh list | grep -Eo "baremetal[0-9]")
+for node in $nodes; do
+ sudo virsh shutdown ${node} --mode acpi
+done
+
+for node in $nodes; do
+ count=0
+ while [ "$count" -lt 10 ]; do
+ sleep 10
+ if sudo virsh list | grep ${node}; then
+ echo "Waiting for $node to shutdown, try $count"
+ else
+ break
+ fi
+ count=$((count+1))
+ done
+
+ if [ "$count" -ge 10 ]; then
+ echo "Node $node failed to shutdown"
+ exit 1
+ fi
+done
+
+pushd ${tmp_dir} > /dev/null
+echo "Gathering virsh definitions"
+# copy qcow2s, virsh definitions
+for node in $nodes; do
+ sudo cp -f /var/lib/libvirt/images/${node}.qcow2 ./
+ sudo virsh dumpxml ${node} > ${node}.xml
+done
+
+# copy virsh net definitions
+for net in admin api external storage tenant; do
+ sudo virsh net-dumpxml ${net} > ${net}.xml
+done
+
+sudo chown jenkins-ci:jenkins-ci *
+
+# tar up artifacts
+DATE=`date +%Y-%m-%d`
+tar czf ../apex-${SNAP_TYPE}-snap-${DATE}.tar.gz .
+popd > /dev/null
+sudo rm -rf ${tmp_dir}
+echo "Snapshot saved as apex-${SNAP_TYPE}-snap-${DATE}.tar.gz"
+
+# update opnfv properties file
+if [ "$SNAP_TYPE" == 'csit' ]; then
+ curl -O -L http://$GS_URL/snapshot.properties
+ sed -i '/^OPNFV_SNAP_URL=/{h;s#=.*#='${GS_URL}'/apex-csit-snap-'${DATE}'.tar.gz#};${x;/^$/{s##OPNFV_SNAP_URL='${GS_URL}'/apex-csit-snap-'${DATE}'.tar.gz#;H};x}' snapshot.properties
+ snap_sha=$(sha512sum apex-csit-snap-${DATE}.tar.gz | cut -d' ' -f1)
+ sed -i '/^OPNFV_SNAP_SHA512SUM=/{h;s/=.*/='${snap_sha}'/};${x;/^$/{s//OPNFV_SNAP_SHA512SUM='${snap_sha}'/;H};x}' snapshot.properties
+ echo "OPNFV_SNAP_URL=$GS_URL/apex-csit-snap-${DATE}.tar.gz"
+ echo "OPNFV_SNAP_SHA512SUM=$(sha512sum apex-csit-snap-${DATE}.tar.gz | cut -d' ' -f1)"
+ echo "Updated properties file: "
+ cat snapshot.properties
+fi
diff --git a/jjb/apex/apex-snapshot-deploy.sh b/jjb/apex/apex-snapshot-deploy.sh
new file mode 100644
index 000000000..8274740c8
--- /dev/null
+++ b/jjb/apex/apex-snapshot-deploy.sh
@@ -0,0 +1,165 @@
+#!/usr/bin/env bash
+##############################################################################
+# Copyright (c) 2016 Tim Rozet (Red Hat) and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+SSH_OPTIONS=(-o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null -o LogLevel=error)
+SNAP_CACHE=$HOME/snap_cache
+
+
+echo "Deploying Apex snapshot..."
+echo "--------------------------"
+echo
+
+echo "Cleaning server"
+pushd ci > /dev/null
+sudo CONFIG=../build/ LIB=../lib ./clean.sh
+popd > /dev/null
+
+echo "Downloading latest snapshot properties file"
+if ! wget -O $WORKSPACE/opnfv.properties http://$GS_URL/snapshot.properties; then
+ echo "ERROR: Unable to find snapshot.properties at ${GS_URL}...exiting"
+ exit 1
+fi
+
+# find latest check sum
+latest_snap_checksum=$(cat opnfv.properties | grep OPNFV_SNAP_SHA512SUM | awk -F "=" '{print $2}')
+if [ -z "$latest_snap_checksum" ]; then
+ echo "ERROR: checksum of latest snapshot from snapshot.properties is null!"
+ exit 1
+fi
+
+local_snap_checksum=""
+
+# check snap cache directory exists
+# if snapshot cache exists, find the checksum
+if [ -d "$SNAP_CACHE" ]; then
+ latest_snap=$(ls ${SNAP_CACHE} | grep tar.gz | tail -n 1)
+ if [ -n "$latest_snap" ]; then
+ local_snap_checksum=$(sha512sum ${SNAP_CACHE}/${latest_snap} | cut -d' ' -f1)
+ fi
+else
+ mkdir -p ${SNAP_CACHE}
+fi
+
+# compare check sum and download latest snap if not up to date
+if [ "$local_snap_checksum" != "$latest_snap_checksum" ]; then
+ snap_url=$(cat opnfv.properties | grep OPNFV_SNAP_URL | awk -F "=" '{print $2}')
+ if [ -z "$snap_url" ]; then
+ echo "ERROR: Snap URL from snapshot.properties is null!"
+ exit 1
+ fi
+ echo "INFO: SHA mismatch, will download latest snapshot"
+ # wipe cache
+ rm -rf ${SNAP_CACHE}/*
+ wget --directory-prefix=${SNAP_CACHE}/ ${snap_url}
+ snap_tar=$(basename ${snap_url})
+else
+ snap_tar=${latest_snap}
+fi
+
+echo "INFO: Snapshot to be used is ${snap_tar}"
+
+# move to snap cache dir and unpack
+pushd ${SNAP_CACHE} > /dev/null
+tar xvf ${snap_tar}
+
+# create each network
+virsh_networks=$(ls *.xml | grep -v baremetal)
+
+if [ -z "$virsh_networks" ]; then
+ echo "ERROR: no virsh networks found in snapshot unpack"
+ exit 1
+fi
+
+echo "Checking overcloudrc"
+if ! stat overcloudrc; then
+ echo "ERROR: overcloudrc does not exist in snap unpack"
+ exit 1
+fi
+
+for network_def in ${virsh_networks}; do
+ sudo virsh net-create ${network_def}
+ network=$(echo ${network_def} | awk -F '.' '{print $1}')
+ if ! sudo virsh net-list | grep ${network}; then
+ sudo virsh net-start ${network}
+ fi
+ echo "Checking if OVS bridge is missing for network: ${network}"
+ if ! sudo ovs-vsctl show | grep "br-${network}"; then
+ sudo ovs-vsctl add-br br-${network}
+ echo "OVS Bridge created: br-${network}"
+ if [ "br-${network}" == 'br-admin' ]; then
+ echo "Configuring IP 192.0.2.99 on br-admin"
+ sudo ip addr add 192.0.2.99/24 dev br-admin
+ sudo ip link set up dev br-admin
+ elif [ "br-${network}" == 'br-external' ]; then
+ echo "Configuring IP 192.168.37.1 on br-external"
+ sudo ip addr add 192.168.37.1/24 dev br-external
+ sudo ip link set up dev br-external
+ # Routes for admin network
+ # The overcloud controller is multi-homed and will fail to respond
+ # to traffic from the functest container due to reverse-path-filtering
+ # This route allows reverse traffic, by forcing admin network destined
+ # traffic through the external network for controller IPs only.
+ # Compute nodes have no ip on external interfaces.
+ controller_ips=$(cat overcloudrc | grep -Eo "192.0.2.[0-9]+")
+ for ip in $controller_ips; do
+ sudo ip route add ${ip}/32 dev br-external
+ done
+ fi
+ fi
+done
+
+echo "Virsh networks up: $(sudo virsh net-list)"
+echo "Bringing up Overcloud VMs..."
+virsh_vm_defs=$(ls baremetal*.xml)
+
+if [ -z "$virsh_vm_defs" ]; then
+ echo "ERROR: no virsh VMs found in snapshot unpack"
+ exit 1
+fi
+
+for node_def in ${virsh_vm_defs}; do
+ sudo virsh define ${node_def}
+ node=$(echo ${node_def} | awk -F '.' '{print $1}')
+ sudo cp -f ${node}.qcow2 /var/lib/libvirt/images/
+ sudo virsh start ${node}
+ echo "Node: ${node} started"
+done
+
+# copy overcloudrc for functest
+mkdir -p $HOME/cloner-info
+cp -f overcloudrc $HOME/cloner-info/
+
+admin_controller_ip=$(cat overcloudrc | grep -Eo -m 1 "192.0.2.[0-9]+")
+netvirt_url="http://${admin_controller_ip}:8081/restconf/operational/network-topology:network-topology/topology/netvirt:1"
+
+source overcloudrc
+counter=1
+while [ "$counter" -le 10 ]; do
+ if curl --fail --silent ${admin_controller_ip}:80 > /dev/null; then
+ echo "Overcloud Horizon is up...Checking if OpenDaylight NetVirt is up..."
+ if curl --fail --silent -u admin:admin ${netvirt_url} > /dev/null; then
+ echo "OpenDaylight is up. Overcloud deployment complete"
+ exit 0
+ else
+ echo "OpenDaylight not yet up, try ${counter}"
+ fi
+ else
+ echo "Horizon/Apache not yet up, try ${counter}"
+ fi
+ counter=$((counter+1))
+ sleep 60
+done
+
+echo "ERROR: Deployment not up after 10 minutes...exiting."
+exit 1
diff --git a/jjb/apex/apex-upload-artifact.sh b/jjb/apex/apex-upload-artifact.sh
index 64f13f4e6..c2de7d70d 100755
--- a/jjb/apex/apex-upload-artifact.sh
+++ b/jjb/apex/apex-upload-artifact.sh
@@ -51,13 +51,13 @@ echo "ISO Upload Complete!"
RPM_INSTALL_PATH=$BUILD_DIRECTORY/noarch
RPM_LIST=$RPM_INSTALL_PATH/$(basename $OPNFV_RPM_URL)
VERSION_EXTENSION=$(echo $(basename $OPNFV_RPM_URL) | sed 's/opnfv-apex-//')
-for pkg in common undercloud onos; do
+for pkg in common undercloud; do # removed onos for danube
RPM_LIST+=" ${RPM_INSTALL_PATH}/opnfv-apex-${pkg}-${VERSION_EXTENSION}"
done
SRPM_INSTALL_PATH=$BUILD_DIRECTORY
SRPM_LIST=$SRPM_INSTALL_PATH/$(basename $OPNFV_SRPM_URL)
VERSION_EXTENSION=$(echo $(basename $OPNFV_SRPM_URL) | sed 's/opnfv-apex-//')
-for pkg in common undercloud onos; do
+for pkg in common undercloud; do # removed onos for danube
SRPM_LIST+=" ${SRPM_INSTALL_PATH}/opnfv-apex-${pkg}-${VERSION_EXTENSION}"
done
}
@@ -73,7 +73,20 @@ gsutil cp $WORKSPACE/opnfv.properties gs://$GS_URL/opnfv-$OPNFV_ARTIFACT_VERSION
gsutil cp $WORKSPACE/opnfv.properties gs://$GS_URL/latest.properties > gsutil.latest.log
}
-if gpg2 --list-keys | grep "opnfv-helpdesk@rt.linuxfoundation.org"; then
+uploadsnap () {
+ # Uploads snapshot artifact and updated properties file
+ echo "Uploading snapshot artifacts"
+ SNAP_TYPE=$(echo ${JOB_NAME} | sed -n 's/^apex-\(.\+\)-promote.*$/\1/p')
+ gsutil cp $WORKSPACE/apex-${SNAP_TYPE}-snap-`date +%Y-%m-%d`.tar.gz gs://$GS_URL/ > gsutil.iso.log
+ if [ "$SNAP_TYPE" == 'csit' ]; then
+ gsutil cp $WORKSPACE/snapshot.properties gs://$GS_URL/snapshot.properties > gsutil.latest.log
+ fi
+ echo "Upload complete for Snapshot"
+}
+
+if echo $WORKSPACE | grep promote > /dev/null; then
+ uploadsnap
+elif gpg2 --list-keys | grep "opnfv-helpdesk@rt.linuxfoundation.org"; then
echo "Signing Key avaliable"
signiso
uploadiso
diff --git a/jjb/apex/apex.yml b/jjb/apex/apex.yml
index 5c1bded4c..9ce83316b 100644
--- a/jjb/apex/apex.yml
+++ b/jjb/apex/apex.yml
@@ -2,6 +2,7 @@
name: apex
jobs:
- 'apex-verify-{stream}'
+ - 'apex-verify-gate-{stream}'
- 'apex-verify-unit-tests-{stream}'
- 'apex-runner-{platform}-{scenario}-{stream}'
- 'apex-runner-cperf-{stream}'
@@ -9,9 +10,8 @@
- 'apex-deploy-virtual-{scenario}-{stream}'
- 'apex-deploy-baremetal-{scenario}-{stream}'
- 'apex-daily-{stream}'
- - 'apex-daily-colorado'
- - 'apex-build-colorado'
- - 'apex-deploy-baremetal-os-odl_l2-fdio-ha-colorado'
+ - 'apex-csit-promote-daily-{stream}'
+ - 'apex-fdio-promote-daily-{stream}'
# stream: branch with - in place of / (eg. stable-arno)
# branch: branch (eg. stable/arno)
@@ -22,6 +22,12 @@
slave: 'lf-pod1'
verify-slave: 'apex-verify-master'
daily-slave: 'apex-daily-master'
+ - danube:
+ branch: 'stable/danube'
+ gs-pathname: '/danube'
+ slave: 'lf-pod1'
+ verify-slave: 'apex-verify-master'
+ daily-slave: 'apex-daily-master'
project: 'apex'
@@ -32,16 +38,22 @@
- 'os-nosdn-ovs-noha'
- 'os-nosdn-fdio-noha'
- 'os-nosdn-fdio-ha'
+ - 'os-nosdn-kvm-ha'
+ - 'os-nosdn-kvm-noha'
+ - 'os-odl_l2-fdio-noha'
- 'os-odl_l2-fdio-ha'
- 'os-odl_l2-netvirt_gbp_fdio-noha'
- 'os-odl_l2-sfc-noha'
- 'os-odl_l3-nofeature-ha'
- - 'os-odl_l3-bgpvpn-ha'
+ - 'os-odl-bgpvpn-ha'
+ - 'os-odl-gluon-noha'
- 'os-odl_l3-fdio-noha'
- 'os-odl_l3-fdio-ha'
- 'os-odl_l3-fdio_dvr-noha'
- 'os-odl_l3-fdio_dvr-ha'
+ - 'os-odl_l3-csit-noha'
- 'os-onos-nofeature-ha'
+ - 'gate'
platform:
- 'baremetal'
@@ -60,7 +72,6 @@
gs-pathname: '{gs-pathname}'
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_BASE
@@ -93,6 +104,7 @@
- compare-type: ANT
pattern: 'tests/**'
properties:
+ - logrotate-default
- throttle:
max-per-node: 1
max-total: 10
@@ -114,7 +126,6 @@
gs-pathname: '{gs-pathname}'
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_BASE
@@ -154,6 +165,7 @@
pattern: 'config/**'
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
block-level: 'NODE'
@@ -172,7 +184,7 @@
- 'apex-unit-test'
- 'apex-build'
- trigger-builds:
- - project: 'apex-deploy-virtual-os-nosdn-nofeature-ha-{stream}'
+ - project: 'apex-deploy-virtual-os-odl_l3-nofeature-ha-{stream}'
predefined-parameters: |
BUILD_DIRECTORY=apex-verify-{stream}
OPNFV_CLEAN=yes
@@ -182,22 +194,87 @@
- trigger-builds:
- project: 'functest-apex-{verify-slave}-suite-{stream}'
predefined-parameters: |
- DEPLOY_SCENARIO=os-nosdn-nofeature-ha
+ DEPLOY_SCENARIO=os-odl_l3-nofeature-ha
FUNCTEST_SUITE_NAME=healthcheck
block: true
same-node: true
+ - 'apex-workspace-cleanup'
+
+# Verify Scenario Gate
+- job-template:
+ name: 'apex-verify-gate-{stream}'
+
+ node: '{verify-slave}'
+
+ concurrent: true
+
+ parameters:
+ - apex-parameter:
+ gs-pathname: '{gs-pathname}'
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: "Used for overriding the GIT URL coming from parameters macro."
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - comment-added-contains-event:
+ comment-contains-value: '^Patch Set [0-9]+: Code-Review\+2.*start-gate-scenario:.*'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: 'apex'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: ANT
+ pattern: 'ci/**'
+ - compare-type: ANT
+ pattern: 'build/**'
+ - compare-type: ANT
+ pattern: 'lib/**'
+ - compare-type: ANT
+ pattern: 'config/**'
+
+ properties:
+ - logrotate-default
+ - build-blocker:
+ use-build-blocker: true
+ block-level: 'NODE'
+ blocking-jobs:
+ - 'apex-daily.*'
+ - 'apex-deploy.*'
+ - 'apex-build.*'
+ - 'apex-runner.*'
+ - 'apex-verify.*'
+ - throttle:
+ max-per-node: 1
+ max-total: 10
+ option: 'project'
+
+ builders:
+ - 'apex-build'
- trigger-builds:
- - project: 'apex-deploy-virtual-os-odl_l3-nofeature-ha-{stream}'
+ - project: 'apex-deploy-virtual-gate-{stream}'
predefined-parameters: |
- BUILD_DIRECTORY=apex-verify-{stream}
+ BUILD_DIRECTORY=apex-verify-gate-{stream}
OPNFV_CLEAN=yes
+ current-parameters: true
git-revision: false
block: true
same-node: true
- trigger-builds:
- project: 'functest-apex-{verify-slave}-suite-{stream}'
predefined-parameters: |
- DEPLOY_SCENARIO=os-odl_l3-nofeature-ha
+ DEPLOY_SCENARIO=os-nosdn-nofeature-ha
FUNCTEST_SUITE_NAME=healthcheck
block: true
same-node: true
@@ -217,7 +294,6 @@
gs-pathname: '{gs-pathname}'
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_BASE
@@ -228,11 +304,13 @@
- git-scm
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
blocking-jobs:
- 'apex-daily.*'
- 'apex-verify.*'
+ - 'apex-.*-promote.*'
builders:
- trigger-builds:
@@ -274,7 +352,6 @@
gs-pathname: '{gs-pathname}'
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_BASE
@@ -285,6 +362,7 @@
- git-scm
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: false
block-level: 'NODE'
@@ -327,10 +405,9 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- apex-parameter:
gs-pathname: '{gs-pathname}'
- - gerrit-parameter:
- branch: '{branch}'
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -340,6 +417,7 @@
- git-scm
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
block-level: 'NODE'
@@ -382,7 +460,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- apex-parameter:
gs-pathname: '{gs-pathname}'
@@ -396,6 +473,7 @@
description: "Use yes in lower case to invoke clean. Indicates if the deploy environment should be cleaned before deployment"
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
block-level: 'NODE'
@@ -428,7 +506,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- apex-parameter:
gs-pathname: '{gs-pathname}'
@@ -438,6 +515,7 @@
description: "Scenario to deploy with."
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
block-level: 'NODE'
@@ -470,12 +548,12 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- apex-parameter:
gs-pathname: '{gs-pathname}'
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
block-level: 'NODE'
@@ -484,6 +562,7 @@
- 'apex-deploy.*'
- 'apex-build.*'
- 'apex-runner.*'
+ - 'apex-.*-promote.*'
triggers:
- 'apex-{stream}'
@@ -525,6 +604,23 @@
build-step-failure-threshold: 'never'
failure-threshold: 'never'
unstable-threshold: 'FAILURE'
+ # 1.dovetail only master by now, not sync with A/B/C branches
+ # 2.here the stream means the SUT stream, dovetail stream is defined in its own job
+ # 3.only debug testsuite here(includes basic testcase,
+ # i.e. one tempest smoke ipv6, two vping from functest)
+ # 4.not used for release criteria or compliance,
+ # only to debug the dovetail tool bugs with apex
+ - trigger-builds:
+ - project: 'dovetail-apex-{slave}-debug-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-nosdn-nofeature-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
- trigger-builds:
- project: 'apex-deploy-baremetal-os-odl_l3-nofeature-ha-{stream}'
predefined-parameters: |
@@ -555,64 +651,167 @@
build-step-failure-threshold: 'never'
failure-threshold: 'never'
unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'apex-deploy-baremetal-os-odl-bgpvpn-ha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-build-{stream}/.build
+ OPNFV_CLEAN=yes
+ git-revision: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ block: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl-bgpvpn-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'yardstick-apex-{slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl-bgpvpn-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'apex-deploy-baremetal-os-odl-gluon-noha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-build-{stream}/.build
+ OPNFV_CLEAN=yes
+ git-revision: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ block: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl-gluon-noha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'yardstick-apex-{slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl-gluon-noha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'apex-deploy-baremetal-os-odl_l2-fdio-noha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-build-{stream}/.build
+ OPNFV_CLEAN=yes
+ git-revision: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ block: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl_l2-fdio-noha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'yardstick-apex-{slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl_l2-fdio-noha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'apex-deploy-baremetal-os-odl_l2-fdio-ha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-build-{stream}/.build
+ OPNFV_CLEAN=yes
+ git-revision: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ block: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl_l2-fdio-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'yardstick-apex-{slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-odl_l2-fdio-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'apex-deploy-baremetal-os-nosdn-kvm-ha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-build-{stream}/.build
+ OPNFV_CLEAN=yes
+ git-revision: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ block: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-nosdn-kvm-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'yardstick-apex-{slave}-daily-{stream}'
+ predefined-parameters:
+ DEPLOY_SCENARIO=os-nosdn-kvm-ha
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
-
-# Colorado Build
-- job-template:
- name: 'apex-build-colorado'
-
- # Job template for builds
- #
- # Required Variables:
- # stream: branch with - in place of / (eg. stable)
- # branch: branch (eg. stable)
- node: 'apex-daily-colorado'
-
- disabled: false
-
- concurrent: true
-
- parameters:
- - project-parameter:
- project: '{project}'
- - apex-parameter:
- gs-pathname: '/colorado'
- - gerrit-parameter:
- branch: 'stable/colorado'
- - string:
- name: GIT_BASE
- default: https://gerrit.opnfv.org/gerrit/$PROJECT
- description: "Used for overriding the GIT URL coming from parameters macro."
-
- scm:
- - git-scm
-
- properties:
- - build-blocker:
- use-build-blocker: true
- block-level: 'NODE'
- blocking-jobs:
- - 'apex-deploy.*'
- - throttle:
- max-per-node: 1
- max-total: 10
- option: 'project'
-
- builders:
- - 'apex-build'
- - 'apex-upload-artifact'
-
-
-# Colorado FDIO Deploy
+# CSIT promote
- job-template:
- name: 'apex-deploy-baremetal-os-odl_l2-fdio-ha-colorado'
+ name: 'apex-csit-promote-daily-{stream}'
- # Job template for baremetal deployment
+ # Job template for promoting CSIT Snapshots
#
# Required Variables:
# stream: branch with - in place of / (eg. stable)
# branch: branch (eg. stable)
- node: 'lf-pod1'
+ node: '{daily-slave}'
disabled: false
@@ -622,14 +821,9 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
- branch: 'stable/colorado'
+ branch: '{branch}'
- apex-parameter:
- gs-pathname: '/colorado'
- - string:
- name: DEPLOY_SCENARIO
- default: 'os-odl_l2-fdio-ha'
- description: "Scenario to deploy with."
+ gs-pathname: '{gs-pathname}'
properties:
- build-blocker:
@@ -639,22 +833,44 @@
- 'apex-verify.*'
- 'apex-deploy.*'
- 'apex-build.*'
+ - 'apex-runner.*'
+ - 'apex-daily.*'
+ triggers:
+ - timed: '0 12 * * 0'
builders:
- - 'apex-deploy'
- - 'apex-workspace-cleanup'
+ - 'apex-build'
+ - trigger-builds:
+ - project: 'apex-deploy-virtual-os-odl_l3-csit-noha-{stream}'
+ predefined-parameters: |
+ BUILD_DIRECTORY=apex-csit-promote-daily-{stream}
+ OPNFV_CLEAN=yes
+ git-revision: false
+ block: true
+ same-node: true
+ - trigger-builds:
+ - project: 'functest-apex-{daily-slave}-suite-{stream}'
+ predefined-parameters: |
+ DEPLOY_SCENARIO=os-odl_l3-nofeature-noha
+ FUNCTEST_SUITE_NAME=tempest_smoke_serial
+ block: true
+ same-node: true
+ - shell:
+ !include-raw-escape: ./apex-snapshot-create.sh
+ - shell:
+ !include-raw-escape: ./apex-upload-artifact.sh
-# Colorado FDIO Daily
+# FDIO promote
- job-template:
- name: 'apex-daily-colorado'
+ name: 'apex-fdio-promote-daily-{stream}'
- # Job template for daily build
+ # Job template for promoting CSIT Snapshots
#
# Required Variables:
# stream: branch with - in place of / (eg. stable)
# branch: branch (eg. stable)
- node: 'apex-daily-colorado'
+ node: '{daily-slave}'
disabled: false
@@ -664,10 +880,9 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
- branch: 'stable/colorado'
+ branch: '{branch}'
- apex-parameter:
- gs-pathname: '/colorado'
+ gs-pathname: '{gs-pathname}'
properties:
- build-blocker:
@@ -678,37 +893,22 @@
- 'apex-deploy.*'
- 'apex-build.*'
- 'apex-runner.*'
-
- triggers:
- - 'apex-colorado'
+ - 'apex-daily.*'
builders:
+ - 'apex-build'
- trigger-builds:
- - project: 'apex-build-colorado'
- git-revision: true
- current-parameters: true
- same-node: true
- block: true
- - trigger-builds:
- - project: 'apex-deploy-baremetal-os-odl_l2-fdio-ha-colorado'
+ - project: 'apex-deploy-virtual-os-odl_l2-fdio-noha-{stream}'
predefined-parameters: |
- BUILD_DIRECTORY=apex-build-colorado/.build
+ BUILD_DIRECTORY=apex-fdio-promote-daily-{stream}
OPNFV_CLEAN=yes
- git-revision: true
- same-node: true
- block-thresholds:
- build-step-failure-threshold: 'never'
- block: true
- - trigger-builds:
- - project: 'functest-apex-apex-daily-colorado-daily-colorado'
- predefined-parameters:
- DEPLOY_SCENARIO=os-odl_l2-fdio-ha
+ git-revision: false
block: true
same-node: true
- block-thresholds:
- build-step-failure-threshold: 'never'
- failure-threshold: 'never'
- unstable-threshold: 'FAILURE'
+ - shell:
+ !include-raw-escape: ./apex-snapshot-create.sh
+ - shell:
+ !include-raw-escape: ./apex-upload-artifact.sh
- job-template:
name: 'apex-gs-clean-{stream}'
@@ -724,6 +924,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- apex-parameter:
gs-pathname: '{gs-pathname}'
@@ -815,9 +1016,9 @@
- trigger:
name: 'apex-master'
triggers:
- - timed: '0 3 * * *'
+ - timed: '0 3 * * 7'
- trigger:
- name: 'apex-colorado'
+ name: 'apex-danube'
triggers:
- timed: '0 12 * * *'
- trigger:
diff --git a/jjb/armband/armband-ci-jobs.yml b/jjb/armband/armband-ci-jobs.yml
index 4e88678b4..4cb58d916 100644
--- a/jjb/armband/armband-ci-jobs.yml
+++ b/jjb/armband/armband-ci-jobs.yml
@@ -104,6 +104,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -125,6 +126,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
installer: '{installer}'
@@ -188,6 +190,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -203,7 +206,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
diff --git a/jjb/armband/armband-deploy.sh b/jjb/armband/armband-deploy.sh
index 4df9acfd8..adabfcaeb 100755
--- a/jjb/armband/armband-deploy.sh
+++ b/jjb/armband/armband-deploy.sh
@@ -8,7 +8,6 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-set -o errexit
set -o nounset
set -o pipefail
@@ -49,8 +48,8 @@ mkdir -p $TMPDIR
cd $WORKSPACE
if [[ $LAB_CONFIG_URL =~ ^(git|ssh):// ]]; then
- echo "Cloning securedlab repo ${GIT_BRANCH##origin/}"
- git clone --quiet --branch ${GIT_BRANCH##origin/} $LAB_CONFIG_URL lab-config
+ echo "Cloning securedlab repo $BRANCH"
+ git clone --quiet --branch $BRANCH $LAB_CONFIG_URL lab-config
LAB_CONFIG_URL=file://${WORKSPACE}/lab-config
# Source local_env if present, which contains POD-specific config
@@ -73,7 +72,7 @@ FUEL_LOG_FILENAME="${JOB_NAME}_${BUILD_NUMBER}.log.tar.gz"
# Deploy Cache (to enable just create the deploy-cache subdir)
# NOTE: Only available when ISO files are cached using ISOSTORE mechanism
-DEPLOY_CACHE=${ISOSTORE:-/iso_mount/opnfv_ci}/${GIT_BRANCH##*/}/deploy-cache
+DEPLOY_CACHE=${ISOSTORE:-/iso_mount/opnfv_ci}/${BRANCH##*/}/deploy-cache
if [[ -d "${DEPLOY_CACHE}" ]]; then
echo "Deploy cache dir present."
echo "--------------------------------------------------------"
diff --git a/jjb/armband/armband-download-artifact.sh b/jjb/armband/armband-download-artifact.sh
index ed7897b8e..e2dd097b6 100755
--- a/jjb/armband/armband-download-artifact.sh
+++ b/jjb/armband/armband-download-artifact.sh
@@ -38,7 +38,7 @@ ISO_FILE=${WORKSPACE}/opnfv.iso
# using ISOs for verify & merge jobs from local storage will be enabled later
if [[ ! "$JOB_NAME" =~ (verify|merge) ]]; then
# check if we already have the ISO to avoid redownload
- ISOSTORE=${ISOSTORE:-/iso_mount/opnfv_ci}/${GIT_BRANCH##*/}
+ ISOSTORE=${ISOSTORE:-/iso_mount/opnfv_ci}/${BRANCH##*/}
if [[ -f "$ISOSTORE/$OPNFV_ARTIFACT" ]]; then
echo "ISO exists locally. Skipping the download and using the file from ISO store"
ln -s $ISOSTORE/$OPNFV_ARTIFACT ${ISO_FILE}
diff --git a/jjb/armband/armband-project-jobs.yml b/jjb/armband/armband-project-jobs.yml
index 981f509c7..fd37c5af6 100644
--- a/jjb/armband/armband-project-jobs.yml
+++ b/jjb/armband/armband-project-jobs.yml
@@ -30,6 +30,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -39,9 +40,8 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'opnfv-build-arm-defaults'
+ - 'opnfv-build-enea-defaults'
- '{installer}-defaults'
- armband-project-parameter:
gs-pathname: '{gs-pathname}'
diff --git a/jjb/armband/armband-verify-jobs.yml b/jjb/armband/armband-verify-jobs.yml
index d5333eb07..3486718e4 100644
--- a/jjb/armband/armband-verify-jobs.yml
+++ b/jjb/armband/armband-verify-jobs.yml
@@ -21,13 +21,13 @@
#####################################
phase:
- 'basic':
- slave-label: 'opnfv-build-arm'
+ slave-label: 'opnfv-build-enea'
- 'build':
- slave-label: 'opnfv-build-arm'
+ slave-label: 'opnfv-build-enea'
- 'deploy-virtual':
- slave-label: 'opnfv-build-arm'
+ slave-label: 'opnfv-build-enea'
- 'smoke-test':
- slave-label: 'opnfv-build-arm'
+ slave-label: 'opnfv-build-enea'
#####################################
# jobs
#####################################
@@ -47,6 +47,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -93,9 +94,8 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'opnfv-build-arm-defaults'
+ - 'opnfv-build-enea-defaults'
- 'armband-verify-defaults':
gs-pathname: '{gs-pathname}'
@@ -109,7 +109,7 @@
- name: 'armband-verify-basic-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -123,7 +123,7 @@
- name: 'armband-verify-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -137,7 +137,7 @@
- name: 'armband-verify-deploy-virtual-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -151,7 +151,7 @@
- name: 'armband-verify-smoke-test-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -167,6 +167,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 6
@@ -189,7 +190,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- '{installer}-defaults'
diff --git a/jjb/armband/build.sh b/jjb/armband/build.sh
index a058ca158..a71cf1112 100755
--- a/jjb/armband/build.sh
+++ b/jjb/armband/build.sh
@@ -96,6 +96,7 @@ ls -al $BUILD_DIRECTORY
echo "OPNFV_GIT_URL=$(git config --get remote.origin.url)"
echo "OPNFV_GIT_SHA1=$(git rev-parse HEAD)"
echo "OPNFV_ARTIFACT_URL=$GS_URL/opnfv-$OPNFV_ARTIFACT_VERSION.iso"
+ echo "OPNFV_ARTIFACT_SHA512SUM=$(sha512sum $BUILD_DIRECTORY/opnfv-$OPNFV_ARTIFACT_VERSION.iso | cut -d' ' -f1)"
echo "OPNFV_BUILD_URL=$BUILD_URL"
) > $WORKSPACE/opnfv.properties
diff --git a/jjb/armband/upload-artifacts.sh b/jjb/armband/upload-artifacts.sh
index 7059ac344..97987e2c5 100755
--- a/jjb/armband/upload-artifacts.sh
+++ b/jjb/armband/upload-artifacts.sh
@@ -28,7 +28,7 @@ if [[ ! "$JOB_NAME" =~ (verify|merge) ]]; then
# store ISO locally on NFS first
ISOSTORE=${ISOSTORE:-/iso_mount/opnfv_ci}
if [[ -d "$ISOSTORE" ]]; then
- ISOSTORE=${ISOSTORE}/${GIT_BRANCH##*/}
+ ISOSTORE=${ISOSTORE}/${BRANCH##*/}
mkdir -p $ISOSTORE
# remove all but most recent 3 ISOs first to keep iso_mount clean & tidy
diff --git a/jjb/availability/availability.yml b/jjb/availability/availability.yml
index c3603a65f..9cb7f8899 100644
--- a/jjb/availability/availability.yml
+++ b/jjb/availability/availability.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/barometer/barometer.yml b/jjb/barometer/barometer.yml
index e789b7f7a..6a17e1706 100644
--- a/jjb/barometer/barometer.yml
+++ b/jjb/barometer/barometer.yml
@@ -30,7 +30,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -77,6 +76,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 3
@@ -86,7 +86,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -128,6 +127,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 3
@@ -137,7 +137,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/bottlenecks/bottlenecks-ci-jobs.yml b/jjb/bottlenecks/bottlenecks-ci-jobs.yml
index 92ec2d866..2779e316b 100644
--- a/jjb/bottlenecks/bottlenecks-ci-jobs.yml
+++ b/jjb/bottlenecks/bottlenecks-ci-jobs.yml
@@ -72,6 +72,8 @@
suite:
- 'rubbos'
- 'vstf'
+ - 'posca_stress_traffic'
+ - 'posca_stress_ping'
jobs:
- 'bottlenecks-{installer}-{suite}-{pod}-daily-{stream}'
@@ -95,7 +97,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- '{installer}-defaults'
@@ -137,65 +138,14 @@
- builder:
name: bottlenecks-env-cleanup
builders:
- - shell: |
- #!/bin/bash
- set -e
- [[ $GERRIT_REFSPEC_DEBUG == true ]] && redirect="/dev/stdout" || redirect="/dev/null"
-
- echo "Bottlenecks: docker containers/images cleaning up"
- if [[ ! -z $(docker ps -a | grep opnfv/bottlenecks) ]]; then
- echo "removing existing opnfv/bottlenecks containers"
- docker ps -a | grep opnfv/bottlenecks | awk '{print $1}' | xargs docker rm -f >$redirect
- fi
-
- if [[ ! -z $(docker images | grep opnfv/bottlenecks) ]]; then
- echo "Bottlenecks: docker images to remove:"
- docker images | head -1 && docker images | grep opnfv/bottlenecks
- image_tags=($(docker images | grep opnfv/bottlenecks | awk '{print $2}'))
- for tag in "${image_tags[@]}"; do
- echo "Removing docker image opnfv/bottlenecks:$tag..."
- docker rmi opnfv/bottlenecks:$tag >$redirect
- done
- fi
+ - shell:
+ !include-raw: ./bottlenecks-cleanup.sh
- builder:
name: bottlenecks-run-suite
builders:
- - shell: |
- #!/bin/bash
- set -e
- [[ $GERRIT_REFSPEC_DEBUG == true ]] && redirect="/dev/stdout" || redirect="/dev/null"
-
- echo "Bottlenecks: to pull image opnfv/bottlenecks:${DOCKER_TAG}"
- docker pull opnfv/bottlenecks:$DOCKER_TAG >${redirect}
-
- echo "Bottlenecks: docker start running"
- opts="--privileged=true -id"
- envs="-e INSTALLER_TYPE=${INSTALLER_TYPE} -e INSTALLER_IP=${INSTALLER_IP} \
- -e NODE_NAME=${NODE_NAME} -e EXTERNAL_NET=${EXTERNAL_NETWORK} \
- -e BOTTLENECKS_BRANCH=${BOTTLENECKS_BRANCH} -e GERRIT_REFSPEC_DEBUG=${GERRIT_REFSPEC_DEBUG} \
- -e BOTTLENECKS_DB_TARGET=${BOTTLENECKS_DB_TARGET} -e PACKAGE_URL=${PACKAGE_URL}"
- cmd="sudo docker run ${opts} ${envs} opnfv/bottlenecks:${DOCKER_TAG} /bin/bash"
- echo "Bottlenecks: docker cmd running ${cmd}"
- ${cmd} >${redirect}
-
- echo "Bottlenecks: obtain docker id"
- container_id=$(docker ps | grep "opnfv/bottlenecks:${DOCKER_TAG}" | awk '{print $1}' | head -1)
- if [ -z ${container_id} ]; then
- echo "Cannot find opnfv/bottlenecks container ID ${container_id}. Please check if it exists."
- docker ps -a
- exit 1
- fi
-
- echo "Bottlenecks: to prepare openstack environment"
- prepare_env="${REPO_DIR}/ci/prepare_env.sh"
- echo "Bottlenecks: docker cmd running: ${prepare_env}"
- sudo docker exec ${container_id} ${prepare_env}
-
- echo "Bottlenecks: to run testsuite ${SUITE_NAME}"
- run_testsuite="${REPO_DIR}/run_tests.sh -s ${SUITE_NAME}"
- echo "Bottlenecks: docker cmd running: ${run_testsuite}"
- sudo docker exec ${container_id} ${run_testsuite}
+ - shell:
+ !include-raw: ./bottlenecks-run-suite.sh
####################
# parameter macros
diff --git a/jjb/bottlenecks/bottlenecks-cleanup.sh b/jjb/bottlenecks/bottlenecks-cleanup.sh
new file mode 100644
index 000000000..0ba042318
--- /dev/null
+++ b/jjb/bottlenecks/bottlenecks-cleanup.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+set -e
+[[ $GERRIT_REFSPEC_DEBUG == true ]] && redirect="/dev/stdout" || redirect="/dev/null"
+
+BOTTLENECKS_IMAGE=opnfv/bottlenecks
+echo "Bottlenecks: docker containers/images cleaning up"
+
+dangling_images=($(docker images -f "dangling=true" | grep $BOTTLENECKS_IMAGE | awk '{print $3}'))
+if [[ -n $dangling_images ]]; then
+ echo "Removing $BOTTLENECKS_IMAGE:<none> dangling images and their containers"
+ docker images | head -1 && docker images | grep $dangling_images
+ for image_id in "${dangling_images[@]}"; do
+ echo "Bottlenecks: Removing dangling image $image_id"
+ docker rmi -f $image_id >${redirect}
+ done
+fi
+
+for image_id in "${dangling_images[@]}"; do
+ if [[ -n $(docker ps -a | grep $image_id) ]]; then
+ echo "Bottlenecks: Removing containers associated with dangling image: $image_id"
+ docker ps -a | head -1 && docker ps -a | grep $image_id
+ docker ps -a | grep $image_id | awk '{print $1}'| xargs docker rm -f >${redirect}
+ fi
+done
+
+if [[ -n $(docker ps -a | grep $BOTTLENECKS_IMAGE) ]]; then
+ echo "Removing existing $BOTTLENECKS_IMAGE containers"
+ docker ps -a | grep $BOTTLENECKS_IMAGE | awk '{print $1}' | xargs docker rm -f >$redirect
+fi
+
+if [[ -n $(docker images | grep $BOTTLENECKS_IMAGE) ]]; then
+ echo "Bottlenecks: docker images to remove:"
+ docker images | head -1 && docker images | grep $BOTTLENECKS_IMAGE
+ image_tags=($(docker images | grep $BOTTLENECKS_IMAGE | awk '{print $2}'))
+ for tag in "${image_tags[@]}"; do
+ echo "Removing docker image $BOTTLENECKS_IMAGE:$tag..."
+ docker rmi $BOTTLENECKS_IMAGE:$tag >$redirect
+ done
+fi
+
+echo "Yardstick: docker containers/images cleaning up"
+YARDSTICK_IMAGE=opnfv/yardstick
+
+dangling_images=($(docker images -f "dangling=true" | grep $YARDSTICK_IMAGE | awk '{print $3}'))
+if [[ -n $dangling_images ]]; then
+ echo "Removing $YARDSTICK_IMAGE:<none> dangling images and their containers"
+ docker images | head -1 && docker images | grep $dangling_images
+ for image_id in "${dangling_images[@]}"; do
+ echo "Yardstick: Removing dangling image $image_id"
+ docker rmi -f $image_id >${redirect}
+ done
+fi
+
+for image_id in "${dangling_images[@]}"; do
+ if [[ -n $(docker ps -a | grep $image_id) ]]; then
+ echo "Yardstick: Removing containers associated with dangling image: $image_id"
+ docker ps -a | head -1 && docker ps -a | grep $image_id
+ docker ps -a | grep $image_id | awk '{print $1}'| xargs docker rm -f >${redirect}
+ fi
+done
+
+if [[ -n $(docker ps -a | grep $YARDSTICK_IMAGE) ]]; then
+ echo "Removing existing $YARDSTICK_IMAGE containers"
+ docker ps -a | grep $YARDSTICK_IMAGE | awk '{print $1}' | xargs docker rm -f >$redirect
+fi
+
+if [[ -n $(docker images | grep $YARDSTICK_IMAGE) ]]; then
+ echo "Yardstick: docker images to remove:"
+ docker images | head -1 && docker images | grep $YARDSTICK_IMAGE
+ image_tags=($(docker images | grep $YARDSTICK_IMAGE | awk '{print $2}'))
+ for tag in "${image_tags[@]}"; do
+ echo "Removing docker image $YARDSTICK_IMAGE:$tag..."
+ docker rmi $YARDSTICK_IMAGE:$tag >$redirect
+ done
+fi
+
+echo "InfluxDB: docker containers/images cleaning up"
+INFLUXDB_IMAGE=tutum/influxdb
+
+dangling_images=($(docker images -f "dangling=true" | grep $INFLUXDB_IMAGE | awk '{print $3}'))
+if [[ -n $dangling_images ]]; then
+ echo "Removing $INFLUXDB_IMAGE:<none> dangling images and their containers"
+ docker images | head -1 && docker images | grep $dangling_images
+ for image_id in "${dangling_images[@]}"; do
+ echo "InfluxDB: Removing dangling image $image_id"
+ docker rmi -f $image_id >${redirect}
+ done
+fi
+
+for image_id in "${dangling_images[@]}"; do
+ if [[ -n $(docker ps -a | grep $image_id) ]]; then
+ echo "InfluxDB: Removing containers associated with dangling image: $image_id"
+ docker ps -a | head -1 && docker ps -a | grep $image_id
+ docker ps -a | grep $image_id | awk '{print $1}'| xargs docker rm -f >${redirect}
+ fi
+done
+
+if [[ -n $(docker ps -a | grep $INFLUXDB_IMAGE) ]]; then
+ echo "Removing existing $INFLUXDB_IMAGE containers"
+ docker ps -a | grep $INFLUXDB_IMAGE | awk '{print $1}' | xargs docker rm -f >$redirect
+fi
+
+if [[ -n $(docker images | grep $INFLUXDB_IMAGE) ]]; then
+ echo "InfluxDB: docker images to remove:"
+ docker images | head -1 && docker images | grep $INFLUXDB_IMAGE
+ image_tags=($(docker images | grep $INFLUXDB_IMAGE | awk '{print $2}'))
+ for tag in "${image_tags[@]}"; do
+ echo "Removing docker image $INFLUXDB_IMAGE:$tag..."
+ docker rmi $INFLUXDB_IMAGE:$tag >$redirect
+ done
+fi \ No newline at end of file
diff --git a/jjb/bottlenecks/bottlenecks-project-jobs.yml b/jjb/bottlenecks/bottlenecks-project-jobs.yml
index 03e40dc0e..a0abb9331 100644
--- a/jjb/bottlenecks/bottlenecks-project-jobs.yml
+++ b/jjb/bottlenecks/bottlenecks-project-jobs.yml
@@ -29,6 +29,8 @@
suite:
- 'rubbos'
- 'vstf'
+ - 'posca_stress_traffic'
+ - 'posca_stress_ping'
################################
# job templates
@@ -42,7 +44,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -80,7 +81,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -114,6 +114,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -123,7 +124,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- bottlenecks-parameter:
diff --git a/jjb/bottlenecks/bottlenecks-run-suite.sh b/jjb/bottlenecks/bottlenecks-run-suite.sh
new file mode 100644
index 000000000..f69463fc2
--- /dev/null
+++ b/jjb/bottlenecks/bottlenecks-run-suite.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#set -e
+[[ $GERRIT_REFSPEC_DEBUG == true ]] && redirect="/dev/stdout" || redirect="/dev/null"
+BOTTLENECKS_IMAGE=opnfv/bottlenecks
+
+if [[ $SUITE_NAME == rubbos || $SUITE_NAME == vstf ]]; then
+ echo "Bottlenecks: to pull image $BOTTLENECKS_IMAGE:${DOCKER_TAG}"
+ docker pull $BOTTLENECKS_IMAGE:$DOCKER_TAG >${redirect}
+
+ echo "Bottlenecks: docker start running"
+ opts="--privileged=true -id"
+ envs="-e INSTALLER_TYPE=${INSTALLER_TYPE} -e INSTALLER_IP=${INSTALLER_IP} \
+ -e NODE_NAME=${NODE_NAME} -e EXTERNAL_NET=${EXTERNAL_NETWORK} \
+ -e BOTTLENECKS_BRANCH=${BOTTLENECKS_BRANCH} -e GERRIT_REFSPEC_DEBUG=${GERRIT_REFSPEC_DEBUG} \
+ -e BOTTLENECKS_DB_TARGET=${BOTTLENECKS_DB_TARGET} -e PACKAGE_URL=${PACKAGE_URL}"
+ cmd="sudo docker run ${opts} ${envs} $BOTTLENECKS_IMAGE:${DOCKER_TAG} /bin/bash"
+ echo "Bottlenecks: docker cmd running ${cmd}"
+ ${cmd} >${redirect}
+
+ echo "Bottlenecks: obtain docker id"
+ container_id=$(docker ps | grep "$BOTTLENECKS_IMAGE:${DOCKER_TAG}" | awk '{print $1}' | head -1)
+ if [ -z ${container_id} ]; then
+ echo "Cannot find $BOTTLENECKS_IMAGE container ID ${container_id}. Please check if it exists."
+ docker ps -a
+ exit 1
+ fi
+
+ echo "Bottlenecks: to prepare openstack environment"
+ prepare_env="${REPO_DIR}/ci/prepare_env.sh"
+ echo "Bottlenecks: docker cmd running: ${prepare_env}"
+ sudo docker exec ${container_id} ${prepare_env}
+
+ echo "Bottlenecks: to run testsuite ${SUITE_NAME}"
+ run_testsuite="${REPO_DIR}/run_tests.sh -s ${SUITE_NAME}"
+ echo "Bottlenecks: docker cmd running: ${run_testsuite}"
+ sudo docker exec ${container_id} ${run_testsuite}
+else
+ echo "Bottlenecks: installing POSCA docker-compose"
+ if [ -d usr/local/bin/docker-compose ]; then
+ rm -rf usr/local/bin/docker-compose
+ fi
+ curl -L https://github.com/docker/compose/releases/download/1.11.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
+ chmod +x /usr/local/bin/docker-compose
+
+ echo "Bottlenecks: composing up dockers"
+ cd $WORKSPACE
+ docker-compose -f $WORKSPACE/docker/bottleneck-compose/docker-compose.yml up -d
+
+ echo "Bottlenecks: running traffic stress/factor testing in posca testsuite "
+ POSCA_SCRIPT=/home/opnfv/bottlenecks/testsuites/posca
+ if [[ $SUITE_NAME == posca_stress_traffic ]]; then
+ TEST_CASE=posca_factor_system_bandwidth
+ echo "Bottlenecks: pulling tutum/influxdb for yardstick"
+ docker pull tutum/influxdb:0.13
+ sleep 5
+ docker exec bottleneckcompose_bottlenecks_1 python ${POSCA_SCRIPT}/run_posca.py testcase $TEST_CASE
+ elif [[ $SUITE_NAME == posca_stress_ping ]]; then
+ TEST_CASE=posca_stress_ping
+ sleep 5
+ docker exec bottleneckcompose_bottlenecks_1 python ${POSCA_SCRIPT}/run_posca.py testcase $TEST_CASE
+ fi
+
+ echo "Bottlenecks: cleaning up docker-compose images and dockers"
+ docker-compose -f $WORKSPACE/docker/bottleneck-compose/docker-compose.yml down --rmi all
+fi \ No newline at end of file
diff --git a/jjb/compass4nfv/compass-ci-jobs.yml b/jjb/compass4nfv/compass-ci-jobs.yml
index 8800a155f..5ccd4b56f 100644
--- a/jjb/compass4nfv/compass-ci-jobs.yml
+++ b/jjb/compass4nfv/compass-ci-jobs.yml
@@ -41,8 +41,8 @@
#--------------------------------
# master
#--------------------------------
- - huawei-pod5:
- slave-label: '{pod}'
+ - baremetal-centos:
+ slave-label: 'intel-pod8'
os-version: 'centos7'
<<: *master
@@ -71,6 +71,10 @@
- 'os-nosdn-kvm-ha':
disabled: false
auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+ - 'os-nosdn-openo-ha':
+ disabled: false
+ auto-trigger-name: 'compass-{scenario}-{pod}-{stream}-trigger'
+
jobs:
- 'compass-{scenario}-{pod}-daily-{stream}'
@@ -87,6 +91,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -106,6 +111,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- compass-ci-parameter:
installer: '{installer}'
gs-pathname: '{gs-pathname}'
@@ -173,6 +179,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -194,7 +201,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- compass-ci-parameter:
installer: '{installer}'
@@ -218,12 +224,6 @@
- shell:
!include-raw-escape: ./compass-deploy.sh
- publishers:
- - archive:
- artifacts: 'ansible.log'
- allow-empty: 'true'
- fingerprint: true
-
########################
# parameter macros
########################
@@ -247,35 +247,39 @@
# trigger macros
########################
- trigger:
- name: 'compass-os-nosdn-nofeature-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-nosdn-nofeature-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 19 * * *'
- trigger:
- name: 'compass-os-odl_l2-nofeature-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-nosdn-openo-ha-baremetal-centos-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'compass-os-odl_l2-nofeature-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 23 * * *'
- trigger:
- name: 'compass-os-odl_l3-nofeature-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-odl_l3-nofeature-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 15 * * *'
- trigger:
- name: 'compass-os-onos-nofeature-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-onos-nofeature-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 7 * * *'
- trigger:
- name: 'compass-os-ocl-nofeature-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-ocl-nofeature-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 11 * * *'
- trigger:
- name: 'compass-os-onos-sfc-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-onos-sfc-ha-baremetal-centos-master-trigger'
triggers:
- timed: '0 3 * * *'
- trigger:
- name: 'compass-os-odl_l2-moon-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-odl_l2-moon-ha-baremetal-centos-master-trigger'
triggers:
- timed: ''
- trigger:
- name: 'compass-os-nosdn-kvm-ha-huawei-pod5-master-trigger'
+ name: 'compass-os-nosdn-kvm-ha-baremetal-centos-master-trigger'
triggers:
- timed: ''
@@ -284,6 +288,10 @@
triggers:
- timed: '0 2 * * *'
- trigger:
+ name: 'compass-os-nosdn-openo-ha-baremetal-master-trigger'
+ triggers:
+ - timed: '0 3 * * *'
+- trigger:
name: 'compass-os-odl_l2-nofeature-ha-baremetal-master-trigger'
triggers:
- timed: '0 22 * * *'
@@ -317,6 +325,10 @@
triggers:
- timed: ''
- trigger:
+ name: 'compass-os-nosdn-openo-ha-baremetal-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
name: 'compass-os-odl_l2-nofeature-ha-baremetal-danube-trigger'
triggers:
- timed: ''
@@ -350,6 +362,10 @@
triggers:
- timed: '0 21 * * *'
- trigger:
+ name: 'compass-os-nosdn-openo-ha-virtual-master-trigger'
+ triggers:
+ - timed: '0 22 * * *'
+- trigger:
name: 'compass-os-odl_l2-nofeature-ha-virtual-master-trigger'
triggers:
- timed: '0 20 * * *'
@@ -383,6 +399,10 @@
triggers:
- timed: '0 21 * * *'
- trigger:
+ name: 'compass-os-nosdn-openo-ha-virtual-danube-trigger'
+ triggers:
+ - timed: '0 22 * * *'
+- trigger:
name: 'compass-os-odl_l2-nofeature-ha-virtual-danube-trigger'
triggers:
- timed: '0 20 * * *'
diff --git a/jjb/compass4nfv/compass-deploy.sh b/jjb/compass4nfv/compass-deploy.sh
index f89d04e6d..534e17e62 100644
--- a/jjb/compass4nfv/compass-deploy.sh
+++ b/jjb/compass4nfv/compass-deploy.sh
@@ -34,6 +34,8 @@ if [[ "${DEPLOY_SCENARIO}" =~ "-ocl" ]]; then
export NETWORK_CONF_FILE=network_ocl.yml
elif [[ "${DEPLOY_SCENARIO}" =~ "-onos" ]]; then
export NETWORK_CONF_FILE=network_onos.yml
+elif [[ "${DEPLOY_SCENARIO}" =~ "-openo" ]]; then
+ export NETWORK_CONF_FILE=network_openo.yml
else
export NETWORK_CONF_FILE=network.yml
fi
@@ -65,7 +67,4 @@ echo
echo "--------------------------------------------------------"
echo "Done!"
-ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
-sshpass -p root scp 2>/dev/null $ssh_options root@${INSTALLER_IP}:/var/ansible/run/openstack_${OPENSTACK_VERSION}-opnfv2/ansible.log ./ &> /dev/null
-
exit $deploy_ret
diff --git a/jjb/compass4nfv/compass-dovetail-jobs.yml b/jjb/compass4nfv/compass-dovetail-jobs.yml
new file mode 100644
index 000000000..d49d0ec5f
--- /dev/null
+++ b/jjb/compass4nfv/compass-dovetail-jobs.yml
@@ -0,0 +1,208 @@
+- project:
+
+ name: 'compass-dovetail-jobs'
+ installer: 'compass'
+ project: 'compass4nfv'
+#----------------------------------
+# BRANCH ANCHORS
+#----------------------------------
+ colorado: &colorado
+ stream: colorado
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: false
+ dovetail-branch: master
+#------------------------------------
+# POD, INSTALLER, AND BRANCH MAPPING
+#------------------------------------
+# CI PODs
+#------------------------------------
+ pod:
+ - baremetal:
+ slave-label: compass-baremetal
+ os-version: 'trusty'
+ <<: *colorado
+#-----------------------------------
+# scenarios
+#-----------------------------------
+ scenario:
+ - 'os-nosdn-nofeature-ha':
+ disabled: false
+ auto-trigger-name: 'compass-{scenario}-{pod}-weekly-{stream}-trigger'
+
+ jobs:
+ - 'compass-{scenario}-{pod}-weekly-{stream}'
+ - 'compass-deploy-{pod}-weekly-{stream}'
+
+########################
+# job templates
+########################
+- job-template:
+ name: 'compass-{scenario}-{pod}-weekly-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: false
+
+ properties:
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'compass-os-.*?-{pod}-daily-.*?'
+ - 'compass-os-.*?-{pod}-weekly-.*?'
+ block-level: 'NODE'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+ triggers:
+ - '{auto-trigger-name}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - compass-dovetail-parameter:
+ installer: '{installer}'
+ gs-pathname: '{gs-pathname}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: '{scenario}'
+ - '{slave-label}-defaults'
+ - '{installer}-defaults'
+
+ triggers:
+ - '{auto-trigger-name}'
+
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+ - trigger-builds:
+ - project: 'compass-deploy-{pod}-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ DEPLOY_SCENARIO={scenario}
+ COMPASS_OS_VERSION={os-version}
+ same-node: true
+ block: true
+ - trigger-builds:
+ - project: 'dovetail-compass-{pod}-compliance_set-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'dovetail-compass-{pod}-debug-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+ - trigger-builds:
+ - project: 'dovetail-compass-{pod}-proposed_tests-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+
+- job-template:
+ name: 'compass-deploy-{pod}-weekly-{stream}'
+
+ disabled: false
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ max-per-node: 1
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'compass-deploy-{pod}-daily-.*?'
+ - 'compass-deploy-{pod}-weekly-.*'
+ - 'compass-verify-deploy-.*?'
+ block-level: 'NODE'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+ - timeout:
+ timeout: 120
+ abort: true
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - compass-dovetail-parameter:
+ installer: '{installer}'
+ gs-pathname: '{gs-pathname}'
+ - '{slave-label}-defaults'
+ - '{installer}-defaults'
+
+ scm:
+ - git-scm
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+ - shell:
+ !include-raw-escape: ./compass-download-artifact.sh
+ - shell:
+ !include-raw-escape: ./compass-deploy.sh
+
+########################
+# parameter macros
+########################
+- parameter:
+ name: compass-dovetail-parameter
+ parameters:
+ - string:
+ name: BUILD_DIRECTORY
+ default: $WORKSPACE/build_output
+ description: "Directory where the build artifact will be located upon the completion of the build."
+ - string:
+ name: GS_URL
+ default: '$GS_BASE{gs-pathname}'
+ description: "URL to Google Storage."
+ - choice:
+ name: COMPASS_OPENSTACK_VERSION
+ choices:
+ - 'mitaka'
+
+########################
+# trigger macros
+########################
+- trigger:
+ name: 'compass-os-nosdn-nofeature-ha-baremetal-weekly-colorado-trigger'
+ triggers:
+ - timed: 'H H * * 0'
+
+- trigger:
+ name: 'dovetail-weekly-trigger'
+ triggers:
+ - timed: 'H H * * 0'
diff --git a/jjb/compass4nfv/compass-project-jobs.yml b/jjb/compass4nfv/compass-project-jobs.yml
index 6b4080384..ed0fee6c0 100644
--- a/jjb/compass4nfv/compass-project-jobs.yml
+++ b/jjb/compass4nfv/compass-project-jobs.yml
@@ -33,6 +33,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -42,7 +43,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- compass-project-parameter:
installer: '{installer}'
@@ -76,6 +76,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -85,7 +86,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- compass-project-parameter:
installer: '{installer}'
diff --git a/jjb/compass4nfv/compass-verify-jobs.yml b/jjb/compass4nfv/compass-verify-jobs.yml
index 6ef5fbb67..039b30a69 100644
--- a/jjb/compass4nfv/compass-verify-jobs.yml
+++ b/jjb/compass4nfv/compass-verify-jobs.yml
@@ -22,6 +22,10 @@
disabled: false
os-version: 'xenial'
openstack-os-version: ''
+ - 'centos7':
+ disabled: false
+ os-version: 'centos7'
+ openstack-os-version: ''
#####################################
# patch verification phases
#####################################
@@ -47,6 +51,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -98,7 +103,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'compass-virtual-defaults'
- '{installer}-defaults'
@@ -121,6 +125,11 @@
node-parameters: true
kill-phase-on: FAILURE
abort-all-job: true
+ - name: 'opnfv-yamllint-verify-{stream}'
+ current-parameters: true
+ node-parameters: true
+ kill-phase-on: FAILURE
+ abort-all-job: true
- multijob:
name: deploy-virtual
condition: SUCCESSFUL
@@ -159,6 +168,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -185,11 +195,6 @@
description: "Built on $NODE_NAME"
- '{project}-verify-{phase}-macro'
- publishers:
- - archive:
- artifacts: 'ansible.log'
- allow-empty: 'true'
- fingerprint: true
#####################################
# builder macros
#####################################
@@ -240,3 +245,4 @@
name: COMPASS_OS_VERSION
choices:
- 'xenial'
+ - 'centos7'
diff --git a/jjb/conductor/conductor.yml b/jjb/conductor/conductor.yml
index fccd53e7f..1d47624e1 100644
--- a/jjb/conductor/conductor.yml
+++ b/jjb/conductor/conductor.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/copper/copper.yml b/jjb/copper/copper.yml
index 24f65a358..e380fd555 100644
--- a/jjb/copper/copper.yml
+++ b/jjb/copper/copper.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -60,4 +59,9 @@
builders:
- shell: |
- echo "Nothing to verify!"
+ #!/bin/bash
+ set -o errexit
+ set -o nounset
+ set -o pipefail
+
+ # shellcheck -f tty tests/*.sh
diff --git a/jjb/cperf/cperf-ci-jobs.yml b/jjb/cperf/cperf-ci-jobs.yml
index 4ffc3b013..125937e80 100644
--- a/jjb/cperf/cperf-ci-jobs.yml
+++ b/jjb/cperf/cperf-ci-jobs.yml
@@ -42,6 +42,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -57,7 +58,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{pod}-defaults'
- '{installer}-defaults'
diff --git a/jjb/daisy4nfv/daisy-daily-jobs.yml b/jjb/daisy4nfv/daisy-daily-jobs.yml
new file mode 100644
index 000000000..ffae70f8f
--- /dev/null
+++ b/jjb/daisy4nfv/daisy-daily-jobs.yml
@@ -0,0 +1,199 @@
+# jenkins job templates for Daisy
+# TODO
+# [ ] enable baremetal jobs after baremetal deployment finish
+# [ ] enable jobs in danuble
+# [ ] add more scenarios
+# [ ] integration with yardstick
+
+- project:
+
+ name: 'daisy'
+ project: '{name}'
+ installer: '{name}'
+
+#--------------------------------
+# BRANCH ANCHORS
+#--------------------------------
+ master: &master
+ stream: master
+ branch: '{stream}'
+ disabled: false
+ gs-pathname: ''
+#--------------------------------
+# POD, INSTALLER, AND BRANCH MAPPING
+#--------------------------------
+# CI PODs
+#--------------------------------
+ pod:
+ - baremetal:
+ slave-label: daisy-baremetal
+ <<: *master
+ - virtual:
+ slave-label: daisy-virtual
+ <<: *master
+#--------------------------------
+# None-CI PODs
+#--------------------------------
+
+#--------------------------------
+# scenarios
+#--------------------------------
+ scenario:
+ # HA scenarios
+ - 'os-nosdn-nofeature-ha':
+ auto-trigger-name: 'daisy-{scenario}-{pod}-daily-{stream}-trigger'
+ # NOHA scenarios
+ - 'os-nosdn-nofeature-noha':
+ auto-trigger-name: 'daisy-{scenario}-{pod}-daily-{stream}-trigger'
+
+ jobs:
+ - '{project}-{scenario}-{pod}-daily-{stream}'
+ - '{project}-deploy-{pod}-daily-{stream}'
+
+########################
+# job templates
+########################
+- job-template:
+ name: '{project}-{scenario}-{pod}-daily-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: false
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ max-per-node: 1
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'daisy.*-deploy-({pod})?-daily-.*'
+ block-level: 'NODE'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+ triggers:
+ - '{auto-trigger-name}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - '{installer}-defaults'
+ - '{slave-label}-defaults':
+ installer: '{installer}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: '{scenario}'
+ - 'daisy-project-parameter':
+ gs-pathname: '{gs-pathname}'
+
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+ - trigger-builds:
+ - project: 'daisy-deploy-{pod}-daily-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ same-node: true
+ block: true
+ - trigger-builds:
+ - project: 'functest-daisy-{pod}-daily-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ same-node: true
+ block: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+
+- job-template:
+ name: '{project}-deploy-{pod}-daily-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ max-per-node: 1
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'daisy.*-deploy-({pod})?-daily-.*'
+ block-level: 'NODE'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - '{installer}-defaults'
+ - '{slave-label}-defaults':
+ installer: '{installer}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-nosdn-nofeature-ha'
+ - 'daisy-project-parameter':
+ gs-pathname: '{gs-pathname}'
+ - string:
+ name: DEPLOY_TIMEOUT
+ default: '150'
+ description: 'Deployment timeout in minutes'
+
+ scm:
+ - git-scm
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+ - shell:
+ !include-raw-escape: ./daisy4nfv-download-artifact.sh
+ - shell:
+ !include-raw-escape: ./daisy-deploy.sh
+
+
+########################
+# trigger macros
+########################
+#-----------------------------------------------
+# Triggers for job running on daisy-baremetal against master branch
+#-----------------------------------------------
+# HA Scenarios
+- trigger:
+ name: 'daisy-os-nosdn-nofeature-ha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: ''
+# NOHA Scenarios
+- trigger:
+ name: 'daisy-os-nosdn-nofeature-noha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: ''
+#-----------------------------------------------
+# Triggers for job running on daisy-virtual against master branch
+#-----------------------------------------------
+- trigger:
+ name: 'daisy-os-nosdn-nofeature-ha-virtual-daily-master-trigger'
+ triggers:
+ - timed: ''
+# NOHA Scenarios
+- trigger:
+ name: 'daisy-os-nosdn-nofeature-noha-virtual-daily-master-trigger'
+ triggers:
+ - timed: 'H 8,22 * * *'
+
diff --git a/jjb/daisy4nfv/daisy-deploy.sh b/jjb/daisy4nfv/daisy-deploy.sh
new file mode 100755
index 000000000..b512e3f60
--- /dev/null
+++ b/jjb/daisy4nfv/daisy-deploy.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+set -o nounset
+set -o pipefail
+
+echo "--------------------------------------------------------"
+echo "This is $INSTALLER_TYPE deploy job!"
+echo "--------------------------------------------------------"
+
+DEPLOY_SCENARIO=${DEPLOY_SCENARIO:-"os-nosdn-nofeature-ha"}
+BRIDGE=${BRIDGE:-pxebr}
+LAB_NAME=${NODE_NAME/-*}
+POD_NAME=${NODE_NAME/*-}
+deploy_ret=0
+
+if [[ ! "$NODE_NAME" =~ "-virtual" ]] && [[ ! "$LAB_NAME" =~ (zte) ]]; then
+ echo "Unsupported lab $LAB_NAME for now, Cannot continue!"
+ exit $deploy_ret
+fi
+
+# clone the securedlab repo
+cd $WORKSPACE
+BASE_DIR=$(cd ./;pwd)
+
+echo "Cloning securedlab repo $BRANCH"
+git clone ssh://jenkins-zte@gerrit.opnfv.org:29418/securedlab --quiet \
+ --branch $BRANCH
+
+# daisy ci/deploy/deploy.sh use $BASE_DIR/labs dir
+cp -r securedlab/labs .
+
+DEPLOY_COMMAND="sudo ./ci/deploy/deploy.sh -b $BASE_DIR \
+ -l $LAB_NAME -p $POD_NAME -B $BRIDGE"
+
+# log info to console
+echo """
+Deployment parameters
+--------------------------------------------------------
+Scenario: $DEPLOY_SCENARIO
+LAB: $LAB_NAME
+POD: $POD_NAME
+BRIDGE: $BRIDGE
+BASE_DIR: $BASE_DIR
+
+Starting the deployment using $INSTALLER_TYPE. This could take some time...
+--------------------------------------------------------
+Issuing command
+$DEPLOY_COMMAND
+"""
+
+# start the deployment
+$DEPLOY_COMMAND
+
+if [ $? -ne 0 ]; then
+ echo
+ echo "Depolyment failed!"
+ deploy_ret=1
+else
+ echo
+ echo "--------------------------------------------------------"
+ echo "Deployment done!"
+fi
+
+exit $deploy_ret
diff --git a/jjb/daisy4nfv/daisy-project-jobs.yml b/jjb/daisy4nfv/daisy-project-jobs.yml
new file mode 100644
index 000000000..0127ed094
--- /dev/null
+++ b/jjb/daisy4nfv/daisy-project-jobs.yml
@@ -0,0 +1,232 @@
+######################################################################
+# Add daily jobs, for buidoing, deploying and testing
+# TODO:
+# - [ ] Add yardstick and functest for test stage
+# - [x] Use daisy-baremetal-defauls for choosing baremetal deployment
+######################################################################
+
+#############################
+# Job configuration for daisy
+#############################
+- project:
+ name: daisy-project-jobs
+
+ project: 'daisy'
+
+ installer: 'daisy'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: true
+
+ phase:
+ - 'build':
+ slave-label: 'opnfv-build-centos'
+ - 'deploy':
+ slave-label: 'daisy-baremetal'
+ - 'test':
+ slave-label: 'opnfv-build-centos'
+ jobs:
+ - '{installer}-daily-{stream}'
+ - '{installer}-{phase}-daily-{stream}'
+
+########################
+# job templates
+########################
+- job-template:
+ name: '{installer}-daily-{stream}'
+
+ project-type: multijob
+
+ disabled: false
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ option: 'project'
+
+ scm:
+ - git-scm
+
+ triggers:
+ - timed: '0 H/8 * * *'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-centos-defaults'
+ - 'daisy-defaults'
+ - '{installer}-project-parameter':
+ gs-pathname: '{gs-pathname}'
+
+ wrappers:
+ - ssh-agent-wrapper
+ - timeout:
+ timeout: 360
+ fail: true
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - multijob:
+ name: build
+ condition: SUCCESSFUL
+ projects:
+ - name: '{installer}-build-daily-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ BRANCH=$BRANCH
+ GERRIT_REFSPEC=$GERRIT_REFSPEC
+ GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
+ GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
+ node-parameters: false
+ kill-phase-on: FAILURE
+ abort-all-job: true
+ - multijob:
+ name: deploy
+ condition: SUCCESSFUL
+ projects:
+ - name: '{installer}-deploy-daily-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ BRANCH=$BRANCH
+ GERRIT_REFSPEC=$GERRIT_REFSPEC
+ GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
+ GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
+ node-parameters: false
+ kill-phase-on: FAILURE
+ abort-all-job: true
+ - multijob:
+ name: test
+ condition: SUCCESSFUL
+ projects:
+ - name: '{installer}-test-daily-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ BRANCH=$BRANCH
+ GERRIT_REFSPEC=$GERRIT_REFSPEC
+ GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
+ GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
+ node-parameters: false
+ kill-phase-on: FAILURE
+ abort-all-job: true
+
+ publishers:
+ - '{installer}-recipients'
+
+- job-template:
+ name: '{installer}-{phase}-daily-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 6
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - '{installer}-.*deploy-.*'
+ block-level: 'NODE'
+
+ scm:
+ - git-scm
+
+ wrappers:
+ - ssh-agent-wrapper
+ - timeout:
+ timeout: 360
+ fail: true
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'daisy-defaults'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-nosdn-nofeature-ha'
+ - 'daisy-defaults'
+ - '{slave-label}-defaults'
+ - '{installer}-project-parameter':
+ gs-pathname: '{gs-pathname}'
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - '{installer}-{phase}-daily-macro'
+
+#####################################
+# builder macros
+#####################################
+- builder:
+ name: 'daisy-build-daily-macro'
+ builders:
+ - shell:
+ !include-raw: ./daisy4nfv-basic.sh
+ - shell:
+ !include-raw: ./daisy4nfv-build.sh
+ - shell:
+ !include-raw: ./daisy4nfv-upload-artifact.sh
+ - shell:
+ !include-raw: ./daisy4nfv-workspace-cleanup.sh
+
+- builder:
+ name: 'daisy-deploy-daily-macro'
+ builders:
+ - shell:
+ !include-raw: ./daisy4nfv-download-artifact.sh
+ - shell:
+ !include-raw: ./daisy-deploy.sh
+
+- builder:
+ name: 'daisy-test-daily-macro'
+ builders:
+ - shell: |
+ #!/bin/bash
+
+ echo "Not activated!"
+
+#####################################
+# parameter macros
+#####################################
+- publisher:
+ name: 'daisy-recipients'
+ publishers:
+ - email:
+ recipients: hu.zhijiang@zte.com.cn lu.yao135@zte.com.cn zhou.ya@zte.com.cn yangyang1@zte.com.cn julienjut@gmail.com
+
+- parameter:
+ name: 'daisy-project-parameter'
+ parameters:
+ - string:
+ name: BUILD_DIRECTORY
+ default: $WORKSPACE/build_output
+ description: "Directory where the build artifact will be located upon the completion of the build."
+ - string:
+ name: CACHE_DIRECTORY
+ default: $HOME/opnfv/cache/$INSTALLER_TYPE
+ description: "Directory where the cache to be used during the build is located."
+ - string:
+ name: GS_URL
+ default: artifacts.opnfv.org/$PROJECT{gs-pathname}
+ description: "URL to Google Storage."
diff --git a/jjb/daisy4nfv/daisy4nfv-build.sh b/jjb/daisy4nfv/daisy4nfv-build.sh
index eb29fed72..375d80733 100755
--- a/jjb/daisy4nfv/daisy4nfv-build.sh
+++ b/jjb/daisy4nfv/daisy4nfv-build.sh
@@ -26,6 +26,7 @@ cd $WORKSPACE
echo "OPNFV_GIT_URL=$(git config --get remote.origin.url)"
echo "OPNFV_GIT_SHA1=$(git rev-parse HEAD)"
echo "OPNFV_ARTIFACT_URL=$GS_URL/opnfv-$OPNFV_ARTIFACT_VERSION.bin"
+ echo "OPNFV_ARTIFACT_SHA512SUM=$(sha512sum $OUTPUT_DIR/opnfv-$OPNFV_ARTIFACT_VERSION.bin | cut -d' ' -f1)"
echo "OPNFV_BUILD_URL=$BUILD_URL"
) > $WORKSPACE/opnfv.properties
diff --git a/jjb/daisy4nfv/daisy4nfv-download-artifact.sh b/jjb/daisy4nfv/daisy4nfv-download-artifact.sh
index 90b5fa62f..1cc0443ad 100755
--- a/jjb/daisy4nfv/daisy4nfv-download-artifact.sh
+++ b/jjb/daisy4nfv/daisy4nfv-download-artifact.sh
@@ -12,7 +12,7 @@ set -o errexit
set -o pipefail
# use proxy url to replace the nomral URL, for googleusercontent.com will be blocked randomly
-[[ "$NODE_NAME" =~ (zte) ]] && GS_URL=$GS_BASE_PROXY
+[[ "$NODE_NAME" =~ (zte) ]] && GS_URL=${GS_BASE_PROXY%%/*}/$GS_URL
if [[ "$JOB_NAME" =~ "merge" ]]; then
echo "Downloading http://$GS_URL/opnfv-gerrit-$GERRIT_CHANGE_NUMBER.properties"
@@ -36,6 +36,25 @@ echo "Using $OPNFV_ARTIFACT for deployment"
[[ "$NODE_NAME" =~ (zte) ]] && OPNFV_ARTIFACT_URL=${GS_BASE_PROXY%%/*}/$OPNFV_ARTIFACT_URL
+if [[ ! "$JOB_NAME" =~ (verify|merge) ]]; then
+ # check if we already have the image to avoid redownload
+ BINSTORE="/bin_mount/opnfv_ci/${BRANCH##*/}"
+ if [[ -f "$BINSTORE/$OPNFV_ARTIFACT" && ! -z $OPNFV_ARTIFACT_SHA512SUM ]]; then
+ echo "BIN exists locally. Starting to check the sha512sum."
+ if [[ $OPNFV_ARTIFACT_SHA512SUM = $(sha512sum -b $BINSTORE/$OPNFV_ARTIFACT | cut -d' ' -f1) ]]; then
+ echo "Sha512sum is verified. Skipping the download and using the file from BIN store."
+ ln -s $BINSTORE/$OPNFV_ARTIFACT $WORKSPACE/opnfv.bin
+ echo "--------------------------------------------------------"
+ echo
+ ls -al $WORKSPACE/opnfv.bin
+ echo
+ echo "--------------------------------------------------------"
+ echo "Done!"
+ exit 0
+ fi
+ fi
+fi
+
# log info to console
echo "Downloading the $INSTALLER_TYPE artifact using URL http://$OPNFV_ARTIFACT_URL"
echo "This could take some time..."
@@ -43,7 +62,7 @@ echo "--------------------------------------------------------"
echo
# download the file
-curl -s -o $WORKSPACE/opnfv.bin http://$OPNFV_ARTIFACT_URL > gsutil.bin.log 2>&1
+curl -L -s -o $WORKSPACE/opnfv.bin http://$OPNFV_ARTIFACT_URL > gsutil.bin.log 2>&1
# list the file
ls -al $WORKSPACE/opnfv.bin
diff --git a/jjb/daisy4nfv/daisy4nfv-merge-jobs.yml b/jjb/daisy4nfv/daisy4nfv-merge-jobs.yml
index b7a5fec92..95d851cca 100644
--- a/jjb/daisy4nfv/daisy4nfv-merge-jobs.yml
+++ b/jjb/daisy4nfv/daisy4nfv-merge-jobs.yml
@@ -2,6 +2,14 @@
name: 'daisy4nfv-merge-jobs'
project: 'daisy'
+
+ installer: 'daisy'
+
+###########################################################
+# use alias to keep the jobs'name existed already unchanged
+###########################################################
+ alias: 'daisy4nfv'
+
#####################################
# branch definitions
#####################################
@@ -10,31 +18,29 @@
branch: '{stream}'
gs-pathname: ''
disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: true
#####################################
# patch merge phases
#####################################
phase:
- - 'basic':
- slave-label: 'opnfv-build-centos'
- 'build':
slave-label: 'opnfv-build-centos'
- 'deploy-virtual':
slave-label: 'opnfv-build-centos'
- - 'smoke-test':
- slave-label: 'opnfv-build-centos'
- - 'promote':
- slave-label: 'opnfv-build-centos'
#####################################
# jobs
#####################################
jobs:
- - 'daisy4nfv-merge-{stream}'
- - 'daisy4nfv-merge-{phase}-{stream}'
+ - '{alias}-merge-{stream}'
+ - '{alias}-merge-{phase}-{stream}'
#####################################
# job templates
#####################################
- job-template:
- name: 'daisy4nfv-merge-{stream}'
+ name: '{alias}-merge-{stream}'
project-type: multijob
@@ -43,13 +49,14 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
option: 'project'
scm:
- - git-scm-gerrit
+ - git-scm
wrappers:
- ssh-agent-wrapper
@@ -65,50 +72,44 @@
- comment-added-contains-event:
comment-contains-value: 'remerge'
projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'docs/**|.gitignore'
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: ANT
+ pattern: 'ci/**'
+ - compare-type: ANT
+ pattern: 'code/**'
+ - compare-type: ANT
+ pattern: 'deploy/**'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**'
+ - compare-type: ANT
+ pattern: '.gitignore'
readable-message: true
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'opnfv-build-defaults'
- - 'daisy4nfv-merge-defaults':
+ - 'opnfv-build-centos-defaults'
+ - '{alias}-merge-defaults':
gs-pathname: '{gs-pathname}'
builders:
- description-setter:
description: "Built on $NODE_NAME"
- multijob:
- name: basic
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-merge-basic-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
name: build
condition: SUCCESSFUL
projects:
- - name: 'daisy4nfv-merge-build-{stream}'
+ - name: '{alias}-merge-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -119,38 +120,10 @@
name: deploy-virtual
condition: SUCCESSFUL
projects:
- - name: 'daisy4nfv-merge-deploy-virtual-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: smoke-test
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-merge-smoke-test-{stream}'
+ - name: '{alias}-merge-deploy-virtual-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: promote
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-merge-promote-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -159,26 +132,26 @@
abort-all-job: true
- job-template:
- name: 'daisy4nfv-merge-{phase}-{stream}'
+ name: '{alias}-merge-{phase}-{stream}'
disabled: '{obj:disabled}'
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
- max-total: 6
+ max-total: 4
option: 'project'
- build-blocker:
use-build-blocker: true
blocking-jobs:
- - 'daisy4nfv-merge-deploy-.*'
- - 'daisy4nfv-merge-test-.*'
+ - '{alias}-merge-deploy-.*'
block-level: 'NODE'
scm:
- - git-scm-gerrit
+ - git-scm
wrappers:
- ssh-agent-wrapper
@@ -189,58 +162,41 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- - 'daisy4nfv-merge-defaults':
+ - '{alias}-merge-defaults':
gs-pathname: '{gs-pathname}'
builders:
- description-setter:
description: "Built on $NODE_NAME"
- '{project}-merge-{phase}-macro'
+
#####################################
# builder macros
#####################################
- builder:
- name: 'daisy-merge-basic-macro'
+ name: 'daisy-merge-build-macro'
builders:
- shell:
!include-raw: ./daisy4nfv-basic.sh
-
-- builder:
- name: 'daisy-merge-build-macro'
- builders:
- shell:
- !include-raw:
- - ./daisy4nfv-build.sh
- - ./daisy4nfv-upload-artifact.sh
- - ./daisy4nfv-workspace-cleanup.sh
+ !include-raw: ./daisy4nfv-build.sh
+ - shell:
+ !include-raw: ./daisy4nfv-upload-artifact.sh
+ - shell:
+ !include-raw: ./daisy4nfv-workspace-cleanup.sh
- builder:
name: 'daisy-merge-deploy-virtual-macro'
builders:
- shell:
- !include-raw:
- - ./daisy4nfv-download-artifact.sh
- - ./daisy4nfv-virtual-deploy.sh
- - ./daisy4nfv-workspace-cleanup.sh
-
-- builder:
- name: 'daisy-merge-smoke-test-macro'
- builders:
- - shell: |
- #!/bin/bash
-
- echo "Not activated!"
-
-- builder:
- name: 'daisy-merge-promote-macro'
- builders:
- - shell: |
- #!/bin/bash
+ !include-raw: ./daisy4nfv-download-artifact.sh
+ - shell:
+ !include-raw: ./daisy-deploy.sh
+ - shell:
+ !include-raw: ./daisy4nfv-workspace-cleanup.sh
- echo "Not activated!"
#####################################
# parameter macros
#####################################
diff --git a/jjb/daisy4nfv/daisy4nfv-verify-jobs.yml b/jjb/daisy4nfv/daisy4nfv-verify-jobs.yml
index cba22643c..ee82c14b2 100644
--- a/jjb/daisy4nfv/daisy4nfv-verify-jobs.yml
+++ b/jjb/daisy4nfv/daisy4nfv-verify-jobs.yml
@@ -2,6 +2,14 @@
name: 'daisy4nfv-verify-jobs'
project: 'daisy'
+
+ installer: 'daisy'
+
+##########################################################
+# use alias to keep the jobs'name existed alread unchanged
+##########################################################
+ alias: 'daisy4nfv'
+
#####################################
# branch definitions
#####################################
@@ -10,29 +18,27 @@
branch: '{stream}'
gs-pathname: ''
disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: true
#####################################
# patch verification phases
#####################################
phase:
- - 'basic':
- slave-label: 'opnfv-build-centos'
- 'build':
slave-label: 'opnfv-build-centos'
- - 'deploy-virtual':
- slave-label: 'opnfv-build'
- - 'smoke-test':
- slave-label: 'opnfv-build'
#####################################
# jobs
#####################################
jobs:
- - 'daisy4nfv-verify-{stream}'
- - 'daisy4nfv-verify-{phase}-{stream}'
+ - '{alias}-verify-{stream}'
+ - '{alias}-verify-{phase}-{stream}'
#####################################
# job templates
#####################################
- job-template:
- name: 'daisy4nfv-verify-{stream}'
+ name: '{alias}-verify-{stream}'
project-type: multijob
@@ -41,13 +47,14 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
option: 'project'
scm:
- - git-scm-gerrit
+ - git-scm
wrappers:
- ssh-agent-wrapper
@@ -69,78 +76,44 @@
- comment-added-contains-event:
comment-contains-value: 'reverify'
projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'docs/**|.gitignore'
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: ANT
+ pattern: 'ci/**'
+ - compare-type: ANT
+ pattern: 'code/**'
+ - compare-type: ANT
+ pattern: 'deploy/**'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**'
+ - compare-type: ANT
+ pattern: '.gitignore'
readable-message: true
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'opnfv-build-defaults'
- - 'daisy4nfv-verify-defaults':
+ - 'opnfv-build-centos-defaults'
+ - '{alias}-verify-defaults':
gs-pathname: '{gs-pathname}'
builders:
- description-setter:
description: "Built on $NODE_NAME"
- multijob:
- name: basic
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-verify-basic-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
name: build
condition: SUCCESSFUL
projects:
- - name: 'daisy4nfv-verify-build-{stream}'
+ - name: '{alias}-verify-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: deploy-virtual
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-verify-deploy-virtual-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: smoke-test
- condition: SUCCESSFUL
- projects:
- - name: 'daisy4nfv-verify-smoke-test-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -149,13 +122,14 @@
abort-all-job: true
- job-template:
- name: 'daisy4nfv-verify-{phase}-{stream}'
+ name: '{alias}-verify-{phase}-{stream}'
disabled: '{obj:disabled}'
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 6
@@ -163,12 +137,11 @@
- build-blocker:
use-build-blocker: true
blocking-jobs:
- - 'daisy4nfv-verify-deploy-.*'
- - 'daisy4nfv-verify-test-.*'
+ - '{alias}-verify-deploy-.*'
block-level: 'NODE'
scm:
- - git-scm-gerrit
+ - git-scm
wrappers:
- ssh-agent-wrapper
@@ -179,44 +152,29 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- - 'daisy4nfv-verify-defaults':
+ - '{alias}-verify-defaults':
gs-pathname: '{gs-pathname}'
builders:
- description-setter:
description: "Built on $NODE_NAME"
- '{project}-verify-{phase}-macro'
+
#####################################
# builder macros
#####################################
- builder:
- name: 'daisy-verify-basic-macro'
+ name: 'daisy-verify-build-macro'
builders:
- shell:
!include-raw: ./daisy4nfv-basic.sh
-
-- builder:
- name: 'daisy-verify-build-macro'
- builders:
- shell:
!include-raw: ./daisy4nfv-build.sh
-
-- builder:
- name: 'daisy-verify-deploy-virtual-macro'
- builders:
- shell:
- !include-raw: ./daisy4nfv-virtual-deploy.sh
-
-- builder:
- name: 'daisy-verify-smoke-test-macro'
- builders:
- - shell: |
- #!/bin/bash
+ !include-raw: ./daisy4nfv-workspace-cleanup.sh
- echo "Not activated!"
#####################################
# parameter macros
#####################################
diff --git a/jjb/daisy4nfv/daisy4nfv-virtual-deploy.sh b/jjb/daisy4nfv/daisy4nfv-virtual-deploy.sh
deleted file mode 100755
index 4aa7b0bd5..000000000
--- a/jjb/daisy4nfv/daisy4nfv-virtual-deploy.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-
-echo "--------------------------------------------------------"
-echo "This is diasy4nfv virtual deploy job!"
-echo "--------------------------------------------------------"
-
-cd $WORKSPACE
-
-if [[ "$NODE_NAME" =~ "-virtual" ]]; then
- export NETWORK_CONF=./deploy/config/vm_environment/$NODE_NAME/network.yml
- export DHA_CONF=./deploy/config/vm_environment/$NODE_NAME/deploy.yml
-else
- # TODO: For the time being, we need to pass this script to let contributors merge their work.
- echo "No support for non-virtual node"
- exit 0
-fi
-
-./ci/deploy/deploy.sh ${DHA_CONF} ${NETWORK_CONF}
-
-if [ $? -ne 0 ]; then
- echo "depolyment failed!"
- deploy_ret=1
-fi
-
-echo
-echo "--------------------------------------------------------"
-echo "Done!"
-
-exit $deploy_ret
diff --git a/jjb/doctor/doctor.yml b/jjb/doctor/doctor.yml
index ab9ef8f6c..c677ef96e 100644
--- a/jjb/doctor/doctor.yml
+++ b/jjb/doctor/doctor.yml
@@ -7,7 +7,7 @@
- master:
branch: '{stream}'
gs-pathname: ''
- docker-tag: 'master'
+ docker-tag: 'latest'
disabled: false
- danube:
branch: 'stable/{stream}'
@@ -19,15 +19,28 @@
- apex:
slave-label: 'ool-virtual1'
pod: 'ool-virtual1'
+ - fuel:
+ slave-label: 'ool-virtual2'
+ pod: 'ool-virtual2'
+ #- joid:
+ # slave-label: 'ool-virtual3'
+ # pod: 'ool-virtual3'
inspector:
- 'sample'
- 'congress'
+ task:
+ - verify:
+ profiler: 'none'
+ auto-trigger-name: 'doctor-verify'
+ - profiling:
+ profiler: 'poc'
+ auto-trigger-name: 'experimental'
+
jobs:
- 'doctor-verify-{stream}'
- - 'doctor-verify-{installer}-{inspector}-{stream}'
- - 'doctor-profiling-{stream}'
+ - 'doctor-{task}-{installer}-{inspector}-{stream}'
- job-template:
name: 'doctor-verify-{stream}'
@@ -37,7 +50,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -71,24 +83,20 @@
- shell: "[ -e tests/run.sh ] && bash -n ./tests/run.sh"
- job-template:
- name: 'doctor-verify-{installer}-{inspector}-{stream}'
+ name: 'doctor-{task}-{installer}-{inspector}-{stream}'
node: '{slave-label}'
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: OS_CREDS
default: /home/jenkins/openstack.creds
description: 'OpenStack credentials'
- '{slave-label}-defaults'
- - string:
- name: INSTALLER_TYPE
- default: '{installer}'
- description: 'Installer used for deploying OPNFV on this POD'
+ - '{installer}-defaults'
- string:
name: DOCKER_TAG
default: '{docker-tag}'
@@ -107,7 +115,7 @@
default: '{project}'
- string:
name: TESTCASE_OPTIONS
- default: '-e INSPECTOR_TYPE={inspector} -v $WORKSPACE:/home/opnfv/repos/doctor'
+ default: '-e INSPECTOR_TYPE={inspector} -e PROFILER_TYPE={profiler} -v $WORKSPACE:/home/opnfv/repos/doctor'
description: 'Addtional parameters specific to test case(s)'
# functest-parameter
- string:
@@ -131,39 +139,22 @@
- git-scm-gerrit
triggers:
- - gerrit:
- server-name: 'gerrit.opnfv.org'
- trigger-on:
- - patchset-created-event:
- exclude-drafts: 'false'
- exclude-trivial-rebase: 'false'
- exclude-no-code-change: 'false'
- - draft-published-event
- - comment-added-contains-event:
- comment-contains-value: 'recheck'
- - comment-added-contains-event:
- comment-contains-value: 'reverify'
- projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- file-paths:
- - compare-type: ANT
- pattern: 'tests/**'
- skip-vote:
- successful: true
- failed: true
- unstable: true
- notbuilt: true
+ - '{auto-trigger-name}':
+ project: '{project}'
+ branch: '{branch}'
builders:
+ - 'clean-workspace-log'
+ - shell: |
+ # NOTE: Create symbolic link, so that we can archive file outside
+ # of $WORKSPACE .
+ # NOTE: We are printing all logs under 'tests/' during test run,
+ # so this symbolic link should not be in 'tests/'. Otherwise,
+ # we'll have the same log twice in jenkins console log.
+ ln -sfn $HOME/opnfv/functest/results/{stream} functest_results
- 'functest-suite-builder'
- shell: |
functest_log="$HOME/opnfv/functest/results/{stream}/{project}.log"
- to_be_archived="$WORKSPACE/tests/functest-{project}.log"
- cp $functest_log $to_be_archived
# NOTE: checking the test result, as the previous job could return
# 0 regardless the result of doctor test scenario.
grep -e ' OK$' $functest_log || exit 1
@@ -171,66 +162,39 @@
publishers:
- archive:
artifacts: 'tests/*.log'
+ - archive:
+ artifacts: 'functest_results/{project}.log'
-- job-template:
- name: 'doctor-profiling-{stream}'
-
- disabled: '{obj:disabled}'
-
- parameters:
- - 'doctor-defaults':
- project: '{project}'
- branch: '{branch}'
- - string:
- name: PROFILER
- default: poc
- description: "Profiler to be used"
-
- scm:
- - git-scm-gerrit
-
- triggers:
- - 'experimental':
- project: '{project}'
- branch: '{branch}'
-
-#####################################
-# parameter macros
-#####################################
-# TODO(yujunz) replace common parameter in doctor-verify-{stream} with macro
-- parameter:
- name: 'doctor-defaults'
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - 'opnfv-build-ubuntu-defaults'
#####################################
# trigger macros
#####################################
-# TODO(yujunz) move to opnfv commom
- trigger:
- name: 'experimental'
+ name: 'doctor-verify'
triggers:
- gerrit:
server-name: 'gerrit.opnfv.org'
trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
- comment-added-contains-event:
- comment-contains-value: 'check-experimental'
+ comment-contains-value: 'reverify'
projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- file-paths:
- - compare-type: 'ANT'
- pattern: 'tests/**'
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: ANT
+ pattern: 'tests/**'
skip-vote:
successful: true
failed: true
unstable: true
notbuilt: true
- silent-start: true
diff --git a/jjb/domino/domino.yml b/jjb/domino/domino.yml
index 532fce687..5fd9db3f1 100644
--- a/jjb/domino/domino.yml
+++ b/jjb/domino/domino.yml
@@ -24,7 +24,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/dovetail/dovetail-artifacts-upload.yml b/jjb/dovetail/dovetail-artifacts-upload.yml
index dc2ae5aa2..3d9af5ed7 100644
--- a/jjb/dovetail/dovetail-artifacts-upload.yml
+++ b/jjb/dovetail/dovetail-artifacts-upload.yml
@@ -33,6 +33,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -42,7 +43,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- dovetail-parameter:
diff --git a/jjb/dovetail/dovetail-ci-jobs.yml b/jjb/dovetail/dovetail-ci-jobs.yml
index 08eb5a5f1..0bd32a4ab 100644
--- a/jjb/dovetail/dovetail-ci-jobs.yml
+++ b/jjb/dovetail/dovetail-ci-jobs.yml
@@ -20,8 +20,8 @@
dovetail-branch: '{stream}'
gs-pathname: ''
docker-tag: 'latest'
- colorado: &colorado
- stream: colorado
+ danube: &danube
+ stream: danube
branch: 'stable/{stream}'
dovetail-branch: master
gs-pathname: '/{stream}'
@@ -54,12 +54,12 @@
slave-label: fuel-baremetal
SUT: fuel
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
- virtual:
slave-label: fuel-virtual
SUT: fuel
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
#compass CI PODs
- baremetal:
slave-label: compass-baremetal
@@ -75,33 +75,29 @@
slave-label: compass-baremetal
SUT: compass
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
- virtual:
slave-label: compass-virtual
SUT: compass
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
-#apex CI PODs
- - apex-verify-master:
- slave-label: '{pod}'
- SUT: apex
- auto-trigger-name: 'daily-trigger-disabled'
- <<: *master
- - apex-daily-master:
+ <<: *danube
+#--------------------------------
+# Installers not using labels
+# CI PODs
+# This section should only contain the installers
+# that have not been switched using labels for slaves
+#--------------------------------
+#apex PODs
+ - lf-pod1:
slave-label: '{pod}'
SUT: apex
auto-trigger-name: 'daily-trigger-disabled'
<<: *master
- - apex-verify-colorado:
+ - lf-pod1:
slave-label: '{pod}'
SUT: apex
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
- - apex-daily-colorado:
- slave-label: '{pod}'
- SUT: apex
- auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
#armband CI PODs
- armband-baremetal:
slave-label: armband-baremetal
@@ -117,17 +113,17 @@
slave-label: armband-baremetal
SUT: fuel
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
- armband-virtual:
slave-label: armband-virtual
SUT: fuel
auto-trigger-name: 'daily-trigger-disabled'
- <<: *colorado
+ <<: *danube
#--------------------------------
# None-CI PODs
#--------------------------------
- - huawei-pod5:
- slave-label: '{pod}'
+ - baremetal-centos:
+ slave-label: 'intel-pod8'
SUT: compass
auto-trigger-name: 'daily-trigger-disabled'
<<: *master
@@ -161,6 +157,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -179,8 +176,7 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
+ branch: '{dovetail-branch}'
- '{SUT}-defaults'
- '{slave-label}-defaults'
- string:
diff --git a/jjb/dovetail/dovetail-project-jobs.yml b/jjb/dovetail/dovetail-project-jobs.yml
index 904841396..9dc4808b4 100644
--- a/jjb/dovetail/dovetail-project-jobs.yml
+++ b/jjb/dovetail/dovetail-project-jobs.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -65,7 +64,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/dovetail/dovetail-weekly-jobs.yml b/jjb/dovetail/dovetail-weekly-jobs.yml
new file mode 100644
index 000000000..8edce4246
--- /dev/null
+++ b/jjb/dovetail/dovetail-weekly-jobs.yml
@@ -0,0 +1,135 @@
+- project:
+ name: dovetail-weekly-jobs
+ project: dovetail
+#--------------------------------
+# BRANCH ANCHORS
+#--------------------------------
+ master: &master
+ stream: master
+ branch: '{stream}'
+ dovetail-branch: '{stream}'
+ gs-pathname: ''
+ docker-tag: 'latest'
+ colorado: &colorado
+ stream: colorado
+ branch: 'stable/{stream}'
+ dovetail-branch: master
+ gs-pathname: '/{stream}'
+ docker-tag: 'latest'
+
+#--------------------------------
+# POD, INSTALLER, AND BRANCH MAPPING
+#--------------------------------
+# Installers using labels
+# CI PODs
+# This section should only contain the installers
+# that have been switched using labels for slaves
+#--------------------------------
+ pod:
+# - baremetal:
+# slave-label: apex-baremetal
+# sut: apex
+# <<: *colorado
+ - baremetal:
+ slave-label: compass-baremetal
+ sut: compass
+ <<: *colorado
+# - baremetal:
+# slave-label: fuel-baremetal
+# sut: fuel
+# <<: *master
+# - baremetal:
+# slave-label: joid-baremetal
+# sut: joid
+# <<: *colorado
+
+ testsuite:
+ - 'debug'
+ - 'proposed_tests'
+ - 'compliance_set'
+
+ loop:
+ - 'weekly':
+ job-timeout: 60
+
+ jobs:
+ - 'dovetail-{sut}-{pod}-{testsuite}-{loop}-{stream}'
+
+################################
+# job template
+################################
+- job-template:
+ name: 'dovetail-{sut}-{pod}-{testsuite}-{loop}-{stream}'
+
+ disabled: false
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-per-node: 1
+ option: 'project'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER Scenario: $DEPLOY_SCENARIO'
+ - timeout:
+ timeout: '{job-timeout}'
+ abort: true
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{dovetail-branch}'
+ - '{sut}-defaults'
+ - '{slave-label}-defaults'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-nosdn-nofeature-ha'
+ - string:
+ name: DOCKER_TAG
+ default: '{docker-tag}'
+ description: 'Tag to pull dovetail docker image'
+ - string:
+ name: CI_DEBUG
+ default: 'true'
+ description: "Show debug output information"
+ - string:
+ name: TESTSUITE
+ default: '{testsuite}'
+ description: "dovetail testsuite to run"
+ - string:
+ name: DOVETAIL_REPO_DIR
+ default: "/home/opnfv/dovetail"
+ description: "Directory where the dovetail repository is cloned"
+
+ scm:
+ - git-scm
+
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+ - 'dovetail-cleanup'
+ - 'dovetail-run'
+
+ publishers:
+ - archive:
+ artifacts: 'results/**/*'
+ allow-empty: true
+ fingerprint: true
+
+########################
+# builder macros
+########################
+- builder:
+ name: dovetail-run-weekly
+ builders:
+ - shell:
+ !include-raw: ./dovetail-run.sh
+- builder:
+ name: dovetail-cleanup-weekly
+ builders:
+ - shell:
+ !include-raw: ./dovetail-cleanup.sh
diff --git a/jjb/dpacc/dpacc.yml b/jjb/dpacc/dpacc.yml
index 27e663507..bc61d7447 100644
--- a/jjb/dpacc/dpacc.yml
+++ b/jjb/dpacc/dpacc.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/escalator/escalator.yml b/jjb/escalator/escalator.yml
index 4149ee93c..2265dafce 100644
--- a/jjb/escalator/escalator.yml
+++ b/jjb/escalator/escalator.yml
@@ -39,6 +39,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -80,7 +81,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-defaults'
- 'escalator-defaults':
@@ -96,7 +96,7 @@
- name: 'escalator-verify-basic-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -110,7 +110,7 @@
- name: 'escalator-verify-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -137,7 +137,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- 'escalator-defaults':
@@ -158,6 +157,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -193,7 +193,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-defaults'
- 'escalator-defaults':
@@ -209,7 +208,7 @@
- name: 'escalator-merge-basic-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -223,7 +222,7 @@
- name: 'escalator-merge-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -239,7 +238,7 @@
concurrent: true
scm:
- - git-scm-gerrit
+ - git-scm
wrappers:
- ssh-agent-wrapper
@@ -250,7 +249,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- 'escalator-defaults':
diff --git a/jjb/fuel/fuel-basic-exp.sh b/jjb/fuel/fuel-basic-exp.sh
deleted file mode 100755
index a70a0c765..000000000
--- a/jjb/fuel/fuel-basic-exp.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-set -o nounset
-
-echo "-----------------------------------------------------------------------"
-echo $GERRIT_CHANGE_COMMIT_MESSAGE
-echo "-----------------------------------------------------------------------"
-
-# proposal for specifying the scenario name in commit message
-# currently only 1 scenario name is supported but depending on
-# the need, it can be expanded, supporting multiple scenarios
-# using comma separated list or something
-SCENARIO_NAME_PATTERN="(?<=@scenario:).*?(?=@)"
-SCENARIO_NAME=(echo $GERRIT_CHANGE_COMMIT_MESSAGE | grep -oP "$SCENARIO_NAME_PATTERN")
-if [[ $? -ne 0 ]]; then
- echo "The patch verification will be done only with build!"
-else
- echo "Will run full verification; build, deploy, and smoke test using scenario $SCENARIO_NAME"
-fi
diff --git a/jjb/fuel/fuel-build-exp.sh b/jjb/fuel/fuel-build-exp.sh
deleted file mode 100755
index f7f613dc0..000000000
--- a/jjb/fuel/fuel-build-exp.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [[ "$JOB_NAME" =~ (verify|merge|daily|weekly) ]]; then
- JOB_TYPE=${BASH_REMATCH[0]}
-else
- echo "Unable to determine job type!"
- exit 1
-fi
-
-echo "Not activated!"
diff --git a/jjb/fuel/fuel-daily-jobs.yml b/jjb/fuel/fuel-daily-jobs.yml
index 87488c886..19336d8ac 100644
--- a/jjb/fuel/fuel-daily-jobs.yml
+++ b/jjb/fuel/fuel-daily-jobs.yml
@@ -15,10 +15,10 @@
branch: '{stream}'
disabled: false
gs-pathname: ''
- colorado: &colorado
- stream: colorado
+ danube: &danube
+ stream: danube
branch: 'stable/{stream}'
- disabled: false
+ disabled: true
gs-pathname: '/{stream}'
#--------------------------------
# POD, INSTALLER, AND BRANCH MAPPING
@@ -34,10 +34,10 @@
<<: *master
- baremetal:
slave-label: fuel-baremetal
- <<: *colorado
+ <<: *danube
- virtual:
slave-label: fuel-virtual
- <<: *colorado
+ <<: *danube
#--------------------------------
# None-CI PODs
#--------------------------------
@@ -52,10 +52,10 @@
<<: *master
- zte-pod1:
slave-label: zte-pod1
- <<: *colorado
+ <<: *danube
- zte-pod3:
slave-label: zte-pod3
- <<: *colorado
+ <<: *danube
#--------------------------------
# scenarios
#--------------------------------
@@ -81,6 +81,10 @@
auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
- 'os-nosdn-kvm_ovs-ha':
auto-trigger-name: 'daily-trigger-disabled'
+ - 'os-nosdn-kvm_ovs_dpdk-ha':
+ auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
+ - 'os-nosdn-kvm_ovs_dpdk_bar-ha':
+ auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
# NOHA scenarios
- 'os-nosdn-nofeature-noha':
auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
@@ -100,6 +104,10 @@
auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
- 'os-nosdn-ovs-noha':
auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
+ - 'os-nosdn-kvm_ovs_dpdk-noha':
+ auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
+ - 'os-nosdn-kvm_ovs_dpdk_bar-noha':
+ auto-trigger-name: 'fuel-{scenario}-{pod}-daily-{stream}-trigger'
jobs:
- 'fuel-{scenario}-{pod}-daily-{stream}'
@@ -116,6 +124,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -125,6 +134,7 @@
use-build-blocker: true
blocking-jobs:
- 'fuel-os-.*?-{pod}-daily-.*'
+ - 'fuel-os-.*?-{pod}-weekly-.*'
block-level: 'NODE'
wrappers:
@@ -137,6 +147,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
installer: '{installer}'
@@ -148,7 +159,7 @@
builders:
- description-setter:
- description: "POD: $NODE_NAME"
+ description: "Built on $NODE_NAME"
- trigger-builds:
- project: 'fuel-deploy-{pod}-daily-{stream}'
current-parameters: false
@@ -191,6 +202,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -201,12 +213,13 @@
blocking-jobs:
- 'fuel-deploy-{pod}-daily-.*'
- 'fuel-deploy-generic-daily-.*'
+ - 'fuel-deploy-{pod}-weekly-.*'
+ - 'fuel-deploy-generic-weekly-.*'
block-level: 'NODE'
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
@@ -230,7 +243,7 @@
builders:
- description-setter:
- description: "POD: $NODE_NAME"
+ description: "Built on $NODE_NAME"
- shell:
!include-raw-escape: ./fuel-download-artifact.sh
- shell:
@@ -238,7 +251,7 @@
publishers:
- email:
- recipients: jonas.bjurel@ericsson.com stefan.k.berg@ericsson.com peter.barabas@ericsson.com fzhadaev@mirantis.com
+ recipients: peter.barabas@ericsson.com fzhadaev@mirantis.com
########################
# parameter macros
@@ -272,11 +285,15 @@
- trigger:
name: 'fuel-os-odl_l2-nofeature-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '' # '5 23 * * *'
+ - timed: '5 23 * * *'
- trigger:
name: 'fuel-os-odl_l3-nofeature-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '' # '5 2 * * *'
+ - timed: '5 2 * * *'
+- trigger:
+ name: 'fuel-os-nosdn-ovs-ha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: '5 5 * * *'
- trigger:
name: 'fuel-os-onos-sfc-ha-baremetal-daily-master-trigger'
triggers:
@@ -288,20 +305,23 @@
- trigger:
name: 'fuel-os-odl_l2-sfc-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '' # '5 11 * * *'
+ - timed: '5 11 * * *'
- trigger:
name: 'fuel-os-odl_l2-bgpvpn-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '' # '5 14 * * *'
+ - timed: '5 14 * * *'
- trigger:
name: 'fuel-os-nosdn-kvm-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '' # '5 17 * * *'
+ - timed: '5 17 * * *'
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-baremetal-daily-master-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-baremetal-daily-master-trigger'
triggers:
- - timed: '5 20 * * *'
-
+ - timed: '30 12 * * *'
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: '30 8 * * *'
# NOHA Scenarios
- trigger:
name: 'fuel-os-nosdn-nofeature-noha-baremetal-daily-master-trigger'
@@ -339,82 +359,105 @@
name: 'fuel-os-nosdn-ovs-noha-baremetal-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-baremetal-daily-master-trigger'
+ triggers:
+ - timed: ''
#-----------------------------------------------
-# Triggers for job running on fuel-baremetal against colorado branch
+# Triggers for job running on fuel-baremetal against danube branch
#-----------------------------------------------
# HA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 20 * * *'
- trigger:
- name: 'fuel-os-odl_l2-nofeature-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 23 * * *'
- trigger:
- name: 'fuel-os-odl_l3-nofeature-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 2 * * *'
- trigger:
- name: 'fuel-os-onos-sfc-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 5 * * *'
- trigger:
- name: 'fuel-os-onos-nofeature-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 8 * * *'
- trigger:
- name: 'fuel-os-odl_l2-sfc-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 11 * * *'
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 14 * * *'
- trigger:
- name: 'fuel-os-nosdn-kvm-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 17 * * *'
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-ha-baremetal-daily-danube-trigger'
triggers:
- timed: '0 20 * * *'
-
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-baremetal-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-baremetal-daily-danube-trigger'
+ triggers:
+ - timed: ''
# NOHA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-noha-baremetal-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-noha-baremetal-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-baremetal-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-baremetal-daily-danube-trigger'
triggers:
- timed: ''
#-----------------------------------------------
@@ -456,6 +499,14 @@
name: 'fuel-os-nosdn-ovs-ha-virtual-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-virtual-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-virtual-daily-master-trigger'
+ triggers:
+ - timed: ''
# NOHA Scenarios
- trigger:
name: 'fuel-os-nosdn-nofeature-noha-virtual-daily-master-trigger'
@@ -472,11 +523,11 @@
- trigger:
name: 'fuel-os-onos-sfc-noha-virtual-daily-master-trigger'
triggers:
- - timed: '35 20 * * *'
+ - timed: '' # '35 20 * * *'
- trigger:
name: 'fuel-os-onos-nofeature-noha-virtual-daily-master-trigger'
triggers:
- - timed: '5 23 * * *'
+ - timed: '' # '5 23 * * *'
- trigger:
name: 'fuel-os-odl_l2-sfc-noha-virtual-daily-master-trigger'
triggers:
@@ -493,82 +544,106 @@
name: 'fuel-os-nosdn-ovs-noha-virtual-daily-master-trigger'
triggers:
- timed: '5 9 * * *'
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-virtual-daily-master-trigger'
+ triggers:
+ - timed: '30 16 * * *'
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-virtual-daily-master-trigger'
+ triggers:
+ - timed: '30 20 * * *'
#-----------------------------------------------
-# Triggers for job running on fuel-virtual against colorado branch
+# Triggers for job running on fuel-virtual against danube branch
#-----------------------------------------------
- trigger:
- name: 'fuel-os-nosdn-nofeature-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-ha-virtual-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-virtual-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-virtual-daily-danube-trigger'
triggers:
- timed: ''
# NOHA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-noha-virtual-daily-danube-trigger'
triggers:
- timed: '0 13 * * *'
- trigger:
- name: 'fuel-os-odl_l2-nofeature-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-noha-virtual-daily-danube-trigger'
triggers:
- timed: '30 15 * * *'
- trigger:
- name: 'fuel-os-odl_l3-nofeature-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-noha-virtual-daily-danube-trigger'
triggers:
- timed: '0 18 * * *'
- trigger:
- name: 'fuel-os-onos-sfc-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-noha-virtual-daily-danube-trigger'
triggers:
- timed: '30 20 * * *'
- trigger:
- name: 'fuel-os-onos-nofeature-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-noha-virtual-daily-danube-trigger'
triggers:
- timed: '0 23 * * *'
- trigger:
- name: 'fuel-os-odl_l2-sfc-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-noha-virtual-daily-danube-trigger'
triggers:
- timed: '30 1 * * *'
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-noha-virtual-daily-danube-trigger'
triggers:
- timed: '0 4 * * *'
- trigger:
- name: 'fuel-os-nosdn-kvm-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-noha-virtual-daily-danube-trigger'
triggers:
- timed: '30 6 * * *'
- trigger:
- name: 'fuel-os-nosdn-ovs-noha-virtual-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-noha-virtual-daily-danube-trigger'
triggers:
- timed: '0 9 * * *'
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-virtual-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-virtual-daily-danube-trigger'
+ triggers:
+ - timed: ''
#-----------------------------------------------
# ZTE POD1 Triggers running against master branch
#-----------------------------------------------
@@ -608,6 +683,14 @@
name: 'fuel-os-nosdn-ovs-ha-zte-pod1-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod1-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod1-daily-master-trigger'
+ triggers:
+ - timed: ''
# NOHA Scenarios
- trigger:
name: 'fuel-os-nosdn-nofeature-noha-zte-pod1-daily-master-trigger'
@@ -645,6 +728,14 @@
name: 'fuel-os-nosdn-ovs-noha-zte-pod1-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod1-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod1-daily-master-trigger'
+ triggers:
+ - timed: ''
#-----------------------------------------------
# ZTE POD2 Triggers running against master branch
@@ -685,6 +776,14 @@
name: 'fuel-os-nosdn-ovs-ha-zte-pod2-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod2-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod2-daily-master-trigger'
+ triggers:
+ - timed: ''
# NOHA Scenarios
- trigger:
name: 'fuel-os-nosdn-nofeature-noha-zte-pod2-daily-master-trigger'
@@ -722,6 +821,14 @@
name: 'fuel-os-nosdn-ovs-noha-zte-pod2-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod2-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod2-daily-master-trigger'
+ triggers:
+ - timed: ''
#-----------------------------------------------
# ZTE POD3 Triggers running against master branch
#-----------------------------------------------
@@ -761,6 +868,14 @@
name: 'fuel-os-nosdn-ovs-ha-zte-pod3-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod3-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod3-daily-master-trigger'
+ triggers:
+ - timed: ''
# NOHA Scenarios
- trigger:
name: 'fuel-os-nosdn-nofeature-noha-zte-pod3-daily-master-trigger'
@@ -798,232 +913,288 @@
name: 'fuel-os-nosdn-ovs-noha-zte-pod3-daily-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod3-daily-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod3-daily-master-trigger'
+ triggers:
+ - timed: ''
#-----------------------------------------------
-# ZTE POD1 Triggers running against colorado branch
+# ZTE POD1 Triggers running against danube branch
#-----------------------------------------------
- trigger:
- name: 'fuel-os-nosdn-nofeature-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: '0 2 * * *'
- trigger:
- name: 'fuel-os-odl_l3-nofeature-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-ha-zte-pod1-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-onos-sfc-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod1-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
# NOHA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-noha-zte-pod1-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-odl_l2-nofeature-noha-zte-pod1-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-odl_l3-nofeature-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-noha-zte-pod1-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod1-daily-danube-trigger'
triggers:
- timed: ''
#-----------------------------------------------
-# ZTE POD2 Triggers running against colorado branch
+# ZTE POD2 Triggers running against danube branch
#-----------------------------------------------
- trigger:
- name: 'fuel-os-nosdn-nofeature-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-ha-zte-pod2-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod2-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
# NOHA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-noha-zte-pod2-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-noha-zte-pod2-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod2-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod2-daily-danube-trigger'
triggers:
- timed: ''
#-----------------------------------------------
-# ZTE POD3 Triggers running against colorado branch
+# ZTE POD3 Triggers running against danube branch
#-----------------------------------------------
- trigger:
- name: 'fuel-os-nosdn-nofeature-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-nofeature-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l3-nofeature-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: '0 18 * * *'
- trigger:
- name: 'fuel-os-nosdn-ovs-ha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-ha-zte-pod3-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-ha-zte-pod3-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-ha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
# NOHA Scenarios
- trigger:
- name: 'fuel-os-nosdn-nofeature-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-nofeature-noha-zte-pod3-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-odl_l2-nofeature-noha-zte-pod3-daily-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'fuel-os-odl_l3-nofeature-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-nofeature-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-onos-sfc-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l3-nofeature-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-onos-nofeature-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-sfc-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-sfc-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-onos-nofeature-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-sfc-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-odl_l2-bgpvpn-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-ovs-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-kvm-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
- trigger:
- name: 'fuel-os-nosdn-ovs-noha-zte-pod3-daily-colorado-trigger'
+ name: 'fuel-os-nosdn-kvm_ovs_dpdk_bar-noha-zte-pod3-daily-danube-trigger'
triggers:
- timed: ''
diff --git a/jjb/fuel/fuel-deploy-exp.sh b/jjb/fuel/fuel-deploy-exp.sh
deleted file mode 100755
index f7f613dc0..000000000
--- a/jjb/fuel/fuel-deploy-exp.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [[ "$JOB_NAME" =~ (verify|merge|daily|weekly) ]]; then
- JOB_TYPE=${BASH_REMATCH[0]}
-else
- echo "Unable to determine job type!"
- exit 1
-fi
-
-echo "Not activated!"
diff --git a/jjb/fuel/fuel-deploy.sh b/jjb/fuel/fuel-deploy.sh
index 48b1dac2f..f5bbd1818 100755
--- a/jjb/fuel/fuel-deploy.sh
+++ b/jjb/fuel/fuel-deploy.sh
@@ -57,9 +57,9 @@ chmod a+x $TMPDIR
# clone the securedlab repo
cd $WORKSPACE
-echo "Cloning securedlab repo ${GIT_BRANCH##origin/}"
+echo "Cloning securedlab repo $BRANCH"
git clone ssh://jenkins-ericsson@gerrit.opnfv.org:29418/securedlab --quiet \
- --branch ${GIT_BRANCH##origin/}
+ --branch $BRANCH
# log file name
FUEL_LOG_FILENAME="${JOB_NAME}_${BUILD_NUMBER}.log.tar.gz"
@@ -95,7 +95,7 @@ echo "Deployment is done!"
# upload logs for baremetal deployments
# work with virtual deployments is still going on so we skip that for the timebeing
-if [[ "$JOB_NAME" =~ "baremetal-daily" ]]; then
+if [[ "$JOB_NAME" =~ (baremetal-daily|baremetal-weekly) ]]; then
echo "Uploading deployment logs"
gsutil cp $WORKSPACE/$FUEL_LOG_FILENAME gs://$GS_URL/logs/$FUEL_LOG_FILENAME > /dev/null 2>&1
echo "Logs are available as http://$GS_URL/logs/$FUEL_LOG_FILENAME"
diff --git a/jjb/fuel/fuel-download-artifact.sh b/jjb/fuel/fuel-download-artifact.sh
index 2a0f09a3f..8cc552e8d 100755
--- a/jjb/fuel/fuel-download-artifact.sh
+++ b/jjb/fuel/fuel-download-artifact.sh
@@ -36,7 +36,7 @@ echo "Using $OPNFV_ARTIFACT for deployment"
# using ISOs for verify & merge jobs from local storage will be enabled later
if [[ ! "$JOB_NAME" =~ (verify|merge) ]]; then
# check if we already have the ISO to avoid redownload
- ISOSTORE="/iso_mount/opnfv_ci/${GIT_BRANCH##*/}"
+ ISOSTORE="/iso_mount/opnfv_ci/${BRANCH##*/}"
if [[ -f "$ISOSTORE/$OPNFV_ARTIFACT" ]]; then
echo "ISO exists locally. Skipping the download and using the file from ISO store"
ln -s $ISOSTORE/$OPNFV_ARTIFACT $WORKSPACE/opnfv.iso
diff --git a/jjb/fuel/fuel-plugin-build.sh b/jjb/fuel/fuel-plugin-build.sh
deleted file mode 100755
index f7f613dc0..000000000
--- a/jjb/fuel/fuel-plugin-build.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [[ "$JOB_NAME" =~ (verify|merge|daily|weekly) ]]; then
- JOB_TYPE=${BASH_REMATCH[0]}
-else
- echo "Unable to determine job type!"
- exit 1
-fi
-
-echo "Not activated!"
diff --git a/jjb/fuel/fuel-plugin-test.sh b/jjb/fuel/fuel-plugin-test.sh
deleted file mode 100755
index f7f613dc0..000000000
--- a/jjb/fuel/fuel-plugin-test.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [[ "$JOB_NAME" =~ (verify|merge|daily|weekly) ]]; then
- JOB_TYPE=${BASH_REMATCH[0]}
-else
- echo "Unable to determine job type!"
- exit 1
-fi
-
-echo "Not activated!"
diff --git a/jjb/fuel/fuel-plugin-verify-jobs.yml b/jjb/fuel/fuel-plugin-verify-jobs.yml
deleted file mode 100644
index bf847edfe..000000000
--- a/jjb/fuel/fuel-plugin-verify-jobs.yml
+++ /dev/null
@@ -1,236 +0,0 @@
-- project:
- name: 'fuel-plugin-verify-jobs'
-
- project: 'fuel-plugin'
-
- installer: 'fuel'
-#####################################
-# branch definitions
-#####################################
- stream:
- - master:
- upstream-branch: '{stream}'
- opnfv-branch: 'experimental'
- gs-pathname: ''
- disabled: false
-#####################################
-# patch verification phases
-#####################################
- phase:
- - 'build':
- slave-label: 'opnfv-build-ubuntu'
- - 'test':
- slave-label: 'opnfv-build-ubuntu'
-#####################################
-# jobs
-#####################################
- jobs:
- - 'fuel-verify-plugin-{stream}'
- - 'fuel-verify-plugin-{phase}-{stream}'
-#####################################
-# job templates
-#####################################
-- job-template:
- name: 'fuel-verify-plugin-{stream}'
-
- project-type: multijob
-
- disabled: '{obj:disabled}'
-
- concurrent: true
-
- properties:
- - throttle:
- enabled: true
- max-total: 4
- option: 'project'
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{upstream-branch}'
- description: 'OpenStack branch to use'
- - string:
- name: OPNFV_BRANCH
- default: '{opnfv-branch}'
- description: 'OPNFV branch to use'
- - 'opnfv-build-defaults'
- - 'fuel-verify-plugin-defaults':
- gs-pathname: '{gs-pathname}'
-
- scm:
- - git:
- url: 'https://git.openstack.org/$GERRIT_PROJECT'
- refspec: '$GERRIT_REFSPEC'
- branches:
- - 'origin/$GERRIT_BRANCH'
- skip-tag: true
- choosing-strategy: 'gerrit'
- timeout: 10
- wipe-workspace: true
-
- wrappers:
- - ssh-agent-wrapper
- - timeout:
- timeout: 360
- fail: true
-
- triggers:
- - gerrit:
- server-name: 'review.openstack.org'
- silent-start: false
- skip-vote:
- successful: true
- failed: true
- unstable: true
- notbuilt: true
- escape-quotes: true
- trigger-on:
- - patchset-created-event:
- exclude-drafts: 'false'
- exclude-trivial-rebase: 'false'
- exclude-no-code-change: 'false'
- - comment-added-contains-event:
- comment-contains-value: 'recheck'
- - comment-added-contains-event:
- comment-contains-value: 'reverify'
- projects:
- - project-compare-type: 'PLAIN'
- project-pattern: 'openstack/fuel-plugin-bgpvpn'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{upstream-branch}'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'README.md|.gitignore|.gitreview'
- - project-compare-type: 'PLAIN'
- project-pattern: 'openstack/fuel-plugin-onos'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{upstream-branch}'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'README.md|.gitignore|.gitreview'
- readable-message: true
-
- builders:
- - description-setter:
- description: "Built on $NODE_NAME"
- - multijob:
- name: build
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-plugin-build-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_PROJECT=$GERRIT_PROJECT
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: test
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-plugin-test-{stream}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_PROJECT=$GERRIT_PROJECT
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
-
-- job-template:
- name: 'fuel-verify-plugin-{phase}-{stream}'
-
- disabled: '{obj:disabled}'
-
- concurrent: true
-
- properties:
- - throttle:
- enabled: true
- max-total: 6
- option: 'project'
- - build-blocker:
- use-build-blocker: true
- blocking-jobs:
- - 'fuel-verify-plugin-test-.*'
- block-level: 'NODE'
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{upstream-branch}'
- description: 'OpenStack branch to use'
- - string:
- name: OPNFV_BRANCH
- default: '{opnfv-branch}'
- description: 'OPNFV branch to use'
- - '{slave-label}-defaults'
- - '{installer}-defaults'
- - 'fuel-verify-plugin-defaults':
- gs-pathname: '{gs-pathname}'
-
- scm:
- - git:
- url: 'https://git.openstack.org/$GERRIT_PROJECT'
- refspec: '$GERRIT_REFSPEC'
- branches:
- - 'origin/$GERRIT_BRANCH'
- skip-tag: true
- choosing-strategy: 'gerrit'
- timeout: 10
- wipe-workspace: true
-
- wrappers:
- - ssh-agent-wrapper
- - timeout:
- timeout: 360
- fail: true
-
- builders:
- - description-setter:
- description: "Built on $NODE_NAME"
- - 'fuel-verify-plugin-{phase}-macro'
-#####################################
-# builder macros
-#####################################
-- builder:
- name: 'fuel-verify-plugin-build-macro'
- builders:
- - shell:
- !include-raw: ./fuel-plugin-build.sh
-
-- builder:
- name: 'fuel-verify-plugin-test-macro'
- builders:
- - shell:
- !include-raw: ./fuel-plugin-test.sh
-#####################################
-# parameter macros
-#####################################
-- parameter:
- name: 'fuel-verify-plugin-defaults'
- parameters:
- - string:
- name: BUILD_DIRECTORY
- default: $WORKSPACE/build_output
- description: "Directory where the build artifact will be located upon the completion of the build."
- - string:
- name: CACHE_DIRECTORY
- default: $HOME/opnfv/cache/$INSTALLER_TYPE
- description: "Directory where the cache to be used during the build is located."
- - string:
- name: GS_URL
- default: artifacts.opnfv.org/$PROJECT{gs-pathname}
- description: "URL to Google Storage."
diff --git a/jjb/fuel/fuel-project-jobs.yml b/jjb/fuel/fuel-project-jobs.yml
index 8fdf8deae..32ad8907e 100644
--- a/jjb/fuel/fuel-project-jobs.yml
+++ b/jjb/fuel/fuel-project-jobs.yml
@@ -13,10 +13,10 @@
branch: '{stream}'
gs-pathname: ''
disabled: false
- - colorado:
+ - danube:
branch: 'stable/{stream}'
gs-pathname: '/{stream}'
- disabled: false
+ disabled: true
jobs:
- 'fuel-build-daily-{stream}'
@@ -35,6 +35,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -44,7 +45,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- '{installer}-defaults'
@@ -78,7 +78,7 @@
publishers:
- email:
- recipients: jonas.bjurel@ericsson.com stefan.k.berg@ericsson.com fzhadaev@mirantis.com
+ recipients: fzhadaev@mirantis.com
- job-template:
name: 'fuel-merge-build-{stream}'
@@ -90,7 +90,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- '{installer}-defaults'
@@ -146,6 +145,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 2
@@ -161,7 +161,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'fuel-virtual-defaults':
installer: '{installer}'
@@ -209,7 +208,7 @@
publishers:
- email:
- recipients: jonas.bjurel@ericsson.com stefan.k.berg@ericsson.com fzhadaev@mirantis.com
+ recipients: fzhadaev@mirantis.com
- job-template:
name: 'fuel-deploy-generic-daily-{stream}'
@@ -219,6 +218,7 @@
disabled: '{obj:disabled}'
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -233,7 +233,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- string:
diff --git a/jjb/fuel/fuel-smoke-test-exp.sh b/jjb/fuel/fuel-smoke-test-exp.sh
deleted file mode 100755
index f7f613dc0..000000000
--- a/jjb/fuel/fuel-smoke-test-exp.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [[ "$JOB_NAME" =~ (verify|merge|daily|weekly) ]]; then
- JOB_TYPE=${BASH_REMATCH[0]}
-else
- echo "Unable to determine job type!"
- exit 1
-fi
-
-echo "Not activated!"
diff --git a/jjb/fuel/fuel-upload-artifact.sh b/jjb/fuel/fuel-upload-artifact.sh
index ca4ba00b0..d1ac3509b 100755
--- a/jjb/fuel/fuel-upload-artifact.sh
+++ b/jjb/fuel/fuel-upload-artifact.sh
@@ -23,7 +23,7 @@ nfsstore () {
# storing ISOs for verify & merge jobs will be done once we get the disk array
if [[ ! "$JOB_NAME" =~ (verify|merge) ]]; then
# store ISO locally on NFS first
- ISOSTORE="/iso_mount/opnfv_ci/${GIT_BRANCH##*/}"
+ ISOSTORE="/iso_mount/opnfv_ci/${BRANCH##*/}"
if [[ -d "$ISOSTORE" ]]; then
# remove all but most recent 5 ISOs first to keep iso_mount clean & tidy
cd $ISOSTORE
diff --git a/jjb/fuel/fuel-verify-jobs-experimental.yml b/jjb/fuel/fuel-verify-jobs-experimental.yml
deleted file mode 100644
index 3aa85b22d..000000000
--- a/jjb/fuel/fuel-verify-jobs-experimental.yml
+++ /dev/null
@@ -1,255 +0,0 @@
-- project:
- # TODO: rename the project name
- # TODO: get rid of appended -exp from the remainder of the file
- name: 'fuel-verify-jobs-experimental'
-
- project: 'fuel'
-
- installer: 'fuel'
-#------------------------------------
-# branch definitions
-#------------------------------------
- # TODO: enable master once things settle
- stream-exp:
- - experimental:
- branch: 'stable/{stream-exp}'
- gs-pathname: '/{stream-exp}'
- disabled: false
-#------------------------------------
-# patch verification phases
-#------------------------------------
- phase:
- - 'basic':
- # this phase does basic commit message check, unit test and so on
- slave-label: 'opnfv-build'
- - 'build':
- # this phase builds artifacts if valid for given installer
- slave-label: 'opnfv-build-ubuntu'
- - 'deploy-virtual':
- # this phase does virtual deployment using the artifacts produced in previous phase
- slave-label: 'fuel-virtual'
- - 'smoke-test':
- # this phase runs functest smoke test
- slave-label: 'fuel-virtual'
-#------------------------------------
-# jobs
-#------------------------------------
- jobs:
- - 'fuel-verify-{stream-exp}'
- - 'fuel-verify-{phase}-{stream-exp}'
-#------------------------------------
-# job templates
-#------------------------------------
-- job-template:
- name: 'fuel-verify-{stream-exp}'
-
- project-type: multijob
-
- disabled: '{obj:disabled}'
-
- # TODO: this is valid for experimental only
- # enable concurrency for master once things settle
- concurrent: false
-
- properties:
- - throttle:
- enabled: true
- max-total: 4
- option: 'project'
-
- scm:
- - git-scm-gerrit
-
- wrappers:
- - ssh-agent-wrapper
- - timeout:
- timeout: 360
- fail: true
-
- triggers:
- - gerrit:
- server-name: 'gerrit.opnfv.org'
- trigger-on:
- - patchset-created-event:
- exclude-drafts: 'false'
- exclude-trivial-rebase: 'false'
- exclude-no-code-change: 'false'
- - draft-published-event
- - comment-added-contains-event:
- comment-contains-value: 'recheck'
- - comment-added-contains-event:
- comment-contains-value: 'reverify'
- projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- file-paths:
- - compare-type: ANT
- pattern: 'ci/**'
- - compare-type: ANT
- pattern: 'build/**'
- - compare-type: ANT
- pattern: 'deploy/**'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'docs/**'
- readable-message: true
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - 'opnfv-build-defaults'
- - 'fuel-verify-defaults-exp':
- gs-pathname: '{gs-pathname}'
-
- builders:
- - description-setter:
- description: "Built on $NODE_NAME"
- - multijob:
- name: basic
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-basic-{stream-exp}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: build
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-build-{stream-exp}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: deploy-virtual
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-deploy-virtual-{stream-exp}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
- - multijob:
- name: smoke-test
- condition: SUCCESSFUL
- projects:
- - name: 'fuel-verify-smoke-test-{stream-exp}'
- current-parameters: false
- predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
- GERRIT_REFSPEC=$GERRIT_REFSPEC
- GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
- GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
- node-parameters: false
- kill-phase-on: FAILURE
- abort-all-job: true
-
-- job-template:
- name: 'fuel-verify-{phase}-{stream-exp}'
-
- disabled: '{obj:disabled}'
-
- concurrent: true
-
- properties:
- - throttle:
- enabled: true
- max-total: 6
- option: 'project'
- - build-blocker:
- use-build-blocker: true
- blocking-jobs:
- - 'fuel-verify-deploy-.*'
- - 'fuel-verify-test-.*'
- block-level: 'NODE'
-
- scm:
- - git-scm-gerrit
-
- wrappers:
- - ssh-agent-wrapper
- - timeout:
- timeout: 360
- fail: true
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - '{slave-label}-defaults'
- - '{installer}-defaults'
- - 'fuel-verify-defaults-exp':
- gs-pathname: '{gs-pathname}'
-
- builders:
- - description-setter:
- description: "Built on $NODE_NAME"
- - '{project}-verify-{phase}-macro-exp'
-#------------------------------------
-# builder macros
-#------------------------------------
-- builder:
- name: 'fuel-verify-basic-macro-exp'
- builders:
- - shell:
- !include-raw: ./fuel-basic-exp.sh
-
-- builder:
- name: 'fuel-verify-build-macro-exp'
- builders:
- - shell:
- !include-raw: ./fuel-build-exp.sh
- - shell:
- !include-raw: ./fuel-workspace-cleanup.sh
-
-- builder:
- name: 'fuel-verify-deploy-virtual-macro-exp'
- builders:
- - shell:
- !include-raw: ./fuel-deploy-exp.sh
-
-- builder:
- name: 'fuel-verify-smoke-test-macro-exp'
- builders:
- - shell:
- !include-raw: ./fuel-smoke-test-exp.sh
-#------------------------------------
-# parameter macros
-#------------------------------------
-- parameter:
- name: 'fuel-verify-defaults-exp'
- parameters:
- - string:
- name: BUILD_DIRECTORY
- default: $WORKSPACE/build_output
- description: "Directory where the build artifact will be located upon the completion of the build."
- - string:
- name: CACHE_DIRECTORY
- default: $HOME/opnfv/cache/$INSTALLER_TYPE
- description: "Directory where the cache to be used during the build is located."
- - string:
- name: GS_URL
- default: artifacts.opnfv.org/$PROJECT{gs-pathname}
- description: "URL to Google Storage."
diff --git a/jjb/fuel/fuel-verify-jobs.yml b/jjb/fuel/fuel-verify-jobs.yml
index 4a76d9da7..7f9eff04d 100644
--- a/jjb/fuel/fuel-verify-jobs.yml
+++ b/jjb/fuel/fuel-verify-jobs.yml
@@ -12,10 +12,10 @@
branch: '{stream}'
gs-pathname: ''
disabled: false
- - colorado:
+ - danube:
branch: 'stable/{stream}'
gs-pathname: '/{stream}'
- disabled: false
+ disabled: true
#####################################
# patch verification phases
#####################################
@@ -47,6 +47,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -95,7 +96,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- 'fuel-verify-defaults':
@@ -111,7 +111,7 @@
- name: 'fuel-verify-basic-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -125,7 +125,7 @@
- name: 'fuel-verify-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -139,7 +139,7 @@
- name: 'fuel-verify-deploy-virtual-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -153,7 +153,7 @@
- name: 'fuel-verify-smoke-test-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -169,6 +169,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 6
@@ -191,7 +192,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- '{installer}-defaults'
diff --git a/jjb/fuel/fuel-weekly-jobs.yml b/jjb/fuel/fuel-weekly-jobs.yml
new file mode 100644
index 000000000..06d813458
--- /dev/null
+++ b/jjb/fuel/fuel-weekly-jobs.yml
@@ -0,0 +1,210 @@
+# jenkins job templates for Fuel
+- project:
+
+ name: fuel-weekly
+
+ project: fuel
+
+ installer: fuel
+
+#--------------------------------
+# BRANCH ANCHORS
+#--------------------------------
+ master: &master
+ stream: master
+ branch: '{stream}'
+ disabled: false
+ gs-pathname: ''
+ danube: &danube
+ stream: danube
+ branch: 'stable/{stream}'
+ disabled: true
+ gs-pathname: '/{stream}'
+#--------------------------------
+# POD, INSTALLER, AND BRANCH MAPPING
+#--------------------------------
+# CI PODs
+#--------------------------------
+ pod:
+ - baremetal:
+ slave-label: fuel-baremetal
+ <<: *master
+ - virtual:
+ slave-label: fuel-virtual
+ <<: *master
+ - baremetal:
+ slave-label: fuel-baremetal
+ <<: *danube
+ - virtual:
+ slave-label: fuel-virtual
+ <<: *danube
+#--------------------------------
+# scenarios
+#--------------------------------
+ scenario:
+ # HA scenarios
+ - 'os-nosdn-nofeature-ha':
+ auto-trigger-name: 'weekly-trigger-disabled'
+
+ jobs:
+ - 'fuel-{scenario}-{pod}-weekly-{stream}'
+ - 'fuel-deploy-{pod}-weekly-{stream}'
+
+########################
+# job templates
+########################
+- job-template:
+ name: 'fuel-{scenario}-{pod}-weekly-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: false
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ max-per-node: 1
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'fuel-os-.*?-{pod}-daily-.*'
+ - 'fuel-os-.*?-{pod}-weekly-.*'
+ block-level: 'NODE'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+ triggers:
+ - '{auto-trigger-name}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - '{installer}-defaults'
+ - '{slave-label}-defaults':
+ installer: '{installer}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: '{scenario}'
+ - fuel-weekly-parameter:
+ gs-pathname: '{gs-pathname}'
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - trigger-builds:
+ - project: 'fuel-deploy-{pod}-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ same-node: true
+ block: true
+ - trigger-builds:
+ - project: 'functest-fuel-{pod}-weekly-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ same-node: true
+ block: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
+
+ publishers:
+ - email:
+ recipients: peter.barabas@ericsson.com fzhadaev@mirantis.com
+
+- job-template:
+ name: 'fuel-deploy-{pod}-weekly-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-total: 4
+ max-per-node: 1
+ option: 'project'
+ - build-blocker:
+ use-build-blocker: true
+ blocking-jobs:
+ - 'fuel-deploy-{pod}-daily-.*'
+ - 'fuel-deploy-generic-daily-.*'
+ - 'fuel-deploy-{pod}-weekly-.*'
+ - 'fuel-deploy-generic-weekly-.*'
+ block-level: 'NODE'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - '{installer}-defaults'
+ - '{slave-label}-defaults':
+ installer: '{installer}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-odl_l2-nofeature-ha'
+ - fuel-weekly-parameter:
+ gs-pathname: '{gs-pathname}'
+ - string:
+ name: DEPLOY_TIMEOUT
+ default: '150'
+ description: 'Deployment timeout in minutes'
+
+ scm:
+ - git-scm
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER - Scenario: $DEPLOY_SCENARIO'
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - shell:
+ !include-raw-escape: ./fuel-download-artifact.sh
+ - shell:
+ !include-raw-escape: ./fuel-deploy.sh
+
+ publishers:
+ - email:
+ recipients: peter.barabas@ericsson.com fzhadaev@mirantis.com
+
+########################
+# parameter macros
+########################
+- parameter:
+ name: fuel-weekly-parameter
+ parameters:
+ - string:
+ name: BUILD_DIRECTORY
+ default: $WORKSPACE/build_output
+ description: "Directory where the build artifact will be located upon the completion of the build."
+ - string:
+ name: CACHE_DIRECTORY
+ default: $HOME/opnfv/cache/$INSTALLER_TYPE
+ description: "Directory where the cache to be used during the build is located."
+ - string:
+ name: GS_URL
+ default: artifacts.opnfv.org/$PROJECT{gs-pathname}
+ description: "URL to Google Storage."
+########################
+# trigger macros
+########################
+#-----------------------------------------------
+# Triggers for job running on fuel-baremetal against master branch
+#-----------------------------------------------
+# HA Scenarios
+- trigger:
+ name: 'fuel-os-nosdn-nofeature-ha-baremetal-weekly-master-trigger'
+ triggers:
+ - timed: ''
diff --git a/jjb/functest/functest-cleanup.sh b/jjb/functest/functest-cleanup.sh
index b03d4778d..fc277b9ed 100755
--- a/jjb/functest/functest-cleanup.sh
+++ b/jjb/functest/functest-cleanup.sh
@@ -3,14 +3,22 @@
[[ $CI_DEBUG == true ]] && redirect="/dev/stdout" || redirect="/dev/null"
echo "Cleaning up docker containers/images..."
+HOST_ARCH=$(uname -m)
FUNCTEST_IMAGE=opnfv/functest
-# Remove containers along with image opnfv/functest:<none>
+if [ "$HOST_ARCH" = "aarch64" ]; then
+ FUNCTEST_IMAGE="${FUNCTEST_IMAGE}_${HOST_ARCH}"
+fi
+
+# Remove containers along with image opnfv/functest*:<none>
dangling_images=($(docker images -f "dangling=true" | grep $FUNCTEST_IMAGE | awk '{print $3}'))
if [[ -n ${dangling_images} ]]; then
echo " Removing $FUNCTEST_IMAGE:<none> images and their containers..."
for image_id in "${dangling_images[@]}"; do
echo " Removing image_id: $image_id and its containers"
- docker ps -a | grep $image_id | awk '{print $1}'| xargs docker rm -f >${redirect}
+ containers=$(docker ps -a | grep $image_id | awk '{print $1}')
+ if [[ -n "$containers" ]];then
+ docker rm -f $containers >${redirect}
+ fi
docker rmi $image_id >${redirect}
done
fi
diff --git a/jjb/functest/functest-ci-jobs.yml b/jjb/functest/functest-daily-jobs.yml
index 4920bffea..5984b3674 100644
--- a/jjb/functest/functest-ci-jobs.yml
+++ b/jjb/functest/functest-daily-jobs.yml
@@ -2,9 +2,9 @@
# job configuration for functest
###################################
- project:
- name: functest
+ name: functest-daily
- project: '{name}'
+ project: functest
#--------------------------------
# BRANCH ANCHORS
@@ -113,6 +113,15 @@
slave-label: armband-virtual
installer: fuel
<<: *danube
+# daisy CI PODs
+ - baremetal:
+ slave-label: daisy-baremetal
+ installer: daisy
+ <<: *master
+ - virtual:
+ slave-label: daisy-virtual
+ installer: daisy
+ <<: *master
# netvirt 3rd party ci
- virtual:
slave-label: odl-netvirt-virtual
@@ -133,8 +142,8 @@
slave-label: '{pod}'
installer: joid
<<: *master
- - huawei-pod5:
- slave-label: '{pod}'
+ - baremetal-centos:
+ slave-label: 'intel-pod8'
installer: compass
<<: *master
- nokia-pod1:
@@ -189,8 +198,6 @@
job-timeout: 60
- 'daily':
job-timeout: 180
- - 'weekly':
- job-timeout: 400
jobs:
- 'functest-{installer}-{pod}-{testsuite}-{stream}'
@@ -204,6 +211,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -219,7 +227,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults'
@@ -234,7 +241,7 @@
- string:
name: CLEAN_DOCKER_IMAGES
default: 'false'
- description: 'Remove downloaded docker images (opnfv/functest:*)'
+ description: 'Remove downloaded docker images (opnfv/functest*:*)'
- functest-parameter:
gs-pathname: '{gs-pathname}'
@@ -243,7 +250,7 @@
builders:
- description-setter:
- description: "POD: $NODE_NAME"
+ description: "Built on $NODE_NAME"
- 'functest-{testsuite}-builder'
########################
@@ -257,13 +264,6 @@
default: 'daily'
description: "Daily suite name to run"
- parameter:
- name: functest-weekly-parameter
- parameters:
- - string:
- name: FUNCTEST_SUITE_NAME
- default: 'weekly'
- description: "Weekly suite name to run"
-- parameter:
name: functest-suite-parameter
parameters:
- choice:
@@ -275,6 +275,7 @@
- 'tempest_smoke_serial'
- 'rally_sanity'
- 'odl'
+ - 'odl_netvirt'
- 'onos'
- 'promise'
- 'doctor'
@@ -333,15 +334,6 @@
- 'functest-exit'
- builder:
- name: functest-weekly-builder
- builders:
- - 'functest-cleanup'
- - 'set-functest-env'
- - 'functest-weekly'
- - 'functest-store-results'
- - 'functest-exit'
-
-- builder:
name: functest-suite-builder
builders:
- 'functest-cleanup'
@@ -354,11 +346,6 @@
- shell:
!include-raw: ./functest-loop.sh
-- builder:
- name: functest-weekly
- builders:
- - shell:
- !include-raw: ./functest-loop.sh
- builder:
name: functest-suite
diff --git a/jjb/functest/functest-exit.sh b/jjb/functest/functest-exit.sh
index 10edab005..925a3cfbb 100644
--- a/jjb/functest/functest-exit.sh
+++ b/jjb/functest/functest-exit.sh
@@ -1,7 +1,6 @@
#!/bin/bash
-branch=${GIT_BRANCH##*/}
-ret_val_file="${HOME}/opnfv/functest/results/${branch}/return_value"
+ret_val_file="${HOME}/opnfv/functest/results/${BRANCH##*/}/return_value"
if [ ! -f ${ret_val_file} ]; then
echo "Return value not found!"
exit -1
@@ -9,4 +8,4 @@ fi
ret_val=`cat ${ret_val_file}`
-exit ${ret_val} \ No newline at end of file
+exit ${ret_val}
diff --git a/jjb/functest/functest-loop.sh b/jjb/functest/functest-loop.sh
index 4528c00d1..893c428a2 100755
--- a/jjb/functest/functest-loop.sh
+++ b/jjb/functest/functest-loop.sh
@@ -3,9 +3,9 @@ set +e
branch=${GIT_BRANCH##*/}
[[ "$PUSH_RESULTS_TO_DB" == "true" ]] && flags+="-r"
-if [[ ${branch} == *"brahmaputra"* ]]; then
+if [[ "$BRANCH" =~ 'brahmaputra' ]]; then
cmd="${FUNCTEST_REPO_DIR}/docker/run_tests.sh -s ${flags}"
-elif [[ ${branch} == *"colorado"* ]]; then
+elif [[ "$BRANCH" =~ 'colorado' ]]; then
cmd="python ${FUNCTEST_REPO_DIR}/ci/run_tests.py -t all ${flags}"
else
cmd="python ${FUNCTEST_REPO_DIR}/functest/ci/run_tests.py -t all ${flags}"
@@ -14,7 +14,7 @@ container_id=$(docker ps -a | grep opnfv/functest | awk '{print $1}' | head -1)
docker exec $container_id $cmd
ret_value=$?
-ret_val_file="${HOME}/opnfv/functest/results/${branch}/return_value"
+ret_val_file="${HOME}/opnfv/functest/results/${BRANCH##*/}/return_value"
echo ${ret_value}>${ret_val_file}
exit 0
diff --git a/jjb/functest/functest-project-jobs.yml b/jjb/functest/functest-project-jobs.yml
index 6a0768c9c..42c19a777 100644
--- a/jjb/functest/functest-project-jobs.yml
+++ b/jjb/functest/functest-project-jobs.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/functest/functest-suite.sh b/jjb/functest/functest-suite.sh
index 9dc8deca0..228cc3da4 100755
--- a/jjb/functest/functest-suite.sh
+++ b/jjb/functest/functest-suite.sh
@@ -1,18 +1,18 @@
#!/bin/bash
-set -e
-branch=${GIT_BRANCH##*/}
-echo "Functest: run $FUNCTEST_SUITE_NAME on branch ${branch}"
-if [[ ${branch} == *"brahmaputra"* ]]; then
- cmd="${FUNCTEST_REPO_DIR}/docker/run_tests.sh --test $FUNCTEST_SUITE_NAME"
-elif [[ ${branch} == *"colorado"* ]]; then
- cmd="python ${FUNCTEST_REPO_DIR}/ci/run_tests.py -t $FUNCTEST_SUITE_NAME"
-else
- cmd="functest testcase run $FUNCTEST_SUITE_NAME"
-fi
container_id=$(docker ps -a | grep opnfv/functest | awk '{print $1}' | head -1)
-docker exec $container_id $cmd
+if [ -z $container_id ]; then
+ echo "Functest container not found"
+ exit 1
+fi
+
+global_ret_val=0
-ret_value=$?
+tests=($(echo $FUNCTEST_SUITE_NAME | tr "," "\n"))
+for test in ${tests[@]}; do
+ cmd="python /home/opnfv/repos/functest/functest/ci/run_tests.py -t $test"
+ docker exec $container_id $cmd
+ let global_ret_val+=$?
+done
-exit $ret_value
+exit $global_ret_val
diff --git a/jjb/functest/functest-weekly-jobs.yml b/jjb/functest/functest-weekly-jobs.yml
new file mode 100644
index 000000000..f44f7b8aa
--- /dev/null
+++ b/jjb/functest/functest-weekly-jobs.yml
@@ -0,0 +1,124 @@
+###################################
+# job configuration for functest
+###################################
+- project:
+ name: functest-weekly
+
+ project: functest
+
+#--------------------------------
+# BRANCH ANCHORS
+#--------------------------------
+ master: &master
+ stream: master
+ branch: '{stream}'
+ gs-pathname: ''
+ docker-tag: 'latest'
+ disabled: false
+ danube: &danube
+ stream: danube
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ docker-tag: 'stable'
+ disabled: true
+#--------------------------------
+# POD, INSTALLER, AND BRANCH MAPPING
+#--------------------------------
+# Installers using labels
+# CI PODs
+# This section should only contain the installers
+# that have been switched using labels for slaves
+#--------------------------------
+ pod:
+# fuel CI PODs
+ - baremetal:
+ slave-label: fuel-baremetal
+ installer: fuel
+ <<: *master
+ - virtual:
+ slave-label: fuel-virtual
+ installer: fuel
+ <<: *master
+ - baremetal:
+ slave-label: fuel-baremetal
+ installer: fuel
+ <<: *danube
+ - virtual:
+ slave-label: fuel-virtual
+ installer: fuel
+ <<: *danube
+#--------------------------------
+ jobs:
+ - 'functest-{installer}-{pod}-weekly-{stream}'
+
+################################
+# job template
+################################
+- job-template:
+ name: 'functest-{installer}-{pod}-weekly-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: true
+
+ properties:
+ - logrotate-default
+ - throttle:
+ enabled: true
+ max-per-node: 1
+ option: 'project'
+
+ wrappers:
+ - build-name:
+ name: '$BUILD_NUMBER Suite: $FUNCTEST_SUITE_NAME Scenario: $DEPLOY_SCENARIO'
+ - timeout:
+ timeout: '400'
+ abort: true
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - '{installer}-defaults'
+ - '{slave-label}-defaults'
+ - string:
+ name: FUNCTEST_SUITE_NAME
+ default: 'weekly'
+ description: "Weekly suite name to run"
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-odl_l2-nofeature-ha'
+ - string:
+ name: DOCKER_TAG
+ default: '{docker-tag}'
+ description: 'Tag to pull docker image'
+ - string:
+ name: CLEAN_DOCKER_IMAGES
+ default: 'false'
+ description: 'Remove downloaded docker images (opnfv/functest*:*)'
+ - functest-parameter:
+ gs-pathname: '{gs-pathname}'
+
+ scm:
+ - git-scm
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - 'functest-weekly-builder'
+########################
+# builder macros
+########################
+- builder:
+ name: functest-weekly-builder
+ builders:
+ - shell:
+ !include-raw: ./functest-cleanup.sh
+ - shell:
+ !include-raw: ./set-functest-env.sh
+ - shell:
+ !include-raw: ./functest-loop.sh
+ - shell:
+ !include-raw: ../../utils/push-test-logs.sh
+ - shell:
+ !include-raw: ./functest-exit.sh
diff --git a/jjb/functest/set-functest-env.sh b/jjb/functest/set-functest-env.sh
index 583ce8041..05e3d5792 100755
--- a/jjb/functest/set-functest-env.sh
+++ b/jjb/functest/set-functest-env.sh
@@ -14,34 +14,37 @@ if [[ ${INSTALLER_TYPE} == 'joid' ]]; then
fi
if [[ ${RC_FILE_PATH} != '' ]] && [[ -f ${RC_FILE_PATH} ]] ; then
+ echo "Credentials file detected: ${RC_FILE_PATH}"
# volume if credentials file path is given to Functest
- rc_file_vol="-v $RC_FILE_PATH:/home/opnfv/functest/conf/openstack.creds"
+ rc_file_vol="-v ${RC_FILE_PATH}:/home/opnfv/functest/conf/openstack.creds"
+ RC_FLAG=1
fi
if [[ ${INSTALLER_TYPE} == 'apex' ]]; then
ssh_options="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
- if sudo virsh list | grep instack; then
- instack_mac=$(sudo virsh domiflist instack | grep default | \
- grep -Eo "[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+")
- elif sudo virsh list | grep undercloud; then
- instack_mac=$(sudo virsh domiflist undercloud | grep default | \
+ if sudo virsh list | grep undercloud; then
+ echo "Installer VM detected"
+ undercloud_mac=$(sudo virsh domiflist undercloud | grep default | \
grep -Eo "[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+")
+ INSTALLER_IP=$(/usr/sbin/arp -e | grep ${undercloud_mac} | awk {'print $1'})
+ sshkey_vol="-v /root/.ssh/id_rsa:/root/.ssh/id_rsa"
+ sudo scp $ssh_options root@${INSTALLER_IP}:/home/stack/stackrc ${HOME}/stackrc
+ stackrc_vol="-v ${HOME}/stackrc:/home/opnfv/functest/conf/stackrc"
+
+ if sudo iptables -C FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable 2> ${redirect}; then
+ sudo iptables -D FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
+ fi
+ if sudo iptables -C FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable 2> ${redirect}; then
+ sudo iptables -D FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
+ fi
+ elif [[ "$RC_FLAG" == 1 ]]; then
+ echo "No available installer VM, but credentials provided...continuing"
else
- echo "No available installer VM exists...exiting"
+ echo "No available installer VM exists and no credentials provided...exiting"
exit 1
fi
- INSTALLER_IP=$(/usr/sbin/arp -e | grep ${instack_mac} | awk {'print $1'})
- sshkey_vol="-v /root/.ssh/id_rsa:/root/.ssh/id_rsa"
- sudo scp $ssh_options root@${INSTALLER_IP}:/home/stack/stackrc ${HOME}/stackrc
- stackrc_vol="-v ${HOME}/stackrc:/home/opnfv/functest/conf/stackrc"
- if sudo iptables -C FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable 2> ${redirect}; then
- sudo iptables -D FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
- fi
- if sudo iptables -C FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable 2> ${redirect}; then
- sudo iptables -D FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
- fi
fi
@@ -56,8 +59,7 @@ DEPLOY_TYPE=baremetal
echo "Functest: Start Docker and prepare environment"
-branch=${GIT_BRANCH##*/}
-dir_result="${HOME}/opnfv/functest/results/${branch}"
+dir_result="${HOME}/opnfv/functest/results/${BRANCH##*/}"
mkdir -p ${dir_result}
sudo rm -rf ${dir_result}/*
results_vol="-v ${dir_result}:/home/opnfv/functest/results"
@@ -71,17 +73,22 @@ envs="-e INSTALLER_TYPE=${INSTALLER_TYPE} -e INSTALLER_IP=${INSTALLER_IP} \
volumes="${results_vol} ${sshkey_vol} ${stackrc_vol} ${rc_file_vol}"
+HOST_ARCH=$(uname -m)
+FUNCTEST_IMAGE="opnfv/functest"
+if [ "$HOST_ARCH" = "aarch64" ]; then
+ FUNCTEST_IMAGE="${FUNCTEST_IMAGE}_${HOST_ARCH}"
+fi
-echo "Functest: Pulling image opnfv/functest:${DOCKER_TAG}"
-docker pull opnfv/functest:$DOCKER_TAG >/dev/null
+echo "Functest: Pulling image ${FUNCTEST_IMAGE}:${DOCKER_TAG}"
+docker pull ${FUNCTEST_IMAGE}:$DOCKER_TAG >/dev/null
cmd="sudo docker run --privileged=true -id ${envs} ${volumes} \
${custom_params} ${TESTCASE_OPTIONS} \
- opnfv/functest:${DOCKER_TAG} /bin/bash"
+ ${FUNCTEST_IMAGE}:${DOCKER_TAG} /bin/bash"
echo "Functest: Running docker run command: ${cmd}"
${cmd} >${redirect}
sleep 5
-container_id=$(docker ps | grep "opnfv/functest:${DOCKER_TAG}" | awk '{print $1}' | head -1)
+container_id=$(docker ps | grep "${FUNCTEST_IMAGE}:${DOCKER_TAG}" | awk '{print $1}' | head -1)
echo "Container ID=${container_id}"
if [ -z ${container_id} ]; then
echo "Cannot find opnfv/functest container ID ${container_id}. Please check if it is existing."
@@ -92,13 +99,13 @@ echo "Starting the container: docker start ${container_id}"
docker start ${container_id}
sleep 5
docker ps >${redirect}
-if [ $(docker ps | grep "opnfv/functest:${DOCKER_TAG}" | wc -l) == 0 ]; then
- echo "The container opnfv/functest with ID=${container_id} has not been properly started. Exiting..."
+if [ $(docker ps | grep "${FUNCTEST_IMAGE}:${DOCKER_TAG}" | wc -l) == 0 ]; then
+ echo "The container ${FUNCTEST_IMAGE} with ID=${container_id} has not been properly started. Exiting..."
exit 1
fi
-if [[ ${branch} == *"brahmaputra"* ]]; then
+if [[ "$BRANCH" =~ 'brahmaputra' ]]; then
cmd="${FUNCTEST_REPO_DIR}/docker/prepare_env.sh"
-elif [[ ${branch} == *"colorado"* ]]; then
+elif [[ "$BRANCH" =~ 'colorado' ]]; then
cmd="python ${FUNCTEST_REPO_DIR}/ci/prepare_env.py start"
else
cmd="python ${FUNCTEST_REPO_DIR}/functest/ci/prepare_env.py start"
diff --git a/jjb/global/installer-params.yml b/jjb/global/installer-params.yml
index 4a50a5906..fc9f34a48 100644
--- a/jjb/global/installer-params.yml
+++ b/jjb/global/installer-params.yml
@@ -10,10 +10,6 @@
default: apex
description: 'Installer used for deploying OPNFV on this POD'
- string:
- name: DEPLOY_SCENARIO
- default: 'none'
- description: 'Scenario to deploy and test'
- - string:
name: EXTERNAL_NETWORK
default: 'external'
description: 'external network for test'
@@ -66,6 +62,10 @@
default: joid
description: 'Installer used for deploying OPNFV on this POD'
- string:
+ name: MODEL
+ default: 'os'
+ description: 'Model to deploy (os|k8)'
+ - string:
name: OS_RELEASE
default: 'newton'
description: 'OpenStack release (mitaka|newton)'
@@ -89,6 +89,19 @@
name: CPU_ARCHITECTURE
default: 'amd64'
description: "CPU Architecture to use for Ubuntu distro "
+
+- parameter:
+ name: 'daisy-defaults'
+ parameters:
+ - string:
+ name: INSTALLER_IP
+ default: '10.20.0.2'
+ description: 'IP of the installer'
+ - string:
+ name: INSTALLER_TYPE
+ default: daisy
+ description: 'Installer used for deploying OPNFV on this POD'
+
- parameter:
name: 'infra-defaults'
parameters:
@@ -109,13 +122,9 @@
description: 'IP of the installer'
- string:
name: INSTALLER_TYPE
- default: netvirt
+ default: apex
description: 'Installer used for deploying OPNFV on this POD'
- string:
- name: DEPLOY_SCENARIO
- default: 'os-odl_l2-bgpvpn-noha'
- description: 'Scenario to deploy and test'
- - string:
name: EXTERNAL_NETWORK
default: 'external'
description: 'external network for test'
diff --git a/jjb/global/releng-defaults.yml b/jjb/global/releng-defaults.yml
index 5003a8f48..283888603 100644
--- a/jjb/global/releng-defaults.yml
+++ b/jjb/global/releng-defaults.yml
@@ -3,15 +3,12 @@
- defaults:
name: global
- logrotate:
- daysToKeep: 60
- numToKeep: 200
- artifactDaysToKeep: 30
- artifactNumToKeep: 100
-
wrappers:
- ssh-agent-wrapper
project-type: freestyle
node: master
+
+ properties:
+ - logrotate-default
diff --git a/jjb/global/releng-macros.yml b/jjb/global/releng-macros.yml
index 10835f6a3..63613f88d 100644
--- a/jjb/global/releng-macros.yml
+++ b/jjb/global/releng-macros.yml
@@ -26,14 +26,23 @@
name: GS_BASE_PROXY
default: build.opnfv.org/artifacts.opnfv.org/$PROJECT
description: "URL to Google Storage proxy"
-
-- parameter:
- name: gerrit-parameter
- parameters:
+ - string:
+ name: BRANCH
+ default: '{branch}'
+ description: "JJB configured BRANCH parameter (e.g. master, stable/danube)"
- string:
name: GERRIT_BRANCH
default: '{branch}'
- description: "JJB configured GERRIT_BRANCH parameter"
+ description: "JJB configured GERRIT_BRANCH parameter (deprecated)"
+
+- property:
+ name: logrotate-default
+ properties:
+ - build-discarder:
+ days-to-keep: 60
+ num-to-keep: 200
+ artifact-days-to-keep: 60
+ artifact-num-to-keep: 200
- scm:
name: git-scm
@@ -42,7 +51,7 @@
credentials-id: '$SSH_CREDENTIAL_ID'
url: '$GIT_BASE'
branches:
- - 'origin/$GERRIT_BRANCH'
+ - 'origin/$BRANCH'
timeout: 15
- scm:
@@ -52,15 +61,33 @@
choosing-strategy: 'gerrit'
refspec: '$GERRIT_REFSPEC'
<<: *git-scm-defaults
-
+- scm:
+ name: git-scm-with-submodules
+ scm:
+ - git:
+ credentials-id: '$SSH_CREDENTIAL_ID'
+ url: '$GIT_BASE'
+ refspec: ''
+ branches:
+ - 'refs/heads/{branch}'
+ skip-tag: true
+ wipe-workspace: true
+ submodule:
+ recursive: true
+ timeout: 20
+ shallow-clone: true
- trigger:
name: 'daily-trigger-disabled'
triggers:
- timed: ''
-# NOTE: unused macro, but we may use this for some jobs.
- trigger:
- name: gerrit-trigger-patch-submitted
+ name: 'weekly-trigger-disabled'
+ triggers:
+ - timed: ''
+
+- trigger:
+ name: gerrit-trigger-patchset-created
triggers:
- gerrit:
server-name: 'gerrit.opnfv.org'
@@ -72,16 +99,25 @@
- draft-published-event
- comment-added-contains-event:
comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
projects:
- project-compare-type: 'ANT'
- project-pattern: '{name}'
+ project-pattern: '{project}'
branches:
- branch-compare-type: 'ANT'
branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: 'ANT'
+ pattern: '{files}'
+ skip-vote:
+ successful: false
+ failed: false
+ unstable: false
+ notbuilt: false
-# NOTE: unused macro, but we may use this for some jobs.
- trigger:
- name: gerrit-trigger-patch-merged
+ name: gerrit-trigger-change-merged
triggers:
- gerrit:
server-name: 'gerrit.opnfv.org'
@@ -91,10 +127,36 @@
comment-contains-value: 'remerge'
projects:
- project-compare-type: 'ANT'
- project-pattern: '{name}'
+ project-pattern: '{project}'
branches:
- branch-compare-type: 'ANT'
branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: 'ANT'
+ pattern: '{files}'
+
+- trigger:
+ name: 'experimental'
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - comment-added-contains-event:
+ comment-contains-value: 'check-experimental'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: 'ANT'
+ pattern: 'tests/**'
+ skip-vote:
+ successful: true
+ failed: true
+ unstable: true
+ notbuilt: true
- wrapper:
name: ssh-agent-wrapper
@@ -386,6 +448,12 @@
sed -r -i '4,$s/^/ /g' lint.log
fi
+- builder:
+ name: clean-workspace-log
+ builders:
+ - shell: |
+ find $WORKSPACE -type f -name '*.log' | xargs rm -f
+
- publisher:
name: archive-artifacts
publishers:
@@ -394,3 +462,23 @@
allow-empty: true
fingerprint: true
latest-only: true
+
+- publisher:
+ name: publish-coverage
+ publishers:
+ - cobertura:
+ report-file: "coverage.xml"
+ only-stable: "true"
+ health-auto-update: "false"
+ stability-auto-update: "false"
+ zoom-coverage-chart: "true"
+ targets:
+ - files:
+ healthy: 10
+ unhealthy: 20
+ failing: 30
+ - method:
+ healthy: 50
+ unhealthy: 40
+ failing: 30
+
diff --git a/jjb/global/slave-params.yml b/jjb/global/slave-params.yml
index 39a1b1b9c..4b3eaaabf 100644
--- a/jjb/global/slave-params.yml
+++ b/jjb/global/slave-params.yml
@@ -178,6 +178,23 @@
name: EXTERNAL_NETWORK
default: ext-net
description: "External network floating ips"
+- parameter:
+ name: 'daisy-baremetal-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - zte-pod2
+ default-slaves:
+ - zte-pod2
+ - label:
+ name: SLAVE_LABEL
+ default: 'daisy-baremetal'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
#####################################################
# Parameters for CI virtual PODs
#####################################################
@@ -239,15 +256,33 @@
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
description: 'Git URL to use on this Jenkins Slave'
+- parameter:
+ name: 'daisy-virtual-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - zte-virtual1
+ - zte-virtual2
+ default-slaves:
+ - zte-virtual1
+ - label:
+ name: SLAVE_LABEL
+ default: 'daisy-virtual'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
#####################################################
# Parameters for build slaves
#####################################################
- parameter:
- name: 'opnfv-build-arm-defaults'
+ name: 'opnfv-build-enea-defaults'
parameters:
- label:
name: SLAVE_LABEL
- default: 'opnfv-build-arm'
+ default: 'opnfv-build-enea'
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -314,6 +349,21 @@
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
description: 'Git URL to use on this Jenkins Slave'
+- parameter:
+ name: 'opnfv-build-ubuntu-arm-defaults'
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'opnfv-build-ubuntu-arm'
+ description: 'Slave label on Jenkins'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+ - string:
+ name: BUILD_DIRECTORY
+ default: $WORKSPACE/build_output
+ description: "Directory where the build artifact will be located upon the completion of the build."
#####################################################
# Parameters for none-CI PODs
#####################################################
@@ -332,6 +382,20 @@
default: https://gerrit.opnfv.org/gerrit/$PROJECT
description: 'Git URL to use on this Jenkins Slave'
- parameter:
+ name: 'cengn-pod1-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - cengn-pod1
+ default-slaves:
+ - cengn-pod1
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+- parameter:
name: 'intel-pod1-defaults'
parameters:
- node:
@@ -364,15 +428,43 @@
default: /root/.ssh/id_rsa
description: 'SSH key to use for Apex'
- parameter:
- name: 'intel-pod3-defaults'
+ name: 'intel-pod9-defaults'
parameters:
- node:
name: SLAVE_NAME
description: 'Slave name on Jenkins'
allowed-slaves:
- - intel-pod3
+ - intel-pod9
default-slaves:
- - intel-pod3
+ - intel-pod9
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+- parameter:
+ name: 'intel-pod10-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - intel-pod10
+ default-slaves:
+ - intel-pod10
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+- parameter:
+ name: 'intel-pod12-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - intel-pod12
+ default-slaves:
+ - intel-pod12
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -412,15 +504,28 @@
default: https://gerrit.opnfv.org/gerrit/$PROJECT
description: 'Git URL to use on this Jenkins Slave'
- parameter:
- name: 'huawei-pod5-defaults'
+ name: 'intel-pod8-defaults'
+ parameters:
+ - node:
+ name: SLAVE_NAME
+ description: 'Slave name on Jenkins'
+ allowed-slaves:
+ - intel-pod8
+ default-slaves:
+ - intel-pod8
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+- parameter:
+ name: 'huawei-virtual7-defaults'
parameters:
- node:
name: SLAVE_NAME
description: 'Slave name on Jenkins'
allowed-slaves:
- - huawei-pod5
+ - huawei-virtual7
default-slaves:
- - huawei-pod5
+ - huawei-virtual7
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -656,15 +761,17 @@
default: https://gerrit.opnfv.org/gerrit/$PROJECT
description: 'Git URL to use on this Jenkins Slave'
- parameter:
- name: 'ool-virtual1-defaults'
+ name: 'ool-defaults'
parameters:
- node:
name: SLAVE_NAME
description: 'Slave name on Jenkins'
allowed-slaves:
- ool-virtual1
+ - ool-virtual2
+ - ool-virtual3
default-slaves:
- - ool-virtual1
+ - '{default-slave}'
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -674,6 +781,21 @@
default: /root/.ssh/id_rsa
description: 'SSH key to be used'
- parameter:
+ name: 'ool-virtual1-defaults'
+ parameters:
+ - 'ool-defaults':
+ default-slave: 'ool-virtual1'
+- parameter:
+ name: 'ool-virtual2-defaults'
+ parameters:
+ - 'ool-defaults':
+ default-slave: 'ool-virtual2'
+- parameter:
+ name: 'ool-virtual3-defaults'
+ parameters:
+ - 'ool-defaults':
+ default-slave: 'ool-virtual3'
+- parameter:
name: 'multisite-virtual-defaults'
parameters:
- label:
@@ -694,6 +816,26 @@
default: https://git.opendaylight.org/gerrit/p/$PROJECT.git
description: 'Git URL to use on this Jenkins Slave'
- parameter:
+ name: 'ericsson-virtual12-defaults'
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'ericsson-virtual12'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+- parameter:
+ name: 'ericsson-virtual13-defaults'
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'ericsson-virtual13'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/$PROJECT
+ description: 'Git URL to use on this Jenkins Slave'
+- parameter:
name: 'odl-netvirt-virtual-defaults'
parameters:
- label:
diff --git a/jjb/infra/bifrost-cleanup-job.yml b/jjb/infra/bifrost-cleanup-job.yml
new file mode 100644
index 000000000..f1b38ca4b
--- /dev/null
+++ b/jjb/infra/bifrost-cleanup-job.yml
@@ -0,0 +1,136 @@
+- project:
+ name: 'openstack-bifrost-cleanup'
+#--------------------------------
+# branches
+#--------------------------------
+ stream:
+ - master:
+ branch: '{stream}'
+
+#--------------------------------
+# projects
+#--------------------------------
+ project:
+ - 'openstack':
+ project-repo: 'https://git.openstack.org/openstack/bifrost'
+ clone-location: '/opt/bifrost'
+ - 'opnfv':
+ project-repo: 'https://gerrit.opnfv.org/gerrit/releng'
+ clone-location: '/opt/releng'
+
+#--------------------------------
+# jobs
+#--------------------------------
+ jobs:
+ - '{project}-bifrost-cleanup-{stream}'
+
+- job-template:
+ name: '{project}-bifrost-cleanup-{stream}'
+
+ concurrent: false
+
+ node: bifrost-verify-virtual
+
+ # Make sure no verify job is running on any of the slaves since that would
+ # produce build logs after we wipe the destination directory.
+ properties:
+ - build-blocker:
+ blocking-jobs:
+ - '{project}-bifrost-verify-*'
+
+ parameters:
+ - string:
+ name: PROJECT
+ default: '{project}'
+
+ builders:
+ - shell: |
+ #!/bin/bash
+
+ set -eu
+
+ # DO NOT change this unless you know what you are doing.
+ BIFROST_GS_URL="gs://artifacts.opnfv.org/cross-community-ci/openstack/bifrost/$GERRIT_NAME/$GERRIT_CHANGE_NUMBER/"
+
+ # This should never happen... even 'recheck' uses the last jobs'
+ # gerrit information. Better exit with error so we can investigate
+ [[ ! -n $GERRIT_NAME ]] || [[ ! -n $GERRIT_CHANGE_NUMBER ]] && exit 1
+
+ echo "Removing build artifacts for $GERRIT_NAME/$GERRIT_CHANGE_NUMBER"
+
+ if ! [[ "$BIFROST_GS_URL" =~ "/cross-community-ci/openstack/bifrost/" ]]; then
+ echo "Oops! BIFROST_GS_URL=$BIFROST_GS_URL does not seem like a valid"
+ echo "bifrost location on the Google storage server. Please double-check"
+ echo "that it's set properly or fix this line if necessary."
+ echo "gsutil will not be executed until this is fixed!"
+ exit 1
+ fi
+ # No force (-f). We always verify upstream jobs so if there are no logs
+ # something else went wrong and we need to break immediately and investigate
+ gsutil -m rm -r $BIFROST_GS_URL
+
+ triggers:
+ - '{project}-gerrit-trigger-cleanup':
+ branch: '{branch}'
+
+ publishers:
+ - email:
+ recipients: fatih.degirmenci@ericsson.com yroblamo@redhat.com mchandras@suse.de jack.morgan@intel.com zhang.jun3g@zte.com.cn
+#--------------------------------
+# trigger macros
+#--------------------------------
+- trigger:
+ name: 'openstack-gerrit-trigger-cleanup'
+ triggers:
+ - gerrit:
+ server-name: 'review.openstack.org'
+ escape-quotes: true
+ trigger-on:
+ # We only run this when the change is merged or
+ # abandoned since we don't need the logs anymore
+ - change-merged-event: 'true'
+ - change-abandoned-event: 'true'
+ - change-restored-event: 'false'
+ - draft-published-event: 'false'
+ # This is an OPNFV maintenance job. We don't want to provide
+ # feedback on Gerrit
+ silent: true
+ silent-start: true
+ projects:
+ - project-compare-type: 'PLAIN'
+ project-pattern: 'openstack/bifrost'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'doc/**'
+ - compare-type: ANT
+ pattern: 'releasenotes/**'
+ readable-message: true
+- trigger:
+ name: 'opnfv-gerrit-trigger-cleanup'
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ # We only run this when the change is merged or
+ # abandoned since we don't need the logs anymore
+ - change-merged-event: 'true'
+ - change-abandoned-event: 'true'
+ - change-restored-event: 'false'
+ - draft-published-event: 'false'
+ # This is an OPNFV maintenance job. We don't want to provide
+ # feedback on Gerrit
+ silent: true
+ silent-start: true
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: 'releng'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: ANT
+ pattern: 'prototypes/bifrost/**'
+ readable-message: true
diff --git a/jjb/infra/bifrost-upload-logs.sh b/jjb/infra/bifrost-upload-logs.sh
deleted file mode 100755
index e2b8126e4..000000000
--- a/jjb/infra/bifrost-upload-logs.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-# SPDX-license-identifier: Apache-2.0
-##############################################################################
-# Copyright (c) 2016 SUSE.
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-set -eu
-set -o pipefail
-
-BIFROST_CONSOLE_LOG="${BUILD_URL}/consoleText"
-BIFROST_GS_URL=${BIFROST_GS_STORAGE/http:/gs:}
-
-echo "Uploading build logs to ${BIFROST_LOG_URL}"
-
-echo "Uploading console output"
-curl -L ${BIFROST_CONSOLE_LOG} | gsutil cp - ${BIFROST_GS_URL}/console.txt
-
-[[ ! -d ${WORKSPACE}/logs ]] && exit 0
-
-pushd ${WORKSPACE}/logs/ &> /dev/null
-for x in *.log; do
- echo "Compressing and uploading $x"
- tar -czf - $x | gsutil cp - ${BIFROST_GS_URL}/$x.tar.gz
-done
-popd ${WORKSPACE}/logs &> /dev/null
diff --git a/jjb/infra/bifrost-verify-jobs.yml b/jjb/infra/bifrost-verify-jobs.yml
index cbe21e256..d595d4bef 100644
--- a/jjb/infra/bifrost-verify-jobs.yml
+++ b/jjb/infra/bifrost-verify-jobs.yml
@@ -38,9 +38,6 @@
dib-os-element: 'opensuse-minimal'
dib-os-packages: 'vim,less,bridge-utils,iputils,rsyslog,curl'
extra-dib-elements: 'openssh-server'
- vm-disk: '30'
- vm-memory: '4096'
- vm-cpu: '2'
#--------------------------------
# type
#--------------------------------
@@ -57,9 +54,10 @@
#--------------------------------
- defaults:
name: vm_defaults
- vm-disk: '100'
- vm-memory: '8192'
- vm-cpu: '4'
+ vm-disk: '30'
+ vm-disk-cache: 'unsafe'
+ vm-memory: '4096'
+ vm-cpu: '2'
#--------------------------------
# job templates
@@ -74,6 +72,7 @@
concurrent: false
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
blocking-jobs:
@@ -109,6 +108,9 @@
name: VM_DISK
default: '{vm-disk}'
- string:
+ name: VM_DISK_CACHE
+ default: '{vm-disk-cache}'
+ - string:
name: VM_MEMORY
default: '{vm-memory}'
- string:
@@ -129,7 +131,7 @@
url: '$PROJECT_REPO'
refspec: '$GERRIT_REFSPEC'
branches:
- - 'origin/$GERRIT_BRANCH'
+ - 'origin/$BRANCH'
skip-tag: true
choosing-strategy: 'gerrit'
timeout: 10
@@ -142,11 +144,10 @@
builders:
- bifrost-set-name
- bifrost-build
- - bifrost-artifacts-upload
publishers:
- email:
- recipients: fatih.degirmenci@ericsson.com yroblamo@redhat.com mchandras@suse.de jack.morgan@intel.com zhang.jun3g@zte.com.cn
+ recipients: fatih.degirmenci@ericsson.com yroblamo@redhat.com mchandras@suse.de jack.morgan@intel.com julienjut@gmail.com
#--------------------------------
# trigger macros
#--------------------------------
@@ -163,7 +164,7 @@
exclude-no-code-change: 'false'
- comment-added-contains-event:
comment-contains-value: 'recheck'
- custom-url: '* $JOB_NAME $BIFROST_LOG_URL'
+ custom-url: '* $JOB_NAME $BIFROST_LOG_URL/index.html'
silent-start: true
projects:
- project-compare-type: 'PLAIN'
@@ -192,7 +193,7 @@
comment-contains-value: 'recheck'
- comment-added-contains-event:
comment-contains-value: 'reverify'
- custom-url: '* $JOB_NAME $BIFROST_LOG_URL'
+ custom-url: '* $JOB_NAME $BIFROST_LOG_URL/index.html'
projects:
- project-compare-type: 'ANT'
project-pattern: 'releng'
@@ -202,8 +203,6 @@
file-paths:
- compare-type: ANT
pattern: 'prototypes/bifrost/**'
- - compare-type: ANT
- pattern: 'jjb/infra/**'
readable-message: true
#---------------------------
@@ -220,9 +219,3 @@
builders:
- shell:
!include-raw: ./bifrost-verify.sh
-
-- builder:
- name: bifrost-artifacts-upload
- builders:
- - shell:
- !include-raw: ./bifrost-upload-logs.sh
diff --git a/jjb/infra/bifrost-verify.sh b/jjb/infra/bifrost-verify.sh
index 9fbb1d0d9..4115ffcc4 100755
--- a/jjb/infra/bifrost-verify.sh
+++ b/jjb/infra/bifrost-verify.sh
@@ -11,7 +11,63 @@ set -o errexit
set -o nounset
set -o pipefail
-trap fix_ownership EXIT
+trap cleanup_and_upload EXIT
+
+function upload_logs() {
+ BIFROST_CONSOLE_LOG="${BUILD_URL}/consoleText"
+ BIFROST_GS_URL=${BIFROST_LOG_URL/http:/gs:}
+
+ # Make sure the old landing page is gone in case
+ # we break later on. We don't want to publish
+ # stale information.
+ # TODO: Maybe cleanup the entire $BIFROST_GS_URL directory
+ # before we upload the new data.
+ gsutil -q rm ${BIFROST_GS_URL}/index.html || true
+
+ echo "Uploading collected bifrost build logs to ${BIFROST_LOG_URL}"
+
+ if [[ -d ${WORKSPACE}/logs ]]; then
+ pushd ${WORKSPACE}/logs &> /dev/null
+ for x in *.log; do
+ echo "Compressing and uploading $x"
+ gsutil -q cp -Z ${x} ${BIFROST_GS_URL}/${x}
+ done
+ popd &> /dev/null
+ fi
+
+ echo "Generating the ${BIFROST_LOG_URL}/index.html landing page"
+ cat > ${WORKSPACE}/index.html <<EOF
+<html>
+<h1>Build results for <a href=https://$GERRIT_NAME/#/c/$GERRIT_CHANGE_NUMBER/$GERRIT_PATCHSET_NUMBER>$GERRIT_NAME/$GERRIT_CHANGE_NUMBER/$GERRIT_PATCHSET_NUMBER</a></h1>
+<h2>Job: <a href=${BUILD_URL}>$JOB_NAME</a></h2>
+<ul>
+<li><a href=${BIFROST_LOG_URL}/build_log.txt>build_log.txt</a></li>
+EOF
+
+ if [[ -d ${WORKSPACE}/logs ]]; then
+ pushd ${WORKSPACE}/logs &> /dev/null
+ for x in *.log; do
+ echo "<li><a href=${BIFROST_LOG_URL}/${x}>${x}</a></li>" >> ${WORKSPACE}/index.html
+ done
+ popd &> /dev/null
+ fi
+
+ cat >> ${WORKSPACE}/index.html << EOF
+</ul>
+</html>
+EOF
+
+ # Finally, download and upload the entire build log so we can retain
+ # as much build information as possible
+ echo "Uploading the final console output"
+ curl -s -L ${BIFROST_CONSOLE_LOG} > ${WORKSPACE}/build_log.txt
+ gsutil -q cp -Z ${WORKSPACE}/build_log.txt ${BIFROST_GS_URL}/build_log.txt
+ rm ${WORKSPACE}/build_log.txt
+
+ # Upload landing page
+ gsutil -q cp ${WORKSPACE}/index.html ${BIFROST_GS_URL}/index.html
+ rm ${WORKSPACE}/index.html
+}
function fix_ownership() {
if [ -z "${JOB_URL+x}" ]; then
@@ -25,6 +81,13 @@ function fix_ownership() {
fi
}
+function cleanup_and_upload() {
+ original_exit=$?
+ fix_ownership
+ upload_logs
+ exit $original_exit
+}
+
# check distro to see if we support it
if [[ ! "$DISTRO" =~ (trusty|centos7|suse) ]]; then
echo "Distro $DISTRO is not supported!"
diff --git a/jjb/ipv6/ipv6.yml b/jjb/ipv6/ipv6.yml
index da54c521e..a6745cd99 100644
--- a/jjb/ipv6/ipv6.yml
+++ b/jjb/ipv6/ipv6.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/joid/joid-daily-jobs.yml b/jjb/joid/joid-daily-jobs.yml
index 91fcc8814..93cad0f0c 100644
--- a/jjb/joid/joid-daily-jobs.yml
+++ b/jjb/joid/joid-daily-jobs.yml
@@ -46,6 +46,9 @@
- orange-pod1:
slave-label: orange-pod1
<<: *master
+ - cengn-pod1:
+ slave-label: cengn-pod1
+ <<: *master
#--------------------------------
# scenarios
#--------------------------------
@@ -72,6 +75,10 @@
auto-trigger-name: 'daily-trigger-disabled'
- 'os-ocl-nofeature-noha':
auto-trigger-name: 'daily-trigger-disabled'
+ - 'k8-nosdn-nofeature-noha':
+ auto-trigger-name: 'joid-{scenario}-{pod}-{stream}-trigger'
+ - 'k8-nosdn-lb-noha':
+ auto-trigger-name: 'daily-trigger-disabled'
jobs:
- 'joid-{scenario}-{pod}-daily-{stream}'
@@ -88,6 +95,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -109,6 +117,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
installer: '{installer}'
@@ -148,6 +157,23 @@
build-step-failure-threshold: 'never'
failure-threshold: 'never'
unstable-threshold: 'FAILURE'
+ # 1.dovetail only master by now, not sync with A/B/C branches
+ # 2.here the stream means the SUT stream, dovetail stream is defined in its own job
+ # 3.only debug testsuite here(includes basic testcase,
+ # i.e. one tempest smoke ipv6, two vping from functest)
+ # 4.not used for release criteria or compliance,
+ # only to debug the dovetail tool bugs with joid
+ - trigger-builds:
+ - project: 'dovetail-joid-{pod}-debug-{stream}'
+ current-parameters: false
+ predefined-parameters:
+ DEPLOY_SCENARIO={scenario}
+ block: true
+ same-node: true
+ block-thresholds:
+ build-step-failure-threshold: 'never'
+ failure-threshold: 'never'
+ unstable-threshold: 'FAILURE'
- job-template:
name: 'joid-deploy-{pod}-daily-{stream}'
@@ -157,6 +183,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -178,7 +205,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults':
@@ -226,6 +252,10 @@
name: 'joid-os-nosdn-nofeature-ha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-nofeature-ha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-nofeature-ha trigger - branch: danube
- trigger:
name: 'joid-os-nosdn-nofeature-ha-baremetal-danube-trigger'
@@ -239,6 +269,10 @@
name: 'joid-os-nosdn-nofeature-ha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-nofeature-ha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-odl_l2-nofeature-ha trigger - branch: master
- trigger:
name: 'joid-os-odl_l2-nofeature-ha-baremetal-master-trigger'
@@ -252,6 +286,10 @@
name: 'joid-os-odl_l2-nofeature-ha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-odl_l2-nofeature-ha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-odl_l2-nofeature-ha trigger - branch: danube
- trigger:
name: 'joid-os-odl_l2-nofeature-ha-baremetal-danube-trigger'
@@ -265,6 +303,10 @@
name: 'joid-os-odl_l2-nofeature-ha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-odl_l2-nofeature-ha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-onos-nofeature-ha trigger - branch: master
- trigger:
name: 'joid-os-onos-nofeature-ha-baremetal-master-trigger'
@@ -278,6 +320,10 @@
name: 'joid-os-onos-nofeature-ha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-onos-nofeature-ha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-onos-nofeature-ha trigger - branch: danube
- trigger:
name: 'joid-os-onos-nofeature-ha-baremetal-danube-trigger'
@@ -291,6 +337,10 @@
name: 'joid-os-onos-nofeature-ha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-onos-nofeature-ha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-onos-sfc-ha trigger - branch: master
- trigger:
name: 'joid-os-onos-sfc-ha-baremetal-master-trigger'
@@ -304,6 +354,10 @@
name: 'joid-os-onos-sfc-ha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-onos-sfc-ha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-onos-sfc-ha trigger - branch: danube
- trigger:
name: 'joid-os-onos-sfc-ha-baremetal-danube-trigger'
@@ -317,6 +371,10 @@
name: 'joid-os-onos-sfc-ha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-onos-sfc-ha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-lxd-noha trigger - branch: master
- trigger:
name: 'joid-os-nosdn-lxd-noha-baremetal-master-trigger'
@@ -330,6 +388,10 @@
name: 'joid-os-nosdn-lxd-noha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-lxd-noha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-lxd-noha trigger - branch: danube
- trigger:
name: 'joid-os-nosdn-lxd-noha-baremetal-danube-trigger'
@@ -343,6 +405,10 @@
name: 'joid-os-nosdn-lxd-noha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-lxd-noha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-lxd-ha trigger - branch: master
- trigger:
name: 'joid-os-nosdn-lxd-ha-baremetal-master-trigger'
@@ -356,6 +422,10 @@
name: 'joid-os-nosdn-lxd-ha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-lxd-ha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-lxd-ha trigger - branch: danube
- trigger:
name: 'joid-os-nosdn-lxd-ha-baremetal-danube-trigger'
@@ -369,6 +439,10 @@
name: 'joid-os-nosdn-lxd-ha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-lxd-ha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-nofeature-noha trigger - branch: master
- trigger:
name: 'joid-os-nosdn-nofeature-noha-baremetal-master-trigger'
@@ -382,6 +456,10 @@
name: 'joid-os-nosdn-nofeature-noha-orange-pod1-master-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-nofeature-noha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
# os-nosdn-nofeature-noha trigger - branch: danube
- trigger:
name: 'joid-os-nosdn-nofeature-noha-baremetal-danube-trigger'
@@ -395,3 +473,75 @@
name: 'joid-os-nosdn-nofeature-noha-orange-pod1-danube-trigger'
triggers:
- timed: ''
+- trigger:
+ name: 'joid-os-nosdn-nofeature-noha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
+# k8-nosdn-nofeature-noha trigger - branch: master
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-baremetal-master-trigger'
+ triggers:
+ - timed: '5 15 * * *'
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-virtual-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-orange-pod1-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
+# k8-nosdn-nofeature-noha trigger - branch: danube
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-baremetal-danube-trigger'
+ triggers:
+ - timed: '0 15 * * *'
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-virtual-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-orange-pod1-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-nofeature-noha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
+# k8-nosdn-lb-noha trigger - branch: master
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-baremetal-master-trigger'
+ triggers:
+ - timed: '5 20 * * *'
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-virtual-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-orange-pod1-master-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-cengn-pod1-master-trigger'
+ triggers:
+ - timed: ''
+# k8-nosdn-lb-noha trigger - branch: danube
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-baremetal-danube-trigger'
+ triggers:
+ - timed: '0 20 * * *'
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-virtual-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-orange-pod1-danube-trigger'
+ triggers:
+ - timed: ''
+- trigger:
+ name: 'joid-k8-nosdn-lb-noha-cengn-pod1-danube-trigger'
+ triggers:
+ - timed: ''
diff --git a/jjb/joid/joid-deploy.sh b/jjb/joid/joid-deploy.sh
index 05c2de1fc..e197dbd8c 100644
--- a/jjb/joid/joid-deploy.sh
+++ b/jjb/joid/joid-deploy.sh
@@ -45,17 +45,24 @@ export POD_NAME=${POD/-}
##
cd $WORKSPACE/ci
-if [ -e "$LAB_CONFIG/environments.yaml" ] && [ "$MAAS_REINSTALL" == "false" ]; then
+
+if [ -e "$LAB_CONFIG/deployconfig.yaml" ] && [ "$MAAS_REINSTALL" == "false" ]; then
echo "------ Recover Juju environment to use MAAS ------"
- cp $LAB_CONFIG/environments.yaml .
- cp $LAB_CONFIG/deployment.yaml .
- if [ -e $LAB_CONFIG/deployconfig.yaml ]; then
+ if [ ! -e deployconfig.yaml ]; then
cp $LAB_CONFIG/deployconfig.yaml .
+ cp $LAB_CONFIG/deployment.yaml .
+ cp $LAB_CONFIG/labconfig.yaml .
fi
else
- echo "------ Redeploy MAAS ------"
- ./00-maasdeploy.sh $POD_NAME
- exit_on_error $? "MAAS Deploy FAILED"
+ if ["$NODE_NAME" == "default" ]; then
+ echo "------ Redeploy MAAS ------"
+ ./03-maasdeploy.sh default
+ exit_on_error $? "MAAS Deploy FAILED"
+ else
+ echo "------ Redeploy MAAS ------"
+ ./03-maasdeploy.sh custom $LAB_CONFIG/labconfig.yaml
+ exit_on_error $? "MAAS Deploy FAILED"
+ fi
fi
##
@@ -64,8 +71,9 @@ fi
# Based on scenario naming we can get joid options
# naming convention:
-# os-<controller>-<nfvfeature>-<mode>[-<extrastuff>]
+# <model>-<controller>-<nfvfeature>-<mode>[-<extrastuff>]
# With parameters:
+# model=(os|k8)
# controller=(nosdn|odl_l3|odl_l2|onos|ocl)
# No odl_l3 today
# nfvfeature=(kvm|ovs|dpdk|nofeature)
@@ -77,6 +85,7 @@ fi
IFS='-' read -r -a DEPLOY_OPTIONS <<< "${DEPLOY_SCENARIO}--"
#last -- need to avoid nounset error
+JOID_MODEL=${DEPLOY_OPTIONS[0]}
SDN_CONTROLLER=${DEPLOY_OPTIONS[1]}
NFV_FEATURES=${DEPLOY_OPTIONS[2]}
HA_MODE=${DEPLOY_OPTIONS[3]}
@@ -103,49 +112,47 @@ fi
## Configure Joid deployment
##
-echo "------ Deploy with juju ------"
-echo "Execute: ./deploy.sh -t $HA_MODE -o $OS_RELEASE -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES"
+if [ "$JOID_MODEL" == 'k8' ]; then
+ echo "------ Deploy with juju ------"
+ echo "Execute: ./deploy.sh -m $JOID_MODEL -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES"
-./deploy.sh -t $HA_MODE -o $OS_RELEASE -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES
-exit_on_error $? "Main deploy FAILED"
+ ./deploy.sh -m kubernetes -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES
+ exit_on_error $? "Main deploy FAILED"
+fi
##
## Set Admin RC
##
-JOID_ADMIN_OPENRC=$LAB_CONFIG/admin-openrc
-echo "------ Create OpenRC file [$JOID_ADMIN_OPENRC] ------"
-
-# get controller IP
-case "$SDN_CONTROLLER" in
- "odl")
- SDN_CONTROLLER_IP=$(juju status odl-controller/0 |grep public-address|sed -- 's/.*\: //')
- ;;
- "onos")
- SDN_CONTROLLER_IP=$(juju status onos-controller/0 |grep public-address|sed -- 's/.*\: //')
- ;;
- *)
- SDN_CONTROLLER_IP='none'
- ;;
-esac
-SDN_PASSWORD='admin'
-
-# export the openrc file by getting the one generated by joid and add SDN
-# controller for Functest
-cp ./cloud/admin-openrc $JOID_ADMIN_OPENRC
-cat << EOF >> $JOID_ADMIN_OPENRC
-export SDN_CONTROLLER=$SDN_CONTROLLER_IP
-export SDN_PASSWORD=$SDN_PASSWORD
-EOF
-
-##
-## Backup local juju env
-##
+if [ "$JOID_MODEL" == 'os' ]; then
+ echo "------ Deploy with juju ------"
+ echo "Execute: ./deploy.sh -m $JOID_MODEL -t $HA_MODE -o $OS_RELEASE -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES"
+
+ ./deploy.sh -m openstack -t $HA_MODE -o $OS_RELEASE -s $SDN_CONTROLLER -l $POD_NAME -d $UBUNTU_DISTRO -f $NFV_FEATURES
+ exit_on_error $? "Main deploy FAILED"
+
+ JOID_ADMIN_OPENRC=$LAB_CONFIG/admin-openrc
+ echo "------ Create OpenRC file [$JOID_ADMIN_OPENRC] ------"
+
+ # get controller IP
+ case "$SDN_CONTROLLER" in
+ "odl")
+ SDN_CONTROLLER_IP=$(juju status odl-controller/0 |grep public-address|sed -- 's/.*\: //')
+ ;;
+ "onos")
+ SDN_CONTROLLER_IP=$(juju status onos-controller/0 |grep public-address|sed -- 's/.*\: //')
+ ;;
+ *)
+ SDN_CONTROLLER_IP='none'
+ ;;
+ esac
+ SDN_PASSWORD='admin'
+
+ # export the openrc file by getting the one generated by joid and add SDN
+ # controller for Functest
+ # cp ./cloud/admin-openrc $JOID_ADMIN_OPENRC
+ echo export SDN_CONTROLLER=$SDN_CONTROLLER_IP >> $JOID_ADMIN_OPENRC
+ echo export SDN_PASSWORD=$SDN_PASSWORD >> $JOID_ADMIN_OPENRC
-echo "------ Backup Juju environment ------"
-cp environments.yaml $LAB_CONFIG/
-cp deployment.yaml $LAB_CONFIG/
-if [ -e deployconfig.yaml ]; then
- cp deployconfig.yaml $LAB_CONFIG
fi
##
diff --git a/jjb/joid/joid-verify-jobs.yml b/jjb/joid/joid-verify-jobs.yml
index 6e821a502..7b8ce7701 100644
--- a/jjb/joid/joid-verify-jobs.yml
+++ b/jjb/joid/joid-verify-jobs.yml
@@ -45,6 +45,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -93,7 +94,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'joid-virtual-defaults'
@@ -107,7 +107,7 @@
- name: 'joid-verify-basic-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -120,7 +120,7 @@
- name: 'joid-verify-deploy-virtual-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -134,7 +134,7 @@
- name: 'joid-verify-smoke-test-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
GERRIT_CHANGE_COMMIT_MESSAGE=$GERRIT_CHANGE_COMMIT_MESSAGE
@@ -150,6 +150,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 4
@@ -174,7 +175,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults'
diff --git a/jjb/kvmfornfv/kvmfornfv-upload-artifact.sh b/jjb/kvmfornfv/kvmfornfv-upload-artifact.sh
index 6f8fff3ff..56fb4f9c1 100755
--- a/jjb/kvmfornfv/kvmfornfv-upload-artifact.sh
+++ b/jjb/kvmfornfv/kvmfornfv-upload-artifact.sh
@@ -11,16 +11,17 @@ fi
case "$JOB_TYPE" in
verify)
- OPNFV_ARTIFACT_VERSION="gerrit-$GERRIT_CHANGE_NUMBER"
- GS_UPLOAD_LOCATION="gs://artifacts.opnfv.org/$PROJECT/review/$GERRIT_CHANGE_NUMBER"
- echo "Removing outdated artifacts produced for the previous patch for the change $GERRIT_CHANGE_NUMBER"
- gsutil ls $GS_UPLOAD_LOCATION > /dev/null 2>&1 && gsutil rm -r $GS_UPLOAD_LOCATION
- echo "Uploading artifacts for the change $GERRIT_CHANGE_NUMBER. This could take some time..."
- ;;
+ OPNFV_ARTIFACT_VERSION="gerrit-$GERRIT_CHANGE_NUMBER"
+ GS_UPLOAD_LOCATION="gs://artifacts.opnfv.org/$PROJECT/review/$GERRIT_CHANGE_NUMBER"
+ echo "Removing outdated artifacts produced for the previous patch for the change $GERRIT_CHANGE_NUMBER"
+ gsutil ls $GS_UPLOAD_LOCATION > /dev/null 2>&1 && gsutil rm -r $GS_UPLOAD_LOCATION
+ echo "Uploading artifacts for the change $GERRIT_CHANGE_NUMBER. This could take some time..."
+ ;;
daily)
echo "Uploading daily artifacts This could take some time..."
OPNFV_ARTIFACT_VERSION=$(date -u +"%Y-%m-%d_%H-%M-%S")
GS_UPLOAD_LOCATION="gs://$GS_URL/$OPNFV_ARTIFACT_VERSION"
+ GS_LOG_LOCATION="gs://$GS_URL/logs-$(date -u +"%Y-%m-%d")"/
;;
*)
echo "Artifact upload is not enabled for $JOB_TYPE jobs"
@@ -38,10 +39,23 @@ esac
source $WORKSPACE/opnfv.properties
# upload artifacts
-gsutil cp -r $WORKSPACE/build_output/* $GS_UPLOAD_LOCATION > $WORKSPACE/gsutil.log 2>&1
-gsutil -m setmeta -r \
- -h "Cache-Control:private, max-age=0, no-transform" \
- $GS_UPLOAD_LOCATION > /dev/null 2>&1
+if [[ "$PHASE" == "build" ]]; then
+ gsutil cp -r $WORKSPACE/build_output/* $GS_UPLOAD_LOCATION > $WORKSPACE/gsutil.log 2>&1
+ gsutil -m setmeta -r \
+ -h "Cache-Control:private, max-age=0, no-transform" \
+ $GS_UPLOAD_LOCATION > /dev/null 2>&1
+else
+ if [[ "$JOB_TYPE" == "daily" ]]; then
+ log_dir=$WORKSPACE/build_output/log
+ if [[ -d "$log_dir" ]]; then
+ #Uploading logs to artifacts
+ echo "Uploading artifacts for future debugging needs...."
+ gsutil cp -r $WORKSPACE/build_output/log-*.tar.gz $GS_LOG_LOCATION > $WORKSPACE/gsutil.log 2>&1
+ else
+ echo "No test logs/artifacts available for uploading"
+ fi
+ fi
+fi
# upload metadata file for the artifacts built by daily job
if [[ "$JOB_TYPE" == "daily" ]]; then
diff --git a/jjb/kvmfornfv/kvmfornfv.yml b/jjb/kvmfornfv/kvmfornfv.yml
index 60f8de8c4..a782ee0fa 100644
--- a/jjb/kvmfornfv/kvmfornfv.yml
+++ b/jjb/kvmfornfv/kvmfornfv.yml
@@ -19,7 +19,7 @@
- 'build':
slave-label: 'opnfv-build-ubuntu'
- 'test':
- slave-label: 'intel-pod1'
+ slave-label: 'intel-pod10'
#####################################
# patch verification phases
#####################################
@@ -48,6 +48,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 3
@@ -56,7 +57,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -90,25 +90,25 @@
- name: 'kvmfornfv-verify-build-{stream}'
current-parameters: false
predefined-parameters: |
- GERRIT_BRANCH=$GERRIT_BRANCH
+ BRANCH=$BRANCH
+ GERRIT_REFSPEC=$GERRIT_REFSPEC
+ GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
+ node-parameters: false
+ kill-phase-on: FAILURE
+ abort-all-job: true
+ - multijob:
+ name: test
+ condition: SUCCESSFUL
+ projects:
+ - name: 'kvmfornfv-verify-test-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ BRANCH=$BRANCH
GERRIT_REFSPEC=$GERRIT_REFSPEC
GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
node-parameters: false
kill-phase-on: FAILURE
abort-all-job: true
-# - multijob:
-# name: test
-# condition: SUCCESSFUL
-# projects:
-# - name: 'kvmfornfv-verify-test-{stream}'
-# current-parameters: false
-# predefined-parameters: |
-# GERRIT_BRANCH=$GERRIT_BRANCH
-# GERRIT_REFSPEC=$GERRIT_REFSPEC
-# GERRIT_CHANGE_NUMBER=$GERRIT_CHANGE_NUMBER
-# node-parameters: false
-# kill-phase-on: FAILURE
-# abort-all-job: true
- job-template:
name: 'kvmfornfv-verify-{phase}-{stream}'
@@ -127,11 +127,14 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- 'kvmfornfv-defaults':
gs-pathname: '{gs-pathname}'
+ - string:
+ name: PHASE
+ default: '{phase}'
+ description: "Execution of kvmfornfv daily '{phase}' job ."
builders:
- description-setter:
@@ -146,7 +149,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- 'kvmfornfv-defaults':
@@ -185,7 +187,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- 'kvmfornfv-defaults':
@@ -194,8 +195,8 @@
scm:
- git-scm
-# triggers:
-# - timed: '@midnight'
+ triggers:
+ - timed: '@midnight'
builders:
- description-setter:
@@ -259,7 +260,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
- 'kvmfornfv-defaults':
@@ -268,6 +268,10 @@
name: TEST_NAME
default: '{testname}'
description: "Daily job to execute kvmfornfv '{testname}' testcase."
+ - string:
+ name: PHASE
+ default: '{phase}'
+ description: "Execution of kvmfornfv daily '{phase}' job ."
builders:
- description-setter:
@@ -304,6 +308,8 @@
!include-raw: ./kvmfornfv-download-artifact.sh
- shell:
!include-raw: ./kvmfornfv-test.sh
+ - shell:
+ !include-raw: ./kvmfornfv-upload-artifact.sh
- builder:
name: 'kvmfornfv-packet_forward-daily-build-macro'
builders:
diff --git a/jjb/models/models.yml b/jjb/models/models.yml
new file mode 100644
index 000000000..89d22bcbd
--- /dev/null
+++ b/jjb/models/models.yml
@@ -0,0 +1,67 @@
+###################################################
+# All the jobs except verify have been removed!
+# They will only be enabled on request by projects!
+###################################################
+- project:
+ name: models
+
+ project: '{name}'
+
+ jobs:
+ - 'models-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: false
+
+- job-template:
+ name: 'models-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**|.gitignore'
+
+ builders:
+ - shell: |
+ #!/bin/bash
+ set -o errexit
+ set -o nounset
+ set -o pipefail
+
+ # shellcheck -f tty tests/*.sh
diff --git a/jjb/moon/moon.yml b/jjb/moon/moon.yml
index 15c3ddec8..a318bc54d 100644
--- a/jjb/moon/moon.yml
+++ b/jjb/moon/moon.yml
@@ -17,7 +17,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/multisite/fuel-deploy-for-multisite.sh b/jjb/multisite/fuel-deploy-for-multisite.sh
new file mode 100755
index 000000000..06617610c
--- /dev/null
+++ b/jjb/multisite/fuel-deploy-for-multisite.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+# SPDX-license-identifier: Apache-2.0
+##############################################################################
+# Copyright (c) 2016 Ericsson AB and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+set -o nounset
+set -o pipefail
+
+# do not continue with the deployment if FRESH_INSTALL is not requested
+if [[ "$FRESH_INSTALL" == "true" ]]; then
+ echo "Fresh install requested. Proceeding with the installation."
+else
+ echo "Fresh install is not requested. Skipping the installation."
+ exit 0
+fi
+
+export TERM="vt220"
+
+# get the latest successful job console log and extract the properties filename
+FUEL_DEPLOY_BUILD_URL="https://build.opnfv.org/ci/job/fuel-deploy-virtual-daily-master/lastSuccessfulBuild/consoleText"
+FUEL_PROPERTIES_FILE=$(curl -s -L ${FUEL_DEPLOY_BUILD_URL} | grep 'ISO:' | awk '{print $2}' | sed 's/iso/properties/g')
+if [[ -z "FUEL_PROPERTIES_FILE" ]]; then
+ echo "Unable to extract the url to Fuel ISO properties from ${FUEL_DEPLOY_URL}"
+ exit 1
+fi
+
+# use known/working version of fuel
+FUEL_PROPERTIES_FILE="opnfv-2017-03-06_16-00-15.properties"
+curl -L -s -o $WORKSPACE/latest.properties http://artifacts.opnfv.org/fuel/$FUEL_PROPERTIES_FILE
+
+# source the file so we get OPNFV vars
+source latest.properties
+
+# echo the info about artifact that is used during the deployment
+echo "Using ${OPNFV_ARTIFACT_URL/*\/} for deployment"
+
+# download the iso
+echo "Downloading the ISO using the link http://$OPNFV_ARTIFACT_URL"
+curl -L -s -o $WORKSPACE/opnfv.iso http://$OPNFV_ARTIFACT_URL > gsutil.iso.log 2>&1
+
+
+# set deployment parameters
+DEPLOY_SCENARIO="os-nosdn-nofeature-noha"
+export TMPDIR=$HOME/tmpdir
+BRIDGE=${BRIDGE:-pxebr}
+LAB_NAME=${NODE_NAME/-*}
+POD_NAME=${NODE_NAME/*-}
+
+if [[ "$NODE_NAME" =~ "virtual" ]]; then
+ POD_NAME="virtual_kvm"
+fi
+
+# we currently support ericsson, intel, lf and zte labs
+if [[ ! "$LAB_NAME" =~ (ericsson|intel|lf|zte) ]]; then
+ echo "Unsupported/unidentified lab $LAB_NAME. Cannot continue!"
+ exit 1
+else
+ echo "Using configuration for $LAB_NAME"
+fi
+
+# create TMPDIR if it doesn't exist
+export TMPDIR=$HOME/tmpdir
+mkdir -p $TMPDIR
+
+# change permissions down to TMPDIR
+chmod a+x $HOME
+chmod a+x $TMPDIR
+
+# clone fuel repo and checkout the sha1 that corresponds to the ISO
+echo "Cloning fuel repo"
+git clone https://gerrit.opnfv.org/gerrit/p/fuel.git fuel
+cd $WORKSPACE/fuel
+echo "Checking out $OPNFV_GIT_SHA1"
+git checkout $OPNFV_GIT_SHA1 --quiet
+
+# clone the securedlab repo
+cd $WORKSPACE
+echo "Cloning securedlab repo ${GIT_BRANCH##origin/}"
+git clone ssh://jenkins-ericsson@gerrit.opnfv.org:29418/securedlab --quiet \
+ --branch ${GIT_BRANCH##origin/}
+
+# log file name
+FUEL_LOG_FILENAME="${JOB_NAME}_${BUILD_NUMBER}.log.tar.gz"
+
+# construct the command
+DEPLOY_COMMAND="sudo $WORKSPACE/fuel/ci/deploy.sh -b file://$WORKSPACE/securedlab \
+ -l $LAB_NAME -p $POD_NAME -s $DEPLOY_SCENARIO -i file://$WORKSPACE/opnfv.iso \
+ -H -B $BRIDGE -S $TMPDIR -L $WORKSPACE/$FUEL_LOG_FILENAME"
+
+# log info to console
+echo "Deployment parameters"
+echo "--------------------------------------------------------"
+echo "Scenario: $DEPLOY_SCENARIO"
+echo "Lab: $LAB_NAME"
+echo "POD: $POD_NAME"
+echo "ISO: ${OPNFV_ARTIFACT_URL/*\/}"
+echo
+echo "Starting the deployment using $INSTALLER_TYPE. This could take some time..."
+echo "--------------------------------------------------------"
+echo
+
+# start the deployment
+echo "Issuing command"
+echo "$DEPLOY_COMMAND"
+echo
+
+$DEPLOY_COMMAND
+exit_code=$?
+
+echo
+echo "--------------------------------------------------------"
+echo "Deployment is done!"
+
+if [[ $exit_code -ne 0 ]]; then
+ echo "Deployment failed!"
+ exit $exit_code
+else
+ echo "Deployment is successful!"
+ exit 0
+fi
diff --git a/jjb/multisite/multisite-daily-jobs.yml b/jjb/multisite/multisite-daily-jobs.yml
index cfb40a1fa..23c95f627 100644
--- a/jjb/multisite/multisite-daily-jobs.yml
+++ b/jjb/multisite/multisite-daily-jobs.yml
@@ -8,19 +8,23 @@
- 'multisite-{phase}-{stream}'
phase:
- - 'fuel-deploy-regionone-virtual'
- - 'fuel-deploy-regiontwo-virtual'
- - 'register-endpoints'
- - 'update-auth'
- - 'kingbird-deploy-virtual'
- - 'kingbird-functest'
+ - 'fuel-deploy-regionone-virtual':
+ slave-label: ericsson-virtual12
+ - 'fuel-deploy-regiontwo-virtual':
+ slave-label: ericsson-virtual13
+ - 'register-endpoints':
+ slave-label: ericsson-virtual12
+ - 'update-auth':
+ slave-label: ericsson-virtual13
+ - 'kingbird-deploy-virtual':
+ slave-label: ericsson-virtual12
stream:
- master:
branch: '{stream}'
gs-pathname: ''
disabled: false
- timed: '#@midnight'
+ timed: '@midnight'
- job-template:
name: 'multisite-kingbird-virtual-daily-{stream}'
@@ -34,13 +38,16 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
+ - choice:
+ name: FRESH_INSTALL
+ choices:
+ - 'true'
+ - 'false'
- string:
name: KINGBIRD_LOG_FILE
default: $WORKSPACE/kingbird.log
- - 'multisite-virtual-defaults'
- - string:
- name: DEPLOY_SCENARIO
- default: 'os-nosdn-multisite-noha'
+ - 'opnfv-build-defaults'
triggers:
- timed: '{timed}'
@@ -56,26 +63,28 @@
current-parameters: false
predefined-parameters: |
FUEL_VERSION=latest
- DEPLOY_SCENARIO=$DEPLOY_SCENARIO
+ DEPLOY_SCENARIO=os-nosdn-nofeature-noha
OS_REGION=RegionOne
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual2
+ node-label: ericsson-virtual12
kill-phase-on: FAILURE
abort-all-job: true
- name: 'multisite-fuel-deploy-regiontwo-virtual-{stream}'
current-parameters: false
predefined-parameters: |
FUEL_VERSION=latest
- DEPLOY_SCENARIO=$DEPLOY_SCENARIO
+ DEPLOY_SCENARIO=os-nosdn-nofeature-noha
OS_REGION=RegionTwo
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual6
+ node-label: ericsson-virtual13
kill-phase-on: FAILURE
abort-all-job: true
- multijob:
@@ -86,22 +95,24 @@
current-parameters: false
predefined-parameters: |
OS_REGION=RegionOne
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual2
+ node-label: ericsson-virtual12
kill-phase-on: FAILURE
abort-all-job: true
- name: 'multisite-update-auth-{stream}'
current-parameters: false
predefined-parameters: |
OS_REGION=RegionTwo
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual6
+ node-label: ericsson-virtual13
kill-phase-on: FAILURE
abort-all-job: true
- multijob:
@@ -112,26 +123,30 @@
current-parameters: false
predefined-parameters: |
OS_REGION=RegionOne
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual2
+ node-label: ericsson-virtual12
kill-phase-on: FAILURE
abort-all-job: true
- multijob:
name: kingbird-functest
condition: SUCCESSFUL
projects:
- - name: 'multisite-kingbird-functest-{stream}'
+ - name: 'functest-fuel-virtual-suite-{stream}'
current-parameters: false
predefined-parameters: |
+ DEPLOY_SCENARIO=os-nosdn-multisite-noha
+ FUNCTEST_SUITE_NAME=multisite
OS_REGION=RegionOne
- REGIONONE_IP=10.2.117.79
- REGIONTWO_IP=10.2.117.181
+ REGIONONE_IP=100.64.209.10
+ REGIONTWO_IP=100.64.209.11
+ FRESH_INSTALL=$FRESH_INSTALL
node-parameters: false
node-label-name: SLAVE_LABEL
- node-label: intel-virtual2
+ node-label: ericsson-virtual12
kill-phase-on: NEVER
abort-all-job: false
@@ -140,6 +155,28 @@
concurrent: false
+ disabled: '{obj:disabled}'
+
+ concurrent: false
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: KINGBIRD_LOG_FILE
+ default: $WORKSPACE/kingbird.log
+ - 'fuel-defaults'
+ - '{slave-label}-defaults'
+ - choice:
+ name: FRESH_INSTALL
+ choices:
+ - 'true'
+ - 'false'
+
+ scm:
+ - git-scm
+
builders:
- description-setter:
description: "Built on $NODE_NAME"
@@ -155,39 +192,57 @@
- builder:
name: 'multisite-fuel-deploy-regionone-virtual-builder'
builders:
+ - shell:
+ !include-raw-escape: ./fuel-deploy-for-multisite.sh
- shell: |
#!/bin/bash
echo "This is where we deploy fuel, extract passwords and save into file"
+
+ cd $WORKSPACE/tools/keystone/
+ ./run.sh -t controller -r fetchpass.sh -o servicepass.ini
+
- builder:
name: 'multisite-fuel-deploy-regiontwo-virtual-builder'
builders:
+ - shell:
+ !include-raw-escape: ./fuel-deploy-for-multisite.sh
- shell: |
#!/bin/bash
echo "This is where we deploy fuel, extract publicUrl, privateUrl, and adminUrl and save into file"
+
+ cd $WORKSPACE/tools/keystone/
+ ./run.sh -t controller -r endpoint.sh -o endpoints.ini
- builder:
name: 'multisite-register-endpoints-builder'
builders:
- copyartifact:
project: 'multisite-fuel-deploy-regiontwo-virtual-{stream}'
which-build: multijob-build
- filter: "RegionTwo-Endpoints.txt"
+ filter: "endpoints.ini"
- shell: |
#!/bin/bash
- echo "This is where we register RegionTwo in RegionOne keystone"
+ echo "This is where we register RegionTwo in RegionOne keystone using endpoints.ini"
+
+ cd $WORKSPACE/tools/keystone/
+ ./run.sh -t controller -r region.sh -d $WORKSPACE/endpoints.ini
- builder:
name: 'multisite-update-auth-builder'
builders:
- copyartifact:
project: 'multisite-fuel-deploy-regionone-virtual-{stream}'
which-build: multijob-build
- filter: "RegionOne-Passwords.txt"
+ filter: "servicepass.ini"
- shell: |
#!/bin/bash
- echo "This is where we read passwords from RegionOne-passwords.txt and replace passwords in RegionTwo"
+ echo "This is where we read passwords from servicepass.ini and replace passwords in RegionTwo"
+
+ cd $WORKSPACE/tools/keystone/
+ ./run.sh -t controller -r writepass.sh -d $WORKSPACE/servicepass.ini
+ ./run.sh -t compute -r writepass.sh -d $WORKSPACE/servicepass.ini
- builder:
name: 'multisite-kingbird-deploy-virtual-builder'
builders:
@@ -195,13 +250,8 @@
#!/bin/bash
echo "This is where we install kingbird"
-- builder:
- name: 'multisite-kingbird-functest-builder'
- builders:
- - shell: |
- #!/bin/bash
-
- echo "This is where we run kingbird-functest"
+ cd $WORKSPACE/tools/kingbird
+ ./deploy.sh
########################
# publisher macros
########################
@@ -209,7 +259,7 @@
name: 'multisite-fuel-deploy-regionone-virtual-publisher'
publishers:
- archive:
- artifacts: '/root/servicepass.ini'
+ artifacts: 'servicepass.ini'
allow-empty: false
only-if-success: true
fingerprint: true
@@ -217,7 +267,7 @@
name: 'multisite-fuel-deploy-regiontwo-virtual-publisher'
publishers:
- archive:
- artifacts: '/root/endpoints.ini'
+ artifacts: 'endpoints.ini'
allow-empty: false
only-if-success: true
fingerprint: true
diff --git a/jjb/multisite/multisite-verify-jobs.yml b/jjb/multisite/multisite-verify-jobs.yml
new file mode 100644
index 000000000..5ecfafb55
--- /dev/null
+++ b/jjb/multisite/multisite-verify-jobs.yml
@@ -0,0 +1,68 @@
+###################################################
+# All the jobs except verify have been removed!
+# They will only be enabled on request by projects!
+###################################################
+- project:
+ name: multisite
+
+ project: '{name}'
+
+ jobs:
+ - 'multisite-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ timed: '@midnight'
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: true
+ timed: ''
+
+- job-template:
+ name: 'multisite-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ concurrent: true
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**|.gitignore'
+
+ builders:
+ - shell: |
+ #!/bin/bash
+
+ echo "Hello World"
diff --git a/jjb/multisite/multisite.yml b/jjb/multisite/multisite.yml
deleted file mode 100644
index 6b6406983..000000000
--- a/jjb/multisite/multisite.yml
+++ /dev/null
@@ -1,149 +0,0 @@
-###################################################
-# All the jobs except verify have been removed!
-# They will only be enabled on request by projects!
-###################################################
-- project:
- name: multisite
-
- project: '{name}'
-
- jobs:
- - 'multisite-verify-{stream}'
- - 'multisite-kingbird-daily-{stream}'
- - 'multisite-kingbird-deploy-{stream}'
-
- stream:
- - master:
- branch: '{stream}'
- gs-pathname: ''
- disabled: false
- timed: '@midnight'
- - danube:
- branch: 'stable/{stream}'
- gs-pathname: '/{stream}'
- disabled: false
- timed: ''
-
-- job-template:
- name: 'multisite-verify-{stream}'
-
- disabled: '{obj:disabled}'
-
- concurrent: true
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - 'opnfv-build-ubuntu-defaults'
-
- scm:
- - git-scm-gerrit
-
- triggers:
- - gerrit:
- server-name: 'gerrit.opnfv.org'
- trigger-on:
- - patchset-created-event:
- exclude-drafts: 'false'
- exclude-trivial-rebase: 'false'
- exclude-no-code-change: 'false'
- - draft-published-event
- - comment-added-contains-event:
- comment-contains-value: 'recheck'
- - comment-added-contains-event:
- comment-contains-value: 'reverify'
- projects:
- - project-compare-type: 'ANT'
- project-pattern: '{project}'
- branches:
- - branch-compare-type: 'ANT'
- branch-pattern: '**/{branch}'
- forbidden-file-paths:
- - compare-type: ANT
- pattern: 'docs/**|.gitignore'
-
- builders:
- - shell: |
- #!/bin/bash
-
- echo "Hello World"
-
-- job-template:
- name: 'multisite-kingbird-daily-{stream}'
-
- project-type: freestyle
-
- disabled: '{obj:disabled}'
-
- concurrent: false
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - string:
- name: KINGBIRD_LOG_FILE
- default: $WORKSPACE/kingbird.log
- - 'intel-virtual6-defaults'
- - string:
- name: DEPLOY_SCENARIO
- default: 'os-nosdn-multisite-ha'
-
- scm:
- - git-scm
-
- triggers:
- - timed: '{timed}'
-
- builders:
- - trigger-builds:
- - project: 'multisite-kingbird-deploy-{stream}'
- current-parameters: true
- same-node: true
- block: true
- - trigger-builds:
- - project: 'functest-fuel-virtual-suite-{stream}'
- current-parameters: true
- predefined-parameters:
- FUNCTEST_SUITE_NAME=multisite
- same-node: true
- block: true
- block-thresholds:
- build-step-failure-threshold: 'never'
- failure-threshold: 'never'
- unstable-threshold: 'FAILURE'
-
-- job-template:
- name: 'multisite-kingbird-deploy-{stream}'
-
- concurrent: false
-
- scm:
- - git-scm-gerrit
-
- builders:
- - 'multisite-kingbird-deploy'
- - 'multisite-kingbird-log-upload'
-
-########################
-# builder macros
-########################
-- builder:
- name: 'multisite-kingbird-deploy'
- builders:
- - shell: |
- #!/bin/bash
-
- $WORKSPACE/tools/kingbird/deploy.sh
-- builder:
- name: 'multisite-kingbird-log-upload'
- builders:
- - shell: |
- #!/bin/bash
-
- echo "Here is where we upload kingbird logs to artifact repo"
- echo "We just check the existence of log file"
- ls -al $KINGBIRD_LOG_FILE
diff --git a/jjb/netready/netready.yml b/jjb/netready/netready.yml
index 3d043f9e3..382434ae6 100644
--- a/jjb/netready/netready.yml
+++ b/jjb/netready/netready.yml
@@ -21,7 +21,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
scm:
- git-scm-gerrit
@@ -65,7 +64,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- 'netready-parameter':
diff --git a/jjb/octopus/octopus.yml b/jjb/octopus/octopus.yml
index b820ecb0e..cb66112fe 100644
--- a/jjb/octopus/octopus.yml
+++ b/jjb/octopus/octopus.yml
@@ -27,7 +27,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/onosfw/onosfw.yml b/jjb/onosfw/onosfw.yml
index d3a845403..13c96718c 100644
--- a/jjb/onosfw/onosfw.yml
+++ b/jjb/onosfw/onosfw.yml
@@ -31,7 +31,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -72,7 +71,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -96,7 +94,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- string:
diff --git a/jjb/openretriever/openretriever-project.yml b/jjb/openretriever/openretriever-project.yml
new file mode 100644
index 000000000..3d53f9b2e
--- /dev/null
+++ b/jjb/openretriever/openretriever-project.yml
@@ -0,0 +1,62 @@
+###################################################
+# All the jobs except verify have been removed!
+# They will only be enabled on request by projects!
+###################################################
+- project:
+ name: openretriever
+
+ project: '{name}'
+
+ jobs:
+ - 'openretriever-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: false
+
+- job-template:
+ name: 'openretriever-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**|.gitignore'
+
+ builders:
+ - shell: |
+ echo "Nothing to verify!"
diff --git a/jjb/opera/opera-daily-jobs.yml b/jjb/opera/opera-daily-jobs.yml
index 556d59fcb..5d2cc03f3 100644
--- a/jjb/opera/opera-daily-jobs.yml
+++ b/jjb/opera/opera-daily-jobs.yml
@@ -38,6 +38,7 @@
concurrent: false
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -51,7 +52,7 @@
- ssh-agent-wrapper
- timeout:
- timeout: 120
+ timeout: 240
fail: true
triggers:
@@ -60,9 +61,8 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'huawei-pod7-defaults'
+ - 'huawei-virtual7-defaults'
builders:
- description-setter:
@@ -80,8 +80,11 @@
name: deploy
condition: SUCCESSFUL
projects:
- - name: 'opera-daily-deploy-{stream}'
- current-parameters: true
+ - name: 'compass-deploy-virtual-daily-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ DEPLOY_SCENARIO=os-nosdn-openo-ha
+ COMPASS_OS_VERSION=xenial
node-parameters: true
kill-phase-on: FAILURE
abort-all-job: true
@@ -90,7 +93,7 @@
# condition: SUCCESSFUL
# projects:
# - name: 'functest-compass-baremetal-suite-{stream}'
-# current-parameters: true
+# current-parameters: false
# predefined-parameters:
# FUNCTEST_SUITE_NAME=opera
# node-parameters: true
@@ -105,6 +108,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
diff --git a/jjb/opera/opera-project-jobs.yml b/jjb/opera/opera-project-jobs.yml
index 19f066b5f..38efbc159 100644
--- a/jjb/opera/opera-project-jobs.yml
+++ b/jjb/opera/opera-project-jobs.yml
@@ -21,6 +21,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -30,9 +31,8 @@
parameters:
- project-parameter:
project: '{project}'
- - 'opnfv-build-ubuntu-defaults'
- - gerrit-parameter:
branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
scm:
- git-scm
diff --git a/jjb/opera/opera-verify-jobs.yml b/jjb/opera/opera-verify-jobs.yml
index 0e9dba01d..b7b5cb3c9 100644
--- a/jjb/opera/opera-verify-jobs.yml
+++ b/jjb/opera/opera-verify-jobs.yml
@@ -38,6 +38,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-total: 1
@@ -83,7 +84,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'huawei-pod7-defaults'
@@ -117,6 +117,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
diff --git a/jjb/opnfvdocs/docs-post-rtd.sh b/jjb/opnfvdocs/docs-post-rtd.sh
new file mode 100644
index 000000000..e3dc9b5f0
--- /dev/null
+++ b/jjb/opnfvdocs/docs-post-rtd.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+if [ $GERRIT_BRANCH == "master" ]; then
+ RTD_BUILD_VERSION=latest
+else
+ RTD_BUILD_VERSION=${{GERRIT_BRANCH/\//-}}
+fi
+curl -X POST --data "version_slug=$RTD_BUILD_VERSION" https://readthedocs.org/build/opnfvdocsdemo
diff --git a/jjb/opnfvdocs/docs-rtd.yaml b/jjb/opnfvdocs/docs-rtd.yaml
new file mode 100644
index 000000000..c78e7f0f6
--- /dev/null
+++ b/jjb/opnfvdocs/docs-rtd.yaml
@@ -0,0 +1,88 @@
+- project:
+ name: docs-rtd
+ jobs:
+ - 'docs-merge-rtd-{stream}'
+ - 'docs-verify-rtd-{stream}'
+
+ stream:
+ - master:
+ branch: 'master'
+
+ project: 'opnfvdocs'
+ rtdproject: 'opnfv'
+ # TODO: Archive Artifacts
+
+- job-template:
+ name: 'docs-merge-rtd-{stream}'
+
+ project-type: freestyle
+
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'lf-build1'
+ description: 'Slave label on Jenkins'
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/releng
+ description: 'Git URL to use on this Jenkins Slave'
+ scm:
+ - git-scm
+
+ triggers:
+ - gerrit-trigger-change-merged:
+ project: '**'
+ branch: '{branch}'
+ files: 'docs/**/*.*'
+
+ builders:
+ - shell: !include-raw: docs-post-rtd.sh
+
+- job-template:
+ name: 'docs-verify-rtd-{stream}'
+
+ project-type: freestyle
+
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'lf-build2'
+ description: 'Slave label on Jenkins'
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/opnfvdocs
+ description: 'Git URL to use on this Jenkins Slave'
+ scm:
+ - git-scm-with-submodules:
+ branch: '{branch}'
+
+ triggers:
+ - gerrit-trigger-patchset-created:
+ server: 'gerrit.opnfv.org'
+ project: '**'
+ branch: '{branch}'
+ files: 'docs/**/*.rst'
+ - timed: 'H H * * *'
+
+ builders:
+ - shell: |
+ if [ "$GERRIT_PROJECT" != "opnfvdocs" ]; then
+ cd docs/submodules/$GERRIT_PROJECT
+ git fetch origin $GERRIT_REFSPEC && git checkout FETCH_HEAD
+ else
+ git fetch origin $GERRIT_REFSPEC && git checkout FETCH_HEAD
+ fi
+ - shell: |
+ sudo pip install virtualenv
+ virtualenv $WORKSPACE/venv
+ . $WORKSPACE/venv/bin/activate
+ pip install --upgrade pip
+ pip freeze
+ pip install tox
+ tox -edocs
diff --git a/jjb/opnfvdocs/opnfvdocs.yml b/jjb/opnfvdocs/opnfvdocs.yml
index 0d4c46199..661f060ee 100644
--- a/jjb/opnfvdocs/opnfvdocs.yml
+++ b/jjb/opnfvdocs/opnfvdocs.yml
@@ -34,7 +34,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_CLONE_BASE
@@ -63,6 +62,11 @@
branches:
- branch-compare-type: 'ANT'
branch-pattern: '**/{branch}'
+ skip-vote:
+ successful: true
+ failed: true
+ unstable: true
+ notbuilt: true
builders:
- check-bash-syntax
@@ -75,7 +79,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_CLONE_BASE
@@ -87,7 +90,7 @@
description: "Directory where the build artifact will be located upon the completion of the build."
scm:
- - git-scm-gerrit
+ - git-scm
triggers:
- gerrit:
@@ -114,6 +117,7 @@
parameters:
- project-parameter:
project: '{project}'
+ branch: '{branch}'
- string:
name: GS_URL
default: '$GS_BASE{gs-pathname}'
@@ -122,10 +126,6 @@
name: GIT_CLONE_BASE
default: ssh://gerrit.opnfv.org:29418
description: "Used for overriding the GIT URL coming from parameters macro."
- - string:
- name: GERRIT_BRANCH
- default: '{branch}'
- description: 'Specify the branch in this way in order to be able to use build-opnfv-composite-docs builder.'
scm:
- git-scm
@@ -136,4 +136,3 @@
builders:
- build-html-and-pdf-docs-output
# - upload-generated-docs-to-opnfv-artifacts
-
diff --git a/jjb/opnfvdocs/project.cfg b/jjb/opnfvdocs/project.cfg
index 186e0ea74..1ea05c1d4 100644
--- a/jjb/opnfvdocs/project.cfg
+++ b/jjb/opnfvdocs/project.cfg
@@ -24,6 +24,7 @@ movie
multisite
octopus
onosfw
+openretriever
ovno
ovsnfv
parser
diff --git a/jjb/ovsnfv/ovsnfv.yml b/jjb/ovsnfv/ovsnfv.yml
index 9b2adf3a8..937a367fb 100644
--- a/jjb/ovsnfv/ovsnfv.yml
+++ b/jjb/ovsnfv/ovsnfv.yml
@@ -26,7 +26,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-centos-defaults'
- string:
@@ -73,7 +72,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-centos-defaults'
- string:
@@ -119,7 +117,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-centos-defaults'
- string:
diff --git a/jjb/parser/parser.yml b/jjb/parser/parser.yml
index de5587ed8..7f3d6cebe 100644
--- a/jjb/parser/parser.yml
+++ b/jjb/parser/parser.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -56,7 +55,11 @@
branch-pattern: '**/{branch}'
forbidden-file-paths:
- compare-type: ANT
- pattern: 'docs/**|.gitignore'
+ pattern: 'docs/**'
+ - compare-type: ANT
+ pattern: 'governance/**'
+ - compare-type: ANT
+ pattern: '*.txt|.gitignore|.gitreview|INFO|LICENSE'
builders:
- shell: |
diff --git a/jjb/pharos/pharos.yml b/jjb/pharos/pharos.yml
index dbf1b92a4..6dae9f33c 100644
--- a/jjb/pharos/pharos.yml
+++ b/jjb/pharos/pharos.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/prediction/prediction.yml b/jjb/prediction/prediction.yml
index fba5741ac..b380d8c86 100644
--- a/jjb/prediction/prediction.yml
+++ b/jjb/prediction/prediction.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/promise/promise.yml b/jjb/promise/promise.yml
index a0af6f41e..a5aa302c7 100644
--- a/jjb/promise/promise.yml
+++ b/jjb/promise/promise.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
diff --git a/jjb/qtip/qtip-cleanup.sh b/jjb/qtip/helpers/cleanup-deploy.sh
index 95babb318..95babb318 100644
--- a/jjb/qtip/qtip-cleanup.sh
+++ b/jjb/qtip/helpers/cleanup-deploy.sh
diff --git a/jjb/qtip/qtip-daily-ci.sh b/jjb/qtip/helpers/validate-deploy.sh
index 4fdc04345..16455371f 100644
--- a/jjb/qtip/qtip-daily-ci.sh
+++ b/jjb/qtip/helpers/validate-deploy.sh
@@ -27,12 +27,7 @@ if [ $(docker ps | grep 'opnfv/qtip' | wc -l) == 0 ]; then
else
echo "The container ID is: ${container_id}"
QTIP_REPO=/home/opnfv/repos/qtip
-
- echo "Run Qtip test"
- docker exec -t ${container_id} $QTIP_REPO/docker/run_qtip.sh
-
- echo "Pushing available results to DB"
- docker exec -t ${container_id} $QTIP_REPO/docker/push_db.sh
+# TODO(yujunz): execute benchmark plan for compute-qpi
fi
echo "Qtip done!"
diff --git a/jjb/qtip/helpers/validate-setup.sh b/jjb/qtip/helpers/validate-setup.sh
new file mode 100644
index 000000000..8d84e120c
--- /dev/null
+++ b/jjb/qtip/helpers/validate-setup.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+##############################################################################
+# Copyright (c) 2017 ZTE and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+set -e
+
+# setup virtualenv
+sudo pip install -u virtualenv virtualenvwrapper
+export WORKON_HOME=$HOME/.virtualenvs
+source /usr/local/bin/virtualenvwrapper.sh
+mkvirtualenv qtip
+workon qtip
+
+# setup qtip
+sudo pip install $HOME/repos/qtip
+
+# testing
+qtip --version
+qtip --help
diff --git a/jjb/qtip/qtip-ci-jobs.yml b/jjb/qtip/qtip-ci-jobs.yml
deleted file mode 100644
index 69cb32b41..000000000
--- a/jjb/qtip/qtip-ci-jobs.yml
+++ /dev/null
@@ -1,101 +0,0 @@
-####################################
-# job configuration for qtip
-####################################
-- project:
- name: qtip
-
- project: 'qtip'
-
-#--------------------------------
-# BRANCH ANCHORS
-#--------------------------------
- master: &master
- stream: master
- branch: '{stream}'
- gs-pathname: ''
- docker-tag: 'latest'
-#--------------------------------
-# POD, INSTALLER, AND BRANCH MAPPING
-#--------------------------------
-# master
-#--------------------------------
- pod:
- - zte-pod2:
- installer: fuel
- auto-trigger-name: 'qtip-daily-zte-pod2-trigger'
- <<: *master
- - zte-pod3:
- installer: fuel
- auto-trigger-name: 'qtip-daily-zte-pod3-trigger'
- <<: *master
-
-#--------------------------------
- jobs:
- - 'qtip-{installer}-{pod}-daily-{stream}'
-
-################################
-# job templates
-################################
-- job-template:
- name: 'qtip-{installer}-{pod}-daily-{stream}'
-
- disabled: true
-
- parameters:
- - project-parameter:
- project: '{project}'
- - gerrit-parameter:
- branch: '{branch}'
- - '{installer}-defaults'
- - '{pod}-defaults'
- - string:
- name: DEPLOY_SCENARIO
- default: 'os-nosdn-nofeature-ha'
- - string:
- name: DOCKER_TAG
- default: '{docker-tag}'
- description: 'Tag to pull docker image'
-
- scm:
- - git-scm
-
- triggers:
- - '{auto-trigger-name}'
-
- builders:
- - description-setter:
- description: "POD: $NODE_NAME"
- - 'qtip-cleanup'
- - 'qtip-daily-ci'
-
- publishers:
- - email:
- recipients: wu.zhihui1@zte.com.cn, zhang.yujunz@zte.com.cn
-
-###########################
-#biuilder macros
-###########################
-- builder:
- name: qtip-daily-ci
- builders:
- - shell:
- !include-raw: ./qtip-daily-ci.sh
-
-- builder:
- name: qtip-cleanup
- builders:
- - shell:
- !include-raw: ./qtip-cleanup.sh
-
-#################
-#trigger macros
-#################
-- trigger:
- name: 'qtip-daily-zte-pod2-trigger'
- triggers:
- - timed: '0 7 * * *'
-
-- trigger:
- name: 'qtip-daily-zte-pod3-trigger'
- triggers:
- - timed: '0 1 * * *'
diff --git a/jjb/qtip/qtip-validate-jobs.yml b/jjb/qtip/qtip-validate-jobs.yml
new file mode 100644
index 000000000..10ee72a8b
--- /dev/null
+++ b/jjb/qtip/qtip-validate-jobs.yml
@@ -0,0 +1,142 @@
+#######################
+# validate after MERGE
+#######################
+- project:
+ name: qtip
+ project: qtip
+
+#--------------------------------
+# BRANCH ANCHORS
+#--------------------------------
+ master: &master
+ stream: master
+ branch: '{stream}'
+ gs-pathname: ''
+ docker-tag: latest
+
+#--------------------------------
+# JOB VARIABLES
+#--------------------------------
+ pod:
+ - zte-pod2:
+ installer: fuel
+ <<: *master
+ - zte-pod3:
+ installer: fuel
+ <<: *master
+ task:
+ - daily:
+ auto-builder-name: qtip-validate-deploy
+ auto-trigger-name: 'qtip-daily-{pod}-trigger'
+ - validate:
+ auto-builder-name: qtip-validate-setup
+ auto-trigger-name: qtip-validate-trigger
+ - experimental:
+ auto-builder-name: qtip-validate-setup
+ auto-trigger-name: experimental
+
+#--------------------------------
+# JOB LIST
+#--------------------------------
+ jobs:
+ - 'qtip-{task}-{installer}-{pod}-{stream}'
+
+################################
+# job templates
+################################
+- job-template:
+ name: 'qtip-{task}-{installer}-{pod}-{stream}'
+ disabled: false
+ parameters:
+ - qtip-common-parameters:
+ project: '{project}'
+ <<: *master
+ - '{installer}-defaults'
+ - '{pod}-defaults'
+ scm:
+ - git-scm
+ triggers:
+ - '{auto-trigger-name}'
+ builders:
+ - qtip-common-builders
+ - '{auto-builder-name}'
+ publishers:
+ - qtip-common-publishers
+
+################
+# MARCOS
+################
+
+#---------
+# builder
+#---------
+
+- builder:
+ name: qtip-common-builders
+ builders:
+ - description-setter:
+ description: "POD: $NODE_NAME"
+
+- builder:
+ name: qtip-validate-deploy
+ builders:
+ - shell:
+ !include-raw: ./helpers/validate-deploy.sh
+ - shell:
+ !include-raw: ./helpers/cleanup-deploy.sh
+
+- builder:
+ name: qtip-validate-setup
+ builders:
+ - shell:
+ !include-raw: ./helpers/validate-setup.sh
+
+#-----------
+# parameter
+#-----------
+
+- parameter:
+ name: qtip-common-parameters
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: DEPLOY_SCENARIO
+ default: 'os-nosdn-nofeature-ha'
+ - string:
+ name: DOCKER_TAG
+ default: '{docker-tag}'
+ description: 'Tag to pull docker image'
+
+#-----------
+# publisher
+#-----------
+
+- publisher:
+ name: qtip-common-publishers
+ publishers:
+ - email:
+ recipients: wu.zhihui1@zte.com.cn, zhang.yujunz@zte.com.cn
+
+#---------
+# trigger
+#---------
+
+- trigger:
+ name: qtip-daily-zte-pod2-trigger
+ triggers:
+ - timed: '0 7 * * *'
+
+- trigger:
+ name: qtip-daily-zte-pod3-trigger
+ triggers:
+ - timed: '0 1 * * *'
+
+- trigger:
+ name: qtip-validate-trigger
+ triggers:
+ - gerrit-trigger-change-merged:
+ project: '{project}'
+ branch: '{branch}'
+ files: '**'
diff --git a/jjb/qtip/qtip-project-jobs.yml b/jjb/qtip/qtip-verify-jobs.yml
index a9c8251fc..5f0292b92 100644
--- a/jjb/qtip/qtip-project-jobs.yml
+++ b/jjb/qtip/qtip-verify-jobs.yml
@@ -1,11 +1,12 @@
-- project:
- name: qtip-project-jobs
-
- project: 'qtip'
+######################
+# verify before MERGE
+######################
+- project:
+ name: qtip-verify-jobs
+ project: qtip
jobs:
- 'qtip-verify-{stream}'
-
stream:
- master:
branch: '{stream}'
@@ -23,7 +24,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -55,6 +55,8 @@
builders:
- qtip-unit-tests-and-docs-build
+ publishers:
+ - publish-coverage
################################
## job builders
diff --git a/jjb/releng/artifact-cleanup.yml b/jjb/releng/artifact-cleanup.yml
index e10d5defb..2d0205660 100644
--- a/jjb/releng/artifact-cleanup.yml
+++ b/jjb/releng/artifact-cleanup.yml
@@ -27,7 +27,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
scm:
diff --git a/jjb/releng/opnfv-docker-arm.yml b/jjb/releng/opnfv-docker-arm.yml
new file mode 100644
index 000000000..09c9f335e
--- /dev/null
+++ b/jjb/releng/opnfv-docker-arm.yml
@@ -0,0 +1,77 @@
+##############################################
+# job configuration for docker build and push
+##############################################
+
+- project:
+
+ name: opnfv-docker-arm
+
+ master: &master
+ stream: master
+ branch: '{stream}'
+ disabled: false
+ danube: &danube
+ stream: danube
+ branch: 'stable/{stream}'
+ disabled: true
+ functest-arm-receivers: &functest-arm-receivers
+ receivers: >
+ cristina.pauna@enea.com
+ alexandru.avadanii@enea.com
+ other-receivers: &other-receivers
+ receivers: ''
+
+ project:
+ # projects with jobs for master
+ - 'functest':
+ <<: *master
+ <<: *functest-arm-receivers
+ # projects with jobs for stable
+
+ jobs:
+ - '{project}-docker-build-arm-push-{stream}'
+
+########################
+# job templates
+########################
+- job-template:
+ name: '{project}-docker-build-arm-push-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters: &parameters
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-arm-defaults'
+ - string:
+ name: PUSH_IMAGE
+ default: "true"
+ description: "To enable/disable pushing the image to Dockerhub."
+ - string:
+ name: DOCKER_REPO_NAME
+ default: "opnfv/{project}_aarch64"
+ description: "Dockerhub repo to be pushed to."
+ - string:
+ name: RELEASE_VERSION
+ default: ""
+ description: "Release version, e.g. 1.0, 2.0, 3.0"
+ - string:
+ name: DOCKERFILE
+ default: "Dockerfile.aarch64"
+ description: "Dockerfile to use for creating the image."
+
+ scm:
+ - git-scm
+
+ builders: &builders
+ - shell:
+ !include-raw-escape: ./opnfv-docker.sh
+
+ triggers:
+ - pollscm:
+ cron: "*/30 * * * *"
+
+ publishers:
+ - email:
+ recipients: '{receivers}'
diff --git a/jjb/releng/opnfv-docker.sh b/jjb/releng/opnfv-docker.sh
index e26727abf..ded743d7e 100644
--- a/jjb/releng/opnfv-docker.sh
+++ b/jjb/releng/opnfv-docker.sh
@@ -12,6 +12,7 @@ set -o nounset
set -o pipefail
+
echo "Starting opnfv-docker for $DOCKER_REPO_NAME ..."
echo "--------------------------------------------------------"
echo
@@ -42,32 +43,29 @@ fi
if [[ -n "$(docker images | grep $DOCKER_REPO_NAME)" ]]; then
echo "Docker images to remove:"
docker images | head -1 && docker images | grep $DOCKER_REPO_NAME
- image_tags=($(docker images | grep $DOCKER_REPO_NAME | awk '{print $2}'))
- for tag in "${image_tags[@]}"; do
- if [[ -n "$(docker images|grep $DOCKER_REPO_NAME|grep $tag)" ]]; then
- echo "Removing docker image $DOCKER_REPO_NAME:$tag..."
- docker rmi -f $DOCKER_REPO_NAME:$tag
+ image_ids=($(docker images | grep $DOCKER_REPO_NAME | awk '{print $3}'))
+ for id in "${image_ids[@]}"; do
+ if [[ -n "$(docker images|grep $DOCKER_REPO_NAME|grep $id)" ]]; then
+ echo "Removing docker image $DOCKER_REPO_NAME:$id..."
+ docker rmi -f $id
fi
done
fi
-
-# cd to directory where Dockerfile is located
cd $WORKSPACE/docker
-if [ ! -f ./Dockerfile ]; then
+if [ ! -f ${DOCKERFILE} ]; then
echo "ERROR: Dockerfile not found."
exit 1
fi
# Get tag version
-branch="${GIT_BRANCH##origin/}"
-echo "Current branch: $branch"
+echo "Current branch: $BRANCH"
-if [[ "$branch" == "master" ]]; then
+if [[ "$BRANCH" == "master" ]]; then
DOCKER_TAG="latest"
else
if [[ "$RELEASE_VERSION" != "" ]]; then
- release=$(echo $branch|sed 's/.*\///')
+ release=${BRANCH##*/}
DOCKER_TAG=${release}.${RELEASE_VERSION}
# e.g. colorado.1.0, colorado.2.0, colorado.3.0
else
@@ -79,7 +77,12 @@ fi
echo "Building docker image: $DOCKER_REPO_NAME:$DOCKER_TAG"
echo "--------------------------------------------------------"
echo
-cmd="docker build --no-cache -t $DOCKER_REPO_NAME:$DOCKER_TAG --build-arg BRANCH=$branch ."
+if [[ $DOCKER_REPO_NAME == *"dovetail"* ]]; then
+ cmd="docker build --no-cache -t $DOCKER_REPO_NAME:$DOCKER_TAG -f $DOCKERFILE ."
+else
+ cmd="docker build --no-cache -t $DOCKER_REPO_NAME:$DOCKER_TAG --build-arg BRANCH=$BRANCH
+ -f $DOCKERFILE ."
+fi
echo ${cmd}
${cmd}
diff --git a/jjb/releng/opnfv-docker.yml b/jjb/releng/opnfv-docker.yml
index 02dfb7560..90a91f802 100644
--- a/jjb/releng/opnfv-docker.yml
+++ b/jjb/releng/opnfv-docker.yml
@@ -14,32 +14,51 @@
stream: danube
branch: 'stable/{stream}'
disabled: true
+ functest-receivers: &functest-receivers
+ receivers: >
+ jose.lausuch@ericsson.com morgan.richomme@orange.com
+ cedric.ollivier@orange.com feng.xiaowei@zte.com.cn
+ yaohelan@huawei.com helanyao@gmail.com
+ juha.kosonen@nokia.com
+ other-receivers: &other-receivers
+ receivers: ''
project:
# projects with jobs for master
- 'bottlenecks':
<<: *master
+ <<: *other-receivers
- 'cperf':
<<: *master
+ <<: *other-receivers
- 'dovetail':
<<: *master
+ <<: *other-receivers
- 'functest':
<<: *master
+ <<: *functest-receivers
- 'qtip':
<<: *master
+ <<: *other-receivers
- 'storperf':
<<: *master
+ <<: *other-receivers
- 'yardstick':
<<: *master
+ <<: *other-receivers
# projects with jobs for stable
- 'bottlenecks':
<<: *danube
+ <<: *other-receivers
- 'functest':
<<: *danube
+ <<: *functest-receivers
- 'storperf':
<<: *danube
+ <<: *other-receivers
- 'yardstick':
<<: *danube
+ <<: *other-receivers
jobs:
- '{project}-docker-build-push-{stream}'
@@ -53,6 +72,8 @@
# projects with jobs for master
- 'daisy':
<<: *master
+ - 'escalator':
+ <<: *master
jobs:
- '{project}-docker-build-push-monitor-{stream}'
@@ -68,7 +89,6 @@
parameters: &parameters
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- string:
@@ -83,6 +103,10 @@
name: RELEASE_VERSION
default: ""
description: "Release version, e.g. 1.0, 2.0, 3.0"
+ - string:
+ name: DOCKERFILE
+ default: "Dockerfile"
+ description: "Dockerfile to use for creating the image."
scm:
- git-scm
@@ -95,6 +119,10 @@
- pollscm:
cron: "*/30 * * * *"
+ publishers:
+ - email:
+ recipients: '{receivers}'
+
- job-template:
name: '{project}-docker-build-push-monitor-{stream}'
disabled: '{obj:disabled}'
diff --git a/jjb/releng/opnfv-docs.yml b/jjb/releng/opnfv-docs.yml
index f6092eee0..f4b25017e 100644
--- a/jjb/releng/opnfv-docs.yml
+++ b/jjb/releng/opnfv-docs.yml
@@ -35,7 +35,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
scm:
@@ -77,7 +76,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GS_URL
@@ -89,7 +87,7 @@
description: "JJB configured GERRIT_REFSPEC parameter"
scm:
- - git-scm-gerrit
+ - git-scm
triggers:
- gerrit:
diff --git a/jjb/releng/opnfv-lint.yml b/jjb/releng/opnfv-lint.yml
index 590790f89..166aea8f9 100644
--- a/jjb/releng/opnfv-lint.yml
+++ b/jjb/releng/opnfv-lint.yml
@@ -33,7 +33,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
scm:
@@ -54,7 +53,7 @@
comment-contains-value: 'reverify'
projects:
- project-compare-type: 'REG_EXP'
- project-pattern: 'functest|sdnvpn|qtip|daisy|sfc|escalator'
+ project-pattern: 'functest|sdnvpn|qtip|daisy|sfc|escalator|releng'
branches:
- branch-compare-type: 'ANT'
branch-pattern: '**/{branch}'
@@ -76,7 +75,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
- node:
name: SLAVE_NAME
@@ -104,7 +102,7 @@
comment-contains-value: 'reverify'
projects:
- project-compare-type: 'REG_EXP'
- project-pattern: 'compass4nfv'
+ project-pattern: ''
branches:
- branch-compare-type: 'ANT'
branch-pattern: '**/{branch}'
diff --git a/jjb/releng/releng-ci-jobs.yml b/jjb/releng/releng-ci-jobs.yml
index 626daffbd..ecc87303f 100644
--- a/jjb/releng/releng-ci-jobs.yml
+++ b/jjb/releng/releng-ci-jobs.yml
@@ -13,7 +13,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: 'master'
scm:
- git-scm-gerrit
@@ -64,7 +63,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: 'master'
scm:
@@ -104,7 +102,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: 'master'
scm:
diff --git a/jjb/releng/testapi-automate.yml b/jjb/releng/testapi-automate.yml
index 2a2f7b6d4..dd76538a3 100644
--- a/jjb/releng/testapi-automate.yml
+++ b/jjb/releng/testapi-automate.yml
@@ -4,23 +4,133 @@
- master:
branch: '{stream}'
gs-pathname: ''
+
+ phase:
+ - 'docker-update'
+ - 'docker-deploy':
+ slave-label: 'testresults'
+ - 'generate-doc'
+
jobs:
- 'testapi-automate-{stream}'
+ - 'testapi-automate-{phase}-{stream}'
+ - 'testapi-verify-{stream}'
+
project: 'releng'
+- job:
+ name: 'testapi-mongodb-backup'
+
+ parameters:
+ - label:
+ name: SLAVE_LABEL
+ default: 'testresults'
+ description: 'Slave label on Jenkins'
+ - project-parameter:
+ project: 'releng'
+ branch: 'master'
+ - string:
+ name: GIT_BASE
+ default: https://gerrit.opnfv.org/gerrit/releng
+ description: 'Git URL to use on this Jenkins Slave'
+
+ scm:
+ - git-scm
+
+ triggers:
+ - timed: '@weekly'
+
+ builders:
+ - mongodb-backup
+
+- job-template:
+ name: 'testapi-verify-{stream}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ file-paths:
+ - compare-type: 'ANT'
+ pattern: 'utils/test/testapi/**'
+
+ builders:
+ - run-unit-tests
+
+ publishers:
+ - junit:
+ results: nosetests.xml
+ - cobertura:
+ report-file: "coverage.xml"
+ only-stable: "true"
+ health-auto-update: "false"
+ stability-auto-update: "false"
+ zoom-coverage-chart: "true"
+ targets:
+ - files:
+ healthy: 10
+ unhealthy: 20
+ failing: 30
+ - method:
+ healthy: 50
+ unhealthy: 40
+ failing: 30
+
- job-template:
name: 'testapi-automate-{stream}'
+ project-type: multijob
+
+ properties:
+ - throttle:
+ enabled: true
+ max-total: 1
+ max-per-node: 1
+ option: 'project'
+
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
+ - string:
+ name: DOCKER_TAG
+ default: "latest"
+ description: "Tag name for testapi docker image"
- 'opnfv-build-defaults'
scm:
- git-scm
+ wrappers:
+ - ssh-agent-wrapper
+ - timeout:
+ timeout: 360
+ fail: true
+
triggers:
- gerrit:
server-name: 'gerrit.opnfv.org'
@@ -36,38 +146,128 @@
branch-pattern: '**/{branch}'
file-paths:
- compare-type: 'ANT'
- pattern: 'utils/**'
+ pattern: 'utils/test/testapi/**'
+
+ builders:
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - multijob:
+ name: docker-update
+ condition: SUCCESSFUL
+ projects:
+ - name: 'testapi-automate-docker-update-{stream}'
+ current-parameters: true
+ kill-phase-on: FAILURE
+ abort-all-job: true
+ - multijob:
+ name: docker-deploy
+ condition: SUCCESSFUL
+ projects:
+ - name: 'testapi-automate-docker-deploy-{stream}'
+ current-parameters: false
+ predefined-parameters: |
+ GIT_BASE=$GIT_BASE
+ node-label-name: SLAVE_LABEL
+ node-label: testresults
+ kill-phase-on: FAILURE
+ abort-all-job: true
+ - multijob:
+ name: generate-doc
+ condition: SUCCESSFUL
+ projects:
+ - name: 'testapi-automate-generate-doc-{stream}'
+ current-parameters: true
+ kill-phase-on: FAILURE
+ abort-all-job: true
+
+ publishers:
+ - 'email-publisher'
+
+- job-template:
+ name: 'testapi-automate-{phase}-{stream}'
+
+ properties:
+ - throttle:
+ enabled: true
+ max-per-node: 1
+ option: 'project'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - string:
+ name: DOCKER_TAG
+ default: "latest"
+ description: "Tag name for testapi docker image"
+
+ wrappers:
+ - ssh-agent-wrapper
+ - timeout:
+ timeout: 120
+ fail: true
+
+ scm:
+ - git-scm
builders:
- - start-testapi-server
- - testapi-doc-build
- - upload-doc-artifact
- - clean-testapi-server
+ - description-setter:
+ description: "Built on $NODE_NAME"
+ - 'testapi-automate-{phase}-macro'
################################
# job builders
################################
+- builder:
+ name: mongodb-backup
+ builders:
+ - shell: |
+ bash ./jjb/releng/testapi-backup-mongodb.sh
- builder:
- name: testapi-doc-build
+ name: 'run-unit-tests'
builders:
- shell: |
- python ./utils/test/testapi/htmlize/htmlize.py -o ${WORKSPACE}/
+ bash ./utils/test/testapi/run_test.sh
- builder:
- name: start-testapi-server
+ name: 'testapi-automate-docker-update-macro'
builders:
- shell: |
- bash ./utils/test/testapi/htmlize/prepare.sh
+ bash ./jjb/releng/testapi-docker-update.sh
- builder:
- name: clean-testapi-server
+ name: 'testapi-automate-generate-doc-macro'
+ builders:
+ - 'testapi-doc-build'
+ - 'upload-doc-artifact'
+
+- builder:
+ name: 'testapi-doc-build'
builders:
- shell: |
- bash ./utils/test/testapi/htmlize/finish.sh
+ bash ./utils/test/testapi/htmlize/doc-build.sh
- builder:
- name: upload-doc-artifact
+ name: 'upload-doc-artifact'
builders:
- shell: |
bash ./utils/test/testapi/htmlize/push-doc-artifact.sh
+
+- builder:
+ name: 'testapi-automate-docker-deploy-macro'
+ builders:
+ - shell: |
+ bash ./jjb/releng/testapi-docker-deploy.sh
+
+################################
+# job publishers
+################################
+
+- publisher:
+ name: 'email-publisher'
+ publishers:
+ - email:
+ recipients: rohitsakala@gmail.com feng.xiaowei@zte.com.cn
+ notify-every-unstable-build: false
+ send-to-individuals: true
diff --git a/jjb/releng/testapi-backup-mongodb.sh b/jjb/releng/testapi-backup-mongodb.sh
new file mode 100644
index 000000000..795e479d9
--- /dev/null
+++ b/jjb/releng/testapi-backup-mongodb.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -e
+
+# Run MongoDB backup
+python $WORKSPACE/utils/test/testapi/update/templates/backup_mongodb.py -o $WORKSPACE/
+
+# Compressing the dump
+now=$(date +"%m_%d_%Y_%H_%M_%S")
+echo $now
+
+file_name="testapi_mongodb_"$now".tar.gz"
+echo $file_name
+
+tar cvfz "$file_name" test_results_collection*
+
+rm -rf test_results_collection*
+
+artifact_dir="testapibackup"
+workspace="$WORKSPACE"
+
+set +e
+/usr/local/bin/gsutil &>/dev/null
+if [ $? != 0 ]; then
+ echo "Not possible to push results to artifact: gsutil not installed"
+ exit 1
+else
+ echo "Uploading mongodump to artifact $artifact_dir"
+ /usr/local/bin/gsutil cp -r "$workspace"/"$file_name" gs://artifacts.opnfv.org/"$artifact_dir"/
+ echo "MongoDump can be found at http://artifacts.opnfv.org/$artifact_dir.html"
+fi
diff --git a/jjb/releng/testapi-docker-deploy.sh b/jjb/releng/testapi-docker-deploy.sh
new file mode 100644
index 000000000..b4e60b09a
--- /dev/null
+++ b/jjb/releng/testapi-docker-deploy.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+function check() {
+
+ # Verify hosted
+ sleep 5
+ cmd=`curl -s --head --request GET http://testresults.opnfv.org/test/swagger/spec | grep '200 OK' > /dev/null`
+ rc=$?
+ echo $rc
+
+ if [[ $rc == 0 ]]
+ then
+ return 0
+ else
+ return 1
+ fi
+
+}
+
+echo "Getting contianer Id of the currently running one"
+contId=$(sudo docker ps | grep "opnfv/testapi:latest" | awk '{print $1}')
+
+echo "Pulling the latest image"
+sudo docker pull opnfv/testapi:latest
+
+echo "Deleting old containers of opnfv/testapi:old"
+sudo docker ps -a | grep "opnfv/testapi" | grep "old" | awk '{print $1}' | xargs -r sudo docker rm -f
+
+echo "Deleting old images of opnfv/testapi:latest"
+sudo docker images | grep "opnfv/testapi" | grep "old" | awk '{print $3}' | xargs -r sudo docker rmi -f
+
+
+if [[ -z "$contId" ]]
+then
+ echo "No running testapi container"
+
+ echo "Removing stopped testapi containers in the previous iterations"
+ sudo docker ps -f status=exited | grep "opnfv_testapi" | awk '{print $1}' | xargs -r sudo docker rm -f
+else
+ echo $contId
+
+ echo "Get the image id of the currently running conatiner"
+ currImgId=$(sudo docker ps | grep "$contId" | awk '{print $2}')
+ echo $currImgId
+
+ if [[ -z "$currImgId" ]]
+ then
+ echo "No image id found for the container id"
+ exit 1
+ fi
+
+ echo "Changing current image tag to old"
+ sudo docker tag "$currImgId" opnfv/testapi:old
+
+ echo "Removing stopped testapi containers in the previous iteration"
+ sudo docker ps -f status=exited | grep "opnfv_testapi" | awk '{print $1}' | xargs -r sudo docker rm -f
+
+ echo "Renaming the running container name to opnfv_testapi as to identify it."
+ sudo docker rename $contId opnfv_testapi
+
+ echo "Stop the currently running container"
+ sudo docker stop $contId
+fi
+
+echo "Running a container with the new image"
+sudo docker run -dti -p "8082:8000" -e "mongodb_url=mongodb://172.17.0.1:27017" -e "swagger_url=http://testresults.opnfv.org/test" opnfv/testapi:latest
+
+if check; then
+ echo "TestResults Hosted."
+else
+ echo "TestResults Hosting Failed"
+ if [[ $(sudo docker images | grep "opnfv/testapi" | grep "old" | awk '{print $3}') ]]; then
+ echo "Running old Image"
+ sudo docker run -dti -p "8082:8000" -e "mongodb_url=mongodb://172.17.0.1:27017" -e "swagger_url=http://testresults.opnfv.org/test" opnfv/testapi:old
+ exit 1
+ fi
+fi
+
+# Echo Images and Containers
+sudo docker images
+sudo docker ps -a
diff --git a/jjb/releng/testapi-docker-update.sh b/jjb/releng/testapi-docker-update.sh
new file mode 100644
index 000000000..84f5c3217
--- /dev/null
+++ b/jjb/releng/testapi-docker-update.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -o errexit
+set -o nounset
+
+cd $WORKSPACE/utils/test/testapi/docker/
+
+# Remove previous containers
+docker ps -a | grep "opnfv/testapi" | awk '{ print $1 }' | xargs -r docker rm -f
+
+# Remove previous images
+docker images | grep "opnfv/testapi" | awk '{ print $3 }' | xargs -r docker rmi -f
+
+# Start build
+docker build --no-cache -t opnfv/testapi:$DOCKER_TAG .
+
+# Push Image
+docker push opnfv/testapi:$DOCKER_TAG
diff --git a/jjb/securityaudit/opnfv-security-audit.yml b/jjb/securityaudit/opnfv-security-audit.yml
index 680be20d2..732df8925 100644
--- a/jjb/securityaudit/opnfv-security-audit.yml
+++ b/jjb/securityaudit/opnfv-security-audit.yml
@@ -27,7 +27,6 @@
parameters:
- project-parameter:
project: $GERRIT_PROJECT
- - gerrit-parameter:
branch: '{branch}'
scm:
diff --git a/jjb/snaps/snaps.yml b/jjb/snaps/snaps.yml
new file mode 100644
index 000000000..32680f54e
--- /dev/null
+++ b/jjb/snaps/snaps.yml
@@ -0,0 +1,62 @@
+###################################################
+# All the jobs except verify have been removed!
+# They will only be enabled on request by projects!
+###################################################
+- project:
+ name: snaps
+
+ project: '{name}'
+
+ jobs:
+ - 'snaps-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: true
+
+- job-template:
+ name: 'snaps-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**|.gitignore'
+
+ builders:
+ - shell: |
+ echo "Nothing to verify!"
diff --git a/jjb/storperf/storperf.yml b/jjb/storperf/storperf.yml
index 95d464c9b..a04a9f4b4 100644
--- a/jjb/storperf/storperf.yml
+++ b/jjb/storperf/storperf.yml
@@ -28,7 +28,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- string:
name: GIT_BASE
@@ -89,8 +88,7 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
- branch: 'master'
+ branch: '{branch}'
- string:
name: GIT_BASE
default: https://gerrit.opnfv.org/gerrit/$PROJECT
@@ -144,15 +142,13 @@
# Required Variables:
# stream: branch with - in place of / (eg. stable)
# branch: branch (eg. stable)
- node: opnfv-build-ubuntu
-
- disabled: true
+ disabled: '{obj:disabled}'
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
+ - 'intel-pod9-defaults'
scm:
- git-scm
diff --git a/jjb/ves/ves.yml b/jjb/ves/ves.yml
new file mode 100644
index 000000000..3d3ba2ca8
--- /dev/null
+++ b/jjb/ves/ves.yml
@@ -0,0 +1,68 @@
+###################################################
+# All the jobs except verify have been removed!
+# They will only be enabled on request by projects!
+###################################################
+- project:
+ name: ves
+
+ project: '{name}'
+
+ jobs:
+ - 'ves-verify-{stream}'
+
+ stream:
+ - master:
+ branch: '{stream}'
+ gs-pathname: ''
+ disabled: false
+ - danube:
+ branch: 'stable/{stream}'
+ gs-pathname: '/{stream}'
+ disabled: false
+
+- job-template:
+ name: 'ves-verify-{stream}'
+
+ disabled: '{obj:disabled}'
+
+ parameters:
+ - project-parameter:
+ project: '{project}'
+ branch: '{branch}'
+ - 'opnfv-build-ubuntu-defaults'
+
+ scm:
+ - git-scm-gerrit
+
+ triggers:
+ - gerrit:
+ server-name: 'gerrit.opnfv.org'
+ trigger-on:
+ - patchset-created-event:
+ exclude-drafts: 'false'
+ exclude-trivial-rebase: 'false'
+ exclude-no-code-change: 'false'
+ - draft-published-event
+ - comment-added-contains-event:
+ comment-contains-value: 'recheck'
+ - comment-added-contains-event:
+ comment-contains-value: 'reverify'
+ projects:
+ - project-compare-type: 'ANT'
+ project-pattern: '{project}'
+ branches:
+ - branch-compare-type: 'ANT'
+ branch-pattern: '**/{branch}'
+ forbidden-file-paths:
+ - compare-type: ANT
+ pattern: 'docs/**|.gitignore'
+
+ builders:
+ - shell: |
+ #!/bin/bash
+ set -o errexit
+ set -o nounset
+ set -o pipefail
+
+ # shellcheck -f tty tests/*.sh
+ # shellcheck -f tty utils/*.sh
diff --git a/jjb/vnf_forwarding_graph/vnf_forwarding_graph.yml b/jjb/vnf_forwarding_graph/vnf_forwarding_graph.yml
index c129f4937..450599eaf 100644
--- a/jjb/vnf_forwarding_graph/vnf_forwarding_graph.yml
+++ b/jjb/vnf_forwarding_graph/vnf_forwarding_graph.yml
@@ -24,7 +24,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
scm:
- git-scm-gerrit
diff --git a/jjb/vswitchperf/vswitchperf.yml b/jjb/vswitchperf/vswitchperf.yml
index 4cfe5d98f..ef0e90a76 100644
--- a/jjb/vswitchperf/vswitchperf.yml
+++ b/jjb/vswitchperf/vswitchperf.yml
@@ -19,7 +19,7 @@
branch: 'stable/{stream}'
gs-pathname: '/{stream}'
disabled: true
- slave-label: 'intel-pod3'
+ slave-label: 'intel-pod12'
- job-template:
@@ -30,9 +30,8 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- - 'intel-pod3-defaults'
+ - 'intel-pod12-defaults'
scm:
- git-scm
@@ -62,6 +61,7 @@
concurrent: true
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
blocking-jobs:
@@ -72,7 +72,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
@@ -123,6 +122,7 @@
concurrent: true
properties:
+ - logrotate-default
- build-blocker:
use-build-blocker: true
blocking-jobs:
@@ -133,7 +133,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{slave-label}-defaults'
diff --git a/jjb/yardstick/yardstick-ci-jobs.yml b/jjb/yardstick/yardstick-ci-jobs.yml
index ed2dccab8..1f2f3122c 100644
--- a/jjb/yardstick/yardstick-ci-jobs.yml
+++ b/jjb/yardstick/yardstick-ci-jobs.yml
@@ -197,8 +197,8 @@
installer: compass
auto-trigger-name: 'yardstick-daily-huawei-pod4-trigger'
<<: *master
- - huawei-pod5:
- slave-label: '{pod}'
+ - baremetal-centos:
+ slave-label: 'intel-pod8'
installer: compass
auto-trigger-name: 'daily-trigger-disabled'
<<: *master
@@ -220,6 +220,7 @@
concurrent: true
properties:
+ - logrotate-default
- throttle:
enabled: true
max-per-node: 1
@@ -238,7 +239,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- '{installer}-defaults'
- '{slave-label}-defaults'
@@ -272,7 +272,7 @@
publishers:
- email:
- recipients: jean.gaoliang@huawei.com matthew.lijun@huawei.com
+ recipients: jean.gaoliang@huawei.com limingjiang@huawei.com
########################
# builder macros
@@ -381,15 +381,6 @@
name: YARDSTICK_DB_BACKEND
default: '-i 104.197.68.199:8086'
description: 'Arguments to use in order to choose the backend DB'
-
-- parameter:
- name: 'yardstick-params-huawei-pod5'
- parameters:
- - string:
- name: YARDSTICK_DB_BACKEND
- default: '-i 104.197.68.199:8086'
- description: 'Arguments to use in order to choose the backend DB'
-
- parameter:
name: 'yardstick-params-zte-pod1'
parameters:
diff --git a/jjb/yardstick/yardstick-daily.sh b/jjb/yardstick/yardstick-daily.sh
index da9042bbc..f769e9cdd 100755
--- a/jjb/yardstick/yardstick-daily.sh
+++ b/jjb/yardstick/yardstick-daily.sh
@@ -31,14 +31,14 @@ fi
opts="--privileged=true --rm"
envs="-e INSTALLER_TYPE=${INSTALLER_TYPE} -e INSTALLER_IP=${INSTALLER_IP} \
-e NODE_NAME=${NODE_NAME} -e EXTERNAL_NETWORK=${EXTERNAL_NETWORK} \
- -e YARDSTICK_BRANCH=${GIT_BRANCH##origin/} -e DEPLOY_SCENARIO=${DEPLOY_SCENARIO}"
+ -e YARDSTICK_BRANCH=${BRANCH} -e DEPLOY_SCENARIO=${DEPLOY_SCENARIO}"
# Pull the image with correct tag
echo "Yardstick: Pulling image opnfv/yardstick:${DOCKER_TAG}"
docker pull opnfv/yardstick:$DOCKER_TAG >$redirect
# map log directory
-branch=${GIT_BRANCH##*/}
+branch=${BRANCH##*/}
dir_result="${HOME}/opnfv/yardstick/results/${branch}"
mkdir -p ${dir_result}
sudo rm -rf ${dir_result}/*
diff --git a/jjb/yardstick/yardstick-project-jobs.yml b/jjb/yardstick/yardstick-project-jobs.yml
index a54750ef7..bbfa152a2 100644
--- a/jjb/yardstick/yardstick-project-jobs.yml
+++ b/jjb/yardstick/yardstick-project-jobs.yml
@@ -33,7 +33,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
@@ -70,7 +69,6 @@
parameters:
- project-parameter:
project: '{project}'
- - gerrit-parameter:
branch: '{branch}'
- 'opnfv-build-ubuntu-defaults'
- string:
@@ -110,18 +108,8 @@
set -o errexit
set -o pipefail
+ sudo apt-get install -y build-essential python-dev python3-dev
+
echo "Running unit tests..."
cd $WORKSPACE
- virtualenv $WORKSPACE/yardstick_venv
- source $WORKSPACE/yardstick_venv/bin/activate
-
- # install python packages
- easy_install -U setuptools
- easy_install -U pip
- pip install -r requirements.txt || pip install -r tests/ci/requirements.txt
- pip install -e .
-
- # unit tests
- ./run_tests.sh
-
- deactivate
+ tox
diff --git a/modules/opnfv/installer_adapters/__init__.py b/modules/opnfv/deployment/__init__.py
index e69de29bb..e69de29bb 100644
--- a/modules/opnfv/installer_adapters/__init__.py
+++ b/modules/opnfv/deployment/__init__.py
diff --git a/modules/opnfv/installer_adapters/apex/__init__.py b/modules/opnfv/deployment/apex/__init__.py
index e69de29bb..e69de29bb 100644
--- a/modules/opnfv/installer_adapters/apex/__init__.py
+++ b/modules/opnfv/deployment/apex/__init__.py
diff --git a/modules/opnfv/deployment/apex/adapter.py b/modules/opnfv/deployment/apex/adapter.py
new file mode 100644
index 000000000..225e17438
--- /dev/null
+++ b/modules/opnfv/deployment/apex/adapter.py
@@ -0,0 +1,100 @@
+##############################################################################
+# Copyright (c) 2017 Ericsson AB and others.
+# Author: Jose Lausuch (jose.lausuch@ericsson.com)
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import re
+
+from opnfv.deployment import manager
+from opnfv.utils import opnfv_logger as logger
+from opnfv.utils import ssh_utils
+
+logger = logger.Logger(__name__).getLogger()
+
+
+class ApexAdapter(manager.DeploymentHandler):
+
+ def __init__(self, installer_ip, installer_user, pkey_file):
+ super(ApexAdapter, self).__init__(installer='apex',
+ installer_ip=installer_ip,
+ installer_user=installer_user,
+ installer_pwd=None,
+ pkey_file=pkey_file)
+
+ def get_nodes(self):
+ nodes = []
+ cmd = "source /home/stack/stackrc;openstack server list"
+ output = self.installer_node.run_cmd(cmd)
+ lines = output.rsplit('\n')
+ if len(lines) < 4:
+ logger.info("No nodes found in the deployment.")
+ return None
+
+ for line in lines:
+ roles = []
+ if any(x in line for x in ['-----', 'Networks']):
+ continue
+ if 'controller' in line:
+ roles.append(manager.Role.CONTROLLER)
+ if 'compute' in line:
+ roles.append(manager.Role.COMPUTE)
+ if 'opendaylight' in line.lower():
+ roles.append(manager.Role.ODL)
+
+ fields = line.split('|')
+ id = re.sub('[!| ]', '', fields[1]).encode()
+ name = re.sub('[!| ]', '', fields[2]).encode()
+ status_node = re.sub('[!| ]', '', fields[3]).encode().lower()
+ ip = re.sub('[!| ctlplane=]', '', fields[4]).encode()
+
+ ssh_client = None
+ if 'active' in status_node:
+ status = manager.NodeStatus.STATUS_OK
+ ssh_client = ssh_utils.get_ssh_client(hostname=ip,
+ username='heat-admin',
+ pkey_file=self.pkey_file)
+ elif 'error' in status_node:
+ status = manager.NodeStatus.STATUS_ERROR
+ elif 'off' in status_node:
+ status = manager.NodeStatus.STATUS_OFFLINE
+ else:
+ status = manager.NodeStatus.STATUS_INACTIVE
+
+ node = manager.Node(id, ip, name, status, roles, ssh_client)
+ nodes.append(node)
+
+ return nodes
+
+ def get_openstack_version(self):
+ cmd = 'source overcloudrc;sudo nova-manage version'
+ result = self.installer_node.run_cmd(cmd)
+ return result
+
+ def get_sdn_version(self):
+ cmd_descr = ("sudo yum info opendaylight 2>/dev/null|"
+ "grep Description|sed 's/^.*\: //'")
+ cmd_ver = ("sudo yum info opendaylight 2>/dev/null|"
+ "grep Version|sed 's/^.*\: //'")
+ description = None
+ for node in self.nodes:
+ if node.is_controller():
+ description = node.run_cmd(cmd_descr)
+ version = node.run_cmd(cmd_ver)
+ break
+
+ if description is None:
+ return None
+ else:
+ return description + ':' + version
+
+ def get_deployment_status(self):
+ cmd = 'source stackrc;openstack stack list|grep CREATE_COMPLETE'
+ result = self.installer_node.run_cmd(cmd)
+ if result is None or len(result) == 0:
+ return 'failed'
+ else:
+ return 'active'
diff --git a/modules/opnfv/deployment/example.py b/modules/opnfv/deployment/example.py
new file mode 100644
index 000000000..3999a11c6
--- /dev/null
+++ b/modules/opnfv/deployment/example.py
@@ -0,0 +1,36 @@
+# This is an example of usage of this Tool
+# Author: Jose Lausuch (jose.lausuch@ericsson.com)
+
+from opnfv.deployment import factory
+
+print("########## APEX ##########")
+handler = factory.Factory.get_handler('apex',
+ '192.168.122.135',
+ 'stack',
+ pkey_file='/root/.ssh/id_rsa')
+
+
+installer_node = handler.get_installer_node()
+print("Hello, I am node '%s'" % installer_node.run_cmd('hostname'))
+installer_node.get_file('/home/stack/overcloudrc', './overcloudrc')
+
+nodes = handler.get_nodes()
+for node in nodes:
+ print("Hello, I am node '%s' and my ip is %s." %
+ (node.run_cmd('hostname'), node.ip))
+
+print(handler.get_deployment_info())
+
+
+print("########## FUEL ##########")
+handler = factory.Factory.get_handler('fuel',
+ '10.20.0.2',
+ 'root',
+ installer_pwd='r00tme')
+
+print(handler.get_deployment_info())
+
+print("List of nodes in cluster 4:")
+nodes = handler.get_nodes({'cluster': '4'})
+for node in nodes:
+ print(node)
diff --git a/modules/opnfv/deployment/factory.py b/modules/opnfv/deployment/factory.py
new file mode 100644
index 000000000..1ccee4e80
--- /dev/null
+++ b/modules/opnfv/deployment/factory.py
@@ -0,0 +1,45 @@
+##############################################################################
+# Copyright (c) 2017 Ericsson AB and others.
+# Author: Jose Lausuch (jose.lausuch@ericsson.com)
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from opnfv.deployment.apex import adapter as apex_adapter
+from opnfv.deployment.fuel import adapter as fuel_adapter
+from opnfv.utils import opnfv_logger as logger
+
+logger = logger.Logger(__name__).getLogger()
+
+
+class Factory(object):
+
+ INSTALLERS = ["fuel", "apex", "compass", "joid", "daisy"]
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def get_handler(installer,
+ installer_ip,
+ installer_user,
+ installer_pwd=None,
+ pkey_file=None):
+
+ if installer not in Factory.INSTALLERS:
+ raise Exception("This is not an OPNFV installer.")
+
+ if installer.lower() == "apex":
+ return apex_adapter.ApexAdapter(installer_ip=installer_ip,
+ installer_user=installer_user,
+ pkey_file=pkey_file)
+ elif installer.lower() == "fuel":
+ return fuel_adapter.FuelAdapter(installer_ip=installer_ip,
+ installer_user=installer_user,
+ installer_pwd=installer_pwd)
+ else:
+ raise Exception("Installer adapter is not implemented for "
+ "the given installer.")
diff --git a/modules/opnfv/installer_adapters/compass/__init__.py b/modules/opnfv/deployment/fuel/__init__.py
index e69de29bb..e69de29bb 100644
--- a/modules/opnfv/installer_adapters/compass/__init__.py
+++ b/modules/opnfv/deployment/fuel/__init__.py
diff --git a/modules/opnfv/deployment/fuel/adapter.py b/modules/opnfv/deployment/fuel/adapter.py
new file mode 100644
index 000000000..a217767ba
--- /dev/null
+++ b/modules/opnfv/deployment/fuel/adapter.py
@@ -0,0 +1,199 @@
+##############################################################################
+# Copyright (c) 2017 Ericsson AB and others.
+# Author: Jose Lausuch (jose.lausuch@ericsson.com)
+# George Paraskevopoulos (geopar@intracom-telecom.com)
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+from opnfv.deployment import manager
+from opnfv.utils import opnfv_logger as logger
+from opnfv.utils import ssh_utils
+
+logger = logger.Logger(__name__).getLogger()
+
+
+class FuelAdapter(manager.DeploymentHandler):
+
+ def __init__(self, installer_ip, installer_user, installer_pwd):
+ super(FuelAdapter, self).__init__(installer='fuel',
+ installer_ip=installer_ip,
+ installer_user=installer_user,
+ installer_pwd=installer_pwd,
+ pkey_file=None)
+
+ def _get_clusters(self):
+ environments = []
+ output = self.runcmd_fuel_env()
+ lines = output.rsplit('\n')
+ if len(lines) < 2:
+ logger.info("No environments found in the deployment.")
+ return None
+ else:
+ fields = lines[0].rsplit(' | ')
+
+ index_id = -1
+ index_status = -1
+ index_name = -1
+ index_release_id = -1
+
+ for i in range(len(fields)):
+ if "id" in fields[i]:
+ index_id = i
+ elif "status" in fields[i]:
+ index_status = i
+ elif "name" in fields[i]:
+ index_name = i
+ elif "release_id" in fields[i]:
+ index_release_id = i
+
+ # order env info
+ for i in range(2, len(lines)):
+ fields = lines[i].rsplit(' | ')
+ dict = {"id": fields[index_id].strip(),
+ "status": fields[index_status].strip(),
+ "name": fields[index_name].strip(),
+ "release_id": fields[index_release_id].strip()}
+ environments.append(dict)
+
+ return environments
+
+ def get_nodes(self, options=None):
+
+ if options and options['cluster'] and len(self.nodes) > 0:
+ n = []
+ for node in self.nodes:
+ if str(node.info['cluster']) == str(options['cluster']):
+ n.append(node)
+ return n
+
+ try:
+ # if we have retrieved previously all the nodes, don't do it again
+ # This fails the first time when the constructor calls this method
+ # therefore the try/except
+ if len(self.nodes) > 0:
+ return self.nodes
+ except:
+ pass
+
+ nodes = []
+ cmd = 'fuel node'
+ output = self.installer_node.run_cmd(cmd)
+ lines = output.rsplit('\n')
+ if len(lines) < 2:
+ logger.info("No nodes found in the deployment.")
+ return nodes
+
+ # get fields indexes
+ fields = lines[0].rsplit(' | ')
+
+ index_id = -1
+ index_status = -1
+ index_name = -1
+ index_cluster = -1
+ index_ip = -1
+ index_mac = -1
+ index_roles = -1
+ index_online = -1
+
+ for i in range(len(fields)):
+ if "group_id" in fields[i]:
+ break
+ elif "id" in fields[i]:
+ index_id = i
+ elif "status" in fields[i]:
+ index_status = i
+ elif "name" in fields[i]:
+ index_name = i
+ elif "cluster" in fields[i]:
+ index_cluster = i
+ elif "ip" in fields[i]:
+ index_ip = i
+ elif "mac" in fields[i]:
+ index_mac = i
+ elif "roles " in fields[i] and "pending_roles" not in fields[i]:
+ index_roles = i
+ elif "online" in fields[i]:
+ index_online = i
+
+ # order nodes info
+ for i in range(2, len(lines)):
+ fields = lines[i].rsplit(' | ')
+ id = fields[index_id].strip().encode()
+ ip = fields[index_ip].strip().encode()
+ status_node = fields[index_status].strip().encode().lower()
+ name = fields[index_name].strip().encode()
+ roles_all = fields[index_roles].strip().encode().lower()
+
+ roles = [x for x in [manager.Role.CONTROLLER,
+ manager.Role.COMPUTE,
+ manager.Role.ODL] if x in roles_all]
+
+ dict = {"cluster": fields[index_cluster].strip().encode(),
+ "mac": fields[index_mac].strip().encode(),
+ "status_node": status_node,
+ "online": fields[index_online].strip().encode()}
+
+ ssh_client = None
+ if status_node == 'ready':
+ status = manager.NodeStatus.STATUS_OK
+ proxy = {'ip': self.installer_ip,
+ 'username': self.installer_user,
+ 'password': self.installer_pwd}
+ ssh_client = ssh_utils.get_ssh_client(hostname=ip,
+ username='root',
+ proxy=proxy)
+ elif 'error' in status_node:
+ status = manager.NodeStatus.STATUS_ERROR
+ elif 'off' in status_node:
+ status = manager.NodeStatus.STATUS_OFFLINE
+ elif 'discover' in status_node:
+ status = manager.NodeStatus.STATUS_UNUSED
+ else:
+ status = manager.NodeStatus.STATUS_INACTIVE
+
+ node = manager.Node(
+ id, ip, name, status, roles, ssh_client, dict)
+ if options and options['cluster']:
+ if fields[index_cluster].strip() == options['cluster']:
+ nodes.append(node)
+ else:
+ nodes.append(node)
+
+ self.get_nodes_called = True
+ return nodes
+
+ def get_openstack_version(self):
+ cmd = 'source openrc;nova-manage version 2>/dev/null'
+ version = None
+ for node in self.nodes:
+ if node.is_controller() and node.is_active():
+ version = node.run_cmd(cmd)
+ break
+ return version
+
+ def get_sdn_version(self):
+ cmd = "apt-cache policy opendaylight|grep Installed"
+ version = None
+ for node in self.nodes:
+ if manager.Role.ODL in node.roles and node.is_active():
+ odl_version = node.run_cmd(cmd)
+ if odl_version:
+ version = 'OpenDaylight ' + odl_version.split(' ')[-1]
+ break
+ return version
+
+ def get_deployment_status(self):
+ cmd = "fuel env|tail -1|awk '{print $3}'"
+ result = self.installer_node.run_cmd(cmd)
+ if result is None or len(result) == 0:
+ return 'unknown'
+ elif 'operational' in result:
+ return 'active'
+ elif 'deploy' in result:
+ return 'deploying'
+ else:
+ return 'active'
diff --git a/modules/opnfv/deployment/manager.py b/modules/opnfv/deployment/manager.py
new file mode 100644
index 000000000..df735f157
--- /dev/null
+++ b/modules/opnfv/deployment/manager.py
@@ -0,0 +1,386 @@
+##############################################################################
+# Copyright (c) 2017 Ericsson AB and others.
+# Author: Jose Lausuch (jose.lausuch@ericsson.com)
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+from abc import abstractmethod
+import os
+
+
+from opnfv.utils import opnfv_logger as logger
+from opnfv.utils import ssh_utils
+
+logger = logger.Logger(__name__).getLogger()
+
+
+class Deployment(object):
+
+ def __init__(self,
+ installer,
+ installer_ip,
+ scenario,
+ pod,
+ status,
+ openstack_version,
+ sdn_controller,
+ nodes=None):
+
+ self.deployment_info = {
+ 'installer': installer,
+ 'installer_ip': installer_ip,
+ 'scenario': scenario,
+ 'pod': pod,
+ 'status': status,
+ 'openstack_version': openstack_version,
+ 'sdn_controller': sdn_controller,
+ 'nodes': nodes
+ }
+
+ def _get_openstack_release(self):
+ '''
+ Translates an openstack version into the release name
+ '''
+ os_versions = {
+ '12': 'Liberty',
+ '13': 'Mitaka',
+ '14': 'Newton',
+ '15': 'Ocata',
+ '16': 'Pike',
+ '17': 'Queens'
+ }
+ try:
+ version = self.deployment_info['openstack_version'].split('.')[0]
+ name = os_versions[version]
+ return name
+ except Exception:
+ return 'Unknown release'
+
+ def get_dict(self):
+ '''
+ Returns a dictionary will all the attributes
+ '''
+ return self.deployment_info
+
+ def __str__(self):
+ '''
+ Override of the str method
+ '''
+ s = '''
+ INSTALLER: {installer}
+ SCENARIO: {scenario}
+ INSTALLER IP: {installer_ip}
+ POD: {pod}
+ STATUS: {status}
+ OPENSTACK: {openstack_version} ({openstack_release})
+ SDN: {sdn_controller}
+ NODES:
+ '''.format(installer=self.deployment_info['installer'],
+ scenario=self.deployment_info['scenario'],
+ installer_ip=self.deployment_info['installer_ip'],
+ pod=self.deployment_info['pod'],
+ status=self.deployment_info['status'],
+ openstack_version=self.deployment_info[
+ 'openstack_version'],
+ openstack_release=self._get_openstack_release(),
+ sdn_controller=self.deployment_info['sdn_controller'])
+
+ for node in self.deployment_info['nodes']:
+ s += '{node_object}\n'.format(node_object=node)
+
+ return s
+
+
+class Role():
+ INSTALLER = 'installer'
+ CONTROLLER = 'controller'
+ COMPUTE = 'compute'
+ ODL = 'opendaylight'
+ ONOS = 'onos'
+
+
+class NodeStatus():
+ STATUS_OK = 'active'
+ STATUS_INACTIVE = 'inactive'
+ STATUS_OFFLINE = 'offline'
+ STATUS_ERROR = 'error'
+ STATUS_UNUSED = 'unused'
+
+
+class Node(object):
+
+ def __init__(self,
+ id,
+ ip,
+ name,
+ status,
+ roles=None,
+ ssh_client=None,
+ info=None):
+ self.id = id
+ self.ip = ip
+ self.name = name
+ self.status = status
+ self.ssh_client = ssh_client
+ self.roles = roles
+ self.info = info
+
+ self.cpu_info = 'unknown'
+ self.memory = 'unknown'
+ self.ovs = 'unknown'
+
+ if ssh_client and Role.INSTALLER not in self.roles:
+ sys_info = self.get_system_info()
+ self.cpu_info = sys_info['cpu_info']
+ self.memory = sys_info['memory']
+ self.ovs = self.get_ovs_info()
+
+ def get_file(self, src, dest):
+ '''
+ SCP file from a node
+ '''
+ if self.status is not NodeStatus.STATUS_OK:
+ logger.info("The node %s is not active" % self.ip)
+ return 1
+ logger.info("Fetching %s from %s" % (src, self.ip))
+ get_file_result = ssh_utils.get_file(self.ssh_client, src, dest)
+ if get_file_result is None:
+ logger.error("SFTP failed to retrieve the file.")
+ else:
+ logger.info("Successfully copied %s:%s to %s" %
+ (self.ip, src, dest))
+ return get_file_result
+
+ def put_file(self, src, dest):
+ '''
+ SCP file to a node
+ '''
+ if self.status is not NodeStatus.STATUS_OK:
+ logger.info("The node %s is not active" % self.ip)
+ return 1
+ logger.info("Copying %s to %s" % (src, self.ip))
+ put_file_result = ssh_utils.put_file(self.ssh_client, src, dest)
+ if put_file_result is None:
+ logger.error("SFTP failed to retrieve the file.")
+ else:
+ logger.info("Successfully copied %s to %s:%s" %
+ (src, dest, self.ip))
+ return put_file_result
+
+ def run_cmd(self, cmd):
+ '''
+ Run command remotely on a node
+ '''
+ if self.status is not NodeStatus.STATUS_OK:
+ logger.error(
+ "Error running command %s. The node %s is not active"
+ % (cmd, self.ip))
+ return None
+ _, stdout, stderr = (self.ssh_client.exec_command(cmd))
+ error = stderr.readlines()
+ if len(error) > 0:
+ logger.error("error %s" % ''.join(error))
+ return None
+ output = ''.join(stdout.readlines()).rstrip()
+ return output
+
+ def get_dict(self):
+ '''
+ Returns a dictionary with all the attributes
+ '''
+ return {
+ 'id': self.id,
+ 'ip': self.ip,
+ 'name': self.name,
+ 'status': self.status,
+ 'roles': self.roles,
+ 'cpu_info': self.cpu_info,
+ 'memory': self.memory,
+ 'ovs': self.ovs,
+ 'info': self.info
+ }
+
+ def is_active(self):
+ '''
+ Returns if the node is active
+ '''
+ if self.status == NodeStatus.STATUS_OK:
+ return True
+ return False
+
+ def is_controller(self):
+ '''
+ Returns if the node is a controller
+ '''
+ return Role.CONTROLLER in self.roles
+
+ def is_compute(self):
+ '''
+ Returns if the node is a compute
+ '''
+ return Role.COMPUTE in self.roles
+
+ def is_odl(self):
+ '''
+ Returns if the node is an opendaylight
+ '''
+ return Role.ODL in self.roles
+
+ def get_ovs_info(self):
+ '''
+ Returns the ovs version installed
+ '''
+ if self.is_active():
+ cmd = "ovs-vsctl --version|head -1| sed 's/^.*) //'"
+ return self.run_cmd(cmd)
+ return None
+
+ def get_system_info(self):
+ '''
+ Returns the ovs version installed
+ '''
+ cmd = 'grep MemTotal /proc/meminfo'
+ memory = self.run_cmd(cmd).partition('MemTotal:')[-1].strip().encode()
+
+ cpu_info = {}
+ cmd = 'lscpu'
+ result = self.run_cmd(cmd)
+ for line in result.splitlines():
+ if line.startswith('CPU(s)'):
+ cpu_info['num_cpus'] = line.split(' ')[-1].encode()
+ elif line.startswith('Thread(s) per core'):
+ cpu_info['threads/core'] = line.split(' ')[-1].encode()
+ elif line.startswith('Core(s) per socket'):
+ cpu_info['cores/socket'] = line.split(' ')[-1].encode()
+ elif line.startswith('Model name'):
+ cpu_info['model'] = line.partition(
+ 'Model name:')[-1].strip().encode()
+ elif line.startswith('Architecture'):
+ cpu_info['arch'] = line.split(' ')[-1].encode()
+
+ return {'memory': memory, 'cpu_info': cpu_info}
+
+ def __str__(self):
+ return '''
+ name: {name}
+ id: {id}
+ ip: {ip}
+ status: {status}
+ roles: {roles}
+ cpu: {cpu_info}
+ memory: {memory}
+ ovs: {ovs}
+ info: {info}'''.format(name=self.name,
+ id=self.id,
+ ip=self.ip,
+ status=self.status,
+ roles=self.roles,
+ cpu_info=self.cpu_info,
+ memory=self.memory,
+ ovs=self.ovs,
+ info=self.info)
+
+
+class DeploymentHandler(object):
+
+ EX_OK = os.EX_OK
+ EX_ERROR = os.EX_SOFTWARE
+ FUNCTION_NOT_IMPLEMENTED = "Function not implemented by adapter!"
+
+ def __init__(self,
+ installer,
+ installer_ip,
+ installer_user,
+ installer_pwd=None,
+ pkey_file=None):
+
+ self.installer = installer.lower()
+ self.installer_ip = installer_ip
+ self.installer_user = installer_user
+ self.installer_pwd = installer_pwd
+ self.pkey_file = pkey_file
+
+ if pkey_file is not None and not os.path.isfile(pkey_file):
+ raise Exception(
+ 'The private key file %s does not exist!' % pkey_file)
+
+ self.installer_connection = ssh_utils.get_ssh_client(
+ hostname=self.installer_ip,
+ username=self.installer_user,
+ password=self.installer_pwd,
+ pkey_file=self.pkey_file)
+
+ if self.installer_connection:
+ self.installer_node = Node(id='',
+ ip=installer_ip,
+ name=installer,
+ status=NodeStatus.STATUS_OK,
+ ssh_client=self.installer_connection,
+ roles=Role.INSTALLER)
+ else:
+ raise Exception(
+ 'Cannot establish connection to the installer node!')
+
+ self.nodes = self.get_nodes()
+
+ @abstractmethod
+ def get_openstack_version(self):
+ '''
+ Returns a string of the openstack version (nova-compute)
+ '''
+ raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
+
+ @abstractmethod
+ def get_sdn_version(self):
+ '''
+ Returns a string of the sdn controller and its version, if exists
+ '''
+ raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
+
+ @abstractmethod
+ def get_deployment_status(self):
+ '''
+ Returns a string of the status of the deployment
+ '''
+ raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
+
+ @abstractmethod
+ def get_nodes(self, options=None):
+ '''
+ Generates a list of all the nodes in the deployment
+ '''
+ raise Exception(DeploymentHandler.FUNCTION_NOT_IMPLEMENTED)
+
+ def get_installer_node(self):
+ '''
+ Returns the installer node object
+ '''
+ return self.installer_node
+
+ def get_arch(self):
+ '''
+ Returns the architecture of the first compute node found
+ '''
+ arch = None
+ for node in self.nodes:
+ if node.is_compute():
+ arch = node.cpu_info.get('arch', None)
+ if arch:
+ break
+ return arch
+
+ def get_deployment_info(self):
+ '''
+ Returns an object of type Deployment
+ '''
+ return Deployment(installer=self.installer,
+ installer_ip=self.installer_ip,
+ scenario=os.getenv('DEPLOY_SCENARIO', 'Unknown'),
+ status=self.get_deployment_status(),
+ pod=os.getenv('NODE_NAME', 'Unknown'),
+ openstack_version=self.get_openstack_version(),
+ sdn_controller=self.get_sdn_version(),
+ nodes=self.get_nodes())
diff --git a/modules/opnfv/installer_adapters/InstallerHandler.py b/modules/opnfv/installer_adapters/InstallerHandler.py
deleted file mode 100644
index dc5bdb9d6..000000000
--- a/modules/opnfv/installer_adapters/InstallerHandler.py
+++ /dev/null
@@ -1,81 +0,0 @@
-##############################################################################
-# Copyright (c) 2015 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from opnfv.installer_adapters.fuel.FuelAdapter import FuelAdapter
-from opnfv.installer_adapters.apex.ApexAdapter import ApexAdapter
-from opnfv.installer_adapters.compass.CompassAdapter import CompassAdapter
-from opnfv.installer_adapters.joid.JoidAdapter import JoidAdapter
-from opnfv.installer_adapters.daisy.DaisyAdapter import DaisyAdapter
-
-
-INSTALLERS = ["fuel", "apex", "compass", "joid", "daisy"]
-
-
-class InstallerHandler:
-
- def __init__(self,
- installer,
- installer_ip,
- installer_user,
- installer_pwd=None):
- self.installer = installer.lower()
- self.installer_ip = installer_ip
- self.installer_user = installer_user
- self.installer_pwd = installer_pwd
-
- if self.installer == INSTALLERS[0]:
- self.InstallerAdapter = FuelAdapter(self.installer_ip,
- self.installer_user,
- self.installer_pwd)
- elif self.installer == INSTALLERS[1]:
- self.InstallerAdapter = ApexAdapter(self.installer_ip)
- elif self.installer == INSTALLERS[2]:
- self.InstallerAdapter = CompassAdapter(self.installer_ip)
- elif self.installer == INSTALLERS[3]:
- self.InstallerAdapter = JoidAdapter(self.installer_ip)
- elif self.installer == INSTALLERS[4]:
- self.InstallerAdapter = DaisyAdapter(self.installer_ip)
- else:
- print("Installer %s is not valid. "
- "Please use one of the followings: %s"
- % (self.installer, INSTALLERS))
- exit(1)
-
- def get_deployment_info(self):
- return self.InstallerAdapter.get_deployment_info()
-
- def get_nodes(self, options=None):
- return self.InstallerAdapter.get_nodes(options=options)
-
- def get_controller_ips(self, options=None):
- return self.InstallerAdapter.get_controller_ips(options=options)
-
- def get_compute_ips(self, options=None):
- return self.InstallerAdapter.get_compute_ips(options=options)
-
- def get_file_from_installer(self,
- remote_path,
- local_path,
- options=None):
- return self.InstallerAdapter.get_file_from_installer(remote_path,
- local_path,
- options=options)
-
- def get_file_from_controller(self,
- remote_path,
- local_path,
- ip=None,
- options=None):
- return self.InstallerAdapter.get_file_from_controller(remote_path,
- local_path,
- ip=ip,
- options=options)
-
- def get_all(self):
- pass
diff --git a/modules/opnfv/installer_adapters/apex/ApexAdapter.py b/modules/opnfv/installer_adapters/apex/ApexAdapter.py
deleted file mode 100644
index 17a27b10a..000000000
--- a/modules/opnfv/installer_adapters/apex/ApexAdapter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-class ApexAdapter:
-
- def __init__(self, installer_ip):
- self.installer_ip = installer_ip
-
- def get_deployment_info(self):
- pass
-
- def get_nodes(self):
- pass
-
- def get_controller_ips(self):
- pass
-
- def get_compute_ips(self):
- pass
-
- def get_file_from_installer(self, origin, target, options=None):
- pass
-
- def get_file_from_controller(self, origin, target, ip=None, options=None):
- pass
diff --git a/modules/opnfv/installer_adapters/compass/CompassAdapter.py b/modules/opnfv/installer_adapters/compass/CompassAdapter.py
deleted file mode 100644
index 47cbc646d..000000000
--- a/modules/opnfv/installer_adapters/compass/CompassAdapter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-class CompassAdapter:
-
- def __init__(self, installer_ip):
- self.installer_ip = installer_ip
-
- def get_deployment_info(self):
- pass
-
- def get_nodes(self):
- pass
-
- def get_controller_ips(self):
- pass
-
- def get_compute_ips(self):
- pass
-
- def get_file_from_installer(self, origin, target, options=None):
- pass
-
- def get_file_from_controller(self, origin, target, ip=None, options=None):
- pass
diff --git a/modules/opnfv/installer_adapters/daisy/DaisyAdapter.py b/modules/opnfv/installer_adapters/daisy/DaisyAdapter.py
deleted file mode 100644
index 9b06f4c3c..000000000
--- a/modules/opnfv/installer_adapters/daisy/DaisyAdapter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-class DaisyAdapter:
-
- def __init__(self, installer_ip):
- self.installer_ip = installer_ip
-
- def get_deployment_info(self):
- pass
-
- def get_nodes(self):
- pass
-
- def get_controller_ips(self):
- pass
-
- def get_compute_ips(self):
- pass
-
- def get_file_from_installer(self, origin, target, options=None):
- pass
-
- def get_file_from_controller(self, origin, target, ip=None, options=None):
- pass
diff --git a/modules/opnfv/installer_adapters/fuel/FuelAdapter.py b/modules/opnfv/installer_adapters/fuel/FuelAdapter.py
deleted file mode 100644
index 8ed8f8937..000000000
--- a/modules/opnfv/installer_adapters/fuel/FuelAdapter.py
+++ /dev/null
@@ -1,236 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# George Paraskevopoulos (geopar@intracom-telecom.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-import opnfv.utils.SSHUtils as ssh_utils
-import opnfv.utils.OPNFVLogger as logger
-
-
-class FuelAdapter:
-
- def __init__(self, installer_ip, user="root", password="r00tme"):
- self.installer_ip = installer_ip
- self.installer_user = user
- self.installer_password = password
- self.installer_connection = ssh_utils.get_ssh_client(
- installer_ip,
- self.installer_user,
- password=self.installer_password)
- self.logger = logger.Logger("FuelHandler").getLogger()
-
- def runcmd_fuel_installer(self, cmd):
- _, stdout, stderr = (self
- .installer_connection
- .exec_command(cmd))
- error = stderr.readlines()
- if len(error) > 0:
- self.logger.error("error %s" % ''.join(error))
- return error
- output = ''.join(stdout.readlines())
- return output
-
- def runcmd_fuel_nodes(self):
- return self.runcmd_fuel_installer('fuel nodes')
-
- def runcmd_fuel_env(self):
- return self.runcmd_fuel_installer('fuel env')
-
- def get_clusters(self):
- environments = []
- output = self.runcmd_fuel_env()
- lines = output.rsplit('\n')
- if len(lines) < 2:
- self.logger.infp("No environments found in the deployment.")
- return None
- else:
- fields = lines[0].rsplit(' | ')
-
- index_id = -1
- index_status = -1
- index_name = -1
- index_release_id = -1
-
- for i in range(0, len(fields) - 1):
- if "id" in fields[i]:
- index_id = i
- elif "status" in fields[i]:
- index_status = i
- elif "name" in fields[i]:
- index_name = i
- elif "release_id" in fields[i]:
- index_release_id = i
-
- # order env info
- for i in range(2, len(lines) - 1):
- fields = lines[i].rsplit(' | ')
- dict = {"id": fields[index_id].strip(),
- "status": fields[index_status].strip(),
- "name": fields[index_name].strip(),
- "release_id": fields[index_release_id].strip()}
- environments.append(dict)
-
- return environments
-
- def get_nodes(self, options=None):
- nodes = []
- output = self.runcmd_fuel_nodes()
- lines = output.rsplit('\n')
- if len(lines) < 2:
- self.logger.info("No nodes found in the deployment.")
- return None
- else:
- # get fields indexes
- fields = lines[0].rsplit(' | ')
-
- index_id = -1
- index_status = -1
- index_name = -1
- index_cluster = -1
- index_ip = -1
- index_mac = -1
- index_roles = -1
- index_online = -1
-
- for i in range(0, len(fields) - 1):
- if "id" in fields[i]:
- index_id = i
- elif "status" in fields[i]:
- index_status = i
- elif "name" in fields[i]:
- index_name = i
- elif "cluster" in fields[i]:
- index_cluster = i
- elif "ip" in fields[i]:
- index_ip = i
- elif "mac" in fields[i]:
- index_mac = i
- elif "roles " in fields[i]:
- index_roles = i
- elif "online" in fields[i]:
- index_online = i
-
- # order nodes info
- for i in range(2, len(lines) - 1):
- fields = lines[i].rsplit(' | ')
- dict = {"id": fields[index_id].strip(),
- "status": fields[index_status].strip(),
- "name": fields[index_name].strip(),
- "cluster": fields[index_cluster].strip(),
- "ip": fields[index_ip].strip(),
- "mac": fields[index_mac].strip(),
- "roles": fields[index_roles].strip(),
- "online": fields[index_online].strip()}
- if options and options['cluster']:
- if fields[index_cluster].strip() == options['cluster']:
- nodes.append(dict)
- else:
- nodes.append(dict)
-
- return nodes
-
- def get_controller_ips(self, options):
- nodes = self.get_nodes(options=options)
- controllers = []
- for node in nodes:
- if "controller" in node["roles"]:
- controllers.append(node['ip'])
- return controllers
-
- def get_compute_ips(self, options=None):
- nodes = self.get_nodes(options=options)
- computes = []
- for node in nodes:
- if "compute" in node["roles"]:
- computes.append(node['ip'])
- return computes
-
- def get_deployment_info(self):
- str = "Deployment details:\n"
- str += "\tInstaller: Fuel\n"
- str += "\tScenario: Unknown\n"
- sdn = "None"
- clusters = self.get_clusters()
- str += "\tN.Clusters: %s\n" % len(clusters)
- for cluster in clusters:
- cluster_dic = {'cluster': cluster['id']}
- str += "\tCluster info:\n"
- str += "\t ID: %s\n" % cluster['id']
- str += "\t NAME: %s\n" % cluster['name']
- str += "\t STATUS: %s\n" % cluster['status']
- nodes = self.get_nodes(options=cluster_dic)
- num_nodes = len(nodes)
- for node in nodes:
- if "opendaylight" in node['roles']:
- sdn = "OpenDaylight"
- elif "onos" in node['roles']:
- sdn = "ONOS"
- num_controllers = len(
- self.get_controller_ips(options=cluster_dic))
- num_computes = len(self.get_compute_ips(options=cluster_dic))
- ha = False
- if num_controllers > 1:
- ha = True
-
- str += "\t HA: %s\n" % ha
- str += "\t NUM.NODES: %s\n" % num_nodes
- str += "\t CONTROLLERS: %s\n" % num_controllers
- str += "\t COMPUTES: %s\n" % num_computes
- str += "\t SDN CONTR.: %s\n\n" % sdn
- str += self.runcmd_fuel_nodes()
- return str
-
- def get_file_from_installer(self, remote_path, local_path, options=None):
- self.logger.debug("Fetching %s from %s" %
- (remote_path, self.installer_ip))
- get_file_result = ssh_utils.get_file(self.installer_connection,
- remote_path,
- local_path)
- if get_file_result is None:
- self.logger.error("SFTP failed to retrieve the file.")
- return 1
- self.logger.info("%s successfully copied from Fuel to %s" %
- (remote_path, local_path))
-
- def get_file_from_controller(self,
- remote_path,
- local_path,
- ip=None,
- user='root',
- options=None):
- if ip is None:
- controllers = self.get_controller_ips(options=options)
- if len(controllers) == 0:
- self.logger.info("No controllers found in the deployment.")
- return 1
- else:
- target_ip = controllers[0]
- else:
- target_ip = ip
-
- installer_proxy = {
- 'ip': self.installer_ip,
- 'username': self.installer_user,
- 'password': self.installer_password
- }
- controller_conn = ssh_utils.get_ssh_client(
- target_ip,
- user,
- proxy=installer_proxy)
-
- self.logger.debug("Fetching %s from %s" %
- (remote_path, target_ip))
-
- get_file_result = ssh_utils.get_file(controller_conn,
- remote_path,
- local_path)
- if get_file_result is None:
- self.logger.error("SFTP failed to retrieve the file.")
- return 1
- self.logger.info("%s successfully copied from %s to %s" %
- (remote_path, target_ip, local_path))
diff --git a/modules/opnfv/installer_adapters/fuel/example.py b/modules/opnfv/installer_adapters/fuel/example.py
deleted file mode 100644
index 7fea4dfd7..000000000
--- a/modules/opnfv/installer_adapters/fuel/example.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# This is an example of usage of this Tool
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-
-import opnfv.installer_adapters.InstallerHandler as ins_handler
-
-fuel_handler = ins_handler.InstallerHandler(installer='fuel',
- installer_ip='10.20.0.2',
- installer_user='root',
- installer_pwd='r00tme')
-print("Nodes in cluster 1:\n%s\n" %
- fuel_handler.get_nodes(options={'cluster': '1'}))
-print("Nodes in cluster 2:\n%s\n" %
- fuel_handler.get_nodes(options={'cluster': '2'}))
-print("Nodes:\n%s\n" % fuel_handler.get_nodes())
-print("Controller nodes:\n%s\n" % fuel_handler.get_controller_ips())
-print("Compute nodes:\n%s\n" % fuel_handler.get_compute_ips())
-print("\n%s\n" % fuel_handler.get_deployment_info())
-fuel_handler.get_file_from_installer('/root/deploy/dea.yaml', './dea.yaml')
-fuel_handler.get_file_from_controller(
- '/etc/neutron/neutron.conf', './neutron.conf')
-fuel_handler.get_file_from_controller(
- '/root/openrc', './openrc')
diff --git a/modules/opnfv/installer_adapters/joid/JoidAdapter.py b/modules/opnfv/installer_adapters/joid/JoidAdapter.py
deleted file mode 100644
index be8c2ebac..000000000
--- a/modules/opnfv/installer_adapters/joid/JoidAdapter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Ericsson AB and others.
-# Author: Jose Lausuch (jose.lausuch@ericsson.com)
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-class JoidAdapter:
-
- def __init__(self, installer_ip):
- self.installer_ip = installer_ip
-
- def get_deployment_info(self):
- pass
-
- def get_nodes(self):
- pass
-
- def get_controller_ips(self):
- pass
-
- def get_compute_ips(self):
- pass
-
- def get_file_from_installer(self, origin, target, options=None):
- pass
-
- def get_file_from_controller(self, origin, target, ip=None, options=None):
- pass
diff --git a/modules/opnfv/installer_adapters/joid/__init__.py b/modules/opnfv/installer_adapters/joid/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/modules/opnfv/installer_adapters/joid/__init__.py
+++ /dev/null
diff --git a/modules/opnfv/utils/Credentials.py b/modules/opnfv/utils/Credentials.py
index 6441b841c..141ecbd93 100644
--- a/modules/opnfv/utils/Credentials.py
+++ b/modules/opnfv/utils/Credentials.py
@@ -77,7 +77,7 @@ class Credentials(object):
creds_file = '/root/openrc'
try:
self.handler.get_file_from_controller(creds_file, target_path)
- except Exception, e:
+ except Exception as e:
self.logger.error(
"Cannot get %s from controller. %e" % (creds_file, e))
pass
diff --git a/modules/opnfv/utils/constants.py b/modules/opnfv/utils/constants.py
index ed83488d4..56008c37f 100644
--- a/modules/opnfv/utils/constants.py
+++ b/modules/opnfv/utils/constants.py
@@ -14,6 +14,7 @@ EXIT_OK = 0
EXIT_RUN_ERROR = -1
EXIT_PUSH_TO_TEST_DB_ERROR = -2
+
class Constants(object):
INSTALLERS = ['apex', 'fuel', 'compass', 'joid', "daisy"]
VERSIONS = ['arno', 'brahmaputra', 'colorado', 'danube']
diff --git a/modules/opnfv/utils/OPNFVLogger.py b/modules/opnfv/utils/opnfv_logger.py
index 6fa4ef2e2..6fa4ef2e2 100644
--- a/modules/opnfv/utils/OPNFVLogger.py
+++ b/modules/opnfv/utils/opnfv_logger.py
diff --git a/modules/opnfv/utils/ovs_logger.py b/modules/opnfv/utils/ovs_logger.py
index 3159609f1..7777a9a16 100644
--- a/modules/opnfv/utils/ovs_logger.py
+++ b/modules/opnfv/utils/ovs_logger.py
@@ -7,7 +7,7 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-import opnfv.utils.OPNFVLogger as OPNFVLogger
+import opnfv.utils.opnfv_logger as OPNFVLogger
import os
import time
import shutil
@@ -16,6 +16,7 @@ logger = OPNFVLogger.Logger('ovs_logger').getLogger()
class OVSLogger(object):
+
def __init__(self, basedir, ft_resdir):
self.ovs_dir = basedir
self.ft_resdir = ft_resdir
@@ -32,7 +33,7 @@ class OVSLogger(object):
hosts = stdout.readline().strip().split(' ')
found_host = [h for h in hosts if h.startswith(host_prefix)][0]
return found_host
- except Exception, e:
+ except Exception as e:
logger.error(e)
def __dump_to_file(self, operation, host, text, timestamp=None):
@@ -55,7 +56,7 @@ class OVSLogger(object):
.format(cmd, host))
output = ''.join(stdout.readlines())
return output
- except Exception, e:
+ except Exception as e:
logger.error('[__remote_command(ssh_client, {0})]: {1}'
.format(cmd, e))
return None
@@ -78,7 +79,7 @@ class OVSLogger(object):
host = self.__ssh_host(ssh_conn)
self.__dump_to_file(operation, host, output, timestamp=timestamp)
return output
- except Exception, e:
+ except Exception as e:
logger.error('[ofctl_dump_flows(ssh_client, {0}, {1})]: {2}'
.format(br, choose_table, e))
return None
@@ -91,7 +92,7 @@ class OVSLogger(object):
host = self.__ssh_host(ssh_conn)
self.__dump_to_file(operation, host, output, timestamp=timestamp)
return output
- except Exception, e:
+ except Exception as e:
logger.error('[vsctl_show(ssh_client)]: {0}'.format(e))
return None
@@ -100,19 +101,13 @@ class OVSLogger(object):
if timestamp is None:
timestamp = time.strftime("%Y%m%d-%H%M%S")
- for controller_client in controller_clients:
- self.ofctl_dump_flows(controller_client,
- timestamp=timestamp)
- self.vsctl_show(controller_client,
- timestamp=timestamp)
-
- for compute_client in compute_clients:
- self.ofctl_dump_flows(compute_client,
- timestamp=timestamp)
- self.vsctl_show(compute_client,
- timestamp=timestamp)
+ clients = controller_clients + compute_clients
+ for client in clients:
+ self.ofctl_dump_flows(client, timestamp=timestamp)
+ self.vsctl_show(client, timestamp=timestamp)
if related_error is not None:
dumpdir = os.path.join(self.ovs_dir, timestamp)
+ self.__mkdir_p(dumpdir)
with open(os.path.join(dumpdir, 'error'), 'w') as f:
f.write(related_error)
diff --git a/modules/opnfv/utils/SSHUtils.py b/modules/opnfv/utils/ssh_utils.py
index 16e34c3e5..d17f5ae81 100644
--- a/modules/opnfv/utils/SSHUtils.py
+++ b/modules/opnfv/utils/ssh_utils.py
@@ -9,14 +9,19 @@
##############################################################################
-import paramiko
-import opnfv.utils.OPNFVLogger as OPNFVLogger
import os
+import paramiko
-logger = OPNFVLogger.Logger('SSHUtils').getLogger()
+from opnfv.utils import opnfv_logger as logger
+logger = logger.Logger("SSH utils").getLogger()
-def get_ssh_client(hostname, username, password=None, proxy=None):
+
+def get_ssh_client(hostname,
+ username,
+ password=None,
+ proxy=None,
+ pkey_file=None):
client = None
try:
if proxy is None:
@@ -26,16 +31,23 @@ def get_ssh_client(hostname, username, password=None, proxy=None):
client.configure_jump_host(proxy['ip'],
proxy['username'],
proxy['password'])
-
if client is None:
raise Exception('Could not connect to client')
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- client.connect(hostname,
- username=username,
- password=password)
+ if pkey_file is not None:
+ key = paramiko.RSAKey.from_private_key_file(pkey_file)
+ client.load_system_host_keys()
+ client.connect(hostname,
+ username=username,
+ pkey=key)
+ else:
+ client.connect(hostname,
+ username=username,
+ password=password)
+
return client
- except Exception, e:
+ except Exception as e:
logger.error(e)
return None
@@ -45,7 +57,7 @@ def get_file(ssh_conn, src, dest):
sftp = ssh_conn.open_sftp()
sftp.get(src, dest)
return True
- except Exception, e:
+ except Exception as e:
logger.error("Error [get_file(ssh_conn, '%s', '%s']: %s" %
(src, dest, e))
return None
@@ -56,7 +68,7 @@ def put_file(ssh_conn, src, dest):
sftp = ssh_conn.open_sftp()
sftp.put(src, dest)
return True
- except Exception, e:
+ except Exception as e:
logger.error("Error [put_file(ssh_conn, '%s', '%s']: %s" %
(src, dest, e))
return None
@@ -66,8 +78,8 @@ class ProxyHopClient(paramiko.SSHClient):
'''
Connect to a remote server using a proxy hop
'''
+
def __init__(self, *args, **kwargs):
- self.logger = OPNFVLogger.Logger("ProxyHopClient").getLogger()
self.proxy_ssh = None
self.proxy_transport = None
self.proxy_channel = None
@@ -116,5 +128,5 @@ class ProxyHopClient(paramiko.SSHClient):
pkey=proxy_key,
sock=self.proxy_channel)
os.remove(self.local_ssh_key)
- except Exception, e:
- self.logger.error(e)
+ except Exception as e:
+ logger.error(e)
diff --git a/prototypes/bifrost/playbooks/test-bifrost-infracloud.yaml b/prototypes/bifrost/playbooks/test-bifrost-infracloud.yaml
index 541a1f7d4..07d5e245b 100644
--- a/prototypes/bifrost/playbooks/test-bifrost-infracloud.yaml
+++ b/prototypes/bifrost/playbooks/test-bifrost-infracloud.yaml
@@ -54,7 +54,7 @@
dib_os_element: "{{ lookup('env','DIB_OS_ELEMENT') }}"
dib_os_release: "{{ lookup('env', 'DIB_OS_RELEASE') }}"
extra_dib_elements: "{{ lookup('env', 'EXTRA_DIB_ELEMENTS') | default('') }}"
- dib_elements: "vm serial-console simple-init devuser infra-cloud-bridge puppet growroot {{ extra_dib_elements }}"
+ dib_elements: "vm enable-serial-console simple-init devuser infra-cloud-bridge puppet growroot {{ extra_dib_elements }}"
dib_packages: "{{ lookup('env', 'DIB_OS_PACKAGES') }}"
when: create_image_via_dib | bool == true and transform_boot_image | bool == false
environment:
diff --git a/prototypes/bifrost/scripts/destroy-env.sh b/prototypes/bifrost/scripts/destroy-env.sh
index cdc55df1b..b73092b0f 100755
--- a/prototypes/bifrost/scripts/destroy-env.sh
+++ b/prototypes/bifrost/scripts/destroy-env.sh
@@ -14,24 +14,23 @@ if [[ $(whoami) != "root" ]]; then
exit 1
fi
-virsh destroy jumphost.opnfvlocal || true
-virsh destroy controller00.opnfvlocal || true
-virsh destroy compute00.opnfvlocal || true
-virsh undefine jumphost.opnfvlocal || true
-virsh undefine controller00.opnfvlocal || true
-virsh undefine compute00.opnfvlocal || true
-
-service ironic-conductor stop
-
-echo "removing from database"
-mysql -u root ironic --execute "truncate table ports;"
-mysql -u root ironic --execute "delete from node_tags;"
-mysql -u root ironic --execute "delete from nodes;"
-mysql -u root ironic --execute "delete from conductors;"
+# Delete all VMs on the slave since proposed patchsets
+# may leave undesired VM leftovers
+for vm in $(virsh list --all --name); do
+ virsh destroy $vm || true
+ virsh undefine $vm || true
+done
+
+service ironic-conductor stop || true
+
+echo "removing ironic database"
+if $(which mysql &> /dev/null); then
+ mysql -u root ironic --execute "drop database ironic;"
+fi
echo "removing leases"
[[ -e /var/lib/misc/dnsmasq/dnsmasq.leases ]] && > /var/lib/misc/dnsmasq/dnsmasq.leases
echo "removing logs"
-rm -rf /var/log/libvirt/baremetal_logs/*.log
+rm -rf /var/log/libvirt/baremetal_logs/*
# clean up dib images only if requested explicitly
CLEAN_DIB_IMAGES=${CLEAN_DIB_IMAGES:-false}
@@ -48,6 +47,6 @@ rm -rf /var/lib/libvirt/images/*.qcow2
echo "restarting services"
service dnsmasq restart || true
service libvirtd restart
-service ironic-api restart
-service ironic-conductor start
-service ironic-inspector restart
+service ironic-api restart || true
+service ironic-conductor start || true
+service ironic-inspector restart || true
diff --git a/prototypes/bifrost/scripts/test-bifrost-deployment.sh b/prototypes/bifrost/scripts/test-bifrost-deployment.sh
index 90f014c74..b7165ffd1 100755
--- a/prototypes/bifrost/scripts/test-bifrost-deployment.sh
+++ b/prototypes/bifrost/scripts/test-bifrost-deployment.sh
@@ -18,6 +18,7 @@ ENABLE_VENV="false"
USE_DHCP="false"
USE_VENV="false"
BUILD_IMAGE=true
+BAREMETAL_DATA_FILE=${BAREMETAL_DATA_FILE:-'/tmp/baremetal.json'}
PROVISION_WAIT_TIMEOUT=${PROVISION_WAIT_TIMEOUT:-3600}
# Set defaults for ansible command-line options to drive the different
@@ -36,6 +37,7 @@ export TEST_VM_NODE_NAMES="jumphost.opnfvlocal controller00.opnfvlocal compute00
export VM_DOMAIN_TYPE="kvm"
export VM_CPU=${VM_CPU:-4}
export VM_DISK=${VM_DISK:-100}
+export VM_DISK_CACHE=${VM_DISK_CACHE:-unsafe}
TEST_PLAYBOOK="test-bifrost-infracloud.yaml"
USE_INSPECTOR=true
USE_CIRROS=false
@@ -49,10 +51,10 @@ INVENTORY_DHCP_STATIC_IP=false
WRITE_INTERFACES_FILE=true
# Set BIFROST_INVENTORY_SOURCE
-export BIFROST_INVENTORY_SOURCE=/tmp/baremetal.csv
+export BIFROST_INVENTORY_SOURCE=/tmp/baremetal.json
# DIB custom elements path
-export ELEMENTS_PATH=/usr/share/diskimage-builder/elements:/opt/puppet-infracloud/files/elements
+export ELEMENTS_PATH=/opt/puppet-infracloud/files/elements
# settings for console access
export DIB_DEV_USER_PWDLESS_SUDO=yes
@@ -78,6 +80,11 @@ source ${ANSIBLE_INSTALL_ROOT}/ansible/hacking/env-setup
ANSIBLE=$(which ansible-playbook)
set -x -o nounset
+logs_on_exit() {
+ $SCRIPT_HOME/collect-test-info.sh
+}
+trap logs_on_exit EXIT
+
# Change working directory
cd $BIFROST_HOME/playbooks
@@ -101,7 +108,8 @@ ${ANSIBLE} -vvvv \
-e test_vm_num_nodes=${TEST_VM_NUM_NODES} \
-e test_vm_memory_size=${VM_MEMORY_SIZE} \
-e enable_venv=${ENABLE_VENV} \
- -e test_vm_domain_type=${VM_DOMAIN_TYPE}
+ -e test_vm_domain_type=${VM_DOMAIN_TYPE} \
+ -e baremetal_json_file=${BAREMETAL_DATA_FILE}
# Execute the installation and VM startup test.
${ANSIBLE} -vvvv \
@@ -128,6 +136,4 @@ if [ $EXITCODE != 0 ]; then
echo "****************************"
fi
-$SCRIPT_HOME/collect-test-info.sh
-
exit $EXITCODE
diff --git a/prototypes/openstack-ansible/README.md b/prototypes/openstack-ansible/README.md
new file mode 100644
index 000000000..34c1d0d03
--- /dev/null
+++ b/prototypes/openstack-ansible/README.md
@@ -0,0 +1,48 @@
+===============================
+How to deploy OpenStack-Ansible
+===============================
+The script and playbooks defined on this repo will deploy an OpenStack
+cloud based on OpenStack-Ansible.
+It needs to be combined with Bifrost. You need use Bifrost to provide six VMs.
+To learn about how to use Bifrost, you can read the document on
+[/opt/releng/prototypes/bifrost/README.md].
+
+Minimal requirements:
+1. You will need to have a least 150G free space for the partition on where
+ "/var/lib/libvirt/images/" lives.
+2. each vm needs to have at least 8 vCPU, 12 GB RAM, 60 GB HDD.
+
+After provisioning the six VMs please follow that steps:
+
+1.Run the script to deploy OpenStack
+ cd /opt/releng/prototypes/openstack-ansible/scripts/
+ sudo ./osa_deploy.sh
+It will take a lot of time. When the deploy is successful, you will see the
+message "OpenStack deployed successfully".
+
+2.To verify the OpenStack operation
+ 2.1 ssh into the controller::
+ ssh 192.168.122.3
+ 2.2 Enter into the lxc container::
+ lxcname=$(lxc-ls | grep utility)
+ lxc-attach -n $lxcname
+ 2.3 Verify the OpenStack API::
+ source /root/openrc
+ openstack user list
+
+This will show the following output::
++----------------------------------+--------------------+
+| ID | Name |
++----------------------------------+--------------------+
+| 056f8fe41336435991fd80872731cada | aodh |
+| 308f6436e68f40b49d3b8e7ce5c5be1e | glance |
+| 351b71b43a66412d83f9b3cd75485875 | nova |
+| 511129e053394aea825cce13b9f28504 | ceilometer |
+| 5596f71319d44c8991fdc65f3927b62e | gnocchi |
+| 586f49e3398a4c47a2f6fe50135d4941 | stack_domain_admin |
+| 601b329e6b1d427f9a1e05ed28753497 | heat |
+| 67fe383b94964a4781345fbcc30ae434 | cinder |
+| 729bb08351264d729506dad84ed3ccf0 | admin |
+| 9f2beb2b270940048fe6844f0b16281e | neutron |
+| fa68f86dd1de4ddbbb7415b4d9a54121 | keystone |
++----------------------------------+--------------------+
diff --git a/prototypes/openstack-ansible/file/cinder.yml b/prototypes/openstack-ansible/file/cinder.yml
new file mode 100644
index 000000000..e40b39256
--- /dev/null
+++ b/prototypes/openstack-ansible/file/cinder.yml
@@ -0,0 +1,13 @@
+---
+# This file contains an example to show how to set
+# the cinder-volume service to run in a container.
+#
+# Important note:
+# When using LVM or any iSCSI-based cinder backends, such as NetApp with
+# iSCSI protocol, the cinder-volume service *must* run on metal.
+# Reference: https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1226855
+
+container_skel:
+ cinder_volumes_container:
+ properties:
+ is_metal: false
diff --git a/prototypes/openstack-ansible/file/exports b/prototypes/openstack-ansible/file/exports
new file mode 100644
index 000000000..315f79d2f
--- /dev/null
+++ b/prototypes/openstack-ansible/file/exports
@@ -0,0 +1,12 @@
+# /etc/exports: the access control list for filesystems which may be exported
+# to NFS clients. See exports(5).
+#
+# Example for NFSv2 and NFSv3:
+# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
+#
+# Example for NFSv4:
+# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
+# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
+#
+/images *(rw,sync,no_subtree_check,no_root_squash)
+
diff --git a/prototypes/openstack-ansible/file/modules b/prototypes/openstack-ansible/file/modules
new file mode 100644
index 000000000..60a517f18
--- /dev/null
+++ b/prototypes/openstack-ansible/file/modules
@@ -0,0 +1,8 @@
+# /etc/modules: kernel modules to load at boot time.
+#
+# This file contains the names of kernel modules that should be loaded
+# at boot time, one per line. Lines beginning with "#" are ignored.
+# Parameters can be specified after the module name.
+
+bonding
+8021q
diff --git a/prototypes/openstack-ansible/file/openstack_user_config.yml b/prototypes/openstack-ansible/file/openstack_user_config.yml
new file mode 100644
index 000000000..2811e62ce
--- /dev/null
+++ b/prototypes/openstack-ansible/file/openstack_user_config.yml
@@ -0,0 +1,278 @@
+---
+cidr_networks:
+ container: 172.29.236.0/22
+ tunnel: 172.29.240.0/22
+ storage: 172.29.244.0/22
+
+used_ips:
+ - "172.29.236.1,172.29.236.50"
+ - "172.29.240.1,172.29.240.50"
+ - "172.29.244.1,172.29.244.50"
+ - "172.29.248.1,172.29.248.50"
+
+global_overrides:
+ internal_lb_vip_address: 172.29.236.222
+ external_lb_vip_address: 192.168.122.220
+ tunnel_bridge: "br-vxlan"
+ management_bridge: "br-mgmt"
+ provider_networks:
+ - network:
+ container_bridge: "br-mgmt"
+ container_type: "veth"
+ container_interface: "eth1"
+ ip_from_q: "container"
+ type: "raw"
+ group_binds:
+ - all_containers
+ - hosts
+ is_container_address: true
+ is_ssh_address: true
+ - network:
+ container_bridge: "br-vxlan"
+ container_type: "veth"
+ container_interface: "eth10"
+ ip_from_q: "tunnel"
+ type: "vxlan"
+ range: "1:1000"
+ net_name: "vxlan"
+ group_binds:
+ - neutron_linuxbridge_agent
+ - network:
+ container_bridge: "br-vlan"
+ container_type: "veth"
+ container_interface: "eth12"
+ host_bind_override: "eth12"
+ type: "flat"
+ net_name: "flat"
+ group_binds:
+ - neutron_linuxbridge_agent
+ - network:
+ container_bridge: "br-vlan"
+ container_type: "veth"
+ container_interface: "eth11"
+ type: "vlan"
+ range: "1:1"
+ net_name: "vlan"
+ group_binds:
+ - neutron_linuxbridge_agent
+ - network:
+ container_bridge: "br-storage"
+ container_type: "veth"
+ container_interface: "eth2"
+ ip_from_q: "storage"
+ type: "raw"
+ group_binds:
+ - glance_api
+ - cinder_api
+ - cinder_volume
+ - nova_compute
+
+###
+### Infrastructure
+###
+
+# galera, memcache, rabbitmq, utility
+shared-infra_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# repository (apt cache, python packages, etc)
+repo-infra_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# load balancer
+# Ideally the load balancer should not use the Infrastructure hosts.
+# Dedicated hardware is best for improved performance and security.
+haproxy_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# rsyslog server
+#log_hosts:
+ # log1:
+ # ip: 172.29.236.14
+
+###
+### OpenStack
+###
+
+# keystone
+identity_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# cinder api services
+storage-infra_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# glance
+# The settings here are repeated for each infra host.
+# They could instead be applied as global settings in
+# user_variables, but are left here to illustrate that
+# each container could have different storage targets.
+image_hosts:
+ controller00:
+ ip: 172.29.236.11
+ container_vars:
+ limit_container_types: glance
+ glance_nfs_client:
+ - server: "172.29.244.15"
+ remote_path: "/images"
+ local_path: "/var/lib/glance/images"
+ type: "nfs"
+ options: "_netdev,auto"
+ controller01:
+ ip: 172.29.236.12
+ container_vars:
+ limit_container_types: glance
+ glance_nfs_client:
+ - server: "172.29.244.15"
+ remote_path: "/images"
+ local_path: "/var/lib/glance/images"
+ type: "nfs"
+ options: "_netdev,auto"
+ controller02:
+ ip: 172.29.236.13
+ container_vars:
+ limit_container_types: glance
+ glance_nfs_client:
+ - server: "172.29.244.15"
+ remote_path: "/images"
+ local_path: "/var/lib/glance/images"
+ type: "nfs"
+ options: "_netdev,auto"
+
+# nova api, conductor, etc services
+compute-infra_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# heat
+orchestration_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# horizon
+dashboard_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# neutron server, agents (L3, etc)
+network_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# ceilometer (telemetry API)
+metering-infra_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# aodh (telemetry alarm service)
+metering-alarm_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# gnocchi (telemetry metrics storage)
+metrics_hosts:
+ controller00:
+ ip: 172.29.236.11
+ controller01:
+ ip: 172.29.236.12
+ controller02:
+ ip: 172.29.236.13
+
+# nova hypervisors
+compute_hosts:
+ compute00:
+ ip: 172.29.236.14
+ compute01:
+ ip: 172.29.236.15
+
+# ceilometer compute agent (telemetry)
+metering-compute_hosts:
+ compute00:
+ ip: 172.29.236.14
+ compute01:
+ ip: 172.29.236.15
+# cinder volume hosts (NFS-backed)
+# The settings here are repeated for each infra host.
+# They could instead be applied as global settings in
+# user_variables, but are left here to illustrate that
+# each container could have different storage targets.
+storage_hosts:
+ controller00:
+ ip: 172.29.236.11
+ container_vars:
+ cinder_backends:
+ limit_container_types: cinder_volume
+ lvm:
+ volume_group: cinder-volumes
+ volume_driver: cinder.volume.drivers.lvm.LVMVolumeDriver
+ volume_backend_name: LVM_iSCSI
+ iscsi_ip_address: "172.29.244.11"
+ controller01:
+ ip: 172.29.236.12
+ container_vars:
+ cinder_backends:
+ limit_container_types: cinder_volume
+ lvm:
+ volume_group: cinder-volumes
+ volume_driver: cinder.volume.drivers.lvm.LVMVolumeDriver
+ volume_backend_name: LVM_iSCSI
+ iscsi_ip_address: "172.29.244.12"
+ controller02:
+ ip: 172.29.236.13
+ container_vars:
+ cinder_backends:
+ limit_container_types: cinder_volume
+ lvm:
+ volume_group: cinder-volumes
+ volume_driver: cinder.volume.drivers.lvm.LVMVolumeDriver
+ volume_backend_name: LVM_iSCSI
+ iscsi_ip_address: "172.29.244.13"
diff --git a/prototypes/openstack-ansible/file/user_variables.yml b/prototypes/openstack-ansible/file/user_variables.yml
new file mode 100644
index 000000000..3e14bc57e
--- /dev/null
+++ b/prototypes/openstack-ansible/file/user_variables.yml
@@ -0,0 +1,27 @@
+---
+# Copyright 2014, Rackspace US, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+###
+### This file contains commonly used overrides for convenience. Please inspect
+### the defaults for each role to find additional override options.
+###
+
+## Debug and Verbose options.
+debug: false
+
+haproxy_keepalived_external_vip_cidr: "192.168.122.220/32"
+haproxy_keepalived_internal_vip_cidr: "172.29.236.222/32"
+haproxy_keepalived_external_interface: br-vlan
+haproxy_keepalived_internal_interface: br-mgmt
diff --git a/prototypes/openstack-ansible/playbooks/inventory b/prototypes/openstack-ansible/playbooks/inventory
new file mode 100644
index 000000000..f53da5305
--- /dev/null
+++ b/prototypes/openstack-ansible/playbooks/inventory
@@ -0,0 +1,11 @@
+[jumphost]
+jumphost ansible_ssh_host=192.168.122.2
+
+[controller]
+controller00 ansible_ssh_host=192.168.122.3
+controller01 ansible_ssh_host=192.168.122.4
+controller02 ansible_ssh_host=192.168.122.5
+
+[compute]
+compute00 ansible_ssh_host=192.168.122.6
+compute01 ansible_ssh_host=192.168.122.7
diff --git a/prototypes/openstack-ansible/playbooks/jumphost_configuration.yml b/prototypes/openstack-ansible/playbooks/jumphost_configuration.yml
new file mode 100644
index 000000000..c51d83073
--- /dev/null
+++ b/prototypes/openstack-ansible/playbooks/jumphost_configuration.yml
@@ -0,0 +1,53 @@
+---
+- hosts: jumphost
+ remote_user: root
+ vars_files:
+ - ../var/ubuntu.yml
+ tasks:
+ - name: generate SSH keys
+ shell: ssh-keygen -b 2048 -t rsa -f /root/.ssh/id_rsa -q -N ""
+ args:
+ creates: /root/.ssh/id_rsa
+ - name: fetch public key
+ fetch: src="/root/.ssh/id_rsa.pub" dest="/"
+ - name: remove the directory
+ shell: "rm -rf {{OSA_PATH}} {{OSA_ETC_PATH}}"
+ - name: git openstack ansible
+ shell: "git clone {{OSA_URL}} {{OSA_PATH}} -b {{OSA_BRANCH}}"
+ - name: copy /opt/openstack-ansible/etc/openstack_deploy to /etc/openstack_deploy
+ shell: "/bin/cp -rf {{OSA_PATH}}/etc/openstack_deploy {{OSA_ETC_PATH}}"
+ - name: bootstrap
+ command: "/bin/bash ./scripts/bootstrap-ansible.sh"
+ args:
+ chdir: "{{OSA_PATH}}"
+ - name: generate password token
+ command: "python pw-token-gen.py --file /etc/openstack_deploy/user_secrets.yml"
+ args:
+ chdir: /opt/openstack-ansible/scripts/
+ - name: copy openstack_user_config.yml to /etc/openstack_deploy
+ copy:
+ src: ../file/openstack_user_config.yml
+ dest: "{{OSA_ETC_PATH}}/openstack_user_config.yml"
+ - name: copy cinder.yml to /etc/openstack_deploy/env.d
+ copy:
+ src: ../file/cinder.yml
+ dest: "{{OSA_ETC_PATH}}/env.d/cinder.yml"
+ - name: copy user_variables.yml to /etc/openstack_deploy/
+ copy:
+ src: ../file/user_variables.yml
+ dest: "{{OSA_ETC_PATH}}/user_variables.yml"
+ - name: configure network
+ template:
+ src: ../template/bifrost/controller.interface.j2
+ dest: /etc/network/interfaces
+ notify:
+ - restart network service
+ handlers:
+ - name: restart network service
+ shell: "/sbin/ifconfig ens3 0 &&/sbin/ifdown -a && /sbin/ifup -a"
+
+- hosts: localhost
+ remote_user: root
+ tasks:
+ - name: Generate authorized_keys
+ shell: "/bin/cat /jumphost/root/.ssh/id_rsa.pub >> ../file/authorized_keys"
diff --git a/prototypes/openstack-ansible/playbooks/targethost_configuration.yml b/prototypes/openstack-ansible/playbooks/targethost_configuration.yml
new file mode 100644
index 000000000..ffe788f0e
--- /dev/null
+++ b/prototypes/openstack-ansible/playbooks/targethost_configuration.yml
@@ -0,0 +1,61 @@
+---
+- hosts: all
+ remote_user: root
+ vars_files:
+ - ../var/ubuntu.yml
+ tasks:
+ - name: add public key to host
+ copy:
+ src: ../file/authorized_keys
+ dest: /root/.ssh/authorized_keys
+ - name: configure modules
+ copy:
+ src: ../file/modules
+ dest: /etc/modules
+
+- hosts: controller
+ remote_user: root
+ vars_files:
+ - ../var/ubuntu.yml
+ tasks:
+ - name: configure network
+ template:
+ src: ../template/bifrost/controller.interface.j2
+ dest: /etc/network/interfaces
+ notify:
+ - restart network service
+ handlers:
+ - name: restart network service
+ shell: "/sbin/ifconfig ens3 0 &&/sbin/ifdown -a && /sbin/ifup -a"
+
+- hosts: compute
+ remote_user: root
+ vars_files:
+ - ../var/ubuntu.yml
+ tasks:
+ - name: configure network
+ template:
+ src: ../template/bifrost/compute.interface.j2
+ dest: /etc/network/interfaces
+ notify:
+ - restart network service
+ handlers:
+ - name: restart network service
+ shell: "/sbin/ifconfig ens3 0 &&/sbin/ifdown -a && /sbin/ifup -a"
+
+- hosts: compute01
+ remote_user: root
+ tasks:
+ - name: make nfs dir
+ file: "dest=/images mode=777 state=directory"
+ - name: configure sdrvice
+ shell: "echo 'nfs 2049/tcp' >> /etc/services && echo 'nfs 2049/udp' >> /etc/services"
+ - name: configure NFS
+ copy:
+ src: ../file/exports
+ dest: /etc/exports
+ notify:
+ - restart nfs service
+ handlers:
+ - name: restart nfs service
+ service: name=nfs-kernel-server state=restarted
diff --git a/prototypes/openstack-ansible/scripts/osa_deploy.sh b/prototypes/openstack-ansible/scripts/osa_deploy.sh
new file mode 100755
index 000000000..95f593194
--- /dev/null
+++ b/prototypes/openstack-ansible/scripts/osa_deploy.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+export OSA_PATH=/opt/openstack-ansible
+export LOG_PATH=$OSA_PATH/log
+export PLAYBOOK_PATH=$OSA_PATH/playbooks
+export OSA_BRANCH=${OSA_BRANCH:-"master"}
+
+JUMPHOST_IP="192.168.122.2"
+
+sudo /bin/rm -rf $LOG_PATH
+sudo /bin/mkdir -p $LOG_PATH
+sudo /bin/cp /root/.ssh/id_rsa.pub ../file/authorized_keys
+sudo echo -e '\n'>>../file/authorized_keys
+
+cd ../playbooks/
+# this will prepare the jump host
+# git clone the Openstack-Ansible, bootstrap and configure network
+sudo ansible-playbook -i inventory jumphost_configuration.yml -vvv
+
+# this will prepare the target host
+# such as configure network and NFS
+sudo ansible-playbook -i inventory targethost_configuration.yml
+
+# using OpenStack-Ansible deploy the OpenStack
+
+echo "set UP Host !"
+sudo /bin/sh -c "ssh root@$JUMPHOST_IP openstack-ansible \
+ $PLAYBOOK_PATH/setup-hosts.yml" | \
+ tee $LOG_PATH/setup-host.log
+
+#check the result of openstack-ansible setup-hosts.yml
+#if failed, exit with exit code 1
+grep "failed=1" $LOG_PATH/setup-host.log>/dev/null \
+ || grep "unreachable=1" $LOG_PATH/setup-host.log>/dev/null
+if [ $? -eq 0 ]; then
+ echo "failed setup host!"
+ exit 1
+else
+ echo "setup host successfully!"
+fi
+
+echo "Set UP Infrastructure !"
+sudo /bin/sh -c "ssh root@$JUMPHOST_IP openstack-ansible \
+ $PLAYBOOK_PATH/setup-infrastructure.yml" | \
+ tee $LOG_PATH/setup-infrastructure.log
+
+grep "failed=1" $LOG_PATH/setup-infrastructure.log>/dev/null \
+ || grep "unreachable=1" $LOG_PATH/setup-infrastructure.log>/dev/null
+if [ $? -eq 0 ]; then
+ echo "failed setup infrastructure!"
+ exit 1
+else
+ echo "setup infrastructure successfully!"
+fi
+
+sudo /bin/sh -c "ssh root@$JUMPHOST_IP ansible -i $PLAYBOOK_PATH/inventory/ \
+ galera_container -m shell \
+ -a "mysql -h localhost -e 'show status like \"%wsrep_cluster_%\";'"" \
+ | tee $LOG_PATH/galera.log
+
+grep "FAILED" $LOG_PATH/galera.log>/dev/null
+if [ $? -eq 0 ]; then
+ echo "failed verify the database cluster!"
+ exit 1
+else
+ echo "verify the database cluster successfully!"
+fi
+
+echo "Set UP OpenStack !"
+sudo /bin/sh -c "ssh root@$JUMPHOST_IP openstack-ansible \
+ $PLAYBOOK_PATH/setup-openstack.yml" | \
+ tee $LOG_PATH/setup-openstack.log
+
+grep "failed=1" $LOG_PATH/setup-openstack.log>/dev/null \
+ || grep "unreachable=1" $LOG_PATH/setup-openstack.log>/dev/null
+if [ $? -eq 0 ]; then
+ echo "failed setup openstack!"
+ exit 1
+else
+ echo "OpenStack successfully deployed!"
+ exit 0
+fi
diff --git a/prototypes/openstack-ansible/template/bifrost/compute.interface.j2 b/prototypes/openstack-ansible/template/bifrost/compute.interface.j2
new file mode 100644
index 000000000..1719f6a08
--- /dev/null
+++ b/prototypes/openstack-ansible/template/bifrost/compute.interface.j2
@@ -0,0 +1,86 @@
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+
+# Physical interface
+auto ens3
+iface ens3 inet manual
+
+# Container/Host management VLAN interface
+auto ens3.10
+iface ens3.10 inet manual
+ vlan-raw-device ens3
+
+# OpenStack Networking VXLAN (tunnel/overlay) VLAN interface
+auto ens3.30
+iface ens3.30 inet manual
+ vlan-raw-device ens3
+
+# Storage network VLAN interface (optional)
+auto ens3.20
+iface ens3.20 inet manual
+ vlan-raw-device ens3
+
+# Container/Host management bridge
+auto br-mgmt
+iface br-mgmt inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.10
+ address {{host_info[inventory_hostname].MGMT_IP}}
+ netmask 255.255.252.0
+
+# compute1 VXLAN (tunnel/overlay) bridge config
+auto br-vxlan
+iface br-vxlan inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.30
+ address {{host_info[inventory_hostname].VXLAN_IP}}
+ netmask 255.255.252.0
+
+# OpenStack Networking VLAN bridge
+auto br-vlan
+iface br-vlan inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3
+ address {{host_info[inventory_hostname].VLAN_IP}}
+ netmask 255.255.255.0
+ gateway 192.168.122.1
+ offload-sg off
+ # Create veth pair, don't bomb if already exists
+ pre-up ip link add br-vlan-veth type veth peer name eth12 || true
+ # Set both ends UP
+ pre-up ip link set br-vlan-veth up
+ pre-up ip link set eth12 up
+ # Delete veth pair on DOWN
+ post-down ip link del br-vlan-veth || true
+ bridge_ports br-vlan-veth
+
+# Add an additional address to br-vlan
+iface br-vlan inet static
+ # Flat network default gateway
+ # -- This needs to exist somewhere for network reachability
+ # -- from the router namespace for floating IP paths.
+ # -- Putting this here is primarily for tempest to work.
+ address {{host_info[inventory_hostname].VLAN_IP_SECOND}}
+ netmask 255.255.252.0
+ dns-nameserver 8.8.8.8 8.8.4.4
+
+# compute1 Storage bridge
+auto br-storage
+iface br-storage inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.20
+ address {{host_info[inventory_hostname].STORAGE_IP}}
+ netmask 255.255.252.0
diff --git a/prototypes/openstack-ansible/template/bifrost/controller.interface.j2 b/prototypes/openstack-ansible/template/bifrost/controller.interface.j2
new file mode 100644
index 000000000..74aeea99d
--- /dev/null
+++ b/prototypes/openstack-ansible/template/bifrost/controller.interface.j2
@@ -0,0 +1,71 @@
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+# Physical interface
+auto ens3
+iface ens3 inet manual
+
+# Container/Host management VLAN interface
+auto ens3.10
+iface ens3.10 inet manual
+ vlan-raw-device ens3
+
+# OpenStack Networking VXLAN (tunnel/overlay) VLAN interface
+auto ens3.30
+iface ens3.30 inet manual
+ vlan-raw-device ens3
+
+# Storage network VLAN interface (optional)
+auto ens3.20
+iface ens3.20 inet manual
+ vlan-raw-device ens3
+
+# Container/Host management bridge
+auto br-mgmt
+iface br-mgmt inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.10
+ address {{host_info[inventory_hostname].MGMT_IP}}
+ netmask 255.255.252.0
+
+# OpenStack Networking VXLAN (tunnel/overlay) bridge
+#
+# Only the COMPUTE and NETWORK nodes must have an IP address
+# on this bridge. When used by infrastructure nodes, the
+# IP addresses are assigned to containers which use this
+# bridge.
+#
+auto br-vxlan
+iface br-vxlan inet manual
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.30
+
+# OpenStack Networking VLAN bridge
+auto br-vlan
+iface br-vlan inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3
+ address {{host_info[inventory_hostname].VLAN_IP}}
+ netmask 255.255.255.0
+ gateway 192.168.122.1
+ dns-nameserver 8.8.8.8 8.8.4.4
+
+# compute1 Storage bridge
+auto br-storage
+iface br-storage inet static
+ bridge_stp off
+ bridge_waitport 0
+ bridge_fd 0
+ bridge_ports ens3.20
+ address {{host_info[inventory_hostname].STORAGE_IP}}
+ netmask 255.255.252.0
diff --git a/prototypes/openstack-ansible/var/ubuntu.yml b/prototypes/openstack-ansible/var/ubuntu.yml
new file mode 100644
index 000000000..71f54ecb5
--- /dev/null
+++ b/prototypes/openstack-ansible/var/ubuntu.yml
@@ -0,0 +1,6 @@
+---
+OSA_URL: https://git.openstack.org/openstack/openstack-ansible
+OSA_PATH: /opt/openstack-ansible
+OSA_ETC_PATH: /etc/openstack_deploy
+JUMPHOST_IP: 192.168.122.2
+host_info: {'jumphost':{'MGMT_IP': '172.29.236.10','VLAN_IP': '192.168.122.2', 'STORAGE_IP': '172.29.244.10'},'controller00':{'MGMT_IP': '172.29.236.11','VLAN_IP': '192.168.122.3', 'STORAGE_IP': '172.29.244.11'},'controller01':{'MGMT_IP': '172.29.236.12','VLAN_IP': '192.168.122.4', 'STORAGE_IP': '172.29.244.12'},'controller02':{'MGMT_IP': '172.29.236.13','VLAN_IP': '192.168.122.5', 'STORAGE_IP': '172.29.240.13'},'compute00':{'MGMT_IP': '172.29.236.14','VLAN_IP': '192.168.122.6','VLAN_IP_SECOND': '173.29.241.1','VXLAN_IP': '172.29.240.14', 'STORAGE_IP': '172.29.244.14'},'compute01':{'MGMT_IP': '172.29.236.15','VLAN_IP': '192.168.122.7','VLAN_IP_SECOND': '173.29.241.2','VXLAN_IP': '172.29.240.15', 'STORAGE_IP': '172.29.244.15'}}
diff --git a/utils/fetch_os_creds.sh b/utils/fetch_os_creds.sh
index 3667dbe6c..c99afaca8 100755
--- a/utils/fetch_os_creds.sh
+++ b/utils/fetch_os_creds.sh
@@ -38,6 +38,16 @@ verify_connectivity() {
error "Can not talk to $ip."
}
+
+swap_to_public() {
+ if [ "$1" != "" ]; then
+ info "Exchanging keystone public IP in rc file to $public_ip"
+ sed -i "/OS_AUTH_URL/c\export OS_AUTH_URL=\'$public_ip'" $dest_path
+ sed -i 's/internalURL/publicURL/g' $dest_path
+ fi
+}
+
+
: ${DEPLOY_TYPE:=''}
#Get options
@@ -111,6 +121,14 @@ if [ "$installer_type" == "fuel" ]; then
# but sometimes the output of endpoint-list is like this: http://172.30.9.70:8004/v1/%(tenant_id)s
# Fuel virtual need a fix
+ #convert to v3 URL
+ auth_url=$(cat $dest_path|grep AUTH_URL)
+ if [[ -z `echo $auth_url |grep v3` ]]; then
+ auth_url=$(echo $auth_url |sed "s|'$|v3&|")
+ fi
+ sed -i '/AUTH_URL/d' $dest_path
+ echo $auth_url >> $dest_path
+
elif [ "$installer_type" == "apex" ]; then
verify_connectivity $installer_ip
@@ -126,7 +144,7 @@ elif [ "$installer_type" == "compass" ]; then
verify_connectivity $installer_ip
controller_ip=$(sshpass -p'root' ssh 2>/dev/null $ssh_options root@${installer_ip} \
'mysql -ucompass -pcompass -Dcompass -e"select * from cluster;"' \
- | awk -F"," '{for(i=1;i<NF;i++)if($i~/\"host[1-5]\"/) {print $(i+1);break;}}' \
+ | awk -F"," '{for(i=1;i<NF;i++)if($i~/\"127.0.0.1\"/) {print $(i+2);break;}}' \
| grep -oP "\d+.\d+.\d+.\d+")
if [ -z $controller_ip ]; then
@@ -151,6 +169,7 @@ elif [ "$installer_type" == "compass" ]; then
| grep identity | awk '{print $14}')
fi
info "public_ip: $public_ip"
+ swap_to_public $public_ip
elif [ "$installer_type" == "joid" ]; then
@@ -182,6 +201,17 @@ elif [ "$installer_type" == "foreman" ]; then
'source keystonerc_admin;keystone endpoint-list'" \
| grep $admin_ip | sed 's/ /\n/g' | grep ^http | head -1) &> /dev/null
+elif [ "$installer_type" == "daisy" ]; then
+ verify_connectivity $installer_ip
+ cluster=$(sshpass -p r00tme ssh 2>/dev/null $ssh_options root@${installer_ip} \
+ "source ~/daisyrc_admin; daisy cluster-list"|grep active|head -1|awk -F "|" '{print $3}') &> /dev/null
+ if [ -z $cluster ]; then
+ echo "No active cluster detected in daisy"
+ exit 1
+ fi
+
+ sshpass -p r00tme scp 2>/dev/null $ssh_options root@${installer_ip}:/etc/kolla/admin-openrc.sh $dest_path &> /dev/null
+
else
error "Installer $installer is not supported by this script"
fi
diff --git a/utils/jenkins-jnlp-connect.sh b/utils/jenkins-jnlp-connect.sh
index be9fe184d..8fce2e021 100755
--- a/utils/jenkins-jnlp-connect.sh
+++ b/utils/jenkins-jnlp-connect.sh
@@ -92,13 +92,16 @@ main () {
exit 1
fi
+ chown=$(type -p chown)
+ mkdir=$(type -p mkdir)
+
makemonit () {
echo "Writing the following as monit config:"
cat << EOF | tee $monitconfdir/jenkins
check directory jenkins_piddir path /var/run/$jenkinsuser
-if does not exist then exec "/usr/bin/mkdir -p /var/run/$jenkinsuser"
-if failed uid $jenkinsuser then exec "/usr/bin/chown $jenkinsuser /var/run/$jenkinsuser"
-if failed gid $jenkinsuser then exec "/usr/bin/chown :$jenkinsuser /var/run/$jenkinsuser"
+if does not exist then exec "$mkdir -p /var/run/$jenkinsuser"
+if failed uid $jenkinsuser then exec "$chown $jenkinsuser /var/run/$jenkinsuser"
+if failed gid $jenkinsuser then exec "$chown :$jenkinsuser /var/run/$jenkinsuser"
check process jenkins with pidfile /var/run/$jenkinsuser/jenkins_jnlp_pid
start program = "/usr/bin/sudo -u $jenkinsuser /bin/bash -c 'cd $jenkinshome; export started_monit=true; $0 $@' with timeout 60 seconds"
@@ -111,9 +114,9 @@ EOF
#test for diff
if [[ "$(diff $monitconfdir/jenkins <(echo "\
check directory jenkins_piddir path /var/run/$jenkinsuser
-if does not exist then exec \"/usr/bin/mkdir -p /var/run/$jenkinsuser\"
-if failed uid $jenkinsuser then exec \"/usr/bin/chown $jenkinsuser /var/run/$jenkinsuser\"
-if failed gid $jenkinsuser then exec \"/usr/bin/chown :$jenkinsuser /var/run/$jenkinsuser\"
+if does not exist then exec \"$mkdir -p /var/run/$jenkinsuser\"
+if failed uid $jenkinsuser then exec \"$chown $jenkinsuser /var/run/$jenkinsuser\"
+if failed gid $jenkinsuser then exec \"$chown :$jenkinsuser /var/run/$jenkinsuser\"
check process jenkins with pidfile /var/run/$jenkinsuser/jenkins_jnlp_pid
start program = \"/usr/bin/sudo -u $jenkinsuser /bin/bash -c 'cd $jenkinshome; export started_monit=true; $0 $@' with timeout 60 seconds\"
diff --git a/utils/lab-reconfiguration/reconfigUcsNet.py b/utils/lab-reconfiguration/reconfigUcsNet.py
index 4c08f3dc9..0dd902f6d 100755
--- a/utils/lab-reconfiguration/reconfigUcsNet.py
+++ b/utils/lab-reconfiguration/reconfigUcsNet.py
@@ -22,8 +22,10 @@
# -p PASSWORD, --password=PASSWORD
# [Mandatory] Account Password for UCSM Login
# -f FILE, --file=FILE
-# [Optional] Yaml file with network config you want to set for POD
-# If not present only current network config will be printed
+# [Optional] Yaml file with network config you
+# want to set for POD
+# If not present only current network config
+# will be printed
#
import getpass
@@ -32,12 +34,14 @@ import platform
import yaml
import time
import sys
-from UcsSdk import *
-from collections import defaultdict
+from UcsSdk import LsmaintAck, LsPower, LsServer, OrgOrg
+from UcsSdk import UcsHandle, VnicEther, VnicEtherIf, YesOrNo
+
POD_PREFIX = "POD-2"
INSTALLER = "POD-21"
+
def getpassword(prompt):
if platform.system() == "Linux":
return getpass.unix_getpass(prompt=prompt)
@@ -51,7 +55,8 @@ def get_servers(handle=None):
"""
Return list of servers
"""
- orgObj = handle.GetManagedObject(None, OrgOrg.ClassId(), {OrgOrg.DN : "org-root"})[0]
+ orgObj = handle.GetManagedObject(
+ None, OrgOrg.ClassId(), {OrgOrg.DN: "org-root"})[0]
servers = handle.GetManagedObject(orgObj, LsServer.ClassId())
for server in servers:
if server.Type == 'instance' and POD_PREFIX in server.Dn:
@@ -63,10 +68,10 @@ def set_boot_policy(handle=None, server=None, policy=None):
Modify Boot policy of server
"""
obj = handle.GetManagedObject(None, LsServer.ClassId(), {
- LsServer.DN: server.Dn})
+ LsServer.DN: server.Dn})
handle.SetManagedObject(obj, LsServer.ClassId(), {
- LsServer.BOOT_POLICY_NAME: policy} )
- print " Configured boot policy: {}".format(policy)
+ LsServer.BOOT_POLICY_NAME: policy})
+ print(" Configured boot policy: {}".format(policy))
def ack_pending(handle=None, server=None):
@@ -74,30 +79,32 @@ def ack_pending(handle=None, server=None):
Acknowledge pending state of server
"""
handle.AddManagedObject(server, LsmaintAck.ClassId(), {
- LsmaintAck.DN: server.Dn + "/ack",
- LsmaintAck.DESCR:"",
- LsmaintAck.ADMIN_STATE:"trigger-immediate",
- LsmaintAck.SCHEDULER:"",
- LsmaintAck.POLICY_OWNER:"local"}, True)
- print " Pending-reboot -> Acknowledged."
+ LsmaintAck.DN: server.Dn + "/ack",
+ LsmaintAck.DESCR: "",
+ LsmaintAck.ADMIN_STATE: "trigger-immediate",
+ LsmaintAck.SCHEDULER: "",
+ LsmaintAck.POLICY_OWNER: "local"}, True)
+ print(" Pending-reboot -> Acknowledged.")
def boot_server(handle=None, server=None):
"""
Boot server (when is in power-off state)
"""
- obj = handle.GetManagedObject(None, LsServer.ClassId(), {LsServer.DN: server.Dn})
+ obj = handle.GetManagedObject(
+ None, LsServer.ClassId(), {LsServer.DN: server.Dn})
handle.AddManagedObject(obj, LsPower.ClassId(), {
- LsPower.DN: server.Dn + "/power",
- LsPower.STATE:"admin-up"}, True)
- print " Booting."
+ LsPower.DN: server.Dn + "/power",
+ LsPower.STATE: "admin-up"}, True)
+ print(" Booting.")
def get_vnics(handle=None, server=None):
"""
Return list of vnics for given server
"""
- vnics = handle.ConfigResolveChildren(VnicEther.ClassId(), server.Dn, None, YesOrNo.TRUE)
+ vnics = handle.ConfigResolveChildren(
+ VnicEther.ClassId(), server.Dn, None, YesOrNo.TRUE)
return vnics.OutConfigs.GetChild()
@@ -105,28 +112,36 @@ def get_network_config(handle=None):
"""
Print current network config
"""
- print "\nCURRENT NETWORK CONFIG:"
- print " d - default, t - tagged"
+ print("\nCURRENT NETWORK CONFIG:")
+ print(" d - default, t - tagged")
for server in get_servers(handle):
- print ' {}'.format(server.Name)
- print ' Boot policy: {}'.format(server.OperBootPolicyName)
+ print(' {}'.format(server.Name))
+ print(' Boot policy: {}'.format(server.OperBootPolicyName))
for vnic in get_vnics(handle, server):
- print ' {}'.format(vnic.Name)
- print ' {}'.format(vnic.Addr)
- vnicIfs = handle.ConfigResolveChildren(VnicEtherIf.ClassId(), vnic.Dn, None, YesOrNo.TRUE)
+ print(' {}'.format(vnic.Name))
+ print(' {}'.format(vnic.Addr))
+ vnicIfs = handle.ConfigResolveChildren(
+ VnicEtherIf.ClassId(), vnic.Dn, None, YesOrNo.TRUE)
for vnicIf in vnicIfs.OutConfigs.GetChild():
if vnicIf.DefaultNet == 'yes':
- print ' Vlan: {}d'.format(vnicIf.Vnet)
+ print(' Vlan: {}d'.format(vnicIf.Vnet))
else:
- print ' Vlan: {}t'.format(vnicIf.Vnet)
+ print(' Vlan: {}t'.format(vnicIf.Vnet))
-def add_interface(handle=None, lsServerDn=None, vnicEther=None, templName=None, order=None, macAddr=None):
+def add_interface(handle=None,
+ lsServerDn=None,
+ vnicEther=None,
+ templName=None,
+ order=None,
+ macAddr=None):
"""
Add interface to server specified by server.DN name
"""
- print " Adding interface: {}, template: {}, server.Dn: {}".format(vnicEther, templName, lsServerDn)
- obj = handle.GetManagedObject(None, LsServer.ClassId(), {LsServer.DN:lsServerDn})
+ print(" Adding interface: {}, template: {}, server.Dn: {}".format(
+ vnicEther, templName, lsServerDn))
+ obj = handle.GetManagedObject(
+ None, LsServer.ClassId(), {LsServer.DN: lsServerDn})
vnicEtherDn = lsServerDn + "/ether-" + vnicEther
params = {
VnicEther.STATS_POLICY_NAME: "default",
@@ -146,8 +161,9 @@ def remove_interface(handle=None, vnicEtherDn=None):
"""
Remove interface specified by Distinguished Name (vnicEtherDn)
"""
- print " Removing interface: {}".format(vnicEtherDn)
- obj = handle.GetManagedObject(None, VnicEther.ClassId(), {VnicEther.DN:vnicEtherDn})
+ print(" Removing interface: {}".format(vnicEtherDn))
+ obj = handle.GetManagedObject(
+ None, VnicEther.ClassId(), {VnicEther.DN: vnicEtherDn})
handle.RemoveManagedObject(obj)
@@ -165,32 +181,37 @@ def set_network(handle=None, yamlFile=None):
Configure VLANs on POD according specified network
"""
# add interfaces and bind them with vNIC templates
- print "\nRECONFIGURING VNICs..."
+ print("\nRECONFIGURING VNICs...")
pod_data = read_yaml_file(yamlFile)
network = pod_data['network']
for index, server in enumerate(get_servers(handle)):
# Assign template to interface
for iface, data in network.iteritems():
- add_interface(handle, server.Dn, iface, data['template'], data['order'], data['mac-list'][index])
+ add_interface(handle, server.Dn, iface, data['template'], data[
+ 'order'], data['mac-list'][index])
- # Remove other interfaces which have not assigned required vnic template
+ # Remove other interfaces which have not assigned required vnic
+ # template
vnics = get_vnics(handle, server)
for vnic in vnics:
- if not any(data['template'] in vnic.OperNwTemplName for iface, data in network.iteritems()):
+ if not any(data['template'] in vnic.OperNwTemplName for
+ iface, data in network.iteritems()):
remove_interface(handle, vnic.Dn)
- print " {} removed, template: {}".format(vnic.Name, vnic.OperNwTemplName)
+ print(" {} removed, template: {}".format(
+ vnic.Name, vnic.OperNwTemplName))
# Set boot policy template
- if not INSTALLER in server.Dn:
+ if INSTALLER not in server.Dn:
set_boot_policy(handle, server, pod_data['boot-policy'])
if __name__ == "__main__":
- print "\n*** SKIPING RECONFIGURATION.***\n"
+ print("\n*** SKIPING RECONFIGURATION.***\n")
sys.exit(0)
# Latest urllib2 validate certs by default
- # The process wide "revert to the old behaviour" hook is to monkeypatch the ssl module
+ # The process wide "revert to the old behaviour" hook is to monkeypatch
+ # the ssl module
# https://bugs.python.org/issue22417
import ssl
if hasattr(ssl, '_create_unverified_context'):
@@ -198,14 +219,15 @@ if __name__ == "__main__":
try:
handle = UcsHandle()
parser = optparse.OptionParser()
- parser.add_option('-i', '--ip',dest="ip",
- help="[Mandatory] UCSM IP Address")
- parser.add_option('-u', '--username',dest="userName",
- help="[Mandatory] Account Username for UCSM Login")
- parser.add_option('-p', '--password',dest="password",
- help="[Mandatory] Account Password for UCSM Login")
- parser.add_option('-f', '--file',dest="yamlFile",
- help="[Optional] Yaml file contains network config you want to set on UCS POD1")
+ parser.add_option('-i', '--ip', dest="ip",
+ help="[Mandatory] UCSM IP Address")
+ parser.add_option('-u', '--username', dest="userName",
+ help="[Mandatory] Account Username for UCSM Login")
+ parser.add_option('-p', '--password', dest="password",
+ help="[Mandatory] Account Password for UCSM Login")
+ parser.add_option('-f', '--file', dest="yamlFile",
+ help=("[Optional] Yaml file contains network "
+ "config you want to set on UCS POD1"))
(options, args) = parser.parse_args()
if not options.ip:
@@ -215,26 +237,27 @@ if __name__ == "__main__":
parser.print_help()
parser.error("Provide UCSM UserName")
if not options.password:
- options.password=getpassword("UCSM Password:")
+ options.password = getpassword("UCSM Password:")
handle.Login(options.ip, options.userName, options.password)
# Change vnic template if specified in cli option
- if (options.yamlFile != None):
+ if (options.yamlFile is not None):
set_network(handle, options.yamlFile)
time.sleep(5)
- print "\nWait until Overall Status of all nodes is OK..."
- timeout = time.time() + 60*10 #10 minutes timeout
+ print("\nWait until Overall Status of all nodes is OK...")
+ timeout = time.time() + 60 * 10 # 10 minutes timeout
while True:
list_of_states = []
for server in get_servers(handle):
if server.OperState == "power-off":
- boot_server(handle,server)
+ boot_server(handle, server)
if server.OperState == "pending-reboot":
- ack_pending(handle,server)
+ ack_pending(handle, server)
list_of_states.append(server.OperState)
- print " {}, {} seconds remains.".format(list_of_states, round(timeout-time.time()))
+ print(" {}, {} seconds remains.".format(
+ list_of_states, round(timeout - time.time())))
if all(state == "ok" for state in list_of_states):
break
if time.time() > timeout:
@@ -246,11 +269,12 @@ if __name__ == "__main__":
handle.Logout()
- except Exception, err:
+ except Exception as err:
handle.Logout()
- print "Exception:", str(err)
- import traceback, sys
- print '-'*60
+ print("Exception:", str(err))
+ import traceback
+ import sys
+ print('-' * 60)
traceback.print_exc(file=sys.stdout)
- print '-'*60
+ print('-' * 60)
sys.exit(1)
diff --git a/utils/opnfv-artifacts.py b/utils/opnfv-artifacts.py
index 876efedba..2f2cc41ba 100644
--- a/utils/opnfv-artifacts.py
+++ b/utils/opnfv-artifacts.py
@@ -28,56 +28,55 @@ from apiclient.errors import HttpError
import argparse
import json
-import os
import sys
api = {
- 'projects': {},
- 'docs': {},
- 'releases': {},
+ 'projects': {},
+ 'docs': {},
+ 'releases': {},
}
releases = [
- 'arno.2015.1.0',
- 'arno.2015.2.0',
- 'brahmaputra.1.0',
+ 'arno.2015.1.0',
+ 'arno.2015.2.0',
+ 'brahmaputra.1.0',
]
# List of file extensions to filter out
ignore_extensions = [
- '.buildinfo',
- '.woff',
- '.ttf',
- '.svg',
- '.eot',
- '.pickle',
- '.doctree',
- '.js',
- '.png',
- '.css',
- '.gif',
- '.jpeg',
- '.jpg',
- '.bmp',
+ '.buildinfo',
+ '.woff',
+ '.ttf',
+ '.svg',
+ '.eot',
+ '.pickle',
+ '.doctree',
+ '.js',
+ '.png',
+ '.css',
+ '.gif',
+ '.jpeg',
+ '.jpg',
+ '.bmp',
]
parser = argparse.ArgumentParser(
- description='OPNFV Artifacts JSON Generator')
+ description='OPNFV Artifacts JSON Generator')
parser.add_argument(
- '-k',
- dest='key',
- default='',
- help='API Key for Google Cloud Storage')
+ '-k',
+ dest='key',
+ default='',
+ help='API Key for Google Cloud Storage')
parser.add_argument(
- '-p',
- default=None,
- dest='pretty',
- action='store_const',
- const=2,
- help='pretty print the output')
+ '-p',
+ default=None,
+ dest='pretty',
+ action='store_const',
+ const=2,
+ help='pretty print the output')
# Parse and assign arguments
args = parser.parse_args()
@@ -130,7 +129,6 @@ def has_logs(gerrit_review):
return False
-
def has_ignorable_extension(filename):
for extension in ignore_extensions:
if filename.lower().endswith(extension):
@@ -148,11 +146,11 @@ def get_results(key):
files = storage.objects().list(bucket='artifacts.opnfv.org',
fields='nextPageToken,'
'items('
- 'name,'
- 'mediaLink,'
- 'updated,'
- 'contentType,'
- 'size'
+ 'name,'
+ 'mediaLink,'
+ 'updated,'
+ 'contentType,'
+ 'size'
')')
while (files is not None):
sites = files.execute()
@@ -173,7 +171,8 @@ def get_results(key):
project = site_split[0]
name = '/'.join(site_split[1:])
- proxy = "http://build.opnfv.org/artifacts.opnfv.org/%s" % site['name']
+ proxy = "http://build.opnfv.org/artifacts.opnfv.org/%s" % site[
+ 'name']
if name.endswith('.html'):
href = "http://artifacts.opnfv.org/%s" % site['name']
href_type = 'view'
@@ -183,7 +182,7 @@ def get_results(key):
gerrit = has_gerrit_review(site_split)
logs = False # has_logs(gerrit)
- documentation = has_documentation(site_split)
+ # documentation = has_documentation(site_split)
release = has_release(site_split)
category = 'project'
diff --git a/utils/push-test-logs.sh b/utils/push-test-logs.sh
index 913748f03..9099657c8 100644
--- a/utils/push-test-logs.sh
+++ b/utils/push-test-logs.sh
@@ -15,19 +15,21 @@ export PATH=$PATH:/usr/local/bin/
git_sha1="$(git rev-parse HEAD)"
res_build_date=${1:-$(date -u +"%Y-%m-%d_%H-%M-%S")}
project=$PROJECT
-branch=${GIT_BRANCH##*/}
+branch=${BRANCH##*/}
testbed=$NODE_NAME
dir_result="${HOME}/opnfv/$project/results/${branch}"
# src: https://wiki.opnfv.org/display/INF/Hardware+Infrastructure
-# + intel-pod3 (vsperf)
+# + intel-pod12 (vsperf)
node_list=(\
-'lf-pod1' 'lf-pod2' 'intel-pod2' 'intel-pod3' \
+'lf-pod1' 'lf-pod2' 'intel-pod2' 'intel-pod12' \
'intel-pod5' 'intel-pod6' 'intel-pod7' 'intel-pod8' \
-'ericsson-pod2' 'ericsson-pod3' 'ericsson-pod4' \
+'ericsson-pod1' 'ericsson-pod2' \
'ericsson-virtual1' 'ericsson-virtual2' 'ericsson-virtual3' \
-'ericsson-virtual4' 'ericsson-virtual5' \
+'ericsson-virtual4' 'ericsson-virtual5' 'ericsson-virtual12' \
'arm-pod1' 'arm-pod3' \
-'huawei-pod1' 'huawei-pod2' 'huawei-virtual1' 'huawei-virtual2' 'huawei-virtual3' 'huawei-virtual4')
+'huawei-pod1' 'huawei-pod2' 'huawei-pod3' 'huawei-pod4' 'huawei-pod5' \
+'huawei-pod6' 'huawei-pod7' 'huawei-pod12' \
+'huawei-virtual1' 'huawei-virtual2' 'huawei-virtual3' 'huawei-virtual4')
if [[ ! " ${node_list[@]} " =~ " ${testbed} " ]]; then
diff --git a/utils/test/dashboard/dashboard/common/elastic_access.py b/utils/test/dashboard/dashboard/common/elastic_access.py
index aaf776f7a..eb29ce879 100644
--- a/utils/test/dashboard/dashboard/common/elastic_access.py
+++ b/utils/test/dashboard/dashboard/common/elastic_access.py
@@ -30,7 +30,7 @@ def publish_docs(url, creds=None, body=None):
def _get_docs_nr(url, creds=None, body=None):
res_data = _get('{}/_search?size=0'.format(url), creds=creds, body=body)
- print type(res_data), res_data
+ print(type(res_data), res_data)
return res_data['hits']['total']
diff --git a/utils/test/dashboard/dashboard/conf/testcases.py b/utils/test/dashboard/dashboard/conf/testcases.py
index ff801b4c9..98ce20984 100644
--- a/utils/test/dashboard/dashboard/conf/testcases.py
+++ b/utils/test/dashboard/dashboard/conf/testcases.py
@@ -21,4 +21,4 @@ def get_format(project, case):
if __name__ == '__main__':
fmt = get_format('functest', 'vping_ssh')
- print fmt
+ print(fmt)
diff --git a/utils/test/dashboard/dashboard/elastic2kibana/utility.py b/utils/test/dashboard/dashboard/elastic2kibana/utility.py
index 55578bd8c..40d9202a6 100644
--- a/utils/test/dashboard/dashboard/elastic2kibana/utility.py
+++ b/utils/test/dashboard/dashboard/elastic2kibana/utility.py
@@ -2,7 +2,8 @@ import json
from jinja2 import Environment, PackageLoader
-env = Environment(loader=PackageLoader('dashboard', 'elastic2kibana/templates'))
+env = Environment(loader=PackageLoader('dashboard',
+ 'elastic2kibana/templates'))
env.filters['jsonify'] = json.dumps
diff --git a/utils/test/dashboard/dashboard/functest/format.py b/utils/test/dashboard/dashboard/functest/format.py
index ef485bae0..75d361ff8 100644
--- a/utils/test/dashboard/dashboard/functest/format.py
+++ b/utils/test/dashboard/dashboard/functest/format.py
@@ -6,7 +6,8 @@ def _convert_value(value):
def _convert_duration(duration):
- if (isinstance(duration, str) or isinstance(duration, unicode)) and ':' in duration:
+ if ((isinstance(duration, str) or
+ isinstance(duration, unicode)) and ':' in duration):
hours, minutes, seconds = duration.split(":")
hours = _convert_value(hours)
minutes = _convert_value(minutes)
@@ -42,11 +43,11 @@ def format_normal(testcase):
testcase_tests = float(testcase_details['tests'])
testcase_failures = float(testcase_details['failures'])
if testcase_tests != 0:
- testcase_details['success_percentage'] = 100 * (testcase_tests - testcase_failures) / testcase_tests
+ testcase_details['success_percentage'] = 100 * \
+ (testcase_tests - testcase_failures) / testcase_tests
else:
testcase_details['success_percentage'] = 0
-
return found
@@ -115,28 +116,33 @@ def format_onos(testcase):
"""
testcase_details = testcase['details']
- if 'FUNCvirNet' not in testcase_details or 'FUNCvirNetL3' not in testcase_details:
+ if ('FUNCvirNet' not in testcase_details or
+ 'FUNCvirNetL3' not in testcase_details):
return False
funcvirnet_details = testcase_details['FUNCvirNet']['status']
- funcvirnet_stats = _get_statistics(funcvirnet_details, ('Case result',), ('PASS', 'FAIL'))
+ funcvirnet_stats = _get_statistics(
+ funcvirnet_details, ('Case result',), ('PASS', 'FAIL'))
funcvirnet_passed = funcvirnet_stats['PASS']
funcvirnet_failed = funcvirnet_stats['FAIL']
funcvirnet_all = funcvirnet_passed + funcvirnet_failed
funcvirnetl3_details = testcase_details['FUNCvirNetL3']['status']
- funcvirnetl3_stats = _get_statistics(funcvirnetl3_details, ('Case result',), ('PASS', 'FAIL'))
+ funcvirnetl3_stats = _get_statistics(
+ funcvirnetl3_details, ('Case result',), ('PASS', 'FAIL'))
funcvirnetl3_passed = funcvirnetl3_stats['PASS']
funcvirnetl3_failed = funcvirnetl3_stats['FAIL']
funcvirnetl3_all = funcvirnetl3_passed + funcvirnetl3_failed
testcase_details['FUNCvirNet'] = {
- 'duration': _convert_duration(testcase_details['FUNCvirNet']['duration']),
+ 'duration':
+ _convert_duration(testcase_details['FUNCvirNet']['duration']),
'tests': funcvirnet_all,
'failures': funcvirnet_failed
}
testcase_details['FUNCvirNetL3'] = {
- 'duration': _convert_duration(testcase_details['FUNCvirNetL3']['duration']),
+ 'duration':
+ _convert_duration(testcase_details['FUNCvirNetL3']['duration']),
'tests': funcvirnetl3_all,
'failures': funcvirnetl3_failed
}
diff --git a/utils/test/dashboard/dashboard/mongo2elastic/main.py b/utils/test/dashboard/dashboard/mongo2elastic/main.py
index 688f55f7d..e33252df2 100644
--- a/utils/test/dashboard/dashboard/mongo2elastic/main.py
+++ b/utils/test/dashboard/dashboard/mongo2elastic/main.py
@@ -27,7 +27,8 @@ parser.add_argument('-ld', '--latest-days',
metavar='N',
help='get entries old at most N days from mongodb and'
' parse those that are not already in elasticsearch.'
- ' If not present, will get everything from mongodb, which is the default')
+ ' If not present, will get everything from mongodb,'
+ ' which is the default')
args = parser.parse_args()
CONF = APIConfig().parse(args.config_file)
@@ -37,6 +38,7 @@ tmp_docs_file = './mongo-{}.json'.format(uuid.uuid4())
class DocumentVerification(object):
+
def __init__(self, doc):
super(DocumentVerification, self).__init__()
self.doc = doc
@@ -55,8 +57,8 @@ class DocumentVerification(object):
for key, value in self.doc.items():
if key in mandatory_fields:
if value is None:
- logger.info("Skip testcase '%s' because field '%s' missing" %
- (self.doc_id, key))
+ logger.info("Skip testcase '%s' because field "
+ "'%s' missing" % (self.doc_id, key))
self.skip = True
else:
mandatory_fields.remove(key)
@@ -131,10 +133,12 @@ class DocumentPublisher(object):
self._publish()
def _publish(self):
- status, data = elastic_access.publish_docs(self.elastic_url, self.creds, self.doc)
+ status, data = elastic_access.publish_docs(
+ self.elastic_url, self.creds, self.doc)
if status > 300:
logger.error('Publish record[{}] failed, due to [{}]'
- .format(self.doc, json.loads(data)['error']['reason']))
+ .format(self.doc,
+ json.loads(data)['error']['reason']))
def _fix_date(self, date_string):
if isinstance(date_string, dict):
@@ -163,7 +167,8 @@ class DocumentsPublisher(object):
def export(self):
if self.days > 0:
- past_time = datetime.datetime.today() - datetime.timedelta(days=self.days)
+ past_time = datetime.datetime.today(
+ ) - datetime.timedelta(days=self.days)
query = '''{{
"project_name": "{}",
"case_name": "{}",
@@ -182,7 +187,7 @@ class DocumentsPublisher(object):
try:
subprocess.check_call(cmd)
return self
- except Exception, err:
+ except Exception as err:
logger.error("export mongodb failed: %s" % err)
self._remove()
exit(-1)
@@ -217,7 +222,8 @@ class DocumentsPublisher(object):
}}'''.format(self.project, self.case, self.days)
else:
raise Exception('Update days must be non-negative')
- self.existed_docs = elastic_access.get_docs(self.elastic_url, self.creds, body)
+ self.existed_docs = elastic_access.get_docs(
+ self.elastic_url, self.creds, body)
return self
def publish(self):
diff --git a/utils/test/dashboard/kibana_cleanup.py b/utils/test/dashboard/kibana_cleanup.py
index ee0190049..7e3662c29 100644
--- a/utils/test/dashboard/kibana_cleanup.py
+++ b/utils/test/dashboard/kibana_cleanup.py
@@ -9,7 +9,8 @@ from dashboard.common import elastic_access
logger = logging.getLogger('clear_kibana')
logger.setLevel(logging.DEBUG)
file_handler = logging.FileHandler('/var/log/{}.log'.format('clear_kibana'))
-file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
+file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: '
+ '%(message)s'))
logger.addHandler(file_handler)
@@ -21,12 +22,17 @@ def delete_all(url, es_creds):
if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='Delete saved kibana searches, visualizations and dashboards')
- parser.add_argument('-e', '--elasticsearch-url', default='http://localhost:9200',
- help='the url of elasticsearch, defaults to http://localhost:9200')
+ parser = argparse.ArgumentParser(
+ description=('Delete saved kibana searches, '
+ 'visualizations and dashboards'))
+ parser.add_argument('-e', '--elasticsearch-url',
+ default='http://localhost:9200',
+ help=('the url of elasticsearch, '
+ 'defaults to http://localhost:9200'))
parser.add_argument('-u', '--elasticsearch-username', default=None,
- help='The username with password for elasticsearch in format username:password')
+ help=('The username with password for elasticsearch '
+ 'in format username:password'))
args = parser.parse_args()
base_elastic_url = args.elasticsearch_url
@@ -38,4 +44,3 @@ if __name__ == '__main__':
for url in urls:
delete_all(url, es_creds)
-
diff --git a/utils/test/declaration/addtestcase.php b/utils/test/declaration/addtestcase.php
deleted file mode 100644
index 0e5bed689..000000000
--- a/utils/test/declaration/addtestcase.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-function sendPostData($url, $post){
- $ch = curl_init($url);
- $headers= array('Accept: application/json','Content-Type: application/json');
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
- curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
- $result = curl_exec($ch);
- curl_close($ch);
- return $result;
-}
-
-if(isset($_REQUEST['url'])){
- $url=$_REQUEST['url'];
-}
-if(isset($_REQUEST['name'])){
- $name=$_REQUEST['name'];
-}
-if(isset($_REQUEST['desc'])){
- $desc=$_REQUEST['desc'];
-}
-if(isset($_REQUEST['project'])){
-
- $url_send=$_REQUEST['project'];
- $url_send="http://testresults.opnfv.org:80/test/api/v1/projects/".$url_send."/cases";
- $str_data=array('url'=>$url,'name'=>$name,'description'=>$desc);
- $str_data=json_encode($str_data);
- $res=sendPostData($url_send, $str_data);
- echo '<div class="alert alert-success"> <strong>Success!</strong> Added New test Case </div>';
-
-}else{
-
- echo '<div class="alert alert-danger"> <strong>Error!</strong> Failed to Add New test Case </div>';
-
-}
-
-?>
-
diff --git a/utils/test/declaration/index.php b/utils/test/declaration/index.php
deleted file mode 100644
index b2c5d0370..000000000
--- a/utils/test/declaration/index.php
+++ /dev/null
@@ -1,221 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <title>OPNFV DashBoard</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
- <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
-<script>
-$(function() {
-
- $('form#new_testcase').on('submit', function(){
- var selected = $('select#sel_pro2').find("option:selected").val();
- var uri = $('input#uri').val();
- var name = $('input#name').val();
- var desc = $('textarea#desc').val();
- var new_url="http://testresults.opnfv.org:80/test/api/v1/projects/"+selected+"/cases";
- $.post("addtestcase.php", {"project":selected,"url":uri,"name":name,"description":desc}, function(result){
- $("div#result").html(result);
- });
- });
-
-});
-
-$(function() {
-
- $('select#sel1').on('change', function(){
- var selected = $(this).find("option:selected").val();
- var new_url="http://testresults.opnfv.org:80/test/api/v1/projects/"+selected+"/cases";
- //$.post('testcases.php', {project: selected});
- console.log(selected);
- $.post("testcases.php", {project: selected}, function(result){
- $("div#4a").html(result);
- });
-
- });
-
-});
-</script>
-<style>
-body {
- padding : 10px ;
-}
-
-#exTab1 .tab-content {
- color : black;
- padding : 5px 15px;
-}
-
-#exTab2 h3 {
- color : white;
- background-color: #428bca;
- padding : 5px 15px;
-}
-
-/* remove border radius for the tab */
-
-#exTab1 .nav-pills > li > a {
- border-radius: 0;
-}
-
-/* change border radius for the tab , apply corners on top*/
-
-#exTab3 .nav-pills > li > a {
- border-radius: 4px 4px 0 0 ;
-}
-
-#exTab3 .tab-content {
- color : white;
- background-color: #428bca;
- padding : 5px 15px;
-}
-
-</style>
-</head>
-<body>
-
-<div class="container">
- <h1>OPNFV DASHBOARD: </h1></div>
-<div id="exTab1" class="container">
- <ul class="nav nav-pills">
- <li class="active">
- <a href="#1a" data-toggle="tab">PODS</a>
- </li>
- <li><a href="#2a" data-toggle="tab">PROJECTS</a>
- </li>
- <li><a href="#3a" data-toggle="tab">TESTCASES</a>
- </li>
- <li><a href="#5a" data-toggle="tab">ADD TESTCASE</a>
- </li>
- <li><a href="http://testresults.opnfv.org/kibana_dashboards/" >RESULTS</a>
- </li>
- </ul>
- <div class="tab-content clearfix">
- <div class="tab-pane active" id="1a">
- <table class="table table-striped">
- <thead>
- <tr>
- <th>#</th>
- <th>Pod Name</th>
- <th>Creation Date</th>
- <th>Role</th>
- <th>Mode</th>
- </tr>
- </thead>
- <?php
- $url = "http://testresults.opnfv.org:80/test/api/v1/pods";
- $response = file_get_contents($url);
- $data = json_decode($response);
- $pods = $data->pods;
- $i=1;
- foreach ( $pods as $pod ){
-
- $column_str="";
- $column_str="<tr><td>".$i."</td>";
- $column_str=$column_str."<td>".$pod->name."</td>";
- $column_str= $column_str."<td>".$pod->creation_date."</td>";
- $column_str= $column_str."<td>".$pod->role."</td>";
- $column_str= $column_str."<td>".$pod->mode."</td>";
- $column_str= $column_str."</tr>";
- echo $column_str;
- $i=$i+1;
- }
- ?>
- </table>
- </div>
- <div class="tab-pane" id="2a">
- <table class="table table-striped">
- <thead>
- <tr>
- <th>#</th>
- <th>Project</th>
- <th>Creation Date</th>
- </tr>
- </thead>
- <?php
- $url = "http://testresults.opnfv.org:80/test/api/v1/projects";
- $response = file_get_contents($url);
- $data = json_decode($response);
- $projects=$data->projects;
- $i=0;
- foreach ( $projects as $project ){
-
- $column_str="";
- $column_str="<tr><td>".$i."</td>";
- $column_str=$column_str."<td>".$project->name."</td>";
- $column_str= $column_str."<td>".$project->creation_date."</td>";
- $column_str= $column_str."</tr>";
- echo $column_str;
- $i=$i+1;
- }
-?>
- </table>
- </div>
- <div class="tab-pane" id="3a">
-<div class="form-group">
- <label for="sel1">Select list:</label>
- <select class="form-control" id="sel1">
-<?php
- $url = "http://testresults.opnfv.org:80/test/api/v1/projects";
- $response = file_get_contents($url);
- $data = json_decode($response);
- $projects=$data->projects;
- $i=0;
- $firstvalue=$projects[0]->name;
- foreach ( $projects as $project ){
- $column_str="";
- $column_str="<option>".$project->name."</option>";
- echo $column_str;
- }
-
-?>
-</select>
-</div>
- <div class="tab-pane" id="4a">
- <?php
- require "testcases.php";
- ?>
- </div>
- </div>
- <div class="tab-pane" id="5a">
- <form role="form" id="new_testcase">
-<div class="form-group">
- <label for="sel1">Select list:</label>
- <select class="form-control" id="sel_pro2">
-<?php
- $url = "http://testresults.opnfv.org:80/test/api/v1/projects";
- $response = file_get_contents($url);
- $data = json_decode($response);
- $projects=$data->projects;
- $i=0;
- $firstvalue=$projects[0]->name;
- foreach ( $projects as $project ){
- $column_str="";
- $column_str="<option>".$project->name."</option>";
- echo $column_str;
- }
-?>
-</select>
-</div>
-<div class="form-group"> <!-- Name field -->
- <label class="control-label " for="name">TestCase URI</label>
- <input class="form-control" id="uri" name="uri" type="text"/>
- </div>
-<div class="form-group"> <!-- Name field -->
- <label class="control-label " for="name">TestCase Name</label>
- <input class="form-control" id="name" name="name" type="text"/>
- </div>
-<div class="form-group"> <!-- Name field -->
- <label class="control-label " for="name">Description</label>
- <textarea class="form-control" rows="5" id="desc"></textarea>
- </div>
- <button type="submit" class="btn btn-default">Submit</button>
-</form>
- </div>
-<div class="container" id="result"></div>
- </div>
-</div>
-</body>
-</html>
diff --git a/utils/test/declaration/testcases.php b/utils/test/declaration/testcases.php
deleted file mode 100644
index 20645807e..000000000
--- a/utils/test/declaration/testcases.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
- if(isset($_REQUEST['project'])){
- $selected=$_REQUEST['project'];
- }
- else{
- $url = "http://testresults.opnfv.org:80/test/api/v1/projects";
- $response = file_get_contents($url);
- $data = json_decode($response);
- $projects=$data->projects;
- $selected=$projects[0]->name;
- }
- $new_url="http://testresults.opnfv.org:80/test/api/v1/projects/".$selected."/cases";
- $response = file_get_contents($new_url);
- $data = json_decode($response);
- $testcases=$data->testcases;
- $i=0;
- $column_str="";
- $column_str=$column_str."<table class=\"table table-striped\"><tr>";
- $column_str=$column_str."<th>#</th><th>Test Case Name</th>";
- $column_str=$column_str."<th>Creation Date</th>";
- $column_str=$column_str."<th>Description</th></tr>";
- foreach ( $testcases as $testcase ){
- $i=$i+1;
- $column_str=$column_str."<tr>";
- $column_str=$column_str."<td>".$i."</td>";
- $column_str=$column_str."<td>".$testcase->name."</td>";
- $column_str=$column_str."<td>".$testcase->creation_date."</td>";
- $column_str=$column_str."<td>".$testcase->description."</td>";
- $column_str=$column_str."</tr>";
-
- }
- $column_str=$column_str."</table>";
- echo $column_str;
-
-?>
-
diff --git a/utils/test/reporting/api/handlers/landing.py b/utils/test/reporting/api/handlers/landing.py
index 137c05007..ae1fd2037 100644
--- a/utils/test/reporting/api/handlers/landing.py
+++ b/utils/test/reporting/api/handlers/landing.py
@@ -6,15 +6,172 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import requests
+
from tornado.web import RequestHandler
from tornado.escape import json_encode
+from tornado.escape import json_decode
-class FiltersHandler(RequestHandler):
- def get(self):
- return self.write(json_encode({'status': 'SUCCESS'}))
+class BaseHandler(RequestHandler):
+ def _set_header(self):
+ self.set_header('Access-Control-Allow-Origin', '*')
+ self.set_header('Access-Control-Allow-Headers',
+ 'Content-Type, Content-Length, Authorization, \
+ Accept, X-Requested-With , PRIVATE-TOKEN')
+ self.set_header('Access-Control-Allow-Methods',
+ 'PUT, POST, GET, DELETE, OPTIONS')
-class ScenariosHandler(RequestHandler):
+class FiltersHandler(BaseHandler):
def get(self):
- return self.write(json_encode({'status': 'SUCCESS'}))
+ self._set_header()
+
+ filters = {
+ 'filters': {
+ 'status': ['success', 'warning', 'danger'],
+ 'projects': ['functest', 'yardstick'],
+ 'installers': ['apex', 'compass', 'fuel', 'joid'],
+ 'version': ['colorado', 'master'],
+ 'loops': ['daily', 'weekly', 'monthly'],
+ 'time': ['10 days', '30 days']
+ }
+ }
+ return self.write(json_encode(filters))
+
+
+class ScenariosHandler(BaseHandler):
+ def post(self):
+ self._set_header()
+
+ body = json_decode(self.request.body)
+ args = self._get_args(body)
+
+ scenarios = self._get_result_data(self._get_scenarios(), args)
+
+ return self.write(json_encode(dict(scenarios=scenarios)))
+
+ def _get_result_data(self, data, args):
+ data = self._filter_status(data, args)
+ return {s: self._get_scenario_result(s, data[s], args) for s in data}
+
+ def _filter_status(self, data, args):
+ return {k: v for k, v in data.items() if v['status'] in args['status']}
+
+ def _get_scenario_result(self, scenario, data, args):
+ result = {
+ 'status': data.get('status'),
+ 'installers': self._get_installers_result(data['installers'], args)
+ }
+ return result
+
+ def _get_installers_result(self, data, args):
+ func = self._get_installer_result
+ return {k: func(k, data.get(k, {}), args) for k in args['installers']}
+
+ def _get_installer_result(self, installer, data, args):
+ projects = data.get(args['version'], [])
+ return [self._get_project_data(projects, p) for p in args['projects']]
+
+ def _get_project_data(self, projects, project):
+ atom = {
+ 'project': project,
+ 'score': None,
+ 'status': None
+ }
+ for p in projects:
+ if p['project'] == project:
+ return p
+ return atom
+
+ def _get_scenarios(self):
+ url = 'http://testresults.opnfv.org/test/api/v1/scenarios'
+ resp = requests.get(url).json()
+ data = self._change_to_utf8(resp).get('scenarios', {})
+ return {a.get('name'): self._get_scenario(a.get('installers', [])
+ ) for a in data}
+
+ def _get_scenario(self, data):
+ installers = {a.get('installer'): self._get_installer(a.get('versions',
+ [])
+ ) for a in data}
+ scenario = {
+ 'status': self._get_status(),
+ 'installers': installers
+ }
+ return scenario
+
+ def _get_status(self):
+ return 'success'
+
+ def _get_installer(self, data):
+ return {a.get('version'): self._get_version(a) for a in data}
+
+ def _get_version(self, data):
+ try:
+ scores = data.get('score', {}).get('projects')[0]
+ trusts = data.get('trust_indicator', {}).get('projects')[0]
+ except (TypeError, IndexError):
+ return []
+ else:
+ scores = {key: [dict(date=a.get('date')[:10],
+ score=a.get('score')
+ ) for a in scores[key]] for key in scores}
+ trusts = {key: [dict(date=a.get('date')[:10],
+ status=a.get('status')
+ ) for a in trusts[key]] for key in trusts}
+ atom = self._get_atom(scores, trusts)
+ return [dict(project=k,
+ score=sorted(atom[k], reverse=True)[0].get('score'),
+ status=sorted(atom[k], reverse=True)[0].get('status')
+ ) for k in atom if atom[k]]
+
+ def _get_atom(self, scores, trusts):
+ s = {k: {a['date']: a['score'] for a in scores[k]} for k in scores}
+ t = {k: {a['date']: a['status'] for a in trusts[k]} for k in trusts}
+ return {k: [dict(score=s[k][a], status=t[k][a], data=a
+ ) for a in s[k] if a in t[k]] for k in s}
+
+ def _change_to_utf8(self, obj):
+ if isinstance(obj, dict):
+ return {str(k): self._change_to_utf8(v) for k, v in obj.items()}
+ elif isinstance(obj, list):
+ return [self._change_to_utf8(ele) for ele in obj]
+ else:
+ try:
+ new = eval(obj)
+ if isinstance(new, int):
+ return obj
+ return self._change_to_utf8(new)
+ except (NameError, TypeError, SyntaxError):
+ return str(obj)
+
+ def _get_args(self, body):
+ status = self._get_status_args(body)
+ projects = self._get_projects_args(body)
+ installers = self._get_installers_args(body)
+
+ args = {
+ 'status': status,
+ 'projects': projects,
+ 'installers': installers,
+ 'version': body.get('version', 'master').lower(),
+ 'loops': body.get('loops', 'daily').lower(),
+ 'time': body.get('times', '10 days')[:2].lower()
+ }
+ return args
+
+ def _get_status_args(self, body):
+ status_all = ['success', 'warning', 'danger']
+ status = [a.lower() for a in body.get('status', ['all'])]
+ return status_all if 'all' in status else status
+
+ def _get_projects_args(self, body):
+ project_all = ['functest', 'yardstick']
+ projects = [a.lower() for a in body.get('projects', ['all'])]
+ return project_all if 'all' in projects else projects
+
+ def _get_installers_args(self, body):
+ installer_all = ['apex', 'compass', 'fuel', 'joid']
+ installers = [a.lower() for a in body.get('installers', ['all'])]
+ return installer_all if 'all' in installers else installers
diff --git a/utils/test/reporting/api/install.sh b/utils/test/reporting/api/install.sh
new file mode 100755
index 000000000..55d6b77ec
--- /dev/null
+++ b/utils/test/reporting/api/install.sh
@@ -0,0 +1,3 @@
+apt-get install -y python-pip
+pip install tornado
+pip install requests
diff --git a/utils/test/reporting/docker/reporting.sh b/utils/test/reporting/docker/reporting.sh
index 1bef1b811..1de13ae32 100755
--- a/utils/test/reporting/docker/reporting.sh
+++ b/utils/test/reporting/docker/reporting.sh
@@ -4,7 +4,7 @@ export PYTHONPATH="${PYTHONPATH}:."
export CONFIG_REPORTING_YAML=./reporting.yaml
declare -a versions=(colorado master)
-declare -a projects=(functest yardstick)
+declare -a projects=(functest storperf yardstick)
project=$1
reporting_type=$2
@@ -30,6 +30,7 @@ cp -Rf js display
# $1 | $2
# functest | status, vims, tempest
# yardstick |
+# storperf |
if [ -z "$1" ]; then
echo "********************************"
@@ -52,6 +53,13 @@ if [ -z "$1" ]; then
echo "********************************"
python ./yardstick/reporting-status.py
echo "Yardstick reporting status...OK"
+
+ echo "********************************"
+ echo " Storperf reporting "
+ echo "********************************"
+ python ./storperf/reporting-status.py
+ echo "Storperf reporting status...OK"
+
else
if [ -z "$2" ]; then
reporting_type="status"
@@ -71,10 +79,4 @@ echo "daemon off;" >> /etc/nginx/nginx.conf
# supervisor config
cp /home/opnfv/utils/test/reporting/docker/supervisor.conf /etc/supervisor/conf.d/
-# build pages
-cd pages
ln -s /usr/bin/nodejs /usr/bin/node
-npm install
-npm install -g grunt bower
-bower install --allow-root
-grunt build
diff --git a/utils/test/reporting/docker/supervisor.conf b/utils/test/reporting/docker/supervisor.conf
index 0c2207793..1e0eed9c8 100644
--- a/utils/test/reporting/docker/supervisor.conf
+++ b/utils/test/reporting/docker/supervisor.conf
@@ -14,3 +14,9 @@ autorestart = true
user = root
command = service nginx restart
autorestart = true
+
+[program:reporting_angular]
+user = root
+directory = /home/opnfv/utils/test/reporting/pages
+command = bash angular.sh
+autorestart = true
diff --git a/utils/test/reporting/functest/reporting-status.py b/utils/test/reporting/functest/reporting-status.py
index 66bdd57c1..95f9e66e8 100755
--- a/utils/test/reporting/functest/reporting-status.py
+++ b/utils/test/reporting/functest/reporting-status.py
@@ -28,9 +28,9 @@ testValid = []
otherTestCases = []
reportingDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
-# init just tempest to get the list of scenarios
-# as all the scenarios run Tempest
-tempest = tc.TestCase("tempest_smoke_serial", "functest", -1)
+# init just connection_check to get the list of scenarios
+# as all the scenarios run connection_check
+healthcheck = tc.TestCase("connection_check", "functest", -1)
# Retrieve the Functest configuration to detect which tests are relevant
# according to the installer, scenario
@@ -40,6 +40,9 @@ versions = rp_utils.get_config('general.versions')
installers = rp_utils.get_config('general.installers')
blacklist = rp_utils.get_config('functest.blacklist')
log_level = rp_utils.get_config('general.log.log_level')
+exclude_noha = rp_utils.get_config('functest.exclude_noha')
+exclude_virtual = rp_utils.get_config('functest.exclude_virtual')
+
response = requests.get(cf)
functest_yaml_config = yaml.safe_load(response.text)
@@ -48,20 +51,23 @@ logger.info("*******************************************")
logger.info("* *")
logger.info("* Generating reporting scenario status *")
logger.info("* Data retention: %s days *" % period)
-logger.info("* Log level: %s *" % log_level)
+logger.info("* Log level: %s *" % log_level)
+logger.info("* *")
+logger.info("* Virtual PODs exluded: %s *" % exclude_virtual)
+logger.info("* NOHA scenarios excluded: %s *" % exclude_noha)
logger.info("* *")
logger.info("*******************************************")
# Retrieve test cases of Tier 1 (smoke)
config_tiers = functest_yaml_config.get("tiers")
-# we consider Tier 1 (smoke),2 (features)
+# we consider Tier 0 (Healthcheck), Tier 1 (smoke),2 (features)
# to validate scenarios
-# Tier > 4 are not used to validate scenarios but we display the results anyway
+# Tier > 2 are not used to validate scenarios but we display the results anyway
# tricky thing for the API as some tests are Functest tests
# other tests are declared directly in the feature projects
for tier in config_tiers:
- if tier['order'] > 0 and tier['order'] < 2:
+ if tier['order'] >= 0 and tier['order'] < 2:
for case in tier['testcases']:
if case['name'] not in blacklist:
testValid.append(tc.TestCase(case['name'],
@@ -86,11 +92,12 @@ for version in versions:
# For all the installers
for installer in installers:
# get scenarios
- scenario_results = rp_utils.getScenarios(tempest, installer, version)
+ scenario_results = rp_utils.getScenarios(healthcheck,
+ installer,
+ version)
scenario_stats = rp_utils.getScenarioStats(scenario_results)
items = {}
scenario_result_criteria = {}
-
scenario_file_name = ("./display/" + version +
"/functest/scenario_history.txt")
# initiate scenario file if it does not exist
diff --git a/utils/test/reporting/functest/reporting-tempest.py b/utils/test/reporting/functest/reporting-tempest.py
index 5d6bcc062..6e6585a32 100755
--- a/utils/test/reporting/functest/reporting-tempest.py
+++ b/utils/test/reporting/functest/reporting-tempest.py
@@ -44,7 +44,7 @@ for version in rp_utils.get_config('general.versions'):
response = urlopen(request)
k = response.read()
results = json.loads(k)
- except URLError, e:
+ except URLError as e:
logger.error("Error code: %s" % e)
test_results = results['results']
@@ -73,9 +73,9 @@ for version in rp_utils.get_config('general.versions'):
nb_tests_run = result['details']['tests']
nb_tests_failed = result['details']['failures']
if nb_tests_run != 0:
- success_rate = 100*((int(nb_tests_run) -
- int(nb_tests_failed)) /
- int(nb_tests_run))
+ success_rate = 100 * ((int(nb_tests_run) -
+ int(nb_tests_failed)) /
+ int(nb_tests_run))
else:
success_rate = 0
diff --git a/utils/test/reporting/functest/reporting-vims.py b/utils/test/reporting/functest/reporting-vims.py
index 2077d2a4a..b236b8963 100755
--- a/utils/test/reporting/functest/reporting-vims.py
+++ b/utils/test/reporting/functest/reporting-vims.py
@@ -51,7 +51,7 @@ for version in versions:
response = urlopen(request)
k = response.read()
results = json.loads(k)
- except URLError, e:
+ except URLError as e:
logger.error("Error code: %s" % e)
test_results = results['results']
@@ -91,7 +91,7 @@ for version in versions:
result['pr_step_ok'] = 0
if nb_step != 0:
- result['pr_step_ok'] = (float(nb_step_ok)/nb_step)*100
+ result['pr_step_ok'] = (float(nb_step_ok) / nb_step) * 100
try:
logger.debug("Scenario %s, Installer %s"
% (s_result[1]['scenario'], installer))
diff --git a/utils/test/reporting/functest/testCase.py b/utils/test/reporting/functest/testCase.py
index 8d90fc861..f77136e11 100644
--- a/utils/test/reporting/functest/testCase.py
+++ b/utils/test/reporting/functest/testCase.py
@@ -27,6 +27,7 @@ class TestCase(object):
'ocl': 'OCL',
'tempest_smoke_serial': 'Tempest (smoke)',
'tempest_full_parallel': 'Tempest (full)',
+ 'tempest_defcore': 'Tempest (Defcore)',
'rally_sanity': 'Rally (smoke)',
'bgpvpn': 'bgpvpn',
'rally_full': 'Rally (full)',
@@ -36,14 +37,17 @@ class TestCase(object):
'moon': 'Moon',
'copper': 'Copper',
'security_scan': 'Security',
- 'multisite':'Multisite',
- 'domino':'Domino',
- 'odl-sfc':'SFC',
- 'onos_sfc':'SFC',
- 'parser':'Parser',
- 'connection_check':'Health (connection)',
- 'api_check':'Health (api)',
- 'snaps_smoke':'SNAPS' }
+ 'multisite': 'Multisite',
+ 'domino': 'Domino',
+ 'odl-sfc': 'SFC',
+ 'onos_sfc': 'SFC',
+ 'parser': 'Parser',
+ 'connection_check': 'Health (connection)',
+ 'api_check': 'Health (api)',
+ 'snaps_smoke': 'SNAPS',
+ 'snaps_health_check': 'Health (dhcp)',
+ 'netready': 'Netready',
+ 'barometer': 'Barometer'}
try:
self.displayName = display_name_matrix[self.name]
except:
@@ -122,6 +126,7 @@ class TestCase(object):
'ocl': 'ocl',
'tempest_smoke_serial': 'tempest_smoke_serial',
'tempest_full_parallel': 'tempest_full_parallel',
+ 'tempest_defcore': 'tempest_defcore',
'rally_sanity': 'rally_sanity',
'bgpvpn': 'bgpvpn',
'rally_full': 'rally_full',
@@ -131,15 +136,17 @@ class TestCase(object):
'moon': 'moon_authentication',
'copper': 'copper-notification',
'security_scan': 'security',
- 'multisite':'multisite',
- 'domino':'domino-multinode',
- 'odl-sfc':'functest-odl-sfc',
- 'onos_sfc':'onos_sfc',
- 'parser':'parser-basics',
- 'connection_check':'connection_check',
- 'api_check':'api_check',
- 'snaps_smoke':'snaps_smoke'
- }
+ 'multisite': 'multisite',
+ 'domino': 'domino-multinode',
+ 'odl-sfc': 'functest-odl-sfc',
+ 'onos_sfc': 'onos_sfc',
+ 'parser': 'parser-basics',
+ 'connection_check': 'connection_check',
+ 'api_check': 'api_check',
+ 'snaps_smoke': 'snaps_smoke',
+ 'snaps_health_check': 'snaps_health_check',
+ 'netready': 'gluon_vping',
+ 'barometer': 'barometercollectd'}
try:
return test_match_matrix[self.name]
except:
@@ -147,4 +154,3 @@ class TestCase(object):
def getDisplayName(self):
return self.displayName
-
diff --git a/utils/test/reporting/html/danube.html b/utils/test/reporting/html/danube.html
index 58d6bc0fe..d21875b53 100644
--- a/utils/test/reporting/html/danube.html
+++ b/utils/test/reporting/html/danube.html
@@ -76,6 +76,17 @@
</div>
</a>
</article>
+ <article class="style4">
+ <span class="image">
+ <img src="img/storperf.jpg" alt="" />
+ </span>
+ <a href="master/storperf/status-apex.html">
+ <h2>Storperf</h2>
+ <div class="content">
+ <p>Storage testing</p>
+ </div>
+ </a>
+ </article>
</section>
</div>
</div>
diff --git a/utils/test/reporting/img/storperf.jpg b/utils/test/reporting/img/storperf.jpg
new file mode 100644
index 000000000..37492e69e
--- /dev/null
+++ b/utils/test/reporting/img/storperf.jpg
Binary files differ
diff --git a/utils/test/reporting/pages/angular.sh b/utils/test/reporting/pages/angular.sh
new file mode 100755
index 000000000..a7f167516
--- /dev/null
+++ b/utils/test/reporting/pages/angular.sh
@@ -0,0 +1,10 @@
+: ${SERVER_URL:='http://testresults.opnfv.org/reporting/api'}
+
+echo "var BASE_URL = 'http://${SERVER_URL}/landing-page'" > app/scripts/app.config.js
+
+apt-get install -y nodejs
+apt-get install -y npm
+npm install
+npm install -g grunt bower
+bower install --allow-root
+grunt build
diff --git a/utils/test/reporting/pages/app/images/green.png b/utils/test/reporting/pages/app/images/green.png
new file mode 100644
index 000000000..57fc59927
--- /dev/null
+++ b/utils/test/reporting/pages/app/images/green.png
Binary files differ
diff --git a/utils/test/reporting/pages/app/images/green@2x.png b/utils/test/reporting/pages/app/images/green@2x.png
new file mode 100644
index 000000000..3bda5beb6
--- /dev/null
+++ b/utils/test/reporting/pages/app/images/green@2x.png
Binary files differ
diff --git a/utils/test/reporting/pages/app/index.html b/utils/test/reporting/pages/app/index.html
index 7673a4cc2..1159c2176 100644
--- a/utils/test/reporting/pages/app/index.html
+++ b/utils/test/reporting/pages/app/index.html
@@ -1,6 +1,7 @@
<!doctype html>
<html ng-app="opnfvApp">
- <head>
+
+<head>
<meta charset="utf-8">
<title>OPNFV-DASHBOARD EXAMPLE</title>
<meta name="description" content="">
@@ -13,17 +14,22 @@
<link rel="stylesheet" href="bower_components/chosen/chosen.css" />
<link rel="stylesheet" href="bower_components/selectize/dist/css/selectize.css" />
<link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.css" />
+ <link rel="stylesheet" href="bower_components/angular-tooltips/dist/angular-tooltips.min.css" />
+ <link rel="stylesheet" href="bower_components/animate.css/animate.css" />
+ <link rel="stylesheet" href="bower_components/ng-dialog/css/ngDialog.css" />
+ <link rel="stylesheet" href="bower_components/ng-dialog/css/ngDialog-theme-default.css" />
+ <link rel="stylesheet" href="bower_components/inspiniacss/style.css" />
<!-- endbower -->
<!-- endbuild -->
<!-- build:css(.tmp) styles/style.css -->
- <!--<link rel="stylesheet" href="styles/main.css">-->
- <link rel="stylesheet" href="styles/animate.css">
- <link rel="stylesheet" href="styles/style.css">
+
<link rel="stylesheet" href="styles/custome.css">
+
<!-- endbuild -->
- </head>
- <body class="{{$state.current.data.specialClass}}" id="page-top">
+</head>
+
+<body class="{{$state.current.data.specialClass}}" id="page-top">
<!--[if lte IE 8]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
@@ -31,7 +37,7 @@
<!-- Add your site or application content here -->
<div ui-view></div>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
- <!--<script>
+ <!--<script>
!function(A,n,g,u,l,a,r){A.GoogleAnalyticsObject=l,A[l]=A[l]||function(){
(A[l].q=A[l].q||[]).push(arguments)},A[l].l=+new Date,a=n.createElement(g),
r=n.getElementsByTagName(g)[0],a.src=u,r.parentNode.insertBefore(a,r)
@@ -59,17 +65,27 @@
<script src="bower_components/microplugin/src/microplugin.js"></script>
<script src="bower_components/selectize/dist/js/selectize.js"></script>
<script src="bower_components/angular-selectize2/dist/angular-selectize.js"></script>
+ <script src="bower_components/angular-tooltips/dist/angular-tooltips.min.js"></script>
+ <script src="bower_components/jQuery-rwdImageMaps/jquery.rwdImageMaps.min.js"></script>
+ <script src="bower_components/ng-dialog/js/ngDialog.js"></script>
<!-- endbower -->
<!-- endbuild -->
- <!-- build:js({.tmp,app}) scripts/scripts.js -->
- <script src="scripts/app.js"></script>
- <!--<script src="scripts/controllers/main.js"></script>-->
- <script src="scripts/config.router.js"></script>
- <script src="scripts/controllers/table.controller.js"></script>
- <script src="scripts/config.js"></script>
- <script src="scripts/directives/mydirective.js"></script>
- <script src="scripts/factory/table.factory.js"></script>
- <!-- endbuild -->
+ <!-- build:js({.tmp,app}) scripts/scripts.js -->
+ <script src="scripts/app.js"></script>
+ <!--<script src="scripts/controllers/main.js"></script>-->
+ <script src="scripts/config.router.js"></script>
+ <script src="scripts/controllers/table.controller.js"></script>
+ <script src="scripts/config.js"></script>
+ <script src="scripts/factory/table.factory.js"></script>
+ <script src="scripts/controllers/case.controller.js"></script>
+ <script src="scripts/controllers/auth.controller.js"></script>
+ <script src="scripts/controllers/admin.controller.js"></script>
+ <script src="scripts/controllers/main.controller.js"></script>
+ <script src="scripts/app.config.js"></script>
+ <script src="scripts/controllers/testvisual.controller.js"></script>
+
+ <!-- endbuild -->
</body>
-</html>
+
+</html> \ No newline at end of file
diff --git a/modules/opnfv/installer_adapters/daisy/__init__.py b/utils/test/reporting/pages/app/scripts/app.config.js
index e69de29bb..e69de29bb 100644
--- a/modules/opnfv/installer_adapters/daisy/__init__.py
+++ b/utils/test/reporting/pages/app/scripts/app.config.js
diff --git a/utils/test/reporting/pages/app/scripts/app.js b/utils/test/reporting/pages/app/scripts/app.js
index 6e99ce3d7..d06019c55 100644
--- a/utils/test/reporting/pages/app/scripts/app.js
+++ b/utils/test/reporting/pages/app/scripts/app.js
@@ -9,12 +9,13 @@
* Main module of the application.
*/
angular
- .module('opnfvApp', [
- 'ngAnimate',
- 'ui.router',
- 'oc.lazyLoad',
- 'ui.bootstrap',
- 'ngResource',
- 'selectize'
+ .module('opnfvApp', [
+ 'ngAnimate',
+ 'ui.router',
+ 'oc.lazyLoad',
+ 'ui.bootstrap',
+ 'ngResource',
+ 'selectize',
+ '720kb.tooltips'
- ]);
+ ]); \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/scripts/config.js b/utils/test/reporting/pages/app/scripts/config.js
index 838460a38..1010169d3 100644
--- a/utils/test/reporting/pages/app/scripts/config.js
+++ b/utils/test/reporting/pages/app/scripts/config.js
@@ -7,8 +7,13 @@
* Main config file of the application.
*/
angular
- .module('opnfvApp').config(function () {
+ .module('opnfvApp').config(['$httpProvider', '$qProvider', function($httpProvider, $qProvider) {
- }
+ $httpProvider.defaults.useXDomain = true;
+ delete $httpProvider.defaults.headers.common['X-Requested-With'];
- )
+ $qProvider.errorOnUnhandledRejections(false);
+
+ }
+
+ ]); \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/scripts/config.router.js b/utils/test/reporting/pages/app/scripts/config.router.js
index 641ea6a74..d38ad7507 100644
--- a/utils/test/reporting/pages/app/scripts/config.router.js
+++ b/utils/test/reporting/pages/app/scripts/config.router.js
@@ -23,7 +23,7 @@ angular.module('opnfvApp')
$stateProvider
.state('landingpage', {
url: "/landingpage",
- //controller: 'MainCtrl',
+ controller: 'MainController',
templateUrl: "views/main.html",
data: { pageTitle: '首页', specialClass: 'landing-page' },
resolve: {
diff --git a/utils/test/reporting/pages/app/scripts/controllers/main.controller.js b/utils/test/reporting/pages/app/scripts/controllers/main.controller.js
new file mode 100644
index 000000000..2054dc2dd
--- /dev/null
+++ b/utils/test/reporting/pages/app/scripts/controllers/main.controller.js
@@ -0,0 +1,32 @@
+'use strict';
+
+/**
+ * @ngdoc function
+ * @name opnfvdashBoardAngularApp.controller:MainPageController
+ * @description
+ * # TableController
+ * Controller of the opnfvdashBoardAngularApp
+ */
+angular.module('opnfvApp')
+ .controller('MainController', ['$scope', '$state', '$stateParams', function($scope, $state, $stateParams) {
+
+ init();
+
+ function init() {
+ $scope.goTest = goTest;
+ $scope.goLogin = goLogin;
+
+ }
+
+ function goTest() {
+ $state.go("select.selectTestCase");
+ }
+
+ function goLogin() {
+ $state.go("login");
+ }
+
+
+
+
+ }]); \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/scripts/controllers/table.controller.js b/utils/test/reporting/pages/app/scripts/controllers/table.controller.js
index 8ca1e474c..0f3a17a03 100644
--- a/utils/test/reporting/pages/app/scripts/controllers/table.controller.js
+++ b/utils/test/reporting/pages/app/scripts/controllers/table.controller.js
@@ -8,255 +8,131 @@
* Controller of the opnfvdashBoardAngularApp
*/
angular.module('opnfvApp')
- .controller('TableController', ['$scope', '$state', '$stateParams', 'TableFactory', function ($scope, $state, $stateParams, TableFactory) {
+ .controller('TableController', ['$scope', '$state', '$stateParams', '$http', 'TableFactory', function($scope, $state, $stateParams, $http, TableFactory) {
$scope.filterlist = [];
$scope.selection = [];
- $scope.statusList = ["Success", "Warning", "Danger"];
- $scope.projectList = ["Deployment", "Functest", "Yardstick"];
- $scope.installerList = ["apex", "compass", "fuel", "joid"];
- $scope.versionlist = ["Colorado", "Master"];
- $scope.loopci = ["Daily", "Weekly", "Monthly"];
- $scope.time = ["10 days", "1 Month"];
+ $scope.statusList = [];
+ $scope.projectList = [];
+ $scope.installerList = [];
+ $scope.versionlist = [];
+ $scope.loopci = [];
+ $scope.time = [];
$scope.tableDataAll = {};
$scope.tableInfoAll = {};
+ $scope.scenario = {};
+ $scope.VersionConfig = {
+ create: true,
+ valueField: 'title',
+ labelField: 'title',
+ delimiter: '|',
+ maxItems: 1,
+ placeholder: 'Version',
+ onChange: function(value) {
+ checkElementArrayValue($scope.selection, $scope.VersionOption);
+ $scope.selection.push(value);
+ // console.log($scope.selection);
+ getScenarioData();
+ }
+ }
- $scope.scenario =
- {
- "scenarios": {
- "os-nosdn-kvm-noha": {
- "status": "Success",
- "installers": {
- "apex": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS",
-
-
- },
- {
- "project": "Functest",
- "score": "null",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "compass": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "fuel": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "joid": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ]
- }
- },
- "os-nosdn-ovs-ha": {
- "status": "Danger",
- "installers": {
- "apex": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS",
-
-
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "compass": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "fuel": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "joid": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ]
- }
- },
- "os-nosdn-ovs-noha": {
- "status": "Warning",
- "installers": {
- "apex": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS",
-
-
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "compass": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "fuel": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ],
- "joid": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "score": "13/14",
- "status": "SUCCESS"
- }
- ]
- }
- }
+ $scope.LoopConfig = {
+ create: true,
+ valueField: 'title',
+ labelField: 'title',
+ delimiter: '|',
+ maxItems: 1,
+ placeholder: 'Loop',
+ onChange: function(value) {
+ checkElementArrayValue($scope.selection, $scope.LoopOption);
+ $scope.selection.push(value);
+ // console.log($scope.selection);
+ getScenarioData();
+
+ }
+ }
+
+ $scope.TimeConfig = {
+ create: true,
+ valueField: 'title',
+ labelField: 'title',
+ delimiter: '|',
+ maxItems: 1,
+ placeholder: 'Time',
+ onChange: function(value) {
+ checkElementArrayValue($scope.selection, $scope.TimeOption);
+ $scope.selection.push(value);
+ // console.log($scope.selection)
+ getScenarioData();
+
+
+ }
+ }
+
+
+ init();
+
+ function init() {
+ $scope.toggleSelection = toggleSelection;
+ getScenarioData();
+ // radioSetting();
+ getFilters();
+ }
+
+ function getFilters() {
+ TableFactory.getFilter().get({
+
+
+ }).$promise.then(function(response) {
+ if (response != null) {
+ $scope.statusList = response.filters.status;
+ $scope.projectList = response.filters.projects;
+ $scope.installerList = response.filters.installers;
+ $scope.versionlist = response.filters.version;
+ $scope.loopci = response.filters.loops;
+ $scope.time = response.filters.time;
+
+ $scope.statusListString = $scope.statusList.toString();
+ $scope.projectListString = $scope.projectList.toString();
+ $scope.installerListString = $scope.installerList.toString();
+ $scope.VersionSelected = $scope.versionlist[1];
+ $scope.LoopCiSelected = $scope.loopci[0];
+ $scope.TimeSelected = $scope.time[0];
+ radioSetting($scope.versionlist, $scope.loopci, $scope.time);
+
+ } else {
+ alert("网络错误");
}
+ })
+ }
+
+ function getScenarioData() {
+
+ var utl = BASE_URL + '/scenarios';
+ var data = {
+ 'status': ['success', 'danger', 'warning'],
+ 'projects': ['functest', 'yardstick'],
+ 'installers': ['apex', 'compass', 'fuel', 'joid'],
+ 'version': $scope.VersionSelected,
+ 'loops': $scope.LoopCiSelected,
+ 'time': $scope.TimeSelected
};
+ var config = {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
+ }
+ }
+ $http.post(utl, data, config).then(function(response) {
+ if (response.status == 200) {
+ $scope.scenario = response.data;
+ constructJson();
+ }
+ })
+ }
- // var headData = Object.keys($scope.scenario.scenarios.os_nosdn_kvm_noha.installers);
- // $scope.headData = headData;
- //construct json
+ //construct json
function constructJson() {
var colspan;
@@ -267,19 +143,22 @@ angular.module('opnfvApp')
for (var item in $scope.scenario.scenarios) {
-
-
-
- var headData = Object.keys($scope.scenario.scenarios[item].installers);
+ var headData = Object.keys($scope.scenario.scenarios[item].installers).sort();
var scenarioStatus = $scope.scenario.scenarios[item].status;
-
+ var scenarioStatusDisplay;
+ if (scenarioStatus == "success") {
+ scenarioStatusDisplay = "navy";
+ } else if (scenarioStatus == "danger") {
+ scenarioStatusDisplay = "danger";
+ } else if (scenarioStatus == "warning") {
+ scenarioStatusDisplay = "warning";
+ }
InstallerData = headData;
var projectData = [];
var datadisplay = [];
var projects = [];
-
for (var j = 0; j < headData.length; j++) {
projectData.push($scope.scenario.scenarios[item].installers[headData[j]]);
@@ -289,9 +168,30 @@ angular.module('opnfvApp')
for (var k = 0; k < projectData[j].length; k++) {
projects.push(projectData[j][k].project);
var temArray = [];
- temArray.push(projectData[j][k].score);
- temArray.push(projectData[j][k].project);
- temArray.push(headData[j]);
+ if (projectData[j][k].score == null) {
+ temArray.push("null");
+ temArray.push(projectData[j][k].project);
+ temArray.push(headData[j]);
+ } else {
+ temArray.push(projectData[j][k].score);
+ temArray.push(projectData[j][k].project);
+ temArray.push(headData[j]);
+ }
+
+
+ if (projectData[j][k].status == "platinium") {
+ temArray.push("primary");
+ temArray.push("P");
+ } else if (projectData[j][k].status == "gold") {
+ temArray.push("danger");
+ temArray.push("G");
+ } else if (projectData[j][k].status == "silver") {
+ temArray.push("warning");
+ temArray.push("S");
+ } else if (projectData[j][k].status == null) {
+ temArray.push("null");
+ }
+
datadisplay.push(temArray);
}
@@ -301,13 +201,21 @@ angular.module('opnfvApp')
colspan = projects.length / headData.length;
var tabledata = {
- scenarioName: item, Installer: InstallerData, projectData: projectData, projects: projects,
- datadisplay: datadisplay, colspan: colspan, status: scenarioStatus
+ scenarioName: item,
+ Installer: InstallerData,
+ projectData: projectData,
+ projects: projects,
+ datadisplay: datadisplay,
+ colspan: colspan,
+ status: scenarioStatus,
+ statusDisplay: scenarioStatusDisplay
};
JSON.stringify(tabledata);
$scope.tableDataAll.scenario.push(tabledata);
+ // console.log(tabledata);
+
}
@@ -315,15 +223,13 @@ angular.module('opnfvApp')
var tempHeadData = [];
-
-
for (var i = 0; i < InstallerData.length; i++) {
for (var j = 0; j < colspan; j++) {
tempHeadData.push(InstallerData[i]);
}
}
- console.log(tempHeadData);
+ //console.log(tempHeadData);
var projectsInfoAll = [];
@@ -334,13 +240,14 @@ angular.module('opnfvApp')
projectsInfoAll.push(tempA);
}
- console.log(projectsInfoAll);
+ //console.log(projectsInfoAll);
$scope.tableDataAll["colspan"] = colspan;
$scope.tableDataAll["Installer"] = InstallerData;
$scope.tableDataAll["Projects"] = projectsInfoAll;
- console.log($scope.tableDataAll);
+ // console.log($scope.tableDataAll);
+ $scope.colspan = $scope.tableDataAll.colspan;
}
@@ -353,58 +260,11 @@ angular.module('opnfvApp')
return size;
}
- init();
- function init() {
- $scope.toggleSelection = toggleSelection;
-
- constructJson();
-
- }
-
- // $scope.test=false;
+ $scope.colspan = $scope.tableDataAll.colspan;
+ // console.log($scope.colspan);
- var statusListString = $scope.statusList.toString();
- var projectListString = $scope.projectList.toString();
- var installerListString = $scope.installerList.toString();
-
- $scope.colspan=$scope.tableDataAll.colspan;
- //filter function
- function filterData() {
-
-
- $scope.selectInstallers = [];
- $scope.selectProjects = [];
- $scope.selectStatus = [];
- for (var i = 0; i < $scope.selection.length; i++) {
- if (statusListString.indexOf($scope.selection[i]) > -1) {
- $scope.selectStatus.push($scope.selection[i]);
- }
- if (projectListString.indexOf($scope.selection[i]) > -1) {
- $scope.selectProjects.push($scope.selection[i]);
- }
- if (installerListString.indexOf($scope.selection[i]) > -1) {
- $scope.selectInstallers.push($scope.selection[i]);
- }
- }
-
- $scope.colspan=$scope.selectProjects.length;
- //when some selection is empty, we set it full
- if($scope.selectInstallers.length==0){
- $scope.selectInstallers=$scope.installerList;
-
- }
- if($scope.selectProjects.length==0){
- $scope.selectProjects=$scope.projectList;
- $scope.colspan=$scope.tableDataAll.colspan;
- }
- if($scope.selectStatus.length==0){
- $scope.selectStatus=$scope.statusList
- }
- }
-
-
- //find all same element index
+ //find all same element index
function getSameElementIndex(array, element) {
var indices = [];
var idx = array.indexOf(element);
@@ -424,64 +284,31 @@ angular.module('opnfvApp')
}
-
- $scope.VersionOption = [
- { title: 'Colorado' },
- { title: 'Master' }
- ];
- $scope.VersionConfig = {
- create: true,
- valueField: 'title',
- labelField: 'title',
- delimiter: '|',
- maxItems: 1,
- placeholder: 'Version',
- onChange: function (value) {
- checkElementArrayValue($scope.selection, $scope.VersionOption);
- $scope.selection.push(value);
- // console.log($scope.selection);
-
+ function radioSetting(array1, array2, array3) {
+ var tempVersion = [];
+ var tempLoop = [];
+ var tempTime = [];
+ for (var i = 0; i < array1.length; i++) {
+ var temp = {
+ title: array1[i]
+ };
+ tempVersion.push(temp);
}
-
- }
-
- $scope.LoopOption = [
- { title: 'Daily' },
- { title: 'Weekly' },
- { title: 'Monthly' }
- ];
- $scope.LoopConfig = {
- create: true,
- valueField: 'title',
- labelField: 'title',
- delimiter: '|',
- maxItems: 1,
- placeholder: 'Loop',
- onChange: function (value) {
- checkElementArrayValue($scope.selection, $scope.LoopOption);
- $scope.selection.push(value);
- // console.log($scope.selection);
-
+ for (var i = 0; i < array2.length; i++) {
+ var temp = {
+ title: array2[i]
+ };
+ tempLoop.push(temp);
}
- }
-
- $scope.TimeOption = [
- { title: '10 days' },
- { title: '1 month' }
- ];
- $scope.TimeConfig = {
- create: true,
- valueField: 'title',
- labelField: 'title',
- delimiter: '|',
- maxItems: 1,
- placeholder: 'Time',
- onChange: function (value) {
- checkElementArrayValue($scope.selection, $scope.TimeOption);
- $scope.selection.push(value);
- // console.log($scope.selection)
-
+ for (var i = 0; i < array3.length; i++) {
+ var temp = {
+ title: array3[i]
+ };
+ tempTime.push(temp);
}
+ $scope.VersionOption = tempVersion;
+ $scope.LoopOption = tempLoop;
+ $scope.TimeOption = tempTime;
}
//remove element in the array
@@ -508,13 +335,51 @@ angular.module('opnfvApp')
if (idx > -1) {
$scope.selection.splice(idx, 1);
- }
- else {
+ filterData($scope.selection)
+ } else {
$scope.selection.push(status);
+ filterData($scope.selection)
}
- console.log($scope.selection);
- filterData();
+ // console.log($scope.selection);
}
- }]);
+ //filter function
+ function filterData(selection) {
+
+ $scope.selectInstallers = [];
+ $scope.selectProjects = [];
+ $scope.selectStatus = [];
+ for (var i = 0; i < selection.length; i++) {
+ if ($scope.statusListString.indexOf(selection[i]) > -1) {
+ $scope.selectStatus.push(selection[i]);
+ }
+ if ($scope.projectListString.indexOf(selection[i]) > -1) {
+ $scope.selectProjects.push(selection[i]);
+ }
+ if ($scope.installerListString.indexOf(selection[i]) > -1) {
+ $scope.selectInstallers.push(selection[i]);
+ }
+ }
+
+ $scope.colspan = $scope.selectProjects.length;
+ //when some selection is empty, we set it full
+ if ($scope.selectInstallers.length == 0) {
+ $scope.selectInstallers = $scope.installerList;
+
+ }
+ if ($scope.selectProjects.length == 0) {
+ $scope.selectProjects = $scope.projectList;
+ $scope.colspan = $scope.tableDataAll.colspan;
+ }
+ if ($scope.selectStatus.length == 0) {
+ $scope.selectStatus = $scope.statusList
+ }
+
+ // console.log($scope.selectStatus);
+ // console.log($scope.selectProjects);
+
+ }
+
+
+ }]); \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/scripts/data.json b/utils/test/reporting/pages/app/scripts/data.json
deleted file mode 100644
index a15fdf37e..000000000
--- a/utils/test/reporting/pages/app/scripts/data.json
+++ /dev/null
@@ -1,76 +0,0 @@
-
-{"scenarios": {
- "os-nosdn-kvm-noha": {
- "status": "Success",
- "installers": {
- "apex": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "socre": "13/14",
- "status": "SUCCESS"
- }
- ],
- "compass": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "socre": "13/14",
- "status": "SUCCESS"
- }
- ],
- "fuel": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "socre": "13/14",
- "status": "SUCCESS"
- }
- ],
- "joid": [
- {
- "project": "Deployment",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Functest",
- "score": "13/14",
- "status": "SUCCESS"
- },
- {
- "project": "Yardstick",
- "socre": "13/14",
- "status": "SUCCESS"
- }
- ]
- }
- }
-}}
diff --git a/utils/test/reporting/pages/app/scripts/factory/table.factory.js b/utils/test/reporting/pages/app/scripts/factory/table.factory.js
index 22443221e..a2e2aeff0 100644
--- a/utils/test/reporting/pages/app/scripts/factory/table.factory.js
+++ b/utils/test/reporting/pages/app/scripts/factory/table.factory.js
@@ -4,17 +4,23 @@
* get data factory
*/
angular.module('opnfvApp')
- .factory('TableFactory', function ($resource, $rootScope) {
- // var baseUrl = base_Url;
+ .factory('TableFactory', function($resource, $rootScope) {
return {
- getFilter: function () {
- return $resource(baseUrl + '/', {}, {
- 'post': {
- method: 'POST',
+ getFilter: function() {
+ return $resource(BASE_URL + '/filters', {}, {
+ 'get': {
+ method: 'GET',
}
});
+ },
+ getScenario: function() {
+ return $resource(BASE_URL + '/scenarios', {}, {
+ 'post': {
+ method: 'POST',
+ }
+ })
}
};
- });
+ }); \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/styles/custome.css b/utils/test/reporting/pages/app/styles/custome.css
index dadc68a03..b498cf04d 100644
--- a/utils/test/reporting/pages/app/styles/custome.css
+++ b/utils/test/reporting/pages/app/styles/custome.css
@@ -1,8 +1,8 @@
.container-tablesize {
- margin: auto 5%;
+ margin: auto 5%;
}
-.btn-outline {
+.btn-outline {
border-color: white;
}
@@ -29,6 +29,68 @@
}
.myhr {
- border:0.5px dashed #e7eaec;
- border-top:1px;margin-bottom: 3px;
-} \ No newline at end of file
+ border: 0.5px dashed #e7eaec;
+ border-top: 1px;
+ margin-bottom: 3px;
+}
+
+td.null {
+ background-color: #e7eaec;
+ color: #e7eaec;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+ cursor: not-allowed;
+ display: none;
+}
+
+body,
+html {
+ margin: 0;
+ padding: 0;
+ min-height: 100%;
+}
+
+
+/*img[usemap] {
+ border: none;
+ height: auto;
+ max-width: 100%;
+ width: auto;
+}*/
+
+.popup {
+ position: absolute;
+ display: none;
+ /*background-color: #dd8;*/
+ border-radius: 5px 5px 5px 5px;
+ background-color: #f3f3f4;
+ opacity: 0.9;
+}
+
+
+/*
+body {
+ height: 1200px;
+}
+
+html {
+ min-height: 100%;
+}*/
+
+
+/*html,
+body {
+ height: 100%;
+}
+
+#page-wrapper {
+ position: inherit;
+ margin: 0 0 0 220px;
+ min-height: 773px;
+}*/ \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/styles/fonts/glyphicons-halflings-regular.svg b/utils/test/reporting/pages/app/styles/fonts/glyphicons-halflings-regular.svg
index 187805af6..94fb5490a 100644
--- a/utils/test/reporting/pages/app/styles/fonts/glyphicons-halflings-regular.svg
+++ b/utils/test/reporting/pages/app/styles/fonts/glyphicons-halflings-regular.svg
@@ -285,4 +285,4 @@
<glyph unicode="&#x1f511;" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" />
<glyph unicode="&#x1f6aa;" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" />
</font>
-</defs></svg>
+</defs></svg> \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/views/commons/table.html b/utils/test/reporting/pages/app/views/commons/table.html
index ed9300edd..f504bd76b 100644
--- a/utils/test/reporting/pages/app/views/commons/table.html
+++ b/utils/test/reporting/pages/app/views/commons/table.html
@@ -29,81 +29,85 @@
<div class=" col-md-12" data-toggle="buttons" aria-pressed="false">
<label> Status </label> &nbsp;&nbsp; &nbsp;
- <label class="btn btn-outline btn-success btn-sm" style="height:25px; margin-right: 5px;" ng-repeat="status in statusList"
- value={{status}} ng-checked="selection.indexOf(status)>-1" ng-click="toggleSelection(status)">
+ <label class="btn btn-outline btn-success btn-sm" style="height:25px; margin-right: 5px;" ng-repeat="status in statusList" value={{status}} ng-checked="selection.indexOf(status)>-1" ng-click="toggleSelection(status)">
<input type="checkbox" disabled="disabled" > {{status}}
+
</label>
</div>
- <hr class="myhr">
+ <hr class="myhr">
<div class=" col-md-12" data-toggle="buttons">
- <label> Projects </label> &nbsp;
- <label class="btn btn-outline btn-success btn-sm " style="height:25px;margin-right: 5px;" ng-repeat="project in projectList"
- value={{project}} ng-checked="selection.indexOf(project)>-1" ng-click="toggleSelection(project)">
+ <label> Projects </label> &nbsp;
+ <label class="btn btn-outline btn-success btn-sm " style="height:25px;margin-right: 5px;" ng-repeat="project in projectList" value={{project}} ng-checked="selection.indexOf(project)>-1" ng-click="toggleSelection(project)">
<input type="checkbox" disabled="disabled"> {{project}}
</label>
</div>
- <hr class="myhr">
+ <hr class="myhr">
<div class=" col-md-12" data-toggle="buttons">
<label> Installers </label>
- <label class="btn btn-outline btn-success btn-sm" style="height:25px;margin-right: 5px;" ng-repeat="installer in installerList"
- value={{installer}} ng-checked="selection.indexOf(installer)" ng-click="toggleSelection(installer)">
+ <label class="btn btn-outline btn-success btn-sm" style="height:25px;margin-right: 5px;" ng-repeat="installer in installerList" value={{installer}} ng-checked="selection.indexOf(installer)>-1" ng-click="toggleSelection(installer)">
<input type="checkbox" disabled="disabled"> {{installer}}
</label>
- </div>
+ </div>
- <hr style="border:0.5px dashed #e7eaec;border-top:1px;margin-bottom:10px;">
+ <hr style="border:0.5px dashed #e7eaec;border-top:1px;margin-bottom:10px;">
- <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
- <selectize options="VersionOption" ng-model="VersionSelected" config="VersionConfig"></selectize>
+ <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
+ <selectize options="VersionOption" ng-model="VersionSelected" config="VersionConfig"></selectize>
- </div>
+ </div>
- <div class=" col-md-1" style="margin-top:5px;margin-left: 5px;margin-right: 5px;">
- <selectize options="LoopOption" ng-model="LoopCiSelected" config="LoopConfig"></selectize>
+ <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
+ <selectize options="LoopOption" ng-model="LoopCiSelected" config="LoopConfig"></selectize>
- </div>
+ </div>
- <div class=" col-md-1" style="margin-top:5px;margin-left: 5px;margin-right: 5px;">
- <selectize options="TimeOption" ng-model="TimeSelected" config="TimeConfig"></selectize>
+ <div class=" col-md-1" style="margin-top:5px;margin-right: 5px;">
+ <selectize options="TimeOption" ng-model="TimeSelected" config="TimeConfig"></selectize>
+ </div>
</div>
+ <div class="table-responsive">
+
+ <table class="table table-bordered" id="table" ng-model="tableDataAll">
+ <thead class="thead">
+ <tr>
+ <th>Scenario </th>
+ <th colspan={{colspan}} ng-show="selectInstallers.indexOf(key)!=-1" value={{key}} ng-repeat="key in tableDataAll.Installer"><a href="notfound.html">{{key}}</a> </th>
+ </tr>
+ <tr>
-</div>
+ <td></td>
+ <td ng-show="selectProjects.indexOf(project[0])!=-1 && selectInstallers.indexOf(project[1])!=-1" ng-repeat="project in tableDataAll.Projects track by $index" data={{project[1]}} value={{project[0]}}>{{project[0]}}</td>
-<table class="table table-bordered" id="table" ng-model="tableDataAll">
- <thead class="thead">
- <tr >
- <th>Scenario </th>
- <th colspan={{colspan}} ng-show="selectInstallers.indexOf(key)!=-1" value={{key}} ng-repeat="key in tableDataAll.Installer"><a href="notfound.html">{{key}}</a> </th>
- </tr>
+ </tr>
+ </thead>
+ <tbody class="tbody">
+ <tr ng-repeat="scenario in tableDataAll.scenario" ng-show="selectStatus.indexOf(scenario.status)!=-1">
- <tr>
+ <td nowrap="nowrap" data={{scenario.status}}><span class="fa fa-circle text-{{scenario.statusDisplay}}"></span> <a href="notfound.html">{{scenario.scenarioName}}</a> </td>
- <td align="justify"></td>
- <td align="justify" ng-show="selectProjects.indexOf(project[0])!=-1 && selectInstallers.indexOf(project[1])!=-1" ng-repeat="project in tableDataAll.Projects track by $index" data={{project[1]}} value={{project[0]}}>{{project[0]}}</td>
- </tr>
- </thead>
- <tbody class="tbody">
- <tr ng-repeat="scenario in tableDataAll.scenario" ng-show="selectStatus.indexOf(scenario.status)!=-1" >
+ <!--<td style="background-color:#e7eaec" align="justify" ng-if="data[0]=='Not Support'" ng-repeat="data in scenario.datadisplay track by $index" data={{data[1]}} value={{data[2]}}></td>-->
- <td align="justify" data={{scenario.status}}><span class="fa fa-circle text-warning"><a href="notfound.html">{{scenario.scenarioName}}</a></span> </td>
- <td align="justify" ng-show="selectInstallers.indexOf(data[2])!=-1 && selectProjects.indexOf(data[1])!=-1" ng-repeat="data in scenario.datadisplay track by $index" data={{data[1]}} value={{data[2]}} ><span class="label label-danger">D<a href="notfound.html"></a></span> {{data[0]}}</td>
- </tr>
- </tbody>
-</table>
+ <td nowrap="nowrap" ng-show="selectInstallers.indexOf(data[2])!=-1 && selectProjects.indexOf(data[1])!=-1" ng-repeat="data in scenario.datadisplay track by $index" data={{data[1]}} value={{data[2]}} class={{data[0]}}>
+ <span class="label label-{{data[3]}}">{{data[4]}}</a></span> {{data[0]}}</td>
- <div class="pull-right">
- <span class="label label-danger">D</span>danger<span style="padding-left:20px"></span>
- <span class="label label-primary">S</span><span>success</span><span style="padding-left:20px"></span>
- <span class="label label-warning">W</span><span>warning</span>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="pull-right" style="margin-top: 5px">
+ <span class="label label-danger">G</span>gold<span style="padding-left:20px"></span>
+ <span class="label label-primary">P</span><span>platinium</span><span style="padding-left:20px"></span>
+ <span class="label label-warning">S</span><span>silver</span>
</div>
+ </div>
</div>
</div>
-</div>
-</section>
+</section> \ No newline at end of file
diff --git a/utils/test/reporting/pages/app/views/main.html b/utils/test/reporting/pages/app/views/main.html
index 1e3fe9e5a..cca893713 100644
--- a/utils/test/reporting/pages/app/views/main.html
+++ b/utils/test/reporting/pages/app/views/main.html
@@ -1,186 +1,171 @@
-
<div class="navbar-wrapper">
- <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
- <div class="container">
- <div class="navbar-header page-scroll">
- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false"
- aria-controls="navbar">
+ <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
+ <div class="container">
+ <div class="navbar-header page-scroll">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- <a class="navbar-brand" href="index.html">OPNFV-DASHBOARD</a>
- </div>
- <div id="navbar" class="navbar-collapse collapse">
- <ul class="nav navbar-nav navbar-right">
- <li><a href="#page-top">Home</a></li>
- <li><a href="#DashBoard">DashBoard</a></li>
- <!--<li><a href="#team">Team</a></li>
+ <a class="navbar-brand" href="index.html">OPNFV-DASHBOARD</a>
+ </div>
+ <div class="navbar-collapse collapse">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="#page-top">Home</a></li>
+ <li><a href="#DashBoard">DashBoard</a></li>
+ <li><a ui-sref="select.selectTestCase">TestCase</a></li>
+ <li><a ui-sref="login">Login</a></li>
+ <!--<li><a href="#team">Team</a></li>
<li><a href="#testimonials">Testimonials</a></li>
<li><a href="#pricing">Pricing</a></li>
<li><a href="#contact">Contact</a></li>-->
- </ul>
- </div>
- </div>
- </nav>
+ </ul>
+ </div>
+ </div>
+ </nav>
</div>
<div id="inSlider" class="carousel carousel-fade" data-ride="carousel">
- <ol class="carousel-indicators">
- <!--<li data-target="#inSlider" data-slide-to="0" class="active"></li>
+ <ol class="carousel-indicators">
+ <!--<li data-target="#inSlider" data-slide-to="0" class="active"></li>
<li data-target="#inSlider" data-slide-to="1"></li>-->
- </ol>
- <div class="carousel-inner" role="listbox">
- <div class="item active">
- <div class="container">
- <div class="carousel-caption">
- <h1>OPNFV<br/> facilitates the development and evolution<br/> of NFV components across<br/> various open source ecosystems<br/>
- </h1>
- <!--<p>Lorem Ipsum is simply dummy text of the printing.</p>
+ </ol>
+ <div class="carousel-inner" role="listbox">
+ <div class="item active">
+ <div class="container">
+ <div class="carousel-caption">
+ <h1>OPNFV<br/> facilitates the development and evolution<br/> of NFV components across<br/> various open source ecosystems<br/>
+ </h1>
+ <!--<p>Lorem Ipsum is simply dummy text of the printing.</p>
<p>-->
- <a class="btn btn-lg btn-primary" href="#" role="button">READ MORE</a>
- <!--<a class="caption-link" href="#" role="button">Inspinia Theme</a>-->
- <!--</p>-->
+ <a class="btn btn-lg btn-primary" href="#" role="button">READ MORE</a>
+ <!--<a class="caption-link" href="#" role="button">Inspinia Theme</a>-->
+ <!--</p>-->
+ </div>
+
+ </div>
+ <!-- Set background for slide in css -->
+ <div class="header-back one" style="background: url('images/header_one.jpg') 50% 0 no-repeat;"></div>
+
</div>
- <!--<div class="carousel-image wow zoomIn">
- <img src="img/landing/laptop.png" alt="laptop"/>
- </div>-->
- </div>
- <!-- Set background for slide in css -->
- <div class="header-back one" style="background: url('images/header_one.jpg') 50% 0 no-repeat;"></div>
</div>
- <!--<div class="item">
- <div class="container">
- <div class="carousel-caption blank">
- <h1>We create meaningful <br/> interfaces that inspire.</h1>
- <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam.</p>
- <p><a class="btn btn-lg btn-primary" href="#" role="button">Learn more</a></p>
- </div>
- </div>-->
- <!-- Set background for slide in css -->
- <!--<div class="header-back two"></div>
- </div>-->
- </div>
- <!--<a class="left carousel-control" href="#inSlider" role="button" data-slide="prev">
- <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
- <span class="sr-only">Previous</span>
- </a>
- <a class="right carousel-control" href="#inSlider" role="button" data-slide="next">
- <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
- <span class="sr-only">Next</span>
- </a>-->
+
</div>
- <section id="DashBoard" class="container services">
- <div class="row">
+<section id="DashBoard" class="container services">
+ <div class="row">
- <h1>
- OPNFV’s goals are to:
- </h1>
- <div class="col-sm-3">
+ <h1>
+ OPNFV’s goals are to:
+ </h1>
+ <div class="col-sm-3">
- <p>Develop an integrated and tested open source platform that can be used to build NFV functionality--accelerating
- the introduction of new products and services</p>
- <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
- </div>
- <div class="col-sm-3">
+ <p>Develop an integrated and tested open source platform that can be used to build NFV functionality--accelerating the introduction of new products and services</p>
+ <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
+ </div>
+ <div class="col-sm-3">
- <p>Include participation of leading end users to validate that OPNFV meets the needs of user community</p>
- <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
- </div>
- <div class="col-sm-3">
+ <p>Include participation of leading end users to validate that OPNFV meets the needs of user community</p>
+ <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
+ </div>
+ <div class="col-sm-3">
- <p>Contribute to and participate in relevant open source projects that will be leveraged in the OPNFV platform;
- ensuring consistency, performance and interoperability among open source components</p>
- <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
- </div>
- <div class="col-sm-3">
+ <p>Contribute to and participate in relevant open source projects that will be leveraged in the OPNFV platform; ensuring consistency, performance and interoperability among open source components</p>
+ <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
+ </div>
+ <div class="col-sm-3">
- <p>Establish an ecosystem for NFV solutions based on open standards and software to meet the needs of end users</p>
- <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
- </div>
- <div class="col-sm-3">
+ <p>Establish an ecosystem for NFV solutions based on open standards and software to meet the needs of end users</p>
+ <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
+ </div>
+ <div class="col-sm-3">
- <p>Promote OPNFV as the preferred platform and community for open source NFV</p>
- <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
- </div>
+ <p>Promote OPNFV as the preferred platform and community for open source NFV</p>
+ <p><a class="navy-link" href="#" role="button">Details &raquo;</a></p>
</div>
- </section>
+ </div>
+</section>
<div ui-view></div>
<section id="contact" class="gray-section contact" style="background-image: url(images/word_map.png)">
- <div class="container">
- <div class="row m-b-lg">
- <div class="col-lg-12 text-center">
- <div class="navy-line"></div>
- <h1>Contact Us</h1>
- </div>
- </div>
- <div class="row m-b-lg">
- <div class="col-lg-3 col-lg-offset-3">
- <address>
+ <div class="container">
+ <div class="row m-b-lg">
+ <div class="col-lg-12 text-center">
+ <div class="navy-line"></div>
+ <h1>Contact Us</h1>
+ </div>
+ </div>
+ <div class="row m-b-lg">
+ <div class="col-lg-3 col-lg-offset-3">
+ <address>
<strong><span class="navy">Press, Analyst, or Speaking Inquiries</span></strong><br/> pr@opnfv.org
<br/>
</address>
- <address>
+ <address>
<strong><span class="navy">OPNFV Events</span></strong><br/> events@opnfv.org
<br/>
</address>
- <address>
+ <address>
<strong><span class="navy">IT Support</span></strong><br/>opnfv-helpdesk@rt.linuxfoundation.org
<br/>
</address>
- </div>
+ </div>
- <div class="col-lg-4">
- <address>
+ <div class="col-lg-4">
+ <address>
<strong><span class="navy">To submit and track bugs related to OPNFV</span></strong><br/>Please visit https://jira.opnfv.org
<br/>
</address>
- <address>
+ <address>
<strong><span class="navy">Newsletter</span></strong><br/>Sign up for the OPNFV newsletter
<br/>
</address>
- <address>
+ <address>
<strong><span class="navy">Membership</span></strong><br/>Please visit the Join as a Member page
<br/>
</address>
- </div>
- </div>
- <div class="row">
- <div class="col-lg-12 text-center">
- <img src="images/logo.png" />
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-lg-12 text-center">
+ <img src="images/logo.png" />
- </div>
- </div>
- <div class="row">
- <div class="col-lg-8 col-lg-offset-2 text-center m-t-lg m-b-lg">
- <p><strong>&copy; 2016 Open Platform for NFV Project, Inc</strong><br/> A Linux Foundation Collaborative Project. All
- Rights Reserved. Open Platform for NFV and OPNFV are trademarks of the Open Platform for NFV Project, Inc. Linux
- Foundation is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds.
- Please see our terms of use, trademark policy, and privacy policy.
- </p>
- </div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-lg-8 col-lg-offset-2 text-center m-t-lg m-b-lg">
+ <p><strong>&copy; 2016 Open Platform for NFV Project, Inc</strong><br/> A Linux Foundation Collaborative Project. All Rights Reserved. Open Platform for NFV and OPNFV are trademarks of the Open Platform for NFV Project, Inc. Linux Foundation
+ is a registered trademark of The Linux Foundation. Linux is a registered trademark of Linus Torvalds. Please see our terms of use, trademark policy, and privacy policy.
+ </p>
+ </div>
+ </div>
</div>
- </div>
</section>
<script>
+ $(document).ready(function() {
- $(document).ready(function () {
+ // $('body').scrollspy({
+ // target: '.navbar-fixed-top',
+ // offset: 80
+ // });
- $('body').scrollspy({
- target: '.navbar-fixed-top',
- offset: 80
+ //Page scrolling feature
+ $('a.page-scroll').bind('click', function(event) {
+ var link = $(this);
+ $('html, body').stop().animate({
+ scrollTop: $(link.attr('href')).offset().top - 50
+ }, 500);
+ event.preventDefault();
+ $("#navbar").collapse('hide');
});
- // Page scrolling feature
$('a.page-scroll').bind('click', function(event) {
var link = $(this);
$('html, body').stop().animate({
@@ -190,63 +175,56 @@
$("#navbar").collapse('hide');
});
-
- console.log( $("selectVersion").val());
-
});
- // $(".select2_demo_1").select2();
- // $(".select2_demo_2").select2();
- // $(".select2_demo_3").select2({
- // placeholder: "Version",
- // allowClear: true
- // });
- // $(".select2_demo_4").select2({
- // placeholder: "Period",
- // allowClear: true
- // });
-
-
var config = {
- '.chosen-select' : {},
- '.chosen-select-deselect' : {allow_single_deselect:true},
- '.chosen-select-no-single' : {disable_search_threshold:10},
- '.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
- '.chosen-select-width' : {width:"95%"}
- }
- for (var selector in config) {
- $(selector).chosen(config[selector]);
- }
+ '.chosen-select': {},
+ '.chosen-select-deselect': {
+ allow_single_deselect: true
+ },
+ '.chosen-select-no-single': {
+ disable_search_threshold: 10
+ },
+ '.chosen-select-no-results': {
+ no_results_text: 'Oops, nothing found!'
+ },
+ '.chosen-select-width': {
+ width: "95%"
+ }
+ }
+ for (var selector in config) {
+ $(selector).chosen(config[selector]);
+ }
var cbpAnimatedHeader = (function() {
var docElem = document.documentElement,
- header = document.querySelector( '.navbar-default' ),
- didScroll = false,
- changeHeaderOn = 200;
+ header = document.querySelector('.navbar-default'),
+ didScroll = false,
+ changeHeaderOn = 200;
+
function init() {
- window.addEventListener( 'scroll', function( event ) {
- if( !didScroll ) {
+ window.addEventListener('scroll', function(event) {
+ if (!didScroll) {
didScroll = true;
- setTimeout( scrollPage, 250 );
+ setTimeout(scrollPage, 250);
}
- }, false );
+ }, false);
}
+
function scrollPage() {
var sy = scrollY();
- if ( sy >= changeHeaderOn ) {
+ if (sy >= changeHeaderOn) {
$(header).addClass('navbar-scroll')
- }
- else {
+ } else {
$(header).removeClass('navbar-scroll')
}
didScroll = false;
}
+
function scrollY() {
return window.pageYOffset || docElem.scrollTop;
}
init();
})();
-
-
-</script>
+</script> \ No newline at end of file
diff --git a/utils/test/reporting/pages/bower.json b/utils/test/reporting/pages/bower.json
index dd0996d18..bfc4df3d9 100644
--- a/utils/test/reporting/pages/bower.json
+++ b/utils/test/reporting/pages/bower.json
@@ -1,32 +1,47 @@
{
- "name": "opnfv",
- "version": "0.0.0",
- "dependencies": {
- "angular": "^1.4.0",
- "bootstrap": "^3.2.0",
- "angular-animate": "^1.4.0",
- "jquery-slimscroll": "slimscroll#^1.3.8",
- "metisMenu": "~2.0.2",
- "chosen": "^1.6.2",
- "oclazyload": "^1.0.9",
- "angular-bootstrap": "~1.1.2",
- "angular-ui-router": "~0.2.15",
- "angular-resource": "^1.6.0",
- "angular-selectize2": "^3.0.1",
- "components-font-awesome": "^4.7.0"
- },
- "devDependencies": {
- "angular-mocks": "^1.4.0"
- },
- "appPath": "app",
- "moduleName": "opnfvApp",
- "overrides": {
- "bootstrap": {
- "main": [
- "less/bootstrap.less",
- "dist/css/bootstrap.css",
- "dist/js/bootstrap.js"
- ]
+ "name": "opnfv",
+ "version": "0.0.0",
+ "dependencies": {
+ "angular": "^1.4.0",
+ "bootstrap": "^3.2.0",
+ "angular-animate": "^1.4.0",
+ "jquery-slimscroll": "slimscroll#^1.3.8",
+ "metisMenu": "~2.0.2",
+ "chosen": "^1.6.2",
+ "oclazyload": "^1.0.9",
+ "angular-bootstrap": "~1.1.2",
+ "angular-ui-router": "~0.2.15",
+ "angular-resource": "^1.6.0",
+ "angular-selectize2": "^3.0.1",
+ "components-font-awesome": "^4.7.0",
+ "angular-tooltips": "^1.1.8",
+ "jQuery-rwdImageMaps": "*",
+ "animate.css": "^3.5.2",
+ "ng-dialog": "^1.0.0",
+ "inspiniacss": "^0.0.1"
+ },
+ "devDependencies": {
+ "angular-mocks": "^1.4.0"
+ },
+ "appPath": "app",
+ "moduleName": "opnfvApp",
+ "overrides": {
+ "bootstrap": {
+ "main": [
+ "less/bootstrap.less",
+ "dist/css/bootstrap.css",
+ "dist/js/bootstrap.js"
+ ]
+ },
+ "jQuery-rwdImageMaps": {
+ "main": [
+ "jquery.rwdImageMaps.min.js"
+ ]
+ },
+ "inspiniacss": {
+ "main": [
+ "style.css"
+ ]
+ }
}
- }
-}
+} \ No newline at end of file
diff --git a/utils/test/reporting/pages/test/karma.conf.js b/utils/test/reporting/pages/test/karma.conf.js
index 2b0f41cd3..5c2e79b9f 100644
--- a/utils/test/reporting/pages/test/karma.conf.js
+++ b/utils/test/reporting/pages/test/karma.conf.js
@@ -36,6 +36,7 @@ module.exports = function(config) {
'bower_components/microplugin/src/microplugin.js',
'bower_components/selectize/dist/js/selectize.js',
'bower_components/angular-selectize2/dist/angular-selectize.js',
+ 'bower_components/angular-tooltips/dist/angular-tooltips.min.js',
'bower_components/angular-mocks/angular-mocks.js',
// endbower
'app/scripts/**/*.js',
diff --git a/utils/test/reporting/reporting.yaml b/utils/test/reporting/reporting.yaml
index fa9862615..81e976a28 100644
--- a/utils/test/reporting/reporting.yaml
+++ b/utils/test/reporting/reporting.yaml
@@ -2,13 +2,13 @@ general:
installers:
- apex
- compass
+ - daisy
- fuel
- joid
- - daisy
versions:
- master
- - colorado
+
log:
log_file: reporting.log
log_level: ERROR
@@ -30,21 +30,38 @@ general:
testapi:
url: testresults.opnfv.org/test/api/v1/results
-
+
functest:
blacklist:
- ovno
- security_scan
+ - rally_sanity
+ - healthcheck
+ - odl_netvirt
+ - aaa
+ - cloudify_ims
+ - orchestra_ims
+ - juju_epc
+ - orchestra
+ - promise
max_scenario_criteria: 50
test_conf: https://git.opnfv.org/cgit/functest/plain/functest/ci/testcases.yaml
log_level: ERROR
- jenkins_url: https://build.opnfv.org/ci/view/functest/job
-
-
+ jenkins_url: https://build.opnfv.org/ci/view/functest/job/
+ exclude_noha: False
+ exclude_virtual: False
+
yardstick:
test_conf: https://git.opnfv.org/cgit/yardstick/plain/tests/ci/report_config.yaml
log_level: ERROR
+storperf:
+ test_list:
+ - snia_steady_state
+ log_level: ERROR
+
qtip:
bottleneck:
+
+vsperf:
diff --git a/utils/test/reporting/storperf/reporting-status.py b/utils/test/reporting/storperf/reporting-status.py
new file mode 100644
index 000000000..888e339f8
--- /dev/null
+++ b/utils/test/reporting/storperf/reporting-status.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+#
+# This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+import datetime
+import jinja2
+import os
+
+# manage conf
+import utils.reporting_utils as rp_utils
+
+import utils.scenarioResult as sr
+
+installers = rp_utils.get_config('general.installers')
+versions = rp_utils.get_config('general.versions')
+PERIOD = rp_utils.get_config('general.period')
+
+# Logger
+logger = rp_utils.getLogger("Storperf-Status")
+reportingDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
+
+logger.info("*******************************************")
+logger.info("* Generating reporting scenario status *")
+logger.info("* Data retention = %s days *" % PERIOD)
+logger.info("* *")
+logger.info("*******************************************")
+
+# retrieve the list of storperf tests
+storperf_tests = rp_utils.get_config('storperf.test_list')
+logger.info("Storperf tests: %s" % storperf_tests)
+
+# For all the versions
+for version in versions:
+ # For all the installers
+ for installer in installers:
+ # get scenarios results data
+ # for the moment we consider only 1 case snia_steady_state
+ scenario_results = rp_utils.getScenarios("snia_steady_state",
+ installer,
+ version)
+ # logger.info("scenario_results: %s" % scenario_results)
+
+ scenario_stats = rp_utils.getScenarioStats(scenario_results)
+ logger.info("scenario_stats: %s" % scenario_stats)
+ items = {}
+ scenario_result_criteria = {}
+
+ # From each scenarios get results list
+ for s, s_result in scenario_results.items():
+ logger.info("---------------------------------")
+ logger.info("installer %s, version %s, scenario %s", installer,
+ version, s)
+ ten_criteria = len(s_result)
+
+ ten_score = 0
+ for v in s_result:
+ if "PASS" in v['criteria']:
+ ten_score += 1
+
+ logger.info("ten_score: %s / %s" % (ten_score, ten_criteria))
+
+ four_score = 0
+ try:
+ LASTEST_TESTS = rp_utils.get_config(
+ 'general.nb_iteration_tests_success_criteria')
+ s_result.sort(key=lambda x: x['start_date'])
+ four_result = s_result[-LASTEST_TESTS:]
+ logger.debug("four_result: {}".format(four_result))
+ logger.debug("LASTEST_TESTS: {}".format(LASTEST_TESTS))
+ # logger.debug("four_result: {}".format(four_result))
+ four_criteria = len(four_result)
+ for v in four_result:
+ if "PASS" in v['criteria']:
+ four_score += 1
+ logger.info("4 Score: %s / %s " % (four_score,
+ four_criteria))
+ except:
+ logger.error("Impossible to retrieve the four_score")
+
+ try:
+ s_status = (four_score * 100) / four_criteria
+ except:
+ s_status = 0
+ logger.info("Score percent = %s" % str(s_status))
+ s_four_score = str(four_score) + '/' + str(four_criteria)
+ s_ten_score = str(ten_score) + '/' + str(ten_criteria)
+ s_score_percent = str(s_status)
+
+ logger.debug(" s_status: {}".format(s_status))
+ if s_status == 100:
+ logger.info(">>>>> scenario OK, save the information")
+ else:
+ logger.info(">>>> scenario not OK, last 4 iterations = %s, \
+ last 10 days = %s" % (s_four_score, s_ten_score))
+
+ s_url = ""
+ if len(s_result) > 0:
+ build_tag = s_result[len(s_result)-1]['build_tag']
+ logger.debug("Build tag: %s" % build_tag)
+ s_url = s_url = rp_utils.getJenkinsUrl(build_tag)
+ logger.info("last jenkins url: %s" % s_url)
+
+ # Save daily results in a file
+ path_validation_file = ("./display/" + version +
+ "/storperf/scenario_history.txt")
+
+ if not os.path.exists(path_validation_file):
+ with open(path_validation_file, 'w') as f:
+ info = 'date,scenario,installer,details,score\n'
+ f.write(info)
+
+ with open(path_validation_file, "a") as f:
+ info = (reportingDate + "," + s + "," + installer +
+ "," + s_ten_score + "," +
+ str(s_score_percent) + "\n")
+ f.write(info)
+
+ scenario_result_criteria[s] = sr.ScenarioResult(s_status,
+ s_four_score,
+ s_ten_score,
+ s_score_percent,
+ s_url)
+
+ logger.info("--------------------------")
+
+ templateLoader = jinja2.FileSystemLoader(".")
+ templateEnv = jinja2.Environment(loader=templateLoader,
+ autoescape=True)
+
+ TEMPLATE_FILE = "./storperf/template/index-status-tmpl.html"
+ template = templateEnv.get_template(TEMPLATE_FILE)
+
+ outputText = template.render(scenario_results=scenario_result_criteria,
+ installer=installer,
+ period=PERIOD,
+ version=version,
+ date=reportingDate)
+
+ with open("./display/" + version +
+ "/storperf/status-" + installer + ".html", "wb") as fh:
+ fh.write(outputText)
diff --git a/utils/test/reporting/storperf/template/index-status-tmpl.html b/utils/test/reporting/storperf/template/index-status-tmpl.html
new file mode 100644
index 000000000..e0fcc6828
--- /dev/null
+++ b/utils/test/reporting/storperf/template/index-status-tmpl.html
@@ -0,0 +1,111 @@
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <!-- Bootstrap core CSS -->
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
+ <link href="../../css/default.css" rel="stylesheet">
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
+ <script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
+ <script type="text/javascript" src="http://d3js.org/d3.v2.min.js"></script>
+ <script type="text/javascript" src="../../js/gauge.js"></script>
+ <script type="text/javascript" src="../../js/trend.js"></script>
+ <script>
+ function onDocumentReady() {
+ // Gauge management
+ {% for scenario in scenario_results.keys() -%}
+ var gaugeScenario{{loop.index}} = gauge('#gaugeScenario{{loop.index}}');
+ {%- endfor %}
+ // assign success rate to the gauge
+ function updateReadings() {
+ {% for scenario in scenario_results.keys() -%}
+ gaugeScenario{{loop.index}}.update({{scenario_results[scenario].getScorePercent()}});
+ {%- endfor %}
+ }
+ updateReadings();
+ }
+
+ // trend line management
+ d3.csv("./scenario_history.txt", function(data) {
+ // ***************************************
+ // Create the trend line
+ {% for scenario in scenario_results.keys() -%}
+ // for scenario {{scenario}}
+ // Filter results
+ var trend{{loop.index}} = data.filter(function(row) {
+ return row["scenario"]=="{{scenario}}" && row["installer"]=="{{installer}}";
+ })
+ // Parse the date
+ trend{{loop.index}}.forEach(function(d) {
+ d.date = parseDate(d.date);
+ d.score = +d.score
+ });
+ // Draw the trend line
+ var mytrend = trend("#trend_svg{{loop.index}}",trend{{loop.index}})
+ // ****************************************
+ {%- endfor %}
+ });
+ if ( !window.isLoaded ) {
+ window.addEventListener("load", function() {
+ onDocumentReady();
+ }, false);
+ } else {
+ onDocumentReady();
+ }
+ </script>
+ <script type="text/javascript">
+ $(document).ready(function (){
+ $(".btn-more").click(function() {
+ $(this).hide();
+ $(this).parent().find(".panel-default").show();
+ });
+ })
+ </script>
+ </head>
+ <body>
+ <div class="container">
+ <div class="masthead">
+ <h3 class="text-muted">Storperf status page ({{version}}, {{date}})</h3>
+ <nav>
+ <ul class="nav nav-justified">
+ <li class="active"><a href="http://testresults.opnfv.org/reporting/index.html">Home</a></li>
+ <li><a href="status-apex.html">Apex</a></li>
+ <li><a href="status-compass.html">Compass</a></li>
+ <li><a href="status-daisy.html">Daisy</a></li>
+ <li><a href="status-fuel.html">Fuel</a></li>
+ <li><a href="status-joid.html">Joid</a></li>
+ </ul>
+ </nav>
+ </div>
+<div class="row">
+ <div class="col-md-1"></div>
+ <div class="col-md-10">
+ <div class="page-header">
+ <h2>{{installer}}</h2>
+ </div>
+
+ <div class="scenario-overview">
+ <div class="panel-heading"><h4><b>List of last scenarios ({{version}}) run over the last {{period}} days </b></h4></div>
+ <table class="table">
+ <tr>
+ <th width="40%">Scenario</th>
+ <th width="20%">Status</th>
+ <th width="20%">Trend</th>
+ <th width="10%">Last 4 Iterations</th>
+ <th width="10%">Last 10 Days</th>
+ </tr>
+ {% for scenario,result in scenario_results.iteritems() -%}
+ <tr class="tr-ok">
+ <td><a href="{{scenario_results[scenario].getLastUrl()}}">{{scenario}}</a></td>
+ <td><div id="gaugeScenario{{loop.index}}"></div></td>
+ <td><div id="trend_svg{{loop.index}}"></div></td>
+ <td>{{scenario_results[scenario].getFourDaysScore()}}</td>
+ <td>{{scenario_results[scenario].getTenDaysScore()}}</td>
+ </tr>
+ {%- endfor %}
+ </table>
+ </div>
+
+
+ </div>
+ <div class="col-md-1"></div>
+</div>
diff --git a/utils/test/reporting/utils/reporting_utils.py b/utils/test/reporting/utils/reporting_utils.py
index 0af60c78a..47d67f362 100644
--- a/utils/test/reporting/utils/reporting_utils.py
+++ b/utils/test/reporting/utils/reporting_utils.py
@@ -93,15 +93,23 @@ def getApiResults(case, installer, scenario, version):
response = urlopen(request)
k = response.read()
results = json.loads(k)
- except URLError, e:
- print 'No kittez. Got an error code:', e
+ except URLError as e:
+ print('No kittez. Got an error code:', e)
return results
def getScenarios(case, installer, version):
- case = case.getName()
+ try:
+ case = case.getName()
+ except:
+ # if case is not an object test case, try the string
+ if type(case) == str:
+ case = case
+ else:
+ raise ValueError("Case cannot be evaluated")
+
period = get_config('general.period')
url_base = get_config('testapi.url')
@@ -115,8 +123,8 @@ def getScenarios(case, installer, version):
k = response.read()
results = json.loads(k)
test_results = results['results']
- except URLError, e:
- print 'Got an error code:', e
+ except URLError as e:
+ print('Got an error code:', e)
if test_results is not None:
test_results.reverse()
@@ -127,7 +135,15 @@ def getScenarios(case, installer, version):
# Retrieve all the scenarios per installer
if not r['scenario'] in scenario_results.keys():
scenario_results[r['scenario']] = []
- scenario_results[r['scenario']].append(r)
+ # Do we consider results from virtual pods ...
+ # Do we consider results for non HA scenarios...
+ exclude_virtual_pod = get_config('functest.exclude_virtual')
+ exclude_noha = get_config('functest.exclude_noha')
+ if ((exclude_virtual_pod and "virtual" in r['pod_name']) or
+ (exclude_noha and "noha" in r['scenario'])):
+ print("exclude virtual pod results...")
+ else:
+ scenario_results[r['scenario']].append(r)
return scenario_results
@@ -156,8 +172,8 @@ def getScenarioStatus(installer, version):
response.close()
results = json.loads(k)
test_results = results['results']
- except URLError, e:
- print 'Got an error code:', e
+ except URLError as e:
+ print('Got an error code:', e)
scenario_results = {}
result_dict = {}
@@ -190,7 +206,7 @@ def getNbtestOk(results):
if "PASS" in v:
nb_test_ok += 1
except:
- print "Cannot retrieve test status"
+ print("Cannot retrieve test status")
return nb_test_ok
@@ -254,16 +270,18 @@ def getResult(testCase, installer, scenario, version):
def getJenkinsUrl(build_tag):
# e.g. jenkins-functest-apex-apex-daily-colorado-daily-colorado-246
# id = 246
+ # jenkins-functest-compass-huawei-pod5-daily-master-136
+ # id = 136
# note it is linked to jenkins format
# if this format changes...function to be adapted....
url_base = get_config('functest.jenkins_url')
try:
build_id = [int(s) for s in build_tag.split("-") if s.isdigit()]
- jenkins_path = filter(lambda c: not c.isdigit(), build_tag)
- url_id = jenkins_path[8:-1] + "/" + str(build_id[0])
+ url_id = (build_tag[8:-(len(str(build_id[0])) + 1)] +
+ "/" + str(build_id[0]))
jenkins_url = url_base + url_id + "/console"
except:
- print 'Impossible to get jenkins url:'
+ print('Impossible to get jenkins url:')
return jenkins_url
@@ -273,7 +291,7 @@ def getScenarioPercent(scenario_score, scenario_criteria):
try:
score = float(scenario_score) / float(scenario_criteria) * 100
except:
- print 'Impossible to calculate the percentage score'
+ print('Impossible to calculate the percentage score')
return score
@@ -321,8 +339,8 @@ def get_percent(four_list, ten_list):
def _test():
status = getScenarioStatus("compass", "master")
- print "status:++++++++++++++++++++++++"
- print json.dumps(status, indent=4)
+ print("status:++++++++++++++++++++++++")
+ print(json.dumps(status, indent=4))
# ----------------------------------------------------------
diff --git a/utils/test/reporting/yardstick/scenarioResult.py b/utils/test/reporting/utils/scenarioResult.py
index 1f7eb2b24..6029d7f42 100644
--- a/utils/test/reporting/yardstick/scenarioResult.py
+++ b/utils/test/reporting/utils/scenarioResult.py
@@ -10,11 +10,12 @@
class ScenarioResult(object):
def __init__(self, status, four_days_score='', ten_days_score='',
- score_percent=0.0):
+ score_percent=0.0, last_url=''):
self.status = status
self.four_days_score = four_days_score
self.ten_days_score = ten_days_score
self.score_percent = score_percent
+ self.last_url = last_url
def getStatus(self):
return self.status
@@ -27,3 +28,6 @@ class ScenarioResult(object):
def getScorePercent(self):
return self.score_percent
+
+ def getLastUrl(self):
+ return self.last_url
diff --git a/utils/test/reporting/yardstick/reporting-status.py b/utils/test/reporting/yardstick/reporting-status.py
index a0f0b0184..12f42ca31 100644
--- a/utils/test/reporting/yardstick/reporting-status.py
+++ b/utils/test/reporting/yardstick/reporting-status.py
@@ -10,7 +10,7 @@ import datetime
import jinja2
import os
-import scenarioResult as sr
+import utils.scenarioResult as sr
from scenarios import config as cf
# manage conf
diff --git a/utils/test/testapi/.coveragerc b/utils/test/testapi/.coveragerc
new file mode 100644
index 000000000..23fb97fba
--- /dev/null
+++ b/utils/test/testapi/.coveragerc
@@ -0,0 +1,27 @@
+# .coveragerc to control coverage.py
+
+[run]
+branch = True
+source =
+ opnfv_testapi
+
+[report]
+# Regexes for lines to exclude from consideration
+exclude_lines =
+ # Have to re-enable the standard pragma
+ pragma: no cover
+
+ # Don't complain about missing debug-only code:
+ def __repr__
+ if self\.debug
+
+ # Don't complain if tests don't hit defensive assertion code:
+ raise AssertionError
+ raise NotImplementedError
+
+ # Don't complain if non-runnable code isn't run:
+ if 0:
+ if __name__ == .__main__.:
+
+ignore_errors = True
+
diff --git a/utils/test/testapi/docker/Dockerfile b/utils/test/testapi/docker/Dockerfile
index 86513e05b..e031e194c 100644
--- a/utils/test/testapi/docker/Dockerfile
+++ b/utils/test/testapi/docker/Dockerfile
@@ -8,13 +8,12 @@
# $ docker build -t opnfv/testapi:tag .
#
# Execution:
-# $ docker run -dti -p 8000:8000 \
-# -e "swagger_url=http://10.63.243.17:8000" \
+# $ docker run -dti -p 8001:8000 \
+# -e "swagger_url=http://10.63.243.17:8001" \
# -e "mongodb_url=mongodb://10.63.243.17:27017/" \
-# -e "api_port=8000"
# opnfv/testapi:tag
#
-# NOTE: providing swagger_url, api_port, mongodb_url is optional.
+# NOTE: providing swagger_url, mongodb_url is optional.
# If not provided, it will use the default one
# configured in config.ini
#
diff --git a/utils/test/testapi/docker/prepare-env.sh b/utils/test/testapi/docker/prepare-env.sh
index 99433cc8c..9f07efbd1 100755
--- a/utils/test/testapi/docker/prepare-env.sh
+++ b/utils/test/testapi/docker/prepare-env.sh
@@ -9,8 +9,3 @@ fi
if [ "$swagger_url" != "" ]; then
sudo crudini --set --existing $FILE swagger base_url $swagger_url
fi
-
-if [ "$api_port" != "" ];then
- sudo crudini --set --existing $FILE api port $api_port
-fi
-
diff --git a/utils/test/testapi/etc/config.ini b/utils/test/testapi/etc/config.ini
index 0edb73a3f..77cc6c6ee 100644
--- a/utils/test/testapi/etc/config.ini
+++ b/utils/test/testapi/etc/config.ini
@@ -11,6 +11,7 @@ dbname = test_results_collection
port = 8000
# With debug_on set to true, error traces will be shown in HTTP responses
debug = True
+authenticate = False
[swagger]
base_url = http://localhost:8000
diff --git a/utils/test/testapi/htmlize/doc-build.sh b/utils/test/testapi/htmlize/doc-build.sh
new file mode 100644
index 000000000..33560ceea
--- /dev/null
+++ b/utils/test/testapi/htmlize/doc-build.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -o errexit
+
+# Create virtual environment
+virtualenv $WORKSPACE/testapi_venv
+source $WORKSPACE/testapi_venv/bin/activate
+
+# Swgger Codegen Tool
+url="http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.1/swagger-codegen-cli-2.2.1.jar"
+
+# Check for jar file locally and in the repo
+if [ ! -f swagger-codegen-cli.jar ];
+then
+ wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.1/swagger-codegen-cli-2.2.1.jar -O swagger-codegen-cli.jar
+fi
+
+# Install Pre-requistics
+pip install requests
+
+python ./utils/test/testapi/htmlize/htmlize.py -o ${WORKSPACE}/
diff --git a/utils/test/testapi/htmlize/finish.sh b/utils/test/testapi/htmlize/finish.sh
deleted file mode 100644
index d24ae056c..000000000
--- a/utils/test/testapi/htmlize/finish.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-
-# Stop opnfv-testapi server
-proc_number=`ps -ef | grep opnfv-testapi | grep -v grep | wc -l`
-
-if [ $proc_number -gt 0 ]; then
- procs=`ps -ef | grep opnfv-testapi | grep -v grep`
- echo "Kill opnfv-testapi server $procs"
- ps -ef | grep opnfv-testapi | grep -v grep | awk '{print $2}' | xargs kill -kill &>/dev/null
-fi
-
-deactivate
diff --git a/utils/test/testapi/htmlize/htmlize.py b/utils/test/testapi/htmlize/htmlize.py
index 68d02febd..b8c4fb43f 100644
--- a/utils/test/testapi/htmlize/htmlize.py
+++ b/utils/test/testapi/htmlize/htmlize.py
@@ -28,7 +28,10 @@ def main(args):
# Generating html page
cmd = 'java -jar swagger-codegen-cli.jar generate \
-i specs.json -l html2 -o %s' % (args.output_directory)
- os.system(cmd)
+ if os.system(cmd) == 0:
+ exit(0)
+ else:
+ exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Create \
@@ -36,12 +39,14 @@ if __name__ == '__main__':
parser.add_argument('-ru', '--resource-listing-url',
type=str,
required=False,
- default='http://localhost:8000/swagger/spec.json',
+ default=('http://testresults.opnfv.org'
+ '/test/swagger/spec.json'),
help='Resource Listing Spec File')
parser.add_argument('-au', '--api-declaration-url',
type=str,
required=False,
- default='http://localhost:8000/swagger/spec',
+ default=('http://testresults.opnfv.org'
+ '/test/swagger/spec'),
help='API Declaration Spec File')
parser.add_argument('-o', '--output-directory',
required=True,
diff --git a/utils/test/testapi/htmlize/prepare.sh b/utils/test/testapi/htmlize/prepare.sh
deleted file mode 100644
index 67158f211..000000000
--- a/utils/test/testapi/htmlize/prepare.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-
-#Creating virtual environment
-virtualenv testapi_venv
-source testapi_venv/bin/activate
-
-# Install Pre-requisites
-pip install requests
-
-# Swgger Codegen Tool
-url="http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.1/swagger-codegen-cli-2.2.1.jar"
-
-#Check for jar file locally and in the repo
-if [ ! -f swagger-codegen-cli.jar ];
-then
- wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.2.1/swagger-codegen-cli-2.2.1.jar -O swagger-codegen-cli.jar
-fi
-
-# Start OPNFV Test API Server
-cd utils/test/testapi/
-pip install -r requirements.txt
-./install.sh
-opnfv-testapi -c ../../../testapi_venv/etc/opnfv_testapi/config.ini &
diff --git a/utils/test/testapi/htmlize/push-doc-artifact.sh b/utils/test/testapi/htmlize/push-doc-artifact.sh
index 383565df1..4cf1988b0 100644
--- a/utils/test/testapi/htmlize/push-doc-artifact.sh
+++ b/utils/test/testapi/htmlize/push-doc-artifact.sh
@@ -7,19 +7,21 @@ export PATH=$PATH:/usr/local/bin/
project=$PROJECT
workspace=$WORKSPACE
-artifact_dir="functest/docs"
+artifact_dir="$project/docs"
set +e
gsutil&>/dev/null
if [ $? != 0 ]; then
echo "Not possible to push results to artifact: gsutil not installed"
+ exit 1
else
gsutil ls gs://artifacts.opnfv.org/"$project"/ &>/dev/null
if [ $? != 0 ]; then
echo "Not possible to push results to artifact: gsutil not installed."
+ exit 1
else
echo "Uploading document to artifact $artifact_dir"
gsutil cp "$workspace"/index.html gs://artifacts.opnfv.org/"$artifact_dir"/testapi.html >/dev/null 2>&1
- echo "Document can be found at http://artifacts.opnfv.org/functest/docs/testapi.html"
+ echo "Document can be found at http://artifacts.opnfv.org/releng/docs/testapi.html"
fi
fi
diff --git a/utils/test/testapi/opnfv_testapi/cmd/server.py b/utils/test/testapi/opnfv_testapi/cmd/server.py
index c3d734607..fa2b72250 100644
--- a/utils/test/testapi/opnfv_testapi/cmd/server.py
+++ b/utils/test/testapi/opnfv_testapi/cmd/server.py
@@ -30,37 +30,43 @@ TODOs :
"""
import argparse
+import sys
-import tornado.ioloop
import motor
+import tornado.ioloop
-from opnfv_testapi.common.config import APIConfig
-from opnfv_testapi.tornado_swagger import swagger
+from opnfv_testapi.common import config
from opnfv_testapi.router import url_mappings
+from opnfv_testapi.tornado_swagger import swagger
+
+CONF = None
+
-# optionally get config file from command line
-parser = argparse.ArgumentParser()
-parser.add_argument("-c", "--config-file", dest='config_file',
- help="Config file location")
-args = parser.parse_args()
-CONF = APIConfig().parse(args.config_file)
+def parse_config(argv=[]):
+ global CONF
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-c", "--config-file", dest='config_file',
+ help="Config file location")
+ args = parser.parse_args(argv)
+ CONF = config.APIConfig().parse(args.config_file)
-# connecting to MongoDB server, and choosing database
-client = motor.MotorClient(CONF.mongo_url)
-db = client[CONF.mongo_dbname]
-swagger.docs(base_url=CONF.swagger_base_url)
+def get_db():
+ return motor.MotorClient(CONF.mongo_url)[CONF.mongo_dbname]
def make_app():
+ swagger.docs(base_url=CONF.swagger_base_url)
return swagger.Application(
url_mappings.mappings,
- db=db,
+ db=get_db(),
debug=CONF.api_debug_on,
+ auth=CONF.api_authenticate_on
)
def main():
+ parse_config(sys.argv[1:])
application = make_app()
application.listen(CONF.api_port)
tornado.ioloop.IOLoop.current().start()
diff --git a/utils/test/testapi/opnfv_testapi/common/config.py b/utils/test/testapi/opnfv_testapi/common/config.py
index ecab88ae3..105d4fabf 100644
--- a/utils/test/testapi/opnfv_testapi/common/config.py
+++ b/utils/test/testapi/opnfv_testapi/common/config.py
@@ -7,9 +7,8 @@
# http://www.apache.org/licenses/LICENSE-2.0
# feng.xiaowei@zte.com.cn remove prepare_put_request 5-30-2016
##############################################################################
-
-
-from ConfigParser import SafeConfigParser, NoOptionError
+import ConfigParser
+import os
class ParseError(Exception):
@@ -24,7 +23,7 @@ class ParseError(Exception):
return 'error parsing config file : %s' % self.msg
-class APIConfig:
+class APIConfig(object):
"""
The purpose of this class is to load values correctly from the config file.
Each key is declared as an attribute in __init__() and linked in parse()
@@ -36,20 +35,21 @@ class APIConfig:
self.mongo_dbname = None
self.api_port = None
self.api_debug_on = None
+ self.api_authenticate_on = None
self._parser = None
self.swagger_base_url = None
def _get_parameter(self, section, param):
try:
return self._parser.get(section, param)
- except NoOptionError:
- raise ParseError("[%s.%s] parameter not found" % (section, param))
+ except ConfigParser.NoOptionError:
+ raise ParseError("No parameter: [%s.%s]" % (section, param))
def _get_int_parameter(self, section, param):
try:
return int(self._get_parameter(section, param))
except ValueError:
- raise ParseError("[%s.%s] not an int" % (section, param))
+ raise ParseError("Not int: [%s.%s]" % (section, param))
def _get_bool_parameter(self, section, param):
result = self._get_parameter(section, param)
@@ -59,7 +59,7 @@ class APIConfig:
return False
raise ParseError(
- "[%s.%s : %s] not a boolean" % (section, param, result))
+ "Not boolean: [%s.%s : %s]" % (section, param, result))
@staticmethod
def parse(config_location=None):
@@ -68,28 +68,21 @@ class APIConfig:
if config_location is None:
config_location = obj._default_config_location
- obj._parser = SafeConfigParser()
- obj._parser.read(config_location)
- if not obj._parser:
+ if not os.path.exists(config_location):
raise ParseError("%s not found" % config_location)
+ obj._parser = ConfigParser.SafeConfigParser()
+ obj._parser.read(config_location)
+
# Linking attributes to keys from file with their sections
obj.mongo_url = obj._get_parameter("mongo", "url")
obj.mongo_dbname = obj._get_parameter("mongo", "dbname")
obj.api_port = obj._get_int_parameter("api", "port")
obj.api_debug_on = obj._get_bool_parameter("api", "debug")
+ obj.api_authenticate_on = obj._get_bool_parameter("api",
+ "authenticate")
+
obj.swagger_base_url = obj._get_parameter("swagger", "base_url")
return obj
-
- def __str__(self):
- return "mongo_url = %s \n" \
- "mongo_dbname = %s \n" \
- "api_port = %s \n" \
- "api_debug_on = %s \n" \
- "swagger_base_url = %s \n" % (self.mongo_url,
- self.mongo_dbname,
- self.api_port,
- self.api_debug_on,
- self.swagger_base_url)
diff --git a/utils/test/testapi/opnfv_testapi/common/constants.py b/utils/test/testapi/opnfv_testapi/common/constants.py
index 4d39a142d..71bd95216 100644
--- a/utils/test/testapi/opnfv_testapi/common/constants.py
+++ b/utils/test/testapi/opnfv_testapi/common/constants.py
@@ -10,6 +10,7 @@
DEFAULT_REPRESENTATION = "application/json"
HTTP_BAD_REQUEST = 400
+HTTP_UNAUTHORIZED = 401
HTTP_FORBIDDEN = 403
HTTP_NOT_FOUND = 404
HTTP_OK = 200
diff --git a/utils/test/testapi/opnfv_testapi/resources/handlers.py b/utils/test/testapi/opnfv_testapi/resources/handlers.py
index 9fc5d6be1..8255b526a 100644
--- a/utils/test/testapi/opnfv_testapi/resources/handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/handlers.py
@@ -20,19 +20,19 @@
# feng.xiaowei@zte.com.cn remove DashboardHandler 5-30-2016
##############################################################################
-import json
from datetime import datetime
+import functools
+import json
from tornado import gen
-from tornado.web import RequestHandler, asynchronous, HTTPError
+from tornado import web
-from models import CreateResponse
-from opnfv_testapi.common.constants import DEFAULT_REPRESENTATION, \
- HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_FORBIDDEN
+import models
+from opnfv_testapi.common import constants
from opnfv_testapi.tornado_swagger import swagger
-class GenericApiHandler(RequestHandler):
+class GenericApiHandler(web.RequestHandler):
def __init__(self, application, request, **kwargs):
super(GenericApiHandler, self).__init__(application, request, **kwargs)
self.db = self.settings["db"]
@@ -44,49 +44,71 @@ class GenericApiHandler(RequestHandler):
self.db_testcases = 'testcases'
self.db_results = 'results'
self.db_scenarios = 'scenarios'
+ self.auth = self.settings["auth"]
def prepare(self):
if self.request.method != "GET" and self.request.method != "DELETE":
if self.request.headers.get("Content-Type") is not None:
if self.request.headers["Content-Type"].startswith(
- DEFAULT_REPRESENTATION):
+ constants.DEFAULT_REPRESENTATION):
try:
self.json_args = json.loads(self.request.body)
except (ValueError, KeyError, TypeError) as error:
- raise HTTPError(HTTP_BAD_REQUEST,
- "Bad Json format [{}]".
- format(error))
+ raise web.HTTPError(constants.HTTP_BAD_REQUEST,
+ "Bad Json format [{}]".
+ format(error))
def finish_request(self, json_object=None):
if json_object:
self.write(json.dumps(json_object))
- self.set_header("Content-Type", DEFAULT_REPRESENTATION)
+ self.set_header("Content-Type", constants.DEFAULT_REPRESENTATION)
self.finish()
def _create_response(self, resource):
href = self.request.full_url() + '/' + str(resource)
- return CreateResponse(href=href).format()
+ return models.CreateResponse(href=href).format()
def format_data(self, data):
cls_data = self.table_cls.from_dict(data)
return cls_data.format_http()
- @asynchronous
+ def authenticate(method):
+ @web.asynchronous
+ @gen.coroutine
+ @functools.wraps(method)
+ def wrapper(self, *args, **kwargs):
+ if self.auth:
+ try:
+ token = self.request.headers['X-Auth-Token']
+ except KeyError:
+ raise web.HTTPError(constants.HTTP_UNAUTHORIZED,
+ "No Authentication Header.")
+ query = {'access_token': token}
+ check = yield self._eval_db_find_one(query, 'tokens')
+ if not check:
+ raise web.HTTPError(constants.HTTP_FORBIDDEN,
+ "Invalid Token.")
+ ret = yield gen.coroutine(method)(self, *args, **kwargs)
+ raise gen.Return(ret)
+ return wrapper
+
+ @web.asynchronous
@gen.coroutine
+ @authenticate
def _create(self, miss_checks, db_checks, **kwargs):
"""
:param miss_checks: [miss1, miss2]
:param db_checks: [(table, exist, query, error)]
"""
if self.json_args is None:
- raise HTTPError(HTTP_BAD_REQUEST, "no body")
+ raise web.HTTPError(constants.HTTP_BAD_REQUEST, "no body")
data = self.table_cls.from_dict(self.json_args)
for miss in miss_checks:
miss_data = data.__getattribute__(miss)
if miss_data is None or miss_data == '':
- raise HTTPError(HTTP_BAD_REQUEST,
- '{} missing'.format(miss))
+ raise web.HTTPError(constants.HTTP_BAD_REQUEST,
+ '{} missing'.format(miss))
for k, v in kwargs.iteritems():
data.__setattr__(k, v)
@@ -95,7 +117,7 @@ class GenericApiHandler(RequestHandler):
check = yield self._eval_db_find_one(query(data), table)
if (exist and not check) or (not exist and check):
code, message = error(data)
- raise HTTPError(code, message)
+ raise web.HTTPError(code, message)
if self.table != 'results':
data.creation_date = datetime.now()
@@ -107,7 +129,7 @@ class GenericApiHandler(RequestHandler):
resource = _id
self.finish_request(self._create_response(resource))
- @asynchronous
+ @web.asynchronous
@gen.coroutine
def _list(self, query=None, res_op=None, *args, **kwargs):
if query is None:
@@ -126,40 +148,42 @@ class GenericApiHandler(RequestHandler):
res = res_op(data, *args)
self.finish_request(res)
- @asynchronous
+ @web.asynchronous
@gen.coroutine
def _get_one(self, query):
data = yield self._eval_db_find_one(query)
if data is None:
- raise HTTPError(HTTP_NOT_FOUND,
- "[{}] not exist in table [{}]"
- .format(query, self.table))
+ raise web.HTTPError(constants.HTTP_NOT_FOUND,
+ "[{}] not exist in table [{}]"
+ .format(query, self.table))
self.finish_request(self.format_data(data))
- @asynchronous
+ @web.asynchronous
@gen.coroutine
+ @authenticate
def _delete(self, query):
data = yield self._eval_db_find_one(query)
if data is None:
- raise HTTPError(HTTP_NOT_FOUND,
- "[{}] not exit in table [{}]"
- .format(query, self.table))
+ raise web.HTTPError(constants.HTTP_NOT_FOUND,
+ "[{}] not exit in table [{}]"
+ .format(query, self.table))
yield self._eval_db(self.table, 'remove', query)
self.finish_request()
- @asynchronous
+ @web.asynchronous
@gen.coroutine
+ @authenticate
def _update(self, query, db_keys):
if self.json_args is None:
- raise HTTPError(HTTP_BAD_REQUEST, "No payload")
+ raise web.HTTPError(constants.HTTP_BAD_REQUEST, "No payload")
# check old data exist
from_data = yield self._eval_db_find_one(query)
if from_data is None:
- raise HTTPError(HTTP_NOT_FOUND,
- "{} could not be found in table [{}]"
- .format(query, self.table))
+ raise web.HTTPError(constants.HTTP_NOT_FOUND,
+ "{} could not be found in table [{}]"
+ .format(query, self.table))
data = self.table_cls.from_dict(from_data)
# check new data exist
@@ -167,13 +191,12 @@ class GenericApiHandler(RequestHandler):
if not equal:
to_data = yield self._eval_db_find_one(new_query)
if to_data is not None:
- raise HTTPError(HTTP_FORBIDDEN,
- "{} already exists in table [{}]"
- .format(new_query, self.table))
+ raise web.HTTPError(constants.HTTP_FORBIDDEN,
+ "{} already exists in table [{}]"
+ .format(new_query, self.table))
# we merge the whole document """
- edit_request = data.format()
- edit_request.update(self._update_requests(data))
+ edit_request = self._update_requests(data)
""" Updating the DB """
yield self._eval_db(self.table, 'update', query, edit_request,
@@ -187,8 +210,11 @@ class GenericApiHandler(RequestHandler):
request = self._update_request(request, k, v,
data.__getattribute__(k))
if not request:
- raise HTTPError(HTTP_FORBIDDEN, "Nothing to update")
- return request
+ raise web.HTTPError(constants.HTTP_FORBIDDEN, "Nothing to update")
+
+ edit_request = data.format()
+ edit_request.update(request)
+ return edit_request
@staticmethod
def _update_request(edit_request, key, new_value, old_value):
@@ -228,7 +254,7 @@ class GenericApiHandler(RequestHandler):
class VersionHandler(GenericApiHandler):
- @swagger.operation(nickname='List all versions')
+ @swagger.operation(nickname='listAllVersions')
def get(self):
"""
@description: list all supported versions
diff --git a/utils/test/testapi/opnfv_testapi/resources/models.py b/utils/test/testapi/opnfv_testapi/resources/models.py
index f518c97a0..0ea482fd2 100644
--- a/utils/test/testapi/opnfv_testapi/resources/models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/models.py
@@ -1,98 +1,116 @@
-##############################################################################
-# Copyright (c) 2015 Orange
-# guyrodrigue.koffi@orange.com / koffirodrigue@gmail.com
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-# feng.xiaowei@zte.com.cn mv Pod to pod_models.py 5-18-2016
-# feng.xiaowei@zte.com.cn add MetaCreateResponse/MetaGetResponse 5-18-2016
-# feng.xiaowei@zte.com.cn mv TestProject to project_models.py 5-19-2016
-# feng.xiaowei@zte.com.cn delete meta class 5-19-2016
-# feng.xiaowei@zte.com.cn add CreateResponse 5-19-2016
-# feng.xiaowei@zte.com.cn mv TestCase to testcase_models.py 5-20-2016
-# feng.xiaowei@zte.com.cn mv TestResut to result_models.py 5-23-2016
-# feng.xiaowei@zte.com.cn add ModelBase 12-20-2016
-##############################################################################
-import copy
-
-from opnfv_testapi.tornado_swagger import swagger
-
-
-class ModelBase(object):
-
- def _format(self, excludes):
- new_obj = copy.deepcopy(self)
- dicts = new_obj.__dict__
- for k in dicts.keys():
- if k in excludes:
- del dicts[k]
- elif dicts[k]:
- if hasattr(dicts[k], 'format'):
- dicts[k] = dicts[k].format()
- elif isinstance(dicts[k], list):
- hs = list()
- [hs.append(h.format() if hasattr(h, 'format') else str(h))
- for h in dicts[k]]
- dicts[k] = hs
- elif not isinstance(dicts[k], (str, int, float, dict)):
- dicts[k] = str(dicts[k])
- return dicts
-
- def format(self):
- return self._format(['_id'])
-
- def format_http(self):
- return self._format([])
-
- @staticmethod
- def attr_parser():
- return {}
-
- @classmethod
- def from_dict(cls, a_dict):
- if a_dict is None:
- return None
-
- attr_parser = cls.attr_parser()
- t = cls()
- for k, v in a_dict.iteritems():
- value = v
- if isinstance(v, dict) and k in attr_parser:
- value = attr_parser[k].from_dict(v)
- elif isinstance(v, list) and k in attr_parser:
- value = []
- for item in v:
- value.append(attr_parser[k].from_dict(item))
-
- t.__setattr__(k, value)
-
- return t
-
-
-@swagger.model()
-class CreateResponse(ModelBase):
- def __init__(self, href=''):
- self.href = href
-
-
-@swagger.model()
-class Versions(ModelBase):
- """
- @property versions:
- @ptype versions: C{list} of L{Version}
- """
-
- def __init__(self):
- self.versions = list()
-
- @staticmethod
- def attr_parser():
- return {'versions': Version}
-
-
-@swagger.model()
-class Version(ModelBase):
- def __init__(self, version=None, description=None):
- self.version = version
- self.description = description
+##############################################################################
+# Copyright (c) 2015 Orange
+# guyrodrigue.koffi@orange.com / koffirodrigue@gmail.com
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+# feng.xiaowei@zte.com.cn mv Pod to pod_models.py 5-18-2016
+# feng.xiaowei@zte.com.cn add MetaCreateResponse/MetaGetResponse 5-18-2016
+# feng.xiaowei@zte.com.cn mv TestProject to project_models.py 5-19-2016
+# feng.xiaowei@zte.com.cn delete meta class 5-19-2016
+# feng.xiaowei@zte.com.cn add CreateResponse 5-19-2016
+# feng.xiaowei@zte.com.cn mv TestCase to testcase_models.py 5-20-2016
+# feng.xiaowei@zte.com.cn mv TestResut to result_models.py 5-23-2016
+# feng.xiaowei@zte.com.cn add ModelBase 12-20-2016
+##############################################################################
+import copy
+import ast
+
+
+from opnfv_testapi.tornado_swagger import swagger
+
+
+class ModelBase(object):
+
+ def format(self):
+ return self._format(['_id'])
+
+ def format_http(self):
+ return self._format([])
+
+ @classmethod
+ def from_dict(cls, a_dict):
+ if a_dict is None:
+ return None
+
+ attr_parser = cls.attr_parser()
+ t = cls()
+ for k, v in a_dict.iteritems():
+ value = v
+ if isinstance(v, dict) and k in attr_parser:
+ value = attr_parser[k].from_dict(v)
+ elif isinstance(v, list) and k in attr_parser:
+ value = []
+ for item in v:
+ value.append(attr_parser[k].from_dict(item))
+
+ t.__setattr__(k, value)
+
+ return t
+
+ @staticmethod
+ def attr_parser():
+ return {}
+
+ def _format(self, excludes):
+ new_obj = copy.deepcopy(self)
+ dicts = new_obj.__dict__
+ for k in dicts.keys():
+ if k in excludes:
+ del dicts[k]
+ elif dicts[k]:
+ dicts[k] = self._obj_format(dicts[k])
+ return dicts
+
+ def _obj_format(self, obj):
+ if self._has_format(obj):
+ obj = obj.format()
+ elif isinstance(obj, unicode):
+ try:
+ obj = self._obj_format(ast.literal_eval(obj))
+ except:
+ try:
+ obj = str(obj)
+ except:
+ obj = obj
+ elif isinstance(obj, list):
+ hs = list()
+ for h in obj:
+ hs.append(self._obj_format(h))
+ obj = hs
+ elif not isinstance(obj, (str, int, float, dict)):
+ obj = str(obj)
+ return obj
+
+ @staticmethod
+ def _has_format(obj):
+ return not isinstance(obj, (str, unicode)) and hasattr(obj, 'format')
+
+
+@swagger.model()
+class CreateResponse(ModelBase):
+ def __init__(self, href=''):
+ self.href = href
+
+
+@swagger.model()
+class Versions(ModelBase):
+ """
+ @property versions:
+ @ptype versions: C{list} of L{Version}
+ """
+
+ def __init__(self):
+ self.versions = list()
+
+ @staticmethod
+ def attr_parser():
+ return {'versions': Version}
+
+
+@swagger.model()
+class Version(ModelBase):
+ def __init__(self, version=None, description=None):
+ self.version = version
+ self.description = description
diff --git a/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py b/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py
index 631d4a91d..65c27f60a 100644
--- a/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/pod_handlers.py
@@ -6,21 +6,21 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import handlers
+from opnfv_testapi.common import constants
from opnfv_testapi.tornado_swagger import swagger
-from handlers import GenericApiHandler
-from pod_models import Pod
-from opnfv_testapi.common.constants import HTTP_FORBIDDEN
+import pod_models
-class GenericPodHandler(GenericApiHandler):
+class GenericPodHandler(handlers.GenericApiHandler):
def __init__(self, application, request, **kwargs):
super(GenericPodHandler, self).__init__(application, request, **kwargs)
self.table = 'pods'
- self.table_cls = Pod
+ self.table_cls = pod_models.Pod
class PodCLHandler(GenericPodHandler):
- @swagger.operation(nickname='List all Pods')
+ @swagger.operation(nickname='listAllPods')
def get(self):
"""
@description: list all pods
@@ -29,7 +29,7 @@ class PodCLHandler(GenericPodHandler):
"""
self._list()
- @swagger.operation(nickname='Create a Pod')
+ @swagger.operation(nickname='createPod')
def post(self):
"""
@description: create a pod
@@ -46,7 +46,7 @@ class PodCLHandler(GenericPodHandler):
def error(data):
message = '{} already exists as a pod'.format(data.name)
- return HTTP_FORBIDDEN, message
+ return constants.HTTP_FORBIDDEN, message
miss_checks = ['name']
db_checks = [(self.table, False, query, error)]
@@ -54,7 +54,7 @@ class PodCLHandler(GenericPodHandler):
class PodGURHandler(GenericPodHandler):
- @swagger.operation(nickname='Get a Pod by pod_name')
+ @swagger.operation(nickname='getPodByName')
def get(self, pod_name):
"""
@description: get a single pod by pod_name
diff --git a/utils/test/testapi/opnfv_testapi/resources/project_handlers.py b/utils/test/testapi/opnfv_testapi/resources/project_handlers.py
index 9cf698623..f3521961d 100644
--- a/utils/test/testapi/opnfv_testapi/resources/project_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/project_handlers.py
@@ -6,23 +6,23 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import handlers
+from opnfv_testapi.common import constants
from opnfv_testapi.tornado_swagger import swagger
-from handlers import GenericApiHandler
-from opnfv_testapi.common.constants import HTTP_FORBIDDEN
-from project_models import Project
+import project_models
-class GenericProjectHandler(GenericApiHandler):
+class GenericProjectHandler(handlers.GenericApiHandler):
def __init__(self, application, request, **kwargs):
super(GenericProjectHandler, self).__init__(application,
request,
**kwargs)
self.table = 'projects'
- self.table_cls = Project
+ self.table_cls = project_models.Project
class ProjectCLHandler(GenericProjectHandler):
- @swagger.operation(nickname="List all Projects")
+ @swagger.operation(nickname="listAllProjects")
def get(self):
"""
@description: list all projects
@@ -31,7 +31,7 @@ class ProjectCLHandler(GenericProjectHandler):
"""
self._list()
- @swagger.operation(nickname="Create a Project")
+ @swagger.operation(nickname="createProject")
def post(self):
"""
@description: create a project
@@ -48,7 +48,7 @@ class ProjectCLHandler(GenericProjectHandler):
def error(data):
message = '{} already exists as a project'.format(data.name)
- return HTTP_FORBIDDEN, message
+ return constants.HTTP_FORBIDDEN, message
miss_checks = ['name']
db_checks = [(self.table, False, query, error)]
@@ -56,7 +56,7 @@ class ProjectCLHandler(GenericProjectHandler):
class ProjectGURHandler(GenericProjectHandler):
- @swagger.operation(nickname='Get a Project by project_name')
+ @swagger.operation(nickname='getProjectByName')
def get(self, project_name):
"""
@description: get a single project by project_name
@@ -66,7 +66,7 @@ class ProjectGURHandler(GenericProjectHandler):
"""
self._get_one({'name': project_name})
- @swagger.operation(nickname="Update a Project by project_name")
+ @swagger.operation(nickname="updateProjectByName")
def put(self, project_name):
"""
@description: update a single project by project_name
@@ -82,7 +82,7 @@ class ProjectGURHandler(GenericProjectHandler):
db_keys = ['name']
self._update(query, db_keys)
- @swagger.operation(nickname='Delete a Project by project_name')
+ @swagger.operation(nickname='deleteProjectByName')
def delete(self, project_name):
"""
@description: delete a project by project_name
diff --git a/utils/test/testapi/opnfv_testapi/resources/result_handlers.py b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
index fe13c09b7..d41ba4820 100644
--- a/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/result_handlers.py
@@ -6,30 +6,32 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from datetime import datetime, timedelta
+from datetime import datetime
+from datetime import timedelta
-from bson.objectid import ObjectId
-from tornado.web import HTTPError
+from bson import objectid
+from tornado import web
-from opnfv_testapi.common.constants import HTTP_BAD_REQUEST, HTTP_NOT_FOUND
-from opnfv_testapi.resources.handlers import GenericApiHandler
-from opnfv_testapi.resources.result_models import TestResult
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import result_models
from opnfv_testapi.tornado_swagger import swagger
-class GenericResultHandler(GenericApiHandler):
+class GenericResultHandler(handlers.GenericApiHandler):
def __init__(self, application, request, **kwargs):
super(GenericResultHandler, self).__init__(application,
request,
**kwargs)
self.table = self.db_results
- self.table_cls = TestResult
+ self.table_cls = result_models.TestResult
def get_int(self, key, value):
try:
value = int(value)
except:
- raise HTTPError(HTTP_BAD_REQUEST, '{} must be int'.format(key))
+ raise web.HTTPError(constants.HTTP_BAD_REQUEST,
+ '{} must be int'.format(key))
return value
def set_query(self):
@@ -52,7 +54,7 @@ class GenericResultHandler(GenericApiHandler):
class ResultsCLHandler(GenericResultHandler):
- @swagger.operation(nickname="List all Test Results")
+ @swagger.operation(nickname="queryTestResults")
def get(self):
"""
@description: Retrieve result(s) for a test project
@@ -127,7 +129,7 @@ class ResultsCLHandler(GenericResultHandler):
self._list(self.set_query(), sort=[('start_date', -1)], last=last)
- @swagger.operation(nickname="Create a Test Result")
+ @swagger.operation(nickname="createTestResult")
def post(self):
"""
@description: create a test result
@@ -144,14 +146,14 @@ class ResultsCLHandler(GenericResultHandler):
def pod_error(data):
message = 'Could not find pod [{}]'.format(data.pod_name)
- return HTTP_NOT_FOUND, message
+ return constants.HTTP_NOT_FOUND, message
def project_query(data):
return {'name': data.project_name}
def project_error(data):
message = 'Could not find project [{}]'.format(data.project_name)
- return HTTP_NOT_FOUND, message
+ return constants.HTTP_NOT_FOUND, message
def testcase_query(data):
return {'project_name': data.project_name, 'name': data.case_name}
@@ -159,7 +161,7 @@ class ResultsCLHandler(GenericResultHandler):
def testcase_error(data):
message = 'Could not find testcase [{}] in project [{}]'\
.format(data.case_name, data.project_name)
- return HTTP_NOT_FOUND, message
+ return constants.HTTP_NOT_FOUND, message
miss_checks = ['pod_name', 'project_name', 'case_name']
db_checks = [('pods', True, pod_query, pod_error),
@@ -169,7 +171,7 @@ class ResultsCLHandler(GenericResultHandler):
class ResultsGURHandler(GenericResultHandler):
- @swagger.operation(nickname='Get a Test Result by result_id')
+ @swagger.operation(nickname='getTestResultById')
def get(self, result_id):
"""
@description: get a single result by result_id
@@ -178,10 +180,10 @@ class ResultsGURHandler(GenericResultHandler):
@raise 404: test result not exist
"""
query = dict()
- query["_id"] = ObjectId(result_id)
+ query["_id"] = objectid.ObjectId(result_id)
self._get_one(query)
- @swagger.operation(nickname="Update a Test Result by result_id")
+ @swagger.operation(nickname="updateTestResultById")
def put(self, result_id):
"""
@description: update a single result by _id
@@ -193,6 +195,6 @@ class ResultsGURHandler(GenericResultHandler):
@raise 404: result not exist
@raise 403: nothing to update
"""
- query = {'_id': ObjectId(result_id)}
+ query = {'_id': objectid.ObjectId(result_id)}
db_keys = []
self._update(query, db_keys)
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
index 7bf3d5d53..083bf59fc 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_handlers.py
@@ -1,31 +1,69 @@
-from opnfv_testapi.common.constants import HTTP_FORBIDDEN
-from opnfv_testapi.resources.handlers import GenericApiHandler
-from opnfv_testapi.resources.scenario_models import Scenario
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import handlers
+import opnfv_testapi.resources.scenario_models as models
from opnfv_testapi.tornado_swagger import swagger
-class GenericScenarioHandler(GenericApiHandler):
+class GenericScenarioHandler(handlers.GenericApiHandler):
def __init__(self, application, request, **kwargs):
super(GenericScenarioHandler, self).__init__(application,
request,
**kwargs)
self.table = self.db_scenarios
- self.table_cls = Scenario
+ self.table_cls = models.Scenario
class ScenariosCLHandler(GenericScenarioHandler):
- @swagger.operation(nickname="List scenarios by queries")
+ @swagger.operation(nickname="queryScenarios")
def get(self):
"""
@description: Retrieve scenario(s).
@notes: Retrieve scenario(s)
- @return 200: all scenarios consist with query,
+ Available filters for this request are :
+ - name : scenario name
+
+ GET /scenarios?name=scenario_1
+ @param name: scenario name
+ @type name: L{string}
+ @in name: query
+ @required name: False
+ @param installer: installer type
+ @type installer: L{string}
+ @in installer: query
+ @required installer: False
+ @param version: version
+ @type version: L{string}
+ @in version: query
+ @required version: False
+ @param project: project name
+ @type project: L{string}
+ @in project: query
+ @required project: False
+ @return 200: all scenarios satisfy queries,
empty list if no scenario is found
@rtype: L{Scenarios}
"""
- self._list()
- @swagger.operation(nickname="Create a new scenario")
+ def _set_query():
+ query = dict()
+ elem_query = dict()
+ for k in self.request.query_arguments.keys():
+ v = self.get_query_argument(k)
+ if k == 'installer':
+ elem_query["installer"] = v
+ elif k == 'version':
+ elem_query["versions.version"] = v
+ elif k == 'project':
+ elem_query["versions.projects.project"] = v
+ else:
+ query[k] = v
+ if elem_query:
+ query['installers'] = {'$elemMatch': elem_query}
+ return query
+
+ self._list(_set_query())
+
+ @swagger.operation(nickname="createScenario")
def post(self):
"""
@description: create a new scenario by name
@@ -42,7 +80,7 @@ class ScenariosCLHandler(GenericScenarioHandler):
def error(data):
message = '{} already exists as a scenario'.format(data.name)
- return HTTP_FORBIDDEN, message
+ return constants.HTTP_FORBIDDEN, message
miss_checks = ['name']
db_checks = [(self.table, False, query, error)]
@@ -50,7 +88,7 @@ class ScenariosCLHandler(GenericScenarioHandler):
class ScenarioGURHandler(GenericScenarioHandler):
- @swagger.operation(nickname='Get the scenario by name')
+ @swagger.operation(nickname='getScenarioByName')
def get(self, name):
"""
@description: get a single scenario by name
@@ -58,18 +96,188 @@ class ScenarioGURHandler(GenericScenarioHandler):
@return 200: scenario exist
@raise 404: scenario not exist
"""
+ self._get_one({'name': name})
pass
- @swagger.operation(nickname="Update the scenario by name")
+ @swagger.operation(nickname="updateScenarioByName")
def put(self, name):
"""
@description: update a single scenario by name
@param body: fields to be updated
- @type body: L{string}
+ @type body: L{ScenarioUpdateRequest}
@in body: body
@rtype: L{Scenario}
@return 200: update success
@raise 404: scenario not exist
@raise 403: nothing to update
"""
- pass
+ query = {'name': name}
+ db_keys = ['name']
+ self._update(query, db_keys)
+
+ @swagger.operation(nickname="deleteScenarioByName")
+ def delete(self, name):
+ """
+ @description: delete a scenario by name
+ @return 200: delete success
+ @raise 404: scenario not exist:
+ """
+
+ query = {'name': name}
+ self._delete(query)
+
+ def _update_query(self, keys, data):
+ query = dict()
+ equal = True
+ if self._is_rename():
+ new = self._term.get('name')
+ if data.name != new:
+ equal = False
+ query['name'] = new
+
+ return equal, query
+
+ def _update_requests(self, data):
+ updates = {
+ ('name', 'update'): self._update_requests_rename,
+ ('installer', 'add'): self._update_requests_add_installer,
+ ('installer', 'delete'): self._update_requests_delete_installer,
+ ('version', 'add'): self._update_requests_add_version,
+ ('version', 'delete'): self._update_requests_delete_version,
+ ('owner', 'update'): self._update_requests_change_owner,
+ ('project', 'add'): self._update_requests_add_project,
+ ('project', 'delete'): self._update_requests_delete_project,
+ ('customs', 'add'): self._update_requests_add_customs,
+ ('customs', 'delete'): self._update_requests_delete_customs,
+ ('score', 'add'): self._update_requests_add_score,
+ ('trust_indicator', 'add'): self._update_requests_add_ti,
+ }
+
+ updates[(self._field, self._op)](data)
+
+ return data.format()
+
+ def _iter_installers(xstep):
+ def magic(self, data):
+ [xstep(self, installer)
+ for installer in self._filter_installers(data.installers)]
+ return magic
+
+ def _iter_versions(xstep):
+ def magic(self, installer):
+ [xstep(self, version)
+ for version in (self._filter_versions(installer.versions))]
+ return magic
+
+ def _iter_projects(xstep):
+ def magic(self, version):
+ [xstep(self, project)
+ for project in (self._filter_projects(version.projects))]
+ return magic
+
+ def _update_requests_rename(self, data):
+ data.name = self._term.get('name')
+
+ def _update_requests_add_installer(self, data):
+ data.installers.append(models.ScenarioInstaller.from_dict(self._term))
+
+ def _update_requests_delete_installer(self, data):
+ data.installers = self._remove_installers(data.installers)
+
+ @_iter_installers
+ def _update_requests_add_version(self, installer):
+ installer.versions.append(models.ScenarioVersion.from_dict(self._term))
+
+ @_iter_installers
+ def _update_requests_delete_version(self, installer):
+ installer.versions = self._remove_versions(installer.versions)
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_change_owner(self, version):
+ version.owner = self._term.get('owner')
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_add_project(self, version):
+ version.projects.append(models.ScenarioProject.from_dict(self._term))
+
+ @_iter_installers
+ @_iter_versions
+ def _update_requests_delete_project(self, version):
+ version.projects = self._remove_projects(version.projects)
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_customs(self, project):
+ project.customs = list(set(project.customs + self._term))
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_delete_customs(self, project):
+ project.customs = filter(
+ lambda f: f not in self._term,
+ project.customs)
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_score(self, project):
+ project.scores.append(
+ models.ScenarioScore.from_dict(self._term))
+
+ @_iter_installers
+ @_iter_versions
+ @_iter_projects
+ def _update_requests_add_ti(self, project):
+ project.trust_indicators.append(
+ models.ScenarioTI.from_dict(self._term))
+
+ def _is_rename(self):
+ return self._field == 'name' and self._op == 'update'
+
+ def _remove_installers(self, installers):
+ return self._remove('installer', installers)
+
+ def _filter_installers(self, installers):
+ return self._filter('installer', installers)
+
+ def _remove_versions(self, versions):
+ return self._remove('version', versions)
+
+ def _filter_versions(self, versions):
+ return self._filter('version', versions)
+
+ def _remove_projects(self, projects):
+ return self._remove('project', projects)
+
+ def _filter_projects(self, projects):
+ return self._filter('project', projects)
+
+ def _remove(self, field, fields):
+ return filter(
+ lambda f: getattr(f, field) != self._locate.get(field),
+ fields)
+
+ def _filter(self, field, fields):
+ return filter(
+ lambda f: getattr(f, field) == self._locate.get(field),
+ fields)
+
+ @property
+ def _field(self):
+ return self.json_args.get('field')
+
+ @property
+ def _op(self):
+ return self.json_args.get('op')
+
+ @property
+ def _locate(self):
+ return self.json_args.get('locate')
+
+ @property
+ def _term(self):
+ return self.json_args.get('term')
diff --git a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
index b4bb3634b..b84accf4d 100644
--- a/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
+++ b/utils/test/testapi/opnfv_testapi/resources/scenario_models.py
@@ -2,6 +2,14 @@ import models
from opnfv_testapi.tornado_swagger import swagger
+def list_default(value):
+ return value if value else list()
+
+
+def dict_default(value):
+ return value if value else dict()
+
+
@swagger.model()
class ScenarioTI(models.ModelBase):
def __init__(self, date=None, status='silver'):
@@ -11,7 +19,7 @@ class ScenarioTI(models.ModelBase):
@swagger.model()
class ScenarioScore(models.ModelBase):
- def __init__(self, date=None, score=''):
+ def __init__(self, date=None, score='0'):
self.date = date
self.score = score
@@ -27,14 +35,37 @@ class ScenarioProject(models.ModelBase):
@ptype trust_indicators: C{list} of L{ScenarioTI}
"""
def __init__(self,
- name='',
+ project='',
customs=None,
scores=None,
trust_indicators=None):
- self.name = name
- self.customs = customs
- self.scores = scores
- self.trust_indicator = trust_indicators
+ self.project = project
+ self.customs = list_default(customs)
+ self.scores = list_default(scores)
+ self.trust_indicators = list_default(trust_indicators)
+
+ @staticmethod
+ def attr_parser():
+ return {'scores': ScenarioScore,
+ 'trust_indicators': ScenarioTI}
+
+ def __eq__(self, other):
+ return [self.project == other.project and
+ self._customs_eq(other) and
+ self._scores_eq(other) and
+ self._ti_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _customs_eq(self, other):
+ return set(self.customs) == set(other.customs)
+
+ def _scores_eq(self, other):
+ return set(self.scores) == set(other.scores)
+
+ def _ti_eq(self, other):
+ return set(self.trust_indicators) == set(other.trust_indicators)
@swagger.model()
@@ -43,9 +74,28 @@ class ScenarioVersion(models.ModelBase):
@property projects:
@ptype projects: C{list} of L{ScenarioProject}
"""
- def __init__(self, version, projects=None):
+ def __init__(self, version=None, projects=None):
self.version = version
- self.projects = projects
+ self.projects = list_default(projects)
+
+ @staticmethod
+ def attr_parser():
+ return {'projects': ScenarioProject}
+
+ def __eq__(self, other):
+ return [self.version == other.version and self._projects_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _projects_eq(self, other):
+ for s_project in self.projects:
+ for o_project in other.projects:
+ if s_project.project == o_project.project:
+ if s_project != o_project:
+ return False
+
+ return True
@swagger.model()
@@ -54,10 +104,28 @@ class ScenarioInstaller(models.ModelBase):
@property versions:
@ptype versions: C{list} of L{ScenarioVersion}
"""
- def __init__(self, installer=None, owner=None, versions=None):
+ def __init__(self, installer=None, versions=None):
self.installer = installer
- self.owner = owner
- self.versions = versions if versions else list()
+ self.versions = list_default(versions)
+
+ @staticmethod
+ def attr_parser():
+ return {'versions': ScenarioVersion}
+
+ def __eq__(self, other):
+ return [self.installer == other.installer and self._versions_eq(other)]
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _versions_eq(self, other):
+ for s_version in self.versions:
+ for o_version in other.versions:
+ if s_version.version == o_version.version:
+ if s_version != o_version:
+ return False
+
+ return True
@swagger.model()
@@ -68,7 +136,26 @@ class ScenarioCreateRequest(models.ModelBase):
"""
def __init__(self, name='', installers=None):
self.name = name
- self.installers = installers if installers else list()
+ self.installers = list_default(installers)
+
+ @staticmethod
+ def attr_parser():
+ return {'installers': ScenarioInstaller}
+
+
+@swagger.model()
+class ScenarioUpdateRequest(models.ModelBase):
+ """
+ @property field: update field
+ @property op: add/delete/update
+ @property locate: information used to locate the field
+ @property term: new value
+ """
+ def __init__(self, field=None, op=None, locate=None, term=None):
+ self.field = field
+ self.op = op
+ self.locate = dict_default(locate)
+ self.term = dict_default(term)
@swagger.model()
@@ -81,7 +168,26 @@ class Scenario(models.ModelBase):
self.name = name
self._id = _id
self.creation_date = create_date
- self.installers = installers if installers else list()
+ self.installers = list_default(installers)
+
+ @staticmethod
+ def attr_parser():
+ return {'installers': ScenarioInstaller}
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ return [self.name == other.name and self._installers_eq(other)]
+
+ def _installers_eq(self, other):
+ for s_install in self.installers:
+ for o_install in other.installers:
+ if s_install.installer == o_install.installer:
+ if s_install != o_install:
+ return False
+
+ return True
@swagger.model()
diff --git a/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py b/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py
index 3692b164f..3debd6918 100644
--- a/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py
+++ b/utils/test/testapi/opnfv_testapi/resources/testcase_handlers.py
@@ -6,23 +6,23 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from opnfv_testapi.common.constants import HTTP_FORBIDDEN
-from opnfv_testapi.resources.handlers import GenericApiHandler
-from opnfv_testapi.resources.testcase_models import Testcase
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import testcase_models
from opnfv_testapi.tornado_swagger import swagger
-class GenericTestcaseHandler(GenericApiHandler):
+class GenericTestcaseHandler(handlers.GenericApiHandler):
def __init__(self, application, request, **kwargs):
super(GenericTestcaseHandler, self).__init__(application,
request,
**kwargs)
self.table = self.db_testcases
- self.table_cls = Testcase
+ self.table_cls = testcase_models.Testcase
class TestcaseCLHandler(GenericTestcaseHandler):
- @swagger.operation(nickname="List all TestCases by project_name")
+ @swagger.operation(nickname="listAllTestCases")
def get(self, project_name):
"""
@description: list all testcases of a project by project_name
@@ -34,7 +34,7 @@ class TestcaseCLHandler(GenericTestcaseHandler):
query['project_name'] = project_name
self._list(query)
- @swagger.operation(nickname="Create a TestCase by project_name")
+ @swagger.operation(nickname="createTestCase")
def post(self, project_name):
"""
@description: create a testcase of a project by project_name
@@ -58,12 +58,12 @@ class TestcaseCLHandler(GenericTestcaseHandler):
def p_error(data):
message = 'Could not find project [{}]'.format(data.project_name)
- return HTTP_FORBIDDEN, message
+ return constants.HTTP_FORBIDDEN, message
def tc_error(data):
message = '{} already exists as a testcase in project {}'\
.format(data.name, data.project_name)
- return HTTP_FORBIDDEN, message
+ return constants.HTTP_FORBIDDEN, message
miss_checks = ['name']
db_checks = [(self.db_projects, True, p_query, p_error),
@@ -72,7 +72,7 @@ class TestcaseCLHandler(GenericTestcaseHandler):
class TestcaseGURHandler(GenericTestcaseHandler):
- @swagger.operation(nickname='Get a TestCase by project and case name')
+ @swagger.operation(nickname='getTestCaseByName')
def get(self, project_name, case_name):
"""
@description: get a single testcase
@@ -86,7 +86,7 @@ class TestcaseGURHandler(GenericTestcaseHandler):
query["name"] = case_name
self._get_one(query)
- @swagger.operation(nickname="Update a TestCase by project and case name")
+ @swagger.operation(nickname="updateTestCaseByName")
def put(self, project_name, case_name):
"""
@description: update a single testcase
@@ -104,7 +104,7 @@ class TestcaseGURHandler(GenericTestcaseHandler):
db_keys = ['name', 'project_name']
self._update(query, db_keys)
- @swagger.operation(nickname='Delete a TestCase by project and case name')
+ @swagger.operation(nickname='deleteTestCaseByName')
def delete(self, project_name, case_name):
"""
@description: delete a testcase by project_name and case_name
diff --git a/utils/test/testapi/opnfv_testapi/router/url_mappings.py b/utils/test/testapi/opnfv_testapi/router/url_mappings.py
index 0ae3c31c3..39cf006af 100644
--- a/utils/test/testapi/opnfv_testapi/router/url_mappings.py
+++ b/utils/test/testapi/opnfv_testapi/router/url_mappings.py
@@ -6,37 +6,34 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from opnfv_testapi.resources.handlers import VersionHandler
-from opnfv_testapi.resources.testcase_handlers import TestcaseCLHandler, \
- TestcaseGURHandler
-from opnfv_testapi.resources.pod_handlers import PodCLHandler, PodGURHandler
-from opnfv_testapi.resources.project_handlers import ProjectCLHandler, \
- ProjectGURHandler
-from opnfv_testapi.resources.result_handlers import ResultsCLHandler, \
- ResultsGURHandler
-from opnfv_testapi.resources.scenario_handlers import ScenariosCLHandler
-from opnfv_testapi.resources.scenario_handlers import ScenarioGURHandler
+from opnfv_testapi.resources import handlers
+from opnfv_testapi.resources import pod_handlers
+from opnfv_testapi.resources import project_handlers
+from opnfv_testapi.resources import result_handlers
+from opnfv_testapi.resources import scenario_handlers
+from opnfv_testapi.resources import testcase_handlers
mappings = [
# GET /versions => GET API version
- (r"/versions", VersionHandler),
+ (r"/versions", handlers.VersionHandler),
# few examples:
# GET /api/v1/pods => Get all pods
# GET /api/v1/pods/1 => Get details on POD 1
- (r"/api/v1/pods", PodCLHandler),
- (r"/api/v1/pods/([^/]+)", PodGURHandler),
+ (r"/api/v1/pods", pod_handlers.PodCLHandler),
+ (r"/api/v1/pods/([^/]+)", pod_handlers.PodGURHandler),
# few examples:
# GET /projects
# GET /projects/yardstick
- (r"/api/v1/projects", ProjectCLHandler),
- (r"/api/v1/projects/([^/]+)", ProjectGURHandler),
+ (r"/api/v1/projects", project_handlers.ProjectCLHandler),
+ (r"/api/v1/projects/([^/]+)", project_handlers.ProjectGURHandler),
# few examples
# GET /projects/qtip/cases => Get cases for qtip
- (r"/api/v1/projects/([^/]+)/cases", TestcaseCLHandler),
- (r"/api/v1/projects/([^/]+)/cases/([^/]+)", TestcaseGURHandler),
+ (r"/api/v1/projects/([^/]+)/cases", testcase_handlers.TestcaseCLHandler),
+ (r"/api/v1/projects/([^/]+)/cases/([^/]+)",
+ testcase_handlers.TestcaseGURHandler),
# new path to avoid a long depth
# GET /results?project=functest&case=keystone.catalog&pod=1
@@ -44,10 +41,10 @@ mappings = [
# POST /results =>
# Push results with mandatory request payload parameters
# (project, case, and pod)
- (r"/api/v1/results", ResultsCLHandler),
- (r"/api/v1/results/([^/]+)", ResultsGURHandler),
+ (r"/api/v1/results", result_handlers.ResultsCLHandler),
+ (r"/api/v1/results/([^/]+)", result_handlers.ResultsGURHandler),
# scenarios
- (r"/api/v1/scenarios", ScenariosCLHandler),
- (r"/api/v1/scenarios/([^/]+)", ScenarioGURHandler),
+ (r"/api/v1/scenarios", scenario_handlers.ScenariosCLHandler),
+ (r"/api/v1/scenarios/([^/]+)", scenario_handlers.ScenarioGURHandler),
]
diff --git a/modules/opnfv/installer_adapters/fuel/__init__.py b/utils/test/testapi/opnfv_testapi/tests/unit/common/__init__.py
index e69de29bb..e69de29bb 100644
--- a/modules/opnfv/installer_adapters/fuel/__init__.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/__init__.py
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/noparam.ini b/utils/test/testapi/opnfv_testapi/tests/unit/common/noparam.ini
new file mode 100644
index 000000000..fda2a09e9
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/noparam.ini
@@ -0,0 +1,16 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
+[mongo]
+# URL of the mongo DB
+# Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
+url = mongodb://127.0.0.1:27017/
+
+[api]
+# Listening port
+port = 8000
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
+authenticate = False
+
+[swagger]
+base_url = http://localhost:8000
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/normal.ini b/utils/test/testapi/opnfv_testapi/tests/unit/common/normal.ini
new file mode 100644
index 000000000..77cc6c6ee
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/normal.ini
@@ -0,0 +1,17 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
+[mongo]
+# URL of the mongo DB
+# Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
+url = mongodb://127.0.0.1:27017/
+dbname = test_results_collection
+
+[api]
+# Listening port
+port = 8000
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
+authenticate = False
+
+[swagger]
+base_url = http://localhost:8000
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/nosection.ini b/utils/test/testapi/opnfv_testapi/tests/unit/common/nosection.ini
new file mode 100644
index 000000000..9988fc0a4
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/nosection.ini
@@ -0,0 +1,11 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
+[api]
+# Listening port
+port = 8000
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
+authenticate = False
+
+[swagger]
+base_url = http://localhost:8000
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/notboolean.ini b/utils/test/testapi/opnfv_testapi/tests/unit/common/notboolean.ini
new file mode 100644
index 000000000..b3f327670
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/notboolean.ini
@@ -0,0 +1,17 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
+[mongo]
+# URL of the mongo DB
+# Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
+url = mongodb://127.0.0.1:27017/
+dbname = test_results_collection
+
+[api]
+# Listening port
+port = 8000
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
+authenticate = notboolean
+
+[swagger]
+base_url = http://localhost:8000
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/notint.ini b/utils/test/testapi/opnfv_testapi/tests/unit/common/notint.ini
new file mode 100644
index 000000000..d1b752a34
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/notint.ini
@@ -0,0 +1,17 @@
+# to add a new parameter in the config file,
+# the CONF object in config.ini must be updated
+[mongo]
+# URL of the mongo DB
+# Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
+url = mongodb://127.0.0.1:27017/
+dbname = test_results_collection
+
+[api]
+# Listening port
+port = notint
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
+authenticate = False
+
+[swagger]
+base_url = http://localhost:8000
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/common/test_config.py b/utils/test/testapi/opnfv_testapi/tests/unit/common/test_config.py
new file mode 100644
index 000000000..aaff6bb91
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/common/test_config.py
@@ -0,0 +1,36 @@
+import ConfigParser
+import os
+
+import pytest
+
+from opnfv_testapi.common import config
+
+
+@pytest.fixture()
+def config_dir():
+ return os.path.dirname(__file__)
+
+
+@pytest.mark.parametrize('exception, config_file, excepted', [
+ (config.ParseError, None, '/etc/opnfv_testapi/config.ini not found'),
+ (ConfigParser.NoSectionError, 'nosection.ini', 'No section:'),
+ (config.ParseError, 'noparam.ini', 'No parameter:'),
+ (config.ParseError, 'notint.ini', 'Not int:'),
+ (config.ParseError, 'notboolean.ini', 'Not boolean:')])
+def pytest_config_exceptions(config_dir, exception, config_file, excepted):
+ file = '{}/{}'.format(config_dir, config_file) if config_file else None
+ with pytest.raises(exception) as error:
+ config.APIConfig().parse(file)
+ assert excepted in str(error.value)
+
+
+def test_config_success():
+ config_dir = os.path.join(os.path.dirname(__file__),
+ '../../../../etc/config.ini')
+ conf = config.APIConfig().parse(config_dir)
+ assert conf.mongo_url == 'mongodb://127.0.0.1:27017/'
+ assert conf.mongo_dbname == 'test_results_collection'
+ assert conf.api_port == 8000
+ assert conf.api_debug_on is True
+ assert conf.api_authenticate_on is False
+ assert conf.swagger_base_url == 'http://localhost:8000'
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/fake_pymongo.py b/utils/test/testapi/opnfv_testapi/tests/unit/fake_pymongo.py
index d86d8eadf..ef74a0857 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/fake_pymongo.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/fake_pymongo.py
@@ -55,7 +55,8 @@ class MemCursor(object):
class MemDb(object):
- def __init__(self):
+ def __init__(self, name):
+ self.name = name
self.contents = []
pass
@@ -109,8 +110,59 @@ class MemDb(object):
return True
return False
- @staticmethod
- def _in(content, *args):
+ def _in(self, content, *args):
+ if self.name == 'scenarios':
+ return self._in_scenarios(content, *args)
+ else:
+ return self._in_others(content, *args)
+
+ def _in_scenarios_installer(self, installer, content):
+ hit = False
+ for s_installer in content['installers']:
+ if installer == s_installer['installer']:
+ hit = True
+
+ return hit
+
+ def _in_scenarios_version(self, version, content):
+ hit = False
+ for s_installer in content['installers']:
+ for s_version in s_installer['versions']:
+ if version == s_version['version']:
+ hit = True
+ return hit
+
+ def _in_scenarios_project(self, project, content):
+ hit = False
+ for s_installer in content['installers']:
+ for s_version in s_installer['versions']:
+ for s_project in s_version['projects']:
+ if project == s_project['project']:
+ hit = True
+
+ return hit
+
+ def _in_scenarios(self, content, *args):
+ for arg in args:
+ for k, v in arg.iteritems():
+ if k == 'installers':
+ for inner in v.values():
+ for i_k, i_v in inner.iteritems():
+ if i_k == 'installer':
+ return self._in_scenarios_installer(i_v,
+ content)
+ elif i_k == 'versions.version':
+ return self._in_scenarios_version(i_v,
+ content)
+ elif i_k == 'versions.projects.project':
+ return self._in_scenarios_project(i_v,
+ content)
+ elif content.get(k, None) != v:
+ return False
+
+ return True
+
+ def _in_others(self, content, *args):
for arg in args:
for k, v in arg.iteritems():
if k == 'start_date':
@@ -185,8 +237,9 @@ def __getattr__(name):
return globals()[name]
-pods = MemDb()
-projects = MemDb()
-testcases = MemDb()
-results = MemDb()
-scenarios = MemDb()
+pods = MemDb('pods')
+projects = MemDb('projects')
+testcases = MemDb('testcases')
+results = MemDb('results')
+scenarios = MemDb('scenarios')
+tokens = MemDb('tokens')
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/scenario-create.json b/utils/test/testapi/opnfv_testapi/tests/unit/scenario-c1.json
index eba8b6c0a..187802215 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/scenario-create.json
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/scenario-c1.json
@@ -35,4 +35,4 @@
]
}
]
-} \ No newline at end of file
+}
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/scenario-c2.json b/utils/test/testapi/opnfv_testapi/tests/unit/scenario-c2.json
new file mode 100644
index 000000000..b6a3b83ab
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/scenario-c2.json
@@ -0,0 +1,73 @@
+{
+ "name": "odl_2-nofeature-ha",
+ "installers":
+ [
+ {
+ "installer": "fuel",
+ "versions":
+ [
+ {
+ "owner": "Lucky",
+ "version": "colorado",
+ "projects":
+ [
+ {
+ "project": "functest",
+ "customs": [ "healthcheck", "vping_ssh"],
+ "scores": [],
+ "trust_indicators": [
+ {
+ "date": "2017-01-18 22:46:44",
+ "status": "silver"
+ }
+
+ ]
+ },
+ {
+ "project": "yardstick",
+ "customs": ["suite-a"],
+ "scores": [
+ {
+ "date": "2017-01-08 22:46:44",
+ "score": "0"
+ }
+ ],
+ "trust_indicators": [
+ {
+ "date": "2017-01-18 22:46:44",
+ "status": "gold"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "owner": "Luke",
+ "version": "colorado",
+ "projects":
+ [
+ {
+ "project": "functest",
+ "customs": [ "healthcheck", "vping_ssh"],
+ "scores":
+ [
+ {
+ "date": "2017-01-09 22:46:44",
+ "score": "11/14"
+ }
+
+ ],
+ "trust_indicators": []
+ },
+ {
+ "project": "yardstick",
+ "customs": [],
+ "scores": [],
+ "trust_indicators": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_base.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_base.py
index 9343ab2fb..b955f4a5a 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_base.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_base.py
@@ -7,21 +7,23 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
import json
+from os import path
-from tornado.web import Application
-from tornado.testing import AsyncHTTPTestCase
+import mock
+from tornado import testing
-from opnfv_testapi.router import url_mappings
-from opnfv_testapi.resources.models import CreateResponse
import fake_pymongo
+from opnfv_testapi.cmd import server
+from opnfv_testapi.resources import models
-class TestBase(AsyncHTTPTestCase):
+class TestBase(testing.AsyncHTTPTestCase):
headers = {'Content-Type': 'application/json; charset=UTF-8'}
def setUp(self):
+ self._patch_server()
self.basePath = ''
- self.create_res = CreateResponse
+ self.create_res = models.CreateResponse
self.get_res = None
self.list_res = None
self.update_res = None
@@ -30,12 +32,24 @@ class TestBase(AsyncHTTPTestCase):
self.addCleanup(self._clear)
super(TestBase, self).setUp()
+ def tearDown(self):
+ self.db_patcher.stop()
+
+ def _patch_server(self):
+ server.parse_config([
+ '--config-file',
+ path.join(path.dirname(__file__), 'common/normal.ini')
+ ])
+ self.db_patcher = mock.patch('opnfv_testapi.cmd.server.get_db',
+ self._fake_pymongo)
+ self.db_patcher.start()
+
+ @staticmethod
+ def _fake_pymongo():
+ return fake_pymongo
+
def get_app(self):
- return Application(
- url_mappings.mappings,
- db=fake_pymongo,
- debug=True,
- )
+ return server.make_app()
def create_d(self, *args):
return self.create(self.req_d, *args)
@@ -47,11 +61,11 @@ class TestBase(AsyncHTTPTestCase):
return self.create_help(self.basePath, req, *args)
def create_help(self, uri, req, *args):
- if req and not isinstance(req, str):
- req = json.dumps(req.format())
+ if req and not isinstance(req, str) and hasattr(req, 'format'):
+ req = req.format()
res = self.fetch(self._update_uri(uri, *args),
method='POST',
- body=req if req else json.dumps(None),
+ body=json.dumps(req),
headers=self.headers)
return self._get_return(res, self.create_res)
@@ -97,7 +111,7 @@ class TestBase(AsyncHTTPTestCase):
return uri.count('%s')
def _get_query_uri(self, query):
- return self.basePath + '?' + query
+ return self.basePath + '?' + query if query else self.basePath
def _get_uri(self, *args):
return self._update_uri(self.basePath, *args)
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_fake_pymongo.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_fake_pymongo.py
index 5f50ba867..7c43fca62 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_fake_pymongo.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_fake_pymongo.py
@@ -9,13 +9,13 @@
import unittest
from tornado import gen
-from tornado.testing import AsyncHTTPTestCase, gen_test
-from tornado.web import Application
+from tornado import testing
+from tornado import web
import fake_pymongo
-class MyTest(AsyncHTTPTestCase):
+class MyTest(testing.AsyncHTTPTestCase):
def setUp(self):
super(MyTest, self).setUp()
self.db = fake_pymongo
@@ -23,7 +23,7 @@ class MyTest(AsyncHTTPTestCase):
self.io_loop.run_sync(self.fixture_setup)
def get_app(self):
- return Application()
+ return web.Application()
@gen.coroutine
def fixture_setup(self):
@@ -32,13 +32,13 @@ class MyTest(AsyncHTTPTestCase):
yield self.db.pods.insert({'_id': '1', 'name': 'test1'})
yield self.db.pods.insert({'name': 'test2'})
- @gen_test
+ @testing.gen_test
def test_find_one(self):
user = yield self.db.pods.find_one({'name': 'test1'})
self.assertEqual(user, self.test1)
self.db.pods.remove()
- @gen_test
+ @testing.gen_test
def test_find(self):
cursor = self.db.pods.find()
names = []
@@ -47,7 +47,7 @@ class MyTest(AsyncHTTPTestCase):
names.append(ob.get('name'))
self.assertItemsEqual(names, ['test1', 'test2'])
- @gen_test
+ @testing.gen_test
def test_update(self):
yield self.db.pods.update({'_id': '1'}, {'name': 'new_test1'})
user = yield self.db.pods.find_one({'_id': '1'})
@@ -71,7 +71,7 @@ class MyTest(AsyncHTTPTestCase):
None,
check_keys=False)
- @gen_test
+ @testing.gen_test
def test_remove(self):
yield self.db.pods.remove({'_id': '1'})
user = yield self.db.pods.find_one({'_id': '1'})
@@ -104,7 +104,7 @@ class MyTest(AsyncHTTPTestCase):
def _insert_assert(self, docs, error=None, **kwargs):
self._db_assert('insert', error, docs, **kwargs)
- @gen_test
+ @testing.gen_test
def _db_assert(self, method, error, *args, **kwargs):
name_error = None
try:
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_pod.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_pod.py
index a1184d554..922bd46e2 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_pod.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_pod.py
@@ -8,20 +8,19 @@
##############################################################################
import unittest
-from test_base import TestBase
-from opnfv_testapi.resources.pod_models import PodCreateRequest, Pod, Pods
-from opnfv_testapi.common.constants import HTTP_OK, HTTP_BAD_REQUEST, \
- HTTP_FORBIDDEN, HTTP_NOT_FOUND
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import pod_models
+import test_base as base
-class TestPodBase(TestBase):
+class TestPodBase(base.TestBase):
def setUp(self):
super(TestPodBase, self).setUp()
- self.req_d = PodCreateRequest('zte-1', 'virtual',
- 'zte pod 1', 'ci-pod')
- self.req_e = PodCreateRequest('zte-2', 'metal', 'zte pod 2')
- self.get_res = Pod
- self.list_res = Pods
+ self.req_d = pod_models.PodCreateRequest('zte-1', 'virtual',
+ 'zte pod 1', 'ci-pod')
+ self.req_e = pod_models.PodCreateRequest('zte-2', 'metal', 'zte pod 2')
+ self.get_res = pod_models.Pod
+ self.list_res = pod_models.Pods
self.basePath = '/api/v1/pods'
def assert_get_body(self, pod, req=None):
@@ -38,36 +37,36 @@ class TestPodBase(TestBase):
class TestPodCreate(TestPodBase):
def test_withoutBody(self):
(code, body) = self.create()
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_emptyName(self):
- req_empty = PodCreateRequest('')
+ req_empty = pod_models.PodCreateRequest('')
(code, body) = self.create(req_empty)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_noneName(self):
- req_none = PodCreateRequest(None)
+ req_none = pod_models.PodCreateRequest(None)
(code, body) = self.create(req_none)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_success(self):
code, body = self.create_d()
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_create_body(body)
def test_alreadyExist(self):
self.create_d()
code, body = self.create_d()
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn('already exists', body)
class TestPodGet(TestPodBase):
def test_notExist(self):
code, body = self.get('notExist')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_getOne(self):
self.create_d()
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_project.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_project.py
index 327ddf7b2..afd4a6601 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_project.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_project.py
@@ -8,21 +8,21 @@
##############################################################################
import unittest
-from test_base import TestBase
-from opnfv_testapi.resources.project_models import ProjectCreateRequest, \
- Project, Projects, ProjectUpdateRequest
-from opnfv_testapi.common.constants import HTTP_OK, HTTP_BAD_REQUEST, \
- HTTP_FORBIDDEN, HTTP_NOT_FOUND
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import project_models
+import test_base as base
-class TestProjectBase(TestBase):
+class TestProjectBase(base.TestBase):
def setUp(self):
super(TestProjectBase, self).setUp()
- self.req_d = ProjectCreateRequest('vping', 'vping-ssh test')
- self.req_e = ProjectCreateRequest('doctor', 'doctor test')
- self.get_res = Project
- self.list_res = Projects
- self.update_res = Project
+ self.req_d = project_models.ProjectCreateRequest('vping',
+ 'vping-ssh test')
+ self.req_e = project_models.ProjectCreateRequest('doctor',
+ 'doctor test')
+ self.get_res = project_models.Project
+ self.list_res = project_models.Projects
+ self.update_res = project_models.Project
self.basePath = '/api/v1/projects'
def assert_body(self, project, req=None):
@@ -37,41 +37,41 @@ class TestProjectBase(TestBase):
class TestProjectCreate(TestProjectBase):
def test_withoutBody(self):
(code, body) = self.create()
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_emptyName(self):
- req_empty = ProjectCreateRequest('')
+ req_empty = project_models.ProjectCreateRequest('')
(code, body) = self.create(req_empty)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_noneName(self):
- req_none = ProjectCreateRequest(None)
+ req_none = project_models.ProjectCreateRequest(None)
(code, body) = self.create(req_none)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_success(self):
(code, body) = self.create_d()
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_create_body(body)
def test_alreadyExist(self):
self.create_d()
(code, body) = self.create_d()
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn('already exists', body)
class TestProjectGet(TestProjectBase):
def test_notExist(self):
code, body = self.get('notExist')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_getOne(self):
self.create_d()
code, body = self.get(self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_body(body)
def test_list(self):
@@ -88,23 +88,23 @@ class TestProjectGet(TestProjectBase):
class TestProjectUpdate(TestProjectBase):
def test_withoutBody(self):
code, _ = self.update(None, 'noBody')
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_notFound(self):
code, _ = self.update(self.req_e, 'notFound')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_newNameExist(self):
self.create_d()
self.create_e()
code, body = self.update(self.req_e, self.req_d.name)
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn("already exists", body)
def test_noUpdate(self):
self.create_d()
code, body = self.update(self.req_d, self.req_d.name)
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn("Nothing to update", body)
def test_success(self):
@@ -112,9 +112,9 @@ class TestProjectUpdate(TestProjectBase):
code, body = self.get(self.req_d.name)
_id = body._id
- req = ProjectUpdateRequest('newName', 'new description')
+ req = project_models.ProjectUpdateRequest('newName', 'new description')
code, body = self.update(req, self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assertEqual(_id, body._id)
self.assert_body(body, req)
@@ -126,16 +126,16 @@ class TestProjectUpdate(TestProjectBase):
class TestProjectDelete(TestProjectBase):
def test_notFound(self):
code, body = self.delete('notFound')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_success(self):
self.create_d()
code, body = self.delete(self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assertEqual(body, '')
code, body = self.get(self.req_d.name)
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
if __name__ == '__main__':
unittest.main()
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_result.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_result.py
index 10575a9f5..2c7268eb6 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_result.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_result.py
@@ -7,17 +7,15 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
import copy
-import unittest
from datetime import datetime, timedelta
+import unittest
-from opnfv_testapi.common.constants import HTTP_OK, HTTP_BAD_REQUEST, \
- HTTP_NOT_FOUND
-from opnfv_testapi.resources.pod_models import PodCreateRequest
-from opnfv_testapi.resources.project_models import ProjectCreateRequest
-from opnfv_testapi.resources.result_models import ResultCreateRequest, \
- TestResult, TestResults, ResultUpdateRequest, TI, TIHistory
-from opnfv_testapi.resources.testcase_models import TestcaseCreateRequest
-from test_base import TestBase
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import pod_models
+from opnfv_testapi.resources import project_models
+from opnfv_testapi.resources import result_models
+from opnfv_testapi.resources import testcase_models
+import test_base as base
class Details(object):
@@ -49,7 +47,7 @@ class Details(object):
return t
-class TestResultBase(TestBase):
+class TestResultBase(base.TestBase):
def setUp(self):
self.pod = 'zte-pod1'
self.project = 'functest'
@@ -59,34 +57,41 @@ class TestResultBase(TestBase):
self.build_tag = 'v3.0'
self.scenario = 'odl-l2'
self.criteria = 'passed'
- self.trust_indicator = TI(0.7)
+ self.trust_indicator = result_models.TI(0.7)
self.start_date = "2016-05-23 07:16:09.477097"
self.stop_date = "2016-05-23 07:16:19.477097"
self.update_date = "2016-05-24 07:16:19.477097"
self.update_step = -0.05
super(TestResultBase, self).setUp()
self.details = Details(timestart='0', duration='9s', status='OK')
- self.req_d = ResultCreateRequest(pod_name=self.pod,
- project_name=self.project,
- case_name=self.case,
- installer=self.installer,
- version=self.version,
- start_date=self.start_date,
- stop_date=self.stop_date,
- details=self.details.format(),
- build_tag=self.build_tag,
- scenario=self.scenario,
- criteria=self.criteria,
- trust_indicator=self.trust_indicator)
- self.get_res = TestResult
- self.list_res = TestResults
- self.update_res = TestResult
+ self.req_d = result_models.ResultCreateRequest(
+ pod_name=self.pod,
+ project_name=self.project,
+ case_name=self.case,
+ installer=self.installer,
+ version=self.version,
+ start_date=self.start_date,
+ stop_date=self.stop_date,
+ details=self.details.format(),
+ build_tag=self.build_tag,
+ scenario=self.scenario,
+ criteria=self.criteria,
+ trust_indicator=self.trust_indicator)
+ self.get_res = result_models.TestResult
+ self.list_res = result_models.TestResults
+ self.update_res = result_models.TestResult
self.basePath = '/api/v1/results'
- self.req_pod = PodCreateRequest(self.pod, 'metal', 'zte pod 1')
- self.req_project = ProjectCreateRequest(self.project, 'vping test')
- self.req_testcase = TestcaseCreateRequest(self.case,
- '/cases/vping',
- 'vping-ssh test')
+ self.req_pod = pod_models.PodCreateRequest(
+ self.pod,
+ 'metal',
+ 'zte pod 1')
+ self.req_project = project_models.ProjectCreateRequest(
+ self.project,
+ 'vping test')
+ self.req_testcase = testcase_models.TestcaseCreateRequest(
+ self.case,
+ '/cases/vping',
+ 'vping-ssh test')
self.create_help('/api/v1/pods', self.req_pod)
self.create_help('/api/v1/projects', self.req_project)
self.create_help('/api/v1/projects/%s/cases',
@@ -94,7 +99,7 @@ class TestResultBase(TestBase):
self.project)
def assert_res(self, code, result, req=None):
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
if req is None:
req = self.req_d
self.assertEqual(result.pod_name, req.pod_name)
@@ -129,78 +134,78 @@ class TestResultBase(TestBase):
class TestResultCreate(TestResultBase):
def test_nobody(self):
(code, body) = self.create(None)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('no body', body)
def test_podNotProvided(self):
req = self.req_d
req.pod_name = None
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('pod_name missing', body)
def test_projectNotProvided(self):
req = self.req_d
req.project_name = None
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('project_name missing', body)
def test_testcaseNotProvided(self):
req = self.req_d
req.case_name = None
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('case_name missing', body)
def test_noPod(self):
req = self.req_d
req.pod_name = 'notExistPod'
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
self.assertIn('Could not find pod', body)
def test_noProject(self):
req = self.req_d
req.project_name = 'notExistProject'
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
self.assertIn('Could not find project', body)
def test_noTestcase(self):
req = self.req_d
req.case_name = 'notExistTestcase'
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
self.assertIn('Could not find testcase', body)
def test_success(self):
(code, body) = self.create_d()
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_href(body)
def test_key_with_doc(self):
req = copy.deepcopy(self.req_d)
req.details = {'1.name': 'dot_name'}
(code, body) = self.create(req)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_href(body)
def test_no_ti(self):
- req = ResultCreateRequest(pod_name=self.pod,
- project_name=self.project,
- case_name=self.case,
- installer=self.installer,
- version=self.version,
- start_date=self.start_date,
- stop_date=self.stop_date,
- details=self.details.format(),
- build_tag=self.build_tag,
- scenario=self.scenario,
- criteria=self.criteria)
+ req = result_models.ResultCreateRequest(pod_name=self.pod,
+ project_name=self.project,
+ case_name=self.case,
+ installer=self.installer,
+ version=self.version,
+ start_date=self.start_date,
+ stop_date=self.stop_date,
+ details=self.details.format(),
+ build_tag=self.build_tag,
+ scenario=self.scenario,
+ criteria=self.criteria)
(code, res) = self.create(req)
_id = res.href.split('/')[-1]
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
code, body = self.get(_id)
self.assert_res(code, body, req)
@@ -240,7 +245,7 @@ class TestResultGet(TestResultBase):
def test_queryPeriodNotInt(self):
code, body = self.query(self._set_query('period=a'))
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('period must be int', body)
def test_queryPeriodFail(self):
@@ -253,7 +258,7 @@ class TestResultGet(TestResultBase):
def test_queryLastNotInt(self):
code, body = self.query(self._set_query('last=a'))
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('last must be int', body)
def test_queryLast(self):
@@ -292,7 +297,7 @@ class TestResultGet(TestResultBase):
req = self._create_changed_date(**kwargs)
code, body = self.query(query)
if not found:
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assertEqual(0, len(body.results))
else:
self.assertEqual(1, len(body.results))
@@ -326,10 +331,11 @@ class TestResultUpdate(TestResultBase):
new_ti = copy.deepcopy(self.trust_indicator)
new_ti.current += self.update_step
- new_ti.histories.append(TIHistory(self.update_date, self.update_step))
+ new_ti.histories.append(
+ result_models.TIHistory(self.update_date, self.update_step))
new_data = copy.deepcopy(self.req_d)
new_data.trust_indicator = new_ti
- update = ResultUpdateRequest(trust_indicator=new_ti)
+ update = result_models.ResultUpdateRequest(trust_indicator=new_ti)
code, body = self.update(update, _id)
self.assertEqual(_id, body._id)
self.assert_res(code, body, new_data)
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_scenario.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_scenario.py
index 8e827813c..7a6e94a93 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_scenario.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_scenario.py
@@ -1,18 +1,21 @@
+from copy import deepcopy
+from datetime import datetime
import json
import os
-from opnfv_testapi.common.constants import HTTP_BAD_REQUEST
-from opnfv_testapi.common.constants import HTTP_FORBIDDEN
-from opnfv_testapi.common.constants import HTTP_OK
-from opnfv_testapi.resources.scenario_models import ScenarioCreateRequest
-from test_testcase import TestBase
+from opnfv_testapi.common import constants
+import opnfv_testapi.resources.scenario_models as models
+import test_base as base
-class TestScenarioBase(TestBase):
+class TestScenarioBase(base.TestBase):
def setUp(self):
super(TestScenarioBase, self).setUp()
+ self.get_res = models.Scenario
+ self.list_res = models.Scenarios
self.basePath = '/api/v1/scenarios'
- self.load_request('scenario-create.json')
+ self.req_d = self._load_request('scenario-c1.json')
+ self.req_2 = self._load_request('scenario-c2.json')
def tearDown(self):
pass
@@ -20,36 +23,294 @@ class TestScenarioBase(TestBase):
def assert_body(self, project, req=None):
pass
- def load_request(self, f_req):
- with open(os.path.join(os.path.dirname(__file__), f_req), 'r') as f:
- self.req_d = json.dumps(json.load(f))
+ @staticmethod
+ def _load_request(f_req):
+ abs_file = os.path.join(os.path.dirname(__file__), f_req)
+ with open(abs_file, 'r') as f:
+ loader = json.load(f)
f.close()
+ return loader
+
+ def create_return_name(self, req):
+ _, res = self.create(req)
+ return res.href.split('/')[-1]
+
+ def assert_res(self, code, scenario, req=None):
+ self.assertEqual(code, constants.HTTP_OK)
+ if req is None:
+ req = self.req_d
+ self.assertIsNotNone(scenario._id)
+ self.assertIsNotNone(scenario.creation_date)
+
+ scenario == models.Scenario.from_dict(req)
+
+ @staticmethod
+ def _set_query(*args):
+ uri = ''
+ for arg in args:
+ uri += arg + '&'
+ return uri[0: -1]
+
+ def _get_and_assert(self, name, req=None):
+ code, body = self.get(name)
+ self.assert_res(code, body, req)
class TestScenarioCreate(TestScenarioBase):
def test_withoutBody(self):
(code, body) = self.create()
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_emptyName(self):
- req_empty = ScenarioCreateRequest('')
+ req_empty = models.ScenarioCreateRequest('')
(code, body) = self.create(req_empty)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_noneName(self):
- req_none = ScenarioCreateRequest(None)
+ req_none = models.ScenarioCreateRequest(None)
(code, body) = self.create(req_none)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_success(self):
(code, body) = self.create_d()
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_create_body(body)
def test_alreadyExist(self):
self.create_d()
(code, body) = self.create_d()
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn('already exists', body)
+
+
+class TestScenarioGet(TestScenarioBase):
+ def setUp(self):
+ super(TestScenarioGet, self).setUp()
+ self.scenario_1 = self.create_return_name(self.req_d)
+ self.scenario_2 = self.create_return_name(self.req_2)
+
+ def test_getByName(self):
+ self._get_and_assert(self.scenario_1, self.req_d)
+
+ def test_getAll(self):
+ self._query_and_assert(query=None, reqs=[self.req_d, self.req_2])
+
+ def test_queryName(self):
+ query = self._set_query('name=nosdn-nofeature-ha')
+ self._query_and_assert(query, reqs=[self.req_d])
+
+ def test_queryInstaller(self):
+ query = self._set_query('installer=apex')
+ self._query_and_assert(query, reqs=[self.req_d])
+
+ def test_queryVersion(self):
+ query = self._set_query('version=master')
+ self._query_and_assert(query, reqs=[self.req_d])
+
+ def test_queryProject(self):
+ query = self._set_query('project=functest')
+ self._query_and_assert(query, reqs=[self.req_d, self.req_2])
+
+ def test_queryCombination(self):
+ query = self._set_query('name=nosdn-nofeature-ha',
+ 'installer=apex',
+ 'version=master',
+ 'project=functest')
+
+ self._query_and_assert(query, reqs=[self.req_d])
+
+ def _query_and_assert(self, query, found=True, reqs=None):
+ code, body = self.query(query)
+ if not found:
+ self.assertEqual(code, constants.HTTP_OK)
+ self.assertEqual(0, len(body.scenarios))
+ else:
+ self.assertEqual(len(reqs), len(body.scenarios))
+ for req in reqs:
+ for scenario in body.scenarios:
+ if req['name'] == scenario.name:
+ self.assert_res(code, scenario, req)
+
+
+class TestScenarioUpdate(TestScenarioBase):
+ def setUp(self):
+ super(TestScenarioUpdate, self).setUp()
+ self.scenario = self.create_return_name(self.req_d)
+
+ def _execute(set_update):
+ def magic(self):
+ update, scenario = set_update(self, deepcopy(self.req_d))
+ self._update_and_assert(update, scenario)
+ return magic
+
+ def test_renameScenario(self):
+ new_name = 'nosdn-nofeature-noha'
+ new_scenario = deepcopy(self.req_d)
+ new_scenario['name'] = new_name
+ update_req = models.ScenarioUpdateRequest(field='name',
+ op='update',
+ locate={},
+ term={'name': new_name})
+ self._update_and_assert(update_req, new_scenario, new_name)
+
+ @_execute
+ def test_addInstaller(self, scenario):
+ add = models.ScenarioInstaller(installer='daisy', versions=list())
+ scenario['installers'].append(add.format())
+ update = models.ScenarioUpdateRequest(field='installer',
+ op='add',
+ locate={},
+ term=add.format())
+ return update, scenario
+
+ @_execute
+ def test_deleteInstaller(self, scenario):
+ scenario['installers'] = filter(lambda f: f['installer'] != 'apex',
+ scenario['installers'])
+
+ update = models.ScenarioUpdateRequest(field='installer',
+ op='delete',
+ locate={'installer': 'apex'})
+ return update, scenario
+
+ @_execute
+ def test_addVersion(self, scenario):
+ add = models.ScenarioVersion(version='danube', projects=list())
+ scenario['installers'][0]['versions'].append(add.format())
+ update = models.ScenarioUpdateRequest(field='version',
+ op='add',
+ locate={'installer': 'apex'},
+ term=add.format())
+ return update, scenario
+
+ @_execute
+ def test_deleteVersion(self, scenario):
+ scenario['installers'][0]['versions'] = filter(
+ lambda f: f['version'] != 'master',
+ scenario['installers'][0]['versions'])
+
+ update = models.ScenarioUpdateRequest(field='version',
+ op='delete',
+ locate={'installer': 'apex',
+ 'version': 'master'})
+ return update, scenario
+
+ @_execute
+ def test_changeOwner(self, scenario):
+ scenario['installers'][0]['versions'][0]['owner'] = 'lucy'
+
+ update = models.ScenarioUpdateRequest(field='owner',
+ op='update',
+ locate={'installer': 'apex',
+ 'version': 'master'},
+ term={'owner': 'lucy'})
+ return update, scenario
+
+ @_execute
+ def test_addProject(self, scenario):
+ add = models.ScenarioProject(project='qtip').format()
+ scenario['installers'][0]['versions'][0]['projects'].append(add)
+ update = models.ScenarioUpdateRequest(field='project',
+ op='add',
+ locate={'installer': 'apex',
+ 'version': 'master'},
+ term=add)
+ return update, scenario
+
+ @_execute
+ def test_deleteProject(self, scenario):
+ scenario['installers'][0]['versions'][0]['projects'] = filter(
+ lambda f: f['project'] != 'functest',
+ scenario['installers'][0]['versions'][0]['projects'])
+
+ update = models.ScenarioUpdateRequest(field='project',
+ op='delete',
+ locate={
+ 'installer': 'apex',
+ 'version': 'master',
+ 'project': 'functest'})
+ return update, scenario
+
+ @_execute
+ def test_addCustoms(self, scenario):
+ add = ['odl', 'parser', 'vping_ssh']
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['customs'] = ['healthcheck', 'odl', 'parser', 'vping_ssh']
+ update = models.ScenarioUpdateRequest(field='customs',
+ op='add',
+ locate={
+ 'installer': 'apex',
+ 'version': 'master',
+ 'project': 'functest'},
+ term=add)
+ return update, scenario
+
+ @_execute
+ def test_deleteCustoms(self, scenario):
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['customs'] = ['healthcheck']
+ update = models.ScenarioUpdateRequest(field='customs',
+ op='delete',
+ locate={
+ 'installer': 'apex',
+ 'version': 'master',
+ 'project': 'functest'},
+ term=['vping_ssh'])
+ return update, scenario
+
+ @_execute
+ def test_addScore(self, scenario):
+ add = models.ScenarioScore(date=str(datetime.now()), score='11/12')
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['scores'].append(add.format())
+ update = models.ScenarioUpdateRequest(field='score',
+ op='add',
+ locate={
+ 'installer': 'apex',
+ 'version': 'master',
+ 'project': 'functest'},
+ term=add.format())
+ return update, scenario
+
+ @_execute
+ def test_addTi(self, scenario):
+ add = models.ScenarioTI(date=str(datetime.now()), status='gold')
+ projects = scenario['installers'][0]['versions'][0]['projects']
+ functest = filter(lambda f: f['project'] == 'functest', projects)[0]
+ functest['trust_indicators'].append(add.format())
+ update = models.ScenarioUpdateRequest(field='trust_indicator',
+ op='add',
+ locate={
+ 'installer': 'apex',
+ 'version': 'master',
+ 'project': 'functest'},
+ term=add.format())
+ return update, scenario
+
+ def _update_and_assert(self, update_req, new_scenario, name=None):
+ code, _ = self.update(update_req, self.scenario)
+ self.assertEqual(code, constants.HTTP_OK)
+ self._get_and_assert(self._none_default(name, self.scenario),
+ new_scenario)
+
+ @staticmethod
+ def _none_default(check, default):
+ return check if check else default
+
+
+class TestScenarioDelete(TestScenarioBase):
+ def test_notFound(self):
+ code, body = self.delete('notFound')
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
+
+ def test_success(self):
+ scenario = self.create_return_name(self.req_d)
+ code, _ = self.delete(scenario)
+ self.assertEqual(code, constants.HTTP_OK)
+ code, _ = self.get(scenario)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_testcase.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_testcase.py
index cb767844a..c0494db5d 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_testcase.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_testcase.py
@@ -6,35 +6,33 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-import unittest
import copy
+import unittest
-from test_base import TestBase
-from opnfv_testapi.resources.testcase_models import TestcaseCreateRequest, \
- Testcase, Testcases, TestcaseUpdateRequest
-from opnfv_testapi.resources.project_models import ProjectCreateRequest
-from opnfv_testapi.common.constants import HTTP_OK, HTTP_BAD_REQUEST, \
- HTTP_FORBIDDEN, HTTP_NOT_FOUND
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import project_models
+from opnfv_testapi.resources import testcase_models
+import test_base as base
-class TestCaseBase(TestBase):
+class TestCaseBase(base.TestBase):
def setUp(self):
super(TestCaseBase, self).setUp()
- self.req_d = TestcaseCreateRequest('vping_1',
- '/cases/vping_1',
- 'vping-ssh test')
- self.req_e = TestcaseCreateRequest('doctor_1',
- '/cases/doctor_1',
- 'create doctor')
- self.update_d = TestcaseUpdateRequest('vping_1',
- 'vping-ssh test',
- 'functest')
- self.update_e = TestcaseUpdateRequest('doctor_1',
- 'create doctor',
- 'functest')
- self.get_res = Testcase
- self.list_res = Testcases
- self.update_res = Testcase
+ self.req_d = testcase_models.TestcaseCreateRequest('vping_1',
+ '/cases/vping_1',
+ 'vping-ssh test')
+ self.req_e = testcase_models.TestcaseCreateRequest('doctor_1',
+ '/cases/doctor_1',
+ 'create doctor')
+ self.update_d = testcase_models.TestcaseUpdateRequest('vping_1',
+ 'vping-ssh test',
+ 'functest')
+ self.update_e = testcase_models.TestcaseUpdateRequest('doctor_1',
+ 'create doctor',
+ 'functest')
+ self.get_res = testcase_models.Testcase
+ self.list_res = testcase_models.Testcases
+ self.update_res = testcase_models.Testcase
self.basePath = '/api/v1/projects/%s/cases'
self.create_project()
@@ -57,7 +55,8 @@ class TestCaseBase(TestBase):
self.assertIsNotNone(new.creation_date)
def create_project(self):
- req_p = ProjectCreateRequest('functest', 'vping-ssh test')
+ req_p = project_models.ProjectCreateRequest('functest',
+ 'vping-ssh test')
self.create_help('/api/v1/projects', req_p)
self.project = req_p.name
@@ -80,46 +79,46 @@ class TestCaseBase(TestBase):
class TestCaseCreate(TestCaseBase):
def test_noBody(self):
(code, body) = self.create(None, 'vping')
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_noProject(self):
code, body = self.create(self.req_d, 'noProject')
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn('Could not find project', body)
def test_emptyName(self):
- req_empty = TestcaseCreateRequest('')
+ req_empty = testcase_models.TestcaseCreateRequest('')
(code, body) = self.create(req_empty, self.project)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_noneName(self):
- req_none = TestcaseCreateRequest(None)
+ req_none = testcase_models.TestcaseCreateRequest(None)
(code, body) = self.create(req_none, self.project)
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
self.assertIn('name missing', body)
def test_success(self):
code, body = self.create_d()
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_create_body(body, None, self.project)
def test_alreadyExist(self):
self.create_d()
code, body = self.create_d()
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn('already exists', body)
class TestCaseGet(TestCaseBase):
def test_notExist(self):
code, body = self.get('notExist')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_getOne(self):
self.create_d()
code, body = self.get(self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assert_body(body)
def test_list(self):
@@ -136,23 +135,23 @@ class TestCaseGet(TestCaseBase):
class TestCaseUpdate(TestCaseBase):
def test_noBody(self):
code, _ = self.update(case='noBody')
- self.assertEqual(code, HTTP_BAD_REQUEST)
+ self.assertEqual(code, constants.HTTP_BAD_REQUEST)
def test_notFound(self):
code, _ = self.update(self.update_e, 'notFound')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_newNameExist(self):
self.create_d()
self.create_e()
code, body = self.update(self.update_e, self.req_d.name)
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn("already exists", body)
def test_noUpdate(self):
self.create_d()
code, body = self.update(self.update_d, self.req_d.name)
- self.assertEqual(code, HTTP_FORBIDDEN)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
self.assertIn("Nothing to update", body)
def test_success(self):
@@ -161,7 +160,7 @@ class TestCaseUpdate(TestCaseBase):
_id = body._id
code, body = self.update(self.update_e, self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assertEqual(_id, body._id)
self.assert_update_body(self.req_d, body, self.update_e)
@@ -174,22 +173,22 @@ class TestCaseUpdate(TestCaseBase):
update = copy.deepcopy(self.update_d)
update.description = {'2. change': 'dollar change'}
code, body = self.update(update, self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
class TestCaseDelete(TestCaseBase):
def test_notFound(self):
code, body = self.delete('notFound')
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
def test_success(self):
self.create_d()
code, body = self.delete(self.req_d.name)
- self.assertEqual(code, HTTP_OK)
+ self.assertEqual(code, constants.HTTP_OK)
self.assertEqual(body, '')
code, body = self.get(self.req_d.name)
- self.assertEqual(code, HTTP_NOT_FOUND)
+ self.assertEqual(code, constants.HTTP_NOT_FOUND)
if __name__ == '__main__':
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_token.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_token.py
new file mode 100644
index 000000000..19b9e3e07
--- /dev/null
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_token.py
@@ -0,0 +1,118 @@
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+
+import unittest
+
+from tornado import web
+
+import fake_pymongo
+from opnfv_testapi.common import constants
+from opnfv_testapi.resources import project_models
+from opnfv_testapi.router import url_mappings
+import test_base as base
+
+
+class TestToken(base.TestBase):
+ def get_app(self):
+ return web.Application(
+ url_mappings.mappings,
+ db=fake_pymongo,
+ debug=True,
+ auth=True
+ )
+
+
+class TestTokenCreateProject(TestToken):
+ def setUp(self):
+ super(TestTokenCreateProject, self).setUp()
+ self.req_d = project_models.ProjectCreateRequest('vping')
+ fake_pymongo.tokens.insert({"access_token": "12345"})
+ self.basePath = '/api/v1/projects'
+
+ def test_projectCreateTokenInvalid(self):
+ self.headers['X-Auth-Token'] = '1234'
+ code, body = self.create_d()
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
+ self.assertIn('Invalid Token.', body)
+
+ def test_projectCreateTokenUnauthorized(self):
+ self.headers.pop('X-Auth-Token')
+ code, body = self.create_d()
+ self.assertEqual(code, constants.HTTP_UNAUTHORIZED)
+ self.assertIn('No Authentication Header.', body)
+
+ def test_projectCreateTokenSuccess(self):
+ self.headers['X-Auth-Token'] = '12345'
+ code, body = self.create_d()
+ self.assertEqual(code, constants.HTTP_OK)
+
+
+class TestTokenDeleteProject(TestToken):
+ def setUp(self):
+ super(TestTokenDeleteProject, self).setUp()
+ self.req_d = project_models.ProjectCreateRequest('vping')
+ fake_pymongo.tokens.insert({"access_token": "12345"})
+ self.basePath = '/api/v1/projects'
+
+ def test_projectDeleteTokenIvalid(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ self.headers['X-Auth-Token'] = '1234'
+ code, body = self.delete(self.req_d.name)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
+ self.assertIn('Invalid Token.', body)
+
+ def test_projectDeleteTokenUnauthorized(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ self.headers.pop('X-Auth-Token')
+ code, body = self.delete(self.req_d.name)
+ self.assertEqual(code, constants.HTTP_UNAUTHORIZED)
+ self.assertIn('No Authentication Header.', body)
+
+ def test_projectDeleteTokenSuccess(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ code, body = self.delete(self.req_d.name)
+ self.assertEqual(code, constants.HTTP_OK)
+
+
+class TestTokenUpdateProject(TestToken):
+ def setUp(self):
+ super(TestTokenUpdateProject, self).setUp()
+ self.req_d = project_models.ProjectCreateRequest('vping')
+ fake_pymongo.tokens.insert({"access_token": "12345"})
+ self.basePath = '/api/v1/projects'
+
+ def test_projectUpdateTokenIvalid(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ code, body = self.get(self.req_d.name)
+ self.headers['X-Auth-Token'] = '1234'
+ req = project_models.ProjectUpdateRequest('newName', 'new description')
+ code, body = self.update(req, self.req_d.name)
+ self.assertEqual(code, constants.HTTP_FORBIDDEN)
+ self.assertIn('Invalid Token.', body)
+
+ def test_projectUpdateTokenUnauthorized(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ code, body = self.get(self.req_d.name)
+ self.headers.pop('X-Auth-Token')
+ req = project_models.ProjectUpdateRequest('newName', 'new description')
+ code, body = self.update(req, self.req_d.name)
+ self.assertEqual(code, constants.HTTP_UNAUTHORIZED)
+ self.assertIn('No Authentication Header.', body)
+
+ def test_projectUpdateTokenSuccess(self):
+ self.headers['X-Auth-Token'] = '12345'
+ self.create_d()
+ code, body = self.get(self.req_d.name)
+ req = project_models.ProjectUpdateRequest('newName', 'new description')
+ code, body = self.update(req, self.req_d.name)
+ self.assertEqual(code, constants.HTTP_OK)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils/test/testapi/opnfv_testapi/tests/unit/test_version.py b/utils/test/testapi/opnfv_testapi/tests/unit/test_version.py
index b6fbf45dc..c8f3f5062 100644
--- a/utils/test/testapi/opnfv_testapi/tests/unit/test_version.py
+++ b/utils/test/testapi/opnfv_testapi/tests/unit/test_version.py
@@ -8,14 +8,14 @@
##############################################################################
import unittest
-from test_base import TestBase
-from opnfv_testapi.resources.models import Versions
+from opnfv_testapi.resources import models
+import test_base as base
-class TestVersionBase(TestBase):
+class TestVersionBase(base.TestBase):
def setUp(self):
super(TestVersionBase, self).setUp()
- self.list_res = Versions
+ self.list_res = models.Versions
self.basePath = '/versions'
diff --git a/utils/test/testapi/run_test.sh b/utils/test/testapi/run_test.sh
index 9b25f8ffc..4efc7af3b 100755
--- a/utils/test/testapi/run_test.sh
+++ b/utils/test/testapi/run_test.sh
@@ -1,10 +1,38 @@
-#! /bin/bash
+#!/bin/bash
-# Before run this script, make sure that testtools and discover
-# had been installed in your env
-# or else using pip to install them as follows:
-# pip install testtools, discover
+set -o errexit
+
+# Get script directory
+SCRIPTDIR=`dirname $0`
+
+echo "Running unit tests..."
+
+# Creating virtual environment
+virtualenv $SCRIPTDIR/testapi_venv
+source $SCRIPTDIR/testapi_venv/bin/activate
+
+# Install requirements
+pip install -r $SCRIPTDIR/requirements.txt
+pip install coverage
+pip install nose>=1.3.1
+pip install pytest
+pip install mock
find . -type f -name "*.pyc" -delete
-testrargs="discover ./opnfv_testapi/tests/unit"
-python -m testtools.run $testrargs
+
+nosetests --with-xunit \
+ --with-coverage \
+ --cover-erase \
+ --cover-package=$SCRIPTDIR/opnfv_testapi/cmd \
+ --cover-package=$SCRIPTDIR/opnfv_testapi/common \
+ --cover-package=$SCRIPTDIR/opnfv_testapi/resources \
+ --cover-package=$SCRIPTDIR/opnfv_testapi/router \
+ --cover-xml \
+ --cover-html \
+ $SCRIPTDIR/opnfv_testapi/tests
+
+exit_code=$?
+
+deactivate
+
+exit $exit_code
diff --git a/utils/test/testapi/test-requirements.txt b/utils/test/testapi/test-requirements.txt
index ddbdefcfd..4633ad637 100644
--- a/utils/test/testapi/test-requirements.txt
+++ b/utils/test/testapi/test-requirements.txt
@@ -2,6 +2,10 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-testtools>=1.4.0
-discover
-futures \ No newline at end of file
+tox
+mock
+pytest
+pytest-cov
+coverage
+pykwalify
+pip_check_reqs
diff --git a/utils/test/testapi/tox.ini b/utils/test/testapi/tox.ini
new file mode 100644
index 000000000..81c9dfab1
--- /dev/null
+++ b/utils/test/testapi/tox.ini
@@ -0,0 +1,41 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py27,pep8
+skipsdist = True
+sitepackages = True
+
+[testenv]
+usedevelop = True
+install_command = pip install -U {opts} {packages}
+deps =
+ -rrequirements.txt
+ -rtest-requirements.txt
+commands=
+ py.test \
+ --basetemp={envtmpdir} \
+ --cov \
+ {posargs}
+setenv=
+ HOME = {envtmpdir}
+ PYTHONPATH = {toxinidir}
+
+[testenv:pep8]
+deps = flake8
+commands = flake8 {toxinidir}
+
+[flake8]
+# H803 skipped on purpose per list discussion.
+# E123, E125 skipped as they are invalid PEP-8.
+
+show-source = True
+ignore = E123,E125,H803,E501
+builtins = _
+exclude = build,dist,doc,legacy,.eggs,.git,.tox,.venv,testapi_venv,venv
+
+[pytest]
+testpaths = opnfv_testapi/tests
+python_functions = test_*
diff --git a/utils/test/testapi/update/templates/utils.py b/utils/test/testapi/update/templates/utils.py
index a18ff0389..4254fee34 100644
--- a/utils/test/testapi/update/templates/utils.py
+++ b/utils/test/testapi/update/templates/utils.py
@@ -44,5 +44,5 @@ def main(method, parser):
args = parser.parse_args()
try:
method(args)
- except AssertionError, msg:
+ except AssertionError as msg:
print(msg)
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/README.md b/utils/test/vnfcatalogue/VNF_Catalogue/README.md
new file mode 100644
index 000000000..32ad65416
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/README.md
@@ -0,0 +1,12 @@
+#VNF_Catalogue Nodejs + Jade + MySql server
+
+
+## Quickstart
+
+First install the dependencies
+
+ ```npm install```
+
+Then Start the Server
+
+ ```npm start```
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/app.js b/utils/test/vnfcatalogue/VNF_Catalogue/app.js
new file mode 100644
index 000000000..0f842b62d
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/app.js
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+var express = require('express');
+var path = require('path');
+var favicon = require('serve-favicon');
+var logger = require('morgan');
+var cookieParser = require('cookie-parser');
+var bodyParser = require('body-parser');
+
+var routes = require('./routes/index');
+var search_projects = require('./routes/search_projects');
+
+var app = express();
+
+// view engine setup
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'jade');
+
+// Database
+var db = require('mysql2');
+
+// uncomment after placing your favicon in /public
+//app.use(favicon(__dirname + '/public/favicon.ico'));
+app.use(logger('dev'));
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(cookieParser());
+app.use(express.static(path.join(__dirname, 'public')));
+
+// Make our db accessible to our router
+app.use(function(req,res,next){
+ req.db = db;
+ next();
+});
+
+app.use('/', routes);
+app.use('/search_projects', search_projects);
+
+
+// Some Error handling for now #TODO Remove
+
+/// catch 404 and forwarding to error handler
+app.use(function(req, res, next) {
+ var err = new Error('Not Found');
+ err.status = 404;
+ next(err);
+});
+
+
+// development error handler
+// will print stacktrace
+if (app.get('env') === 'development') {
+ app.use(function(err, req, res, next) {
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: err
+ });
+ });
+}
+
+// production error handler
+// no stacktraces leaked to user
+app.use(function(err, req, res, next) {
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: {}
+ });
+});
+
+module.exports = app;
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/bin/www b/utils/test/vnfcatalogue/VNF_Catalogue/bin/www
new file mode 100644
index 000000000..3cfbf7796
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/bin/www
@@ -0,0 +1,9 @@
+#!/usr/bin/env node
+var debug = require('debug')('my-application');
+var app = require('../app');
+
+app.set('port', process.env.PORT || 3000);
+
+var server = app.listen(app.get('port'), function() {
+ debug('Express server listening on port ' + server.address().port);
+});
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/package.json b/utils/test/vnfcatalogue/VNF_Catalogue/package.json
new file mode 100644
index 000000000..7c6a86730
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "VNF_Catalogue",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "start": "node ./bin/www"
+ },
+ "dependencies": {
+ "body-parser": "~1.15.1",
+ "cookie-parser": "~1.4.3",
+ "debug": "~2.2.0",
+ "express": "~4.13.4",
+ "jade": "~1.11.0",
+ "morgan": "~1.7.0",
+ "serve-favicon": "~2.3.0",
+ "mysql2": "*"
+ }
+} \ No newline at end of file
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/.DS_Store b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/.DS_Store
new file mode 100644
index 000000000..2828ef680
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/.DS_Store
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/LICENSE b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/LICENSE
new file mode 100644
index 000000000..44bd03eab
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2017 Materialize
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/README.md b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/README.md
new file mode 100644
index 000000000..497b01ae0
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/README.md
@@ -0,0 +1,49 @@
+![alt tag](https://raw.github.com/dogfalo/materialize/master/images/materialize.gif)
+===========
+
+[![Travis CI](https://travis-ci.org/Dogfalo/materialize.svg?branch=master)](https://travis-ci.org/Dogfalo/materialize)[![devDependency Status](https://david-dm.org/Dogfalo/materialize/dev-status.svg)](https://david-dm.org/Dogfalo/materialize#info=devDependencies)[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/Dogfalo/materialize?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+[Materialize](http://materializecss.com/), a CSS Framework based on material design
+
+### Current Version : v0.97.8
+
+## Sass Requirements:
+- Ruby Sass 3.3+, LibSass 0.6+
+
+## Supported Browsers:
+Chrome 35+, Firefox 31+, Safari 7+, IE 10+
+
+## Changelog
+- v0.97.8 (October 30th, 2016)
+ - **Refactored Modal plugin**
+ - Tabs now supported in navbar
+ - Chips data can now be reinitiailized
+ - Minor side nav fixes
+ - FAB to toolbar component added
+ - Fixed dropdown options bug
+- v0.97.7 (July 23rd, 2016)
+ - Basic horizontal cards
+ - Carousel bug fixes and new features
+ - Updated sidenav styles and new component
+ - Meteor package now supports Sass
+ - Autocomplete form component
+ - Chips jQuery plugin
+- v0.97.6 (April 1st, 2016)
+ - **Removed deprecated material icons from project**
+ - **Changed /font directory to /fonts**
+ - Datepicker and ScrollSpy now compatible with jQuery 2.2.x
+ - Responsive tables now work with empty cells
+ - Added focus states to checkboxes, switches, and radio buttons
+ - Sidenav and Modals no longer cause flicker with scrollbar
+ - Materialbox overflow and z-index issues fixed
+ - Added new option for Card actions within a Card reveal
+- v0.97.5 (December 21st, 2015)
+ - Fixed Meteor package crash
+
+
+
+## Contributing
+[Please read CONTRIBUTING.md for more information](CONTRIBUTING.md)
+
+## Testing
+We use Jasmine as our testing framework and we're trying to write a robust test suite for our components. If you want to help, [here's a starting guide on how to write tests in Jasmine](https://docs.google.com/document/d/1dVM6qGt_b_y9RRhr9X7oZfFydaJIEqB9CT7yekv-4XE/edit?usp=sharing)
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.css b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.css
new file mode 100644
index 000000000..27de210a1
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.css
@@ -0,0 +1,8582 @@
+/*!
+ * Materialize v0.98.0 (http://materializecss.com)
+ * Copyright 2014-2015 Materialize
+ * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
+ */
+.materialize-red {
+ background-color: #e51c23 !important;
+}
+
+.materialize-red-text {
+ color: #e51c23 !important;
+}
+
+.materialize-red.lighten-5 {
+ background-color: #fdeaeb !important;
+}
+
+.materialize-red-text.text-lighten-5 {
+ color: #fdeaeb !important;
+}
+
+.materialize-red.lighten-4 {
+ background-color: #f8c1c3 !important;
+}
+
+.materialize-red-text.text-lighten-4 {
+ color: #f8c1c3 !important;
+}
+
+.materialize-red.lighten-3 {
+ background-color: #f3989b !important;
+}
+
+.materialize-red-text.text-lighten-3 {
+ color: #f3989b !important;
+}
+
+.materialize-red.lighten-2 {
+ background-color: #ee6e73 !important;
+}
+
+.materialize-red-text.text-lighten-2 {
+ color: #ee6e73 !important;
+}
+
+.materialize-red.lighten-1 {
+ background-color: #ea454b !important;
+}
+
+.materialize-red-text.text-lighten-1 {
+ color: #ea454b !important;
+}
+
+.materialize-red.darken-1 {
+ background-color: #d0181e !important;
+}
+
+.materialize-red-text.text-darken-1 {
+ color: #d0181e !important;
+}
+
+.materialize-red.darken-2 {
+ background-color: #b9151b !important;
+}
+
+.materialize-red-text.text-darken-2 {
+ color: #b9151b !important;
+}
+
+.materialize-red.darken-3 {
+ background-color: #a21318 !important;
+}
+
+.materialize-red-text.text-darken-3 {
+ color: #a21318 !important;
+}
+
+.materialize-red.darken-4 {
+ background-color: #8b1014 !important;
+}
+
+.materialize-red-text.text-darken-4 {
+ color: #8b1014 !important;
+}
+
+.red {
+ background-color: #F44336 !important;
+}
+
+.red-text {
+ color: #F44336 !important;
+}
+
+.red.lighten-5 {
+ background-color: #FFEBEE !important;
+}
+
+.red-text.text-lighten-5 {
+ color: #FFEBEE !important;
+}
+
+.red.lighten-4 {
+ background-color: #FFCDD2 !important;
+}
+
+.red-text.text-lighten-4 {
+ color: #FFCDD2 !important;
+}
+
+.red.lighten-3 {
+ background-color: #EF9A9A !important;
+}
+
+.red-text.text-lighten-3 {
+ color: #EF9A9A !important;
+}
+
+.red.lighten-2 {
+ background-color: #E57373 !important;
+}
+
+.red-text.text-lighten-2 {
+ color: #E57373 !important;
+}
+
+.red.lighten-1 {
+ background-color: #EF5350 !important;
+}
+
+.red-text.text-lighten-1 {
+ color: #EF5350 !important;
+}
+
+.red.darken-1 {
+ background-color: #E53935 !important;
+}
+
+.red-text.text-darken-1 {
+ color: #E53935 !important;
+}
+
+.red.darken-2 {
+ background-color: #D32F2F !important;
+}
+
+.red-text.text-darken-2 {
+ color: #D32F2F !important;
+}
+
+.red.darken-3 {
+ background-color: #C62828 !important;
+}
+
+.red-text.text-darken-3 {
+ color: #C62828 !important;
+}
+
+.red.darken-4 {
+ background-color: #B71C1C !important;
+}
+
+.red-text.text-darken-4 {
+ color: #B71C1C !important;
+}
+
+.red.accent-1 {
+ background-color: #FF8A80 !important;
+}
+
+.red-text.text-accent-1 {
+ color: #FF8A80 !important;
+}
+
+.red.accent-2 {
+ background-color: #FF5252 !important;
+}
+
+.red-text.text-accent-2 {
+ color: #FF5252 !important;
+}
+
+.red.accent-3 {
+ background-color: #FF1744 !important;
+}
+
+.red-text.text-accent-3 {
+ color: #FF1744 !important;
+}
+
+.red.accent-4 {
+ background-color: #D50000 !important;
+}
+
+.red-text.text-accent-4 {
+ color: #D50000 !important;
+}
+
+.pink {
+ background-color: #e91e63 !important;
+}
+
+.pink-text {
+ color: #e91e63 !important;
+}
+
+.pink.lighten-5 {
+ background-color: #fce4ec !important;
+}
+
+.pink-text.text-lighten-5 {
+ color: #fce4ec !important;
+}
+
+.pink.lighten-4 {
+ background-color: #f8bbd0 !important;
+}
+
+.pink-text.text-lighten-4 {
+ color: #f8bbd0 !important;
+}
+
+.pink.lighten-3 {
+ background-color: #f48fb1 !important;
+}
+
+.pink-text.text-lighten-3 {
+ color: #f48fb1 !important;
+}
+
+.pink.lighten-2 {
+ background-color: #f06292 !important;
+}
+
+.pink-text.text-lighten-2 {
+ color: #f06292 !important;
+}
+
+.pink.lighten-1 {
+ background-color: #ec407a !important;
+}
+
+.pink-text.text-lighten-1 {
+ color: #ec407a !important;
+}
+
+.pink.darken-1 {
+ background-color: #d81b60 !important;
+}
+
+.pink-text.text-darken-1 {
+ color: #d81b60 !important;
+}
+
+.pink.darken-2 {
+ background-color: #c2185b !important;
+}
+
+.pink-text.text-darken-2 {
+ color: #c2185b !important;
+}
+
+.pink.darken-3 {
+ background-color: #ad1457 !important;
+}
+
+.pink-text.text-darken-3 {
+ color: #ad1457 !important;
+}
+
+.pink.darken-4 {
+ background-color: #880e4f !important;
+}
+
+.pink-text.text-darken-4 {
+ color: #880e4f !important;
+}
+
+.pink.accent-1 {
+ background-color: #ff80ab !important;
+}
+
+.pink-text.text-accent-1 {
+ color: #ff80ab !important;
+}
+
+.pink.accent-2 {
+ background-color: #ff4081 !important;
+}
+
+.pink-text.text-accent-2 {
+ color: #ff4081 !important;
+}
+
+.pink.accent-3 {
+ background-color: #f50057 !important;
+}
+
+.pink-text.text-accent-3 {
+ color: #f50057 !important;
+}
+
+.pink.accent-4 {
+ background-color: #c51162 !important;
+}
+
+.pink-text.text-accent-4 {
+ color: #c51162 !important;
+}
+
+.purple {
+ background-color: #9c27b0 !important;
+}
+
+.purple-text {
+ color: #9c27b0 !important;
+}
+
+.purple.lighten-5 {
+ background-color: #f3e5f5 !important;
+}
+
+.purple-text.text-lighten-5 {
+ color: #f3e5f5 !important;
+}
+
+.purple.lighten-4 {
+ background-color: #e1bee7 !important;
+}
+
+.purple-text.text-lighten-4 {
+ color: #e1bee7 !important;
+}
+
+.purple.lighten-3 {
+ background-color: #ce93d8 !important;
+}
+
+.purple-text.text-lighten-3 {
+ color: #ce93d8 !important;
+}
+
+.purple.lighten-2 {
+ background-color: #ba68c8 !important;
+}
+
+.purple-text.text-lighten-2 {
+ color: #ba68c8 !important;
+}
+
+.purple.lighten-1 {
+ background-color: #ab47bc !important;
+}
+
+.purple-text.text-lighten-1 {
+ color: #ab47bc !important;
+}
+
+.purple.darken-1 {
+ background-color: #8e24aa !important;
+}
+
+.purple-text.text-darken-1 {
+ color: #8e24aa !important;
+}
+
+.purple.darken-2 {
+ background-color: #7b1fa2 !important;
+}
+
+.purple-text.text-darken-2 {
+ color: #7b1fa2 !important;
+}
+
+.purple.darken-3 {
+ background-color: #6a1b9a !important;
+}
+
+.purple-text.text-darken-3 {
+ color: #6a1b9a !important;
+}
+
+.purple.darken-4 {
+ background-color: #4a148c !important;
+}
+
+.purple-text.text-darken-4 {
+ color: #4a148c !important;
+}
+
+.purple.accent-1 {
+ background-color: #ea80fc !important;
+}
+
+.purple-text.text-accent-1 {
+ color: #ea80fc !important;
+}
+
+.purple.accent-2 {
+ background-color: #e040fb !important;
+}
+
+.purple-text.text-accent-2 {
+ color: #e040fb !important;
+}
+
+.purple.accent-3 {
+ background-color: #d500f9 !important;
+}
+
+.purple-text.text-accent-3 {
+ color: #d500f9 !important;
+}
+
+.purple.accent-4 {
+ background-color: #aa00ff !important;
+}
+
+.purple-text.text-accent-4 {
+ color: #aa00ff !important;
+}
+
+.deep-purple {
+ background-color: #673ab7 !important;
+}
+
+.deep-purple-text {
+ color: #673ab7 !important;
+}
+
+.deep-purple.lighten-5 {
+ background-color: #ede7f6 !important;
+}
+
+.deep-purple-text.text-lighten-5 {
+ color: #ede7f6 !important;
+}
+
+.deep-purple.lighten-4 {
+ background-color: #d1c4e9 !important;
+}
+
+.deep-purple-text.text-lighten-4 {
+ color: #d1c4e9 !important;
+}
+
+.deep-purple.lighten-3 {
+ background-color: #b39ddb !important;
+}
+
+.deep-purple-text.text-lighten-3 {
+ color: #b39ddb !important;
+}
+
+.deep-purple.lighten-2 {
+ background-color: #9575cd !important;
+}
+
+.deep-purple-text.text-lighten-2 {
+ color: #9575cd !important;
+}
+
+.deep-purple.lighten-1 {
+ background-color: #7e57c2 !important;
+}
+
+.deep-purple-text.text-lighten-1 {
+ color: #7e57c2 !important;
+}
+
+.deep-purple.darken-1 {
+ background-color: #5e35b1 !important;
+}
+
+.deep-purple-text.text-darken-1 {
+ color: #5e35b1 !important;
+}
+
+.deep-purple.darken-2 {
+ background-color: #512da8 !important;
+}
+
+.deep-purple-text.text-darken-2 {
+ color: #512da8 !important;
+}
+
+.deep-purple.darken-3 {
+ background-color: #4527a0 !important;
+}
+
+.deep-purple-text.text-darken-3 {
+ color: #4527a0 !important;
+}
+
+.deep-purple.darken-4 {
+ background-color: #311b92 !important;
+}
+
+.deep-purple-text.text-darken-4 {
+ color: #311b92 !important;
+}
+
+.deep-purple.accent-1 {
+ background-color: #b388ff !important;
+}
+
+.deep-purple-text.text-accent-1 {
+ color: #b388ff !important;
+}
+
+.deep-purple.accent-2 {
+ background-color: #7c4dff !important;
+}
+
+.deep-purple-text.text-accent-2 {
+ color: #7c4dff !important;
+}
+
+.deep-purple.accent-3 {
+ background-color: #651fff !important;
+}
+
+.deep-purple-text.text-accent-3 {
+ color: #651fff !important;
+}
+
+.deep-purple.accent-4 {
+ background-color: #6200ea !important;
+}
+
+.deep-purple-text.text-accent-4 {
+ color: #6200ea !important;
+}
+
+.indigo {
+ background-color: #3f51b5 !important;
+}
+
+.indigo-text {
+ color: #3f51b5 !important;
+}
+
+.indigo.lighten-5 {
+ background-color: #e8eaf6 !important;
+}
+
+.indigo-text.text-lighten-5 {
+ color: #e8eaf6 !important;
+}
+
+.indigo.lighten-4 {
+ background-color: #c5cae9 !important;
+}
+
+.indigo-text.text-lighten-4 {
+ color: #c5cae9 !important;
+}
+
+.indigo.lighten-3 {
+ background-color: #9fa8da !important;
+}
+
+.indigo-text.text-lighten-3 {
+ color: #9fa8da !important;
+}
+
+.indigo.lighten-2 {
+ background-color: #7986cb !important;
+}
+
+.indigo-text.text-lighten-2 {
+ color: #7986cb !important;
+}
+
+.indigo.lighten-1 {
+ background-color: #5c6bc0 !important;
+}
+
+.indigo-text.text-lighten-1 {
+ color: #5c6bc0 !important;
+}
+
+.indigo.darken-1 {
+ background-color: #3949ab !important;
+}
+
+.indigo-text.text-darken-1 {
+ color: #3949ab !important;
+}
+
+.indigo.darken-2 {
+ background-color: #303f9f !important;
+}
+
+.indigo-text.text-darken-2 {
+ color: #303f9f !important;
+}
+
+.indigo.darken-3 {
+ background-color: #283593 !important;
+}
+
+.indigo-text.text-darken-3 {
+ color: #283593 !important;
+}
+
+.indigo.darken-4 {
+ background-color: #1a237e !important;
+}
+
+.indigo-text.text-darken-4 {
+ color: #1a237e !important;
+}
+
+.indigo.accent-1 {
+ background-color: #8c9eff !important;
+}
+
+.indigo-text.text-accent-1 {
+ color: #8c9eff !important;
+}
+
+.indigo.accent-2 {
+ background-color: #536dfe !important;
+}
+
+.indigo-text.text-accent-2 {
+ color: #536dfe !important;
+}
+
+.indigo.accent-3 {
+ background-color: #3d5afe !important;
+}
+
+.indigo-text.text-accent-3 {
+ color: #3d5afe !important;
+}
+
+.indigo.accent-4 {
+ background-color: #304ffe !important;
+}
+
+.indigo-text.text-accent-4 {
+ color: #304ffe !important;
+}
+
+.blue {
+ background-color: #2196F3 !important;
+}
+
+.blue-text {
+ color: #2196F3 !important;
+}
+
+.blue.lighten-5 {
+ background-color: #E3F2FD !important;
+}
+
+.blue-text.text-lighten-5 {
+ color: #E3F2FD !important;
+}
+
+.blue.lighten-4 {
+ background-color: #BBDEFB !important;
+}
+
+.blue-text.text-lighten-4 {
+ color: #BBDEFB !important;
+}
+
+.blue.lighten-3 {
+ background-color: #90CAF9 !important;
+}
+
+.blue-text.text-lighten-3 {
+ color: #90CAF9 !important;
+}
+
+.blue.lighten-2 {
+ background-color: #64B5F6 !important;
+}
+
+.blue-text.text-lighten-2 {
+ color: #64B5F6 !important;
+}
+
+.blue.lighten-1 {
+ background-color: #42A5F5 !important;
+}
+
+.blue-text.text-lighten-1 {
+ color: #42A5F5 !important;
+}
+
+.blue.darken-1 {
+ background-color: #1E88E5 !important;
+}
+
+.blue-text.text-darken-1 {
+ color: #1E88E5 !important;
+}
+
+.blue.darken-2 {
+ background-color: #1976D2 !important;
+}
+
+.blue-text.text-darken-2 {
+ color: #1976D2 !important;
+}
+
+.blue.darken-3 {
+ background-color: #1565C0 !important;
+}
+
+.blue-text.text-darken-3 {
+ color: #1565C0 !important;
+}
+
+.blue.darken-4 {
+ background-color: #0D47A1 !important;
+}
+
+.blue-text.text-darken-4 {
+ color: #0D47A1 !important;
+}
+
+.blue.accent-1 {
+ background-color: #82B1FF !important;
+}
+
+.blue-text.text-accent-1 {
+ color: #82B1FF !important;
+}
+
+.blue.accent-2 {
+ background-color: #448AFF !important;
+}
+
+.blue-text.text-accent-2 {
+ color: #448AFF !important;
+}
+
+.blue.accent-3 {
+ background-color: #2979FF !important;
+}
+
+.blue-text.text-accent-3 {
+ color: #2979FF !important;
+}
+
+.blue.accent-4 {
+ background-color: #2962FF !important;
+}
+
+.blue-text.text-accent-4 {
+ color: #2962FF !important;
+}
+
+.light-blue {
+ background-color: #03a9f4 !important;
+}
+
+.light-blue-text {
+ color: #03a9f4 !important;
+}
+
+.light-blue.lighten-5 {
+ background-color: #e1f5fe !important;
+}
+
+.light-blue-text.text-lighten-5 {
+ color: #e1f5fe !important;
+}
+
+.light-blue.lighten-4 {
+ background-color: #b3e5fc !important;
+}
+
+.light-blue-text.text-lighten-4 {
+ color: #b3e5fc !important;
+}
+
+.light-blue.lighten-3 {
+ background-color: #81d4fa !important;
+}
+
+.light-blue-text.text-lighten-3 {
+ color: #81d4fa !important;
+}
+
+.light-blue.lighten-2 {
+ background-color: #4fc3f7 !important;
+}
+
+.light-blue-text.text-lighten-2 {
+ color: #4fc3f7 !important;
+}
+
+.light-blue.lighten-1 {
+ background-color: #29b6f6 !important;
+}
+
+.light-blue-text.text-lighten-1 {
+ color: #29b6f6 !important;
+}
+
+.light-blue.darken-1 {
+ background-color: #039be5 !important;
+}
+
+.light-blue-text.text-darken-1 {
+ color: #039be5 !important;
+}
+
+.light-blue.darken-2 {
+ background-color: #0288d1 !important;
+}
+
+.light-blue-text.text-darken-2 {
+ color: #0288d1 !important;
+}
+
+.light-blue.darken-3 {
+ background-color: #0277bd !important;
+}
+
+.light-blue-text.text-darken-3 {
+ color: #0277bd !important;
+}
+
+.light-blue.darken-4 {
+ background-color: #01579b !important;
+}
+
+.light-blue-text.text-darken-4 {
+ color: #01579b !important;
+}
+
+.light-blue.accent-1 {
+ background-color: #80d8ff !important;
+}
+
+.light-blue-text.text-accent-1 {
+ color: #80d8ff !important;
+}
+
+.light-blue.accent-2 {
+ background-color: #40c4ff !important;
+}
+
+.light-blue-text.text-accent-2 {
+ color: #40c4ff !important;
+}
+
+.light-blue.accent-3 {
+ background-color: #00b0ff !important;
+}
+
+.light-blue-text.text-accent-3 {
+ color: #00b0ff !important;
+}
+
+.light-blue.accent-4 {
+ background-color: #0091ea !important;
+}
+
+.light-blue-text.text-accent-4 {
+ color: #0091ea !important;
+}
+
+.cyan {
+ background-color: #00bcd4 !important;
+}
+
+.cyan-text {
+ color: #00bcd4 !important;
+}
+
+.cyan.lighten-5 {
+ background-color: #e0f7fa !important;
+}
+
+.cyan-text.text-lighten-5 {
+ color: #e0f7fa !important;
+}
+
+.cyan.lighten-4 {
+ background-color: #b2ebf2 !important;
+}
+
+.cyan-text.text-lighten-4 {
+ color: #b2ebf2 !important;
+}
+
+.cyan.lighten-3 {
+ background-color: #80deea !important;
+}
+
+.cyan-text.text-lighten-3 {
+ color: #80deea !important;
+}
+
+.cyan.lighten-2 {
+ background-color: #4dd0e1 !important;
+}
+
+.cyan-text.text-lighten-2 {
+ color: #4dd0e1 !important;
+}
+
+.cyan.lighten-1 {
+ background-color: #26c6da !important;
+}
+
+.cyan-text.text-lighten-1 {
+ color: #26c6da !important;
+}
+
+.cyan.darken-1 {
+ background-color: #00acc1 !important;
+}
+
+.cyan-text.text-darken-1 {
+ color: #00acc1 !important;
+}
+
+.cyan.darken-2 {
+ background-color: #0097a7 !important;
+}
+
+.cyan-text.text-darken-2 {
+ color: #0097a7 !important;
+}
+
+.cyan.darken-3 {
+ background-color: #00838f !important;
+}
+
+.cyan-text.text-darken-3 {
+ color: #00838f !important;
+}
+
+.cyan.darken-4 {
+ background-color: #006064 !important;
+}
+
+.cyan-text.text-darken-4 {
+ color: #006064 !important;
+}
+
+.cyan.accent-1 {
+ background-color: #84ffff !important;
+}
+
+.cyan-text.text-accent-1 {
+ color: #84ffff !important;
+}
+
+.cyan.accent-2 {
+ background-color: #18ffff !important;
+}
+
+.cyan-text.text-accent-2 {
+ color: #18ffff !important;
+}
+
+.cyan.accent-3 {
+ background-color: #00e5ff !important;
+}
+
+.cyan-text.text-accent-3 {
+ color: #00e5ff !important;
+}
+
+.cyan.accent-4 {
+ background-color: #00b8d4 !important;
+}
+
+.cyan-text.text-accent-4 {
+ color: #00b8d4 !important;
+}
+
+.teal {
+ background-color: #009688 !important;
+}
+
+.teal-text {
+ color: #009688 !important;
+}
+
+.teal.lighten-5 {
+ background-color: #e0f2f1 !important;
+}
+
+.teal-text.text-lighten-5 {
+ color: #e0f2f1 !important;
+}
+
+.teal.lighten-4 {
+ background-color: #b2dfdb !important;
+}
+
+.teal-text.text-lighten-4 {
+ color: #b2dfdb !important;
+}
+
+.teal.lighten-3 {
+ background-color: #80cbc4 !important;
+}
+
+.teal-text.text-lighten-3 {
+ color: #80cbc4 !important;
+}
+
+.teal.lighten-2 {
+ background-color: #4db6ac !important;
+}
+
+.teal-text.text-lighten-2 {
+ color: #4db6ac !important;
+}
+
+.teal.lighten-1 {
+ background-color: #26a69a !important;
+}
+
+.teal-text.text-lighten-1 {
+ color: #26a69a !important;
+}
+
+.teal.darken-1 {
+ background-color: #00897b !important;
+}
+
+.teal-text.text-darken-1 {
+ color: #00897b !important;
+}
+
+.teal.darken-2 {
+ background-color: #00796b !important;
+}
+
+.teal-text.text-darken-2 {
+ color: #00796b !important;
+}
+
+.teal.darken-3 {
+ background-color: #00695c !important;
+}
+
+.teal-text.text-darken-3 {
+ color: #00695c !important;
+}
+
+.teal.darken-4 {
+ background-color: #004d40 !important;
+}
+
+.teal-text.text-darken-4 {
+ color: #004d40 !important;
+}
+
+.teal.accent-1 {
+ background-color: #a7ffeb !important;
+}
+
+.teal-text.text-accent-1 {
+ color: #a7ffeb !important;
+}
+
+.teal.accent-2 {
+ background-color: #64ffda !important;
+}
+
+.teal-text.text-accent-2 {
+ color: #64ffda !important;
+}
+
+.teal.accent-3 {
+ background-color: #1de9b6 !important;
+}
+
+.teal-text.text-accent-3 {
+ color: #1de9b6 !important;
+}
+
+.teal.accent-4 {
+ background-color: #00bfa5 !important;
+}
+
+.teal-text.text-accent-4 {
+ color: #00bfa5 !important;
+}
+
+.green {
+ background-color: #4CAF50 !important;
+}
+
+.green-text {
+ color: #4CAF50 !important;
+}
+
+.green.lighten-5 {
+ background-color: #E8F5E9 !important;
+}
+
+.green-text.text-lighten-5 {
+ color: #E8F5E9 !important;
+}
+
+.green.lighten-4 {
+ background-color: #C8E6C9 !important;
+}
+
+.green-text.text-lighten-4 {
+ color: #C8E6C9 !important;
+}
+
+.green.lighten-3 {
+ background-color: #A5D6A7 !important;
+}
+
+.green-text.text-lighten-3 {
+ color: #A5D6A7 !important;
+}
+
+.green.lighten-2 {
+ background-color: #81C784 !important;
+}
+
+.green-text.text-lighten-2 {
+ color: #81C784 !important;
+}
+
+.green.lighten-1 {
+ background-color: #66BB6A !important;
+}
+
+.green-text.text-lighten-1 {
+ color: #66BB6A !important;
+}
+
+.green.darken-1 {
+ background-color: #43A047 !important;
+}
+
+.green-text.text-darken-1 {
+ color: #43A047 !important;
+}
+
+.green.darken-2 {
+ background-color: #388E3C !important;
+}
+
+.green-text.text-darken-2 {
+ color: #388E3C !important;
+}
+
+.green.darken-3 {
+ background-color: #2E7D32 !important;
+}
+
+.green-text.text-darken-3 {
+ color: #2E7D32 !important;
+}
+
+.green.darken-4 {
+ background-color: #1B5E20 !important;
+}
+
+.green-text.text-darken-4 {
+ color: #1B5E20 !important;
+}
+
+.green.accent-1 {
+ background-color: #B9F6CA !important;
+}
+
+.green-text.text-accent-1 {
+ color: #B9F6CA !important;
+}
+
+.green.accent-2 {
+ background-color: #69F0AE !important;
+}
+
+.green-text.text-accent-2 {
+ color: #69F0AE !important;
+}
+
+.green.accent-3 {
+ background-color: #00E676 !important;
+}
+
+.green-text.text-accent-3 {
+ color: #00E676 !important;
+}
+
+.green.accent-4 {
+ background-color: #00C853 !important;
+}
+
+.green-text.text-accent-4 {
+ color: #00C853 !important;
+}
+
+.light-green {
+ background-color: #8bc34a !important;
+}
+
+.light-green-text {
+ color: #8bc34a !important;
+}
+
+.light-green.lighten-5 {
+ background-color: #f1f8e9 !important;
+}
+
+.light-green-text.text-lighten-5 {
+ color: #f1f8e9 !important;
+}
+
+.light-green.lighten-4 {
+ background-color: #dcedc8 !important;
+}
+
+.light-green-text.text-lighten-4 {
+ color: #dcedc8 !important;
+}
+
+.light-green.lighten-3 {
+ background-color: #c5e1a5 !important;
+}
+
+.light-green-text.text-lighten-3 {
+ color: #c5e1a5 !important;
+}
+
+.light-green.lighten-2 {
+ background-color: #aed581 !important;
+}
+
+.light-green-text.text-lighten-2 {
+ color: #aed581 !important;
+}
+
+.light-green.lighten-1 {
+ background-color: #9ccc65 !important;
+}
+
+.light-green-text.text-lighten-1 {
+ color: #9ccc65 !important;
+}
+
+.light-green.darken-1 {
+ background-color: #7cb342 !important;
+}
+
+.light-green-text.text-darken-1 {
+ color: #7cb342 !important;
+}
+
+.light-green.darken-2 {
+ background-color: #689f38 !important;
+}
+
+.light-green-text.text-darken-2 {
+ color: #689f38 !important;
+}
+
+.light-green.darken-3 {
+ background-color: #558b2f !important;
+}
+
+.light-green-text.text-darken-3 {
+ color: #558b2f !important;
+}
+
+.light-green.darken-4 {
+ background-color: #33691e !important;
+}
+
+.light-green-text.text-darken-4 {
+ color: #33691e !important;
+}
+
+.light-green.accent-1 {
+ background-color: #ccff90 !important;
+}
+
+.light-green-text.text-accent-1 {
+ color: #ccff90 !important;
+}
+
+.light-green.accent-2 {
+ background-color: #b2ff59 !important;
+}
+
+.light-green-text.text-accent-2 {
+ color: #b2ff59 !important;
+}
+
+.light-green.accent-3 {
+ background-color: #76ff03 !important;
+}
+
+.light-green-text.text-accent-3 {
+ color: #76ff03 !important;
+}
+
+.light-green.accent-4 {
+ background-color: #64dd17 !important;
+}
+
+.light-green-text.text-accent-4 {
+ color: #64dd17 !important;
+}
+
+.lime {
+ background-color: #cddc39 !important;
+}
+
+.lime-text {
+ color: #cddc39 !important;
+}
+
+.lime.lighten-5 {
+ background-color: #f9fbe7 !important;
+}
+
+.lime-text.text-lighten-5 {
+ color: #f9fbe7 !important;
+}
+
+.lime.lighten-4 {
+ background-color: #f0f4c3 !important;
+}
+
+.lime-text.text-lighten-4 {
+ color: #f0f4c3 !important;
+}
+
+.lime.lighten-3 {
+ background-color: #e6ee9c !important;
+}
+
+.lime-text.text-lighten-3 {
+ color: #e6ee9c !important;
+}
+
+.lime.lighten-2 {
+ background-color: #dce775 !important;
+}
+
+.lime-text.text-lighten-2 {
+ color: #dce775 !important;
+}
+
+.lime.lighten-1 {
+ background-color: #d4e157 !important;
+}
+
+.lime-text.text-lighten-1 {
+ color: #d4e157 !important;
+}
+
+.lime.darken-1 {
+ background-color: #c0ca33 !important;
+}
+
+.lime-text.text-darken-1 {
+ color: #c0ca33 !important;
+}
+
+.lime.darken-2 {
+ background-color: #afb42b !important;
+}
+
+.lime-text.text-darken-2 {
+ color: #afb42b !important;
+}
+
+.lime.darken-3 {
+ background-color: #9e9d24 !important;
+}
+
+.lime-text.text-darken-3 {
+ color: #9e9d24 !important;
+}
+
+.lime.darken-4 {
+ background-color: #827717 !important;
+}
+
+.lime-text.text-darken-4 {
+ color: #827717 !important;
+}
+
+.lime.accent-1 {
+ background-color: #f4ff81 !important;
+}
+
+.lime-text.text-accent-1 {
+ color: #f4ff81 !important;
+}
+
+.lime.accent-2 {
+ background-color: #eeff41 !important;
+}
+
+.lime-text.text-accent-2 {
+ color: #eeff41 !important;
+}
+
+.lime.accent-3 {
+ background-color: #c6ff00 !important;
+}
+
+.lime-text.text-accent-3 {
+ color: #c6ff00 !important;
+}
+
+.lime.accent-4 {
+ background-color: #aeea00 !important;
+}
+
+.lime-text.text-accent-4 {
+ color: #aeea00 !important;
+}
+
+.yellow {
+ background-color: #ffeb3b !important;
+}
+
+.yellow-text {
+ color: #ffeb3b !important;
+}
+
+.yellow.lighten-5 {
+ background-color: #fffde7 !important;
+}
+
+.yellow-text.text-lighten-5 {
+ color: #fffde7 !important;
+}
+
+.yellow.lighten-4 {
+ background-color: #fff9c4 !important;
+}
+
+.yellow-text.text-lighten-4 {
+ color: #fff9c4 !important;
+}
+
+.yellow.lighten-3 {
+ background-color: #fff59d !important;
+}
+
+.yellow-text.text-lighten-3 {
+ color: #fff59d !important;
+}
+
+.yellow.lighten-2 {
+ background-color: #fff176 !important;
+}
+
+.yellow-text.text-lighten-2 {
+ color: #fff176 !important;
+}
+
+.yellow.lighten-1 {
+ background-color: #ffee58 !important;
+}
+
+.yellow-text.text-lighten-1 {
+ color: #ffee58 !important;
+}
+
+.yellow.darken-1 {
+ background-color: #fdd835 !important;
+}
+
+.yellow-text.text-darken-1 {
+ color: #fdd835 !important;
+}
+
+.yellow.darken-2 {
+ background-color: #fbc02d !important;
+}
+
+.yellow-text.text-darken-2 {
+ color: #fbc02d !important;
+}
+
+.yellow.darken-3 {
+ background-color: #f9a825 !important;
+}
+
+.yellow-text.text-darken-3 {
+ color: #f9a825 !important;
+}
+
+.yellow.darken-4 {
+ background-color: #f57f17 !important;
+}
+
+.yellow-text.text-darken-4 {
+ color: #f57f17 !important;
+}
+
+.yellow.accent-1 {
+ background-color: #ffff8d !important;
+}
+
+.yellow-text.text-accent-1 {
+ color: #ffff8d !important;
+}
+
+.yellow.accent-2 {
+ background-color: #ffff00 !important;
+}
+
+.yellow-text.text-accent-2 {
+ color: #ffff00 !important;
+}
+
+.yellow.accent-3 {
+ background-color: #ffea00 !important;
+}
+
+.yellow-text.text-accent-3 {
+ color: #ffea00 !important;
+}
+
+.yellow.accent-4 {
+ background-color: #ffd600 !important;
+}
+
+.yellow-text.text-accent-4 {
+ color: #ffd600 !important;
+}
+
+.amber {
+ background-color: #ffc107 !important;
+}
+
+.amber-text {
+ color: #ffc107 !important;
+}
+
+.amber.lighten-5 {
+ background-color: #fff8e1 !important;
+}
+
+.amber-text.text-lighten-5 {
+ color: #fff8e1 !important;
+}
+
+.amber.lighten-4 {
+ background-color: #ffecb3 !important;
+}
+
+.amber-text.text-lighten-4 {
+ color: #ffecb3 !important;
+}
+
+.amber.lighten-3 {
+ background-color: #ffe082 !important;
+}
+
+.amber-text.text-lighten-3 {
+ color: #ffe082 !important;
+}
+
+.amber.lighten-2 {
+ background-color: #ffd54f !important;
+}
+
+.amber-text.text-lighten-2 {
+ color: #ffd54f !important;
+}
+
+.amber.lighten-1 {
+ background-color: #ffca28 !important;
+}
+
+.amber-text.text-lighten-1 {
+ color: #ffca28 !important;
+}
+
+.amber.darken-1 {
+ background-color: #ffb300 !important;
+}
+
+.amber-text.text-darken-1 {
+ color: #ffb300 !important;
+}
+
+.amber.darken-2 {
+ background-color: #ffa000 !important;
+}
+
+.amber-text.text-darken-2 {
+ color: #ffa000 !important;
+}
+
+.amber.darken-3 {
+ background-color: #ff8f00 !important;
+}
+
+.amber-text.text-darken-3 {
+ color: #ff8f00 !important;
+}
+
+.amber.darken-4 {
+ background-color: #ff6f00 !important;
+}
+
+.amber-text.text-darken-4 {
+ color: #ff6f00 !important;
+}
+
+.amber.accent-1 {
+ background-color: #ffe57f !important;
+}
+
+.amber-text.text-accent-1 {
+ color: #ffe57f !important;
+}
+
+.amber.accent-2 {
+ background-color: #ffd740 !important;
+}
+
+.amber-text.text-accent-2 {
+ color: #ffd740 !important;
+}
+
+.amber.accent-3 {
+ background-color: #ffc400 !important;
+}
+
+.amber-text.text-accent-3 {
+ color: #ffc400 !important;
+}
+
+.amber.accent-4 {
+ background-color: #ffab00 !important;
+}
+
+.amber-text.text-accent-4 {
+ color: #ffab00 !important;
+}
+
+.orange {
+ background-color: #ff9800 !important;
+}
+
+.orange-text {
+ color: #ff9800 !important;
+}
+
+.orange.lighten-5 {
+ background-color: #fff3e0 !important;
+}
+
+.orange-text.text-lighten-5 {
+ color: #fff3e0 !important;
+}
+
+.orange.lighten-4 {
+ background-color: #ffe0b2 !important;
+}
+
+.orange-text.text-lighten-4 {
+ color: #ffe0b2 !important;
+}
+
+.orange.lighten-3 {
+ background-color: #ffcc80 !important;
+}
+
+.orange-text.text-lighten-3 {
+ color: #ffcc80 !important;
+}
+
+.orange.lighten-2 {
+ background-color: #ffb74d !important;
+}
+
+.orange-text.text-lighten-2 {
+ color: #ffb74d !important;
+}
+
+.orange.lighten-1 {
+ background-color: #ffa726 !important;
+}
+
+.orange-text.text-lighten-1 {
+ color: #ffa726 !important;
+}
+
+.orange.darken-1 {
+ background-color: #fb8c00 !important;
+}
+
+.orange-text.text-darken-1 {
+ color: #fb8c00 !important;
+}
+
+.orange.darken-2 {
+ background-color: #f57c00 !important;
+}
+
+.orange-text.text-darken-2 {
+ color: #f57c00 !important;
+}
+
+.orange.darken-3 {
+ background-color: #ef6c00 !important;
+}
+
+.orange-text.text-darken-3 {
+ color: #ef6c00 !important;
+}
+
+.orange.darken-4 {
+ background-color: #e65100 !important;
+}
+
+.orange-text.text-darken-4 {
+ color: #e65100 !important;
+}
+
+.orange.accent-1 {
+ background-color: #ffd180 !important;
+}
+
+.orange-text.text-accent-1 {
+ color: #ffd180 !important;
+}
+
+.orange.accent-2 {
+ background-color: #ffab40 !important;
+}
+
+.orange-text.text-accent-2 {
+ color: #ffab40 !important;
+}
+
+.orange.accent-3 {
+ background-color: #ff9100 !important;
+}
+
+.orange-text.text-accent-3 {
+ color: #ff9100 !important;
+}
+
+.orange.accent-4 {
+ background-color: #ff6d00 !important;
+}
+
+.orange-text.text-accent-4 {
+ color: #ff6d00 !important;
+}
+
+.deep-orange {
+ background-color: #ff5722 !important;
+}
+
+.deep-orange-text {
+ color: #ff5722 !important;
+}
+
+.deep-orange.lighten-5 {
+ background-color: #fbe9e7 !important;
+}
+
+.deep-orange-text.text-lighten-5 {
+ color: #fbe9e7 !important;
+}
+
+.deep-orange.lighten-4 {
+ background-color: #ffccbc !important;
+}
+
+.deep-orange-text.text-lighten-4 {
+ color: #ffccbc !important;
+}
+
+.deep-orange.lighten-3 {
+ background-color: #ffab91 !important;
+}
+
+.deep-orange-text.text-lighten-3 {
+ color: #ffab91 !important;
+}
+
+.deep-orange.lighten-2 {
+ background-color: #ff8a65 !important;
+}
+
+.deep-orange-text.text-lighten-2 {
+ color: #ff8a65 !important;
+}
+
+.deep-orange.lighten-1 {
+ background-color: #ff7043 !important;
+}
+
+.deep-orange-text.text-lighten-1 {
+ color: #ff7043 !important;
+}
+
+.deep-orange.darken-1 {
+ background-color: #f4511e !important;
+}
+
+.deep-orange-text.text-darken-1 {
+ color: #f4511e !important;
+}
+
+.deep-orange.darken-2 {
+ background-color: #e64a19 !important;
+}
+
+.deep-orange-text.text-darken-2 {
+ color: #e64a19 !important;
+}
+
+.deep-orange.darken-3 {
+ background-color: #d84315 !important;
+}
+
+.deep-orange-text.text-darken-3 {
+ color: #d84315 !important;
+}
+
+.deep-orange.darken-4 {
+ background-color: #bf360c !important;
+}
+
+.deep-orange-text.text-darken-4 {
+ color: #bf360c !important;
+}
+
+.deep-orange.accent-1 {
+ background-color: #ff9e80 !important;
+}
+
+.deep-orange-text.text-accent-1 {
+ color: #ff9e80 !important;
+}
+
+.deep-orange.accent-2 {
+ background-color: #ff6e40 !important;
+}
+
+.deep-orange-text.text-accent-2 {
+ color: #ff6e40 !important;
+}
+
+.deep-orange.accent-3 {
+ background-color: #ff3d00 !important;
+}
+
+.deep-orange-text.text-accent-3 {
+ color: #ff3d00 !important;
+}
+
+.deep-orange.accent-4 {
+ background-color: #dd2c00 !important;
+}
+
+.deep-orange-text.text-accent-4 {
+ color: #dd2c00 !important;
+}
+
+.brown {
+ background-color: #795548 !important;
+}
+
+.brown-text {
+ color: #795548 !important;
+}
+
+.brown.lighten-5 {
+ background-color: #efebe9 !important;
+}
+
+.brown-text.text-lighten-5 {
+ color: #efebe9 !important;
+}
+
+.brown.lighten-4 {
+ background-color: #d7ccc8 !important;
+}
+
+.brown-text.text-lighten-4 {
+ color: #d7ccc8 !important;
+}
+
+.brown.lighten-3 {
+ background-color: #bcaaa4 !important;
+}
+
+.brown-text.text-lighten-3 {
+ color: #bcaaa4 !important;
+}
+
+.brown.lighten-2 {
+ background-color: #a1887f !important;
+}
+
+.brown-text.text-lighten-2 {
+ color: #a1887f !important;
+}
+
+.brown.lighten-1 {
+ background-color: #8d6e63 !important;
+}
+
+.brown-text.text-lighten-1 {
+ color: #8d6e63 !important;
+}
+
+.brown.darken-1 {
+ background-color: #6d4c41 !important;
+}
+
+.brown-text.text-darken-1 {
+ color: #6d4c41 !important;
+}
+
+.brown.darken-2 {
+ background-color: #5d4037 !important;
+}
+
+.brown-text.text-darken-2 {
+ color: #5d4037 !important;
+}
+
+.brown.darken-3 {
+ background-color: #4e342e !important;
+}
+
+.brown-text.text-darken-3 {
+ color: #4e342e !important;
+}
+
+.brown.darken-4 {
+ background-color: #3e2723 !important;
+}
+
+.brown-text.text-darken-4 {
+ color: #3e2723 !important;
+}
+
+.blue-grey {
+ background-color: #607d8b !important;
+}
+
+.blue-grey-text {
+ color: #607d8b !important;
+}
+
+.blue-grey.lighten-5 {
+ background-color: #eceff1 !important;
+}
+
+.blue-grey-text.text-lighten-5 {
+ color: #eceff1 !important;
+}
+
+.blue-grey.lighten-4 {
+ background-color: #cfd8dc !important;
+}
+
+.blue-grey-text.text-lighten-4 {
+ color: #cfd8dc !important;
+}
+
+.blue-grey.lighten-3 {
+ background-color: #b0bec5 !important;
+}
+
+.blue-grey-text.text-lighten-3 {
+ color: #b0bec5 !important;
+}
+
+.blue-grey.lighten-2 {
+ background-color: #90a4ae !important;
+}
+
+.blue-grey-text.text-lighten-2 {
+ color: #90a4ae !important;
+}
+
+.blue-grey.lighten-1 {
+ background-color: #78909c !important;
+}
+
+.blue-grey-text.text-lighten-1 {
+ color: #78909c !important;
+}
+
+.blue-grey.darken-1 {
+ background-color: #546e7a !important;
+}
+
+.blue-grey-text.text-darken-1 {
+ color: #546e7a !important;
+}
+
+.blue-grey.darken-2 {
+ background-color: #455a64 !important;
+}
+
+.blue-grey-text.text-darken-2 {
+ color: #455a64 !important;
+}
+
+.blue-grey.darken-3 {
+ background-color: #37474f !important;
+}
+
+.blue-grey-text.text-darken-3 {
+ color: #37474f !important;
+}
+
+.blue-grey.darken-4 {
+ background-color: #263238 !important;
+}
+
+.blue-grey-text.text-darken-4 {
+ color: #263238 !important;
+}
+
+.grey {
+ background-color: #9e9e9e !important;
+}
+
+.grey-text {
+ color: #9e9e9e !important;
+}
+
+.grey.lighten-5 {
+ background-color: #fafafa !important;
+}
+
+.grey-text.text-lighten-5 {
+ color: #fafafa !important;
+}
+
+.grey.lighten-4 {
+ background-color: #f5f5f5 !important;
+}
+
+.grey-text.text-lighten-4 {
+ color: #f5f5f5 !important;
+}
+
+.grey.lighten-3 {
+ background-color: #eeeeee !important;
+}
+
+.grey-text.text-lighten-3 {
+ color: #eeeeee !important;
+}
+
+.grey.lighten-2 {
+ background-color: #e0e0e0 !important;
+}
+
+.grey-text.text-lighten-2 {
+ color: #e0e0e0 !important;
+}
+
+.grey.lighten-1 {
+ background-color: #bdbdbd !important;
+}
+
+.grey-text.text-lighten-1 {
+ color: #bdbdbd !important;
+}
+
+.grey.darken-1 {
+ background-color: #757575 !important;
+}
+
+.grey-text.text-darken-1 {
+ color: #757575 !important;
+}
+
+.grey.darken-2 {
+ background-color: #616161 !important;
+}
+
+.grey-text.text-darken-2 {
+ color: #616161 !important;
+}
+
+.grey.darken-3 {
+ background-color: #424242 !important;
+}
+
+.grey-text.text-darken-3 {
+ color: #424242 !important;
+}
+
+.grey.darken-4 {
+ background-color: #212121 !important;
+}
+
+.grey-text.text-darken-4 {
+ color: #212121 !important;
+}
+
+.black {
+ background-color: #000000 !important;
+}
+
+.black-text {
+ color: #000000 !important;
+}
+
+.white {
+ background-color: #FFFFFF !important;
+}
+
+.white-text {
+ color: #FFFFFF !important;
+}
+
+.transparent {
+ background-color: transparent !important;
+}
+
+.transparent-text {
+ color: transparent !important;
+}
+
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS and IE text size adjust after device orientation change,
+ * without disabling user zoom.
+ */
+html {
+ font-family: sans-serif;
+ /* 1 */
+ -ms-text-size-adjust: 100%;
+ /* 2 */
+ -webkit-text-size-adjust: 100%;
+ /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+body {
+ margin: 0;
+}
+
+/* HTML5 display definitions
+ ========================================================================== */
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11
+ * and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+audio,
+canvas,
+progress,
+video {
+ display: inline-block;
+ /* 1 */
+ vertical-align: baseline;
+ /* 2 */
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
+ */
+[hidden],
+template {
+ display: none;
+}
+
+/* Links
+ ========================================================================== */
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+a {
+ background-color: transparent;
+}
+
+/**
+ * Improve readability of focused elements when they are also in an
+ * active/hover state.
+ */
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* Text-level semantics
+ ========================================================================== */
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* Embedded content
+ ========================================================================== */
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+img {
+ border: 0;
+}
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* Grouping content
+ ========================================================================== */
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+figure {
+ margin: 1em 40px;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+hr {
+ box-sizing: content-box;
+ height: 0;
+}
+
+/**
+ * Contain overflow in all browsers.
+ */
+pre {
+ overflow: auto;
+}
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+/* Forms
+ ========================================================================== */
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit;
+ /* 1 */
+ font: inherit;
+ /* 2 */
+ margin: 0;
+ /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button;
+ /* 2 */
+ cursor: pointer;
+ /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+input {
+ line-height: normal;
+}
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box;
+ /* 1 */
+ padding: 0;
+ /* 2 */
+}
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
+ */
+input[type="search"] {
+ -webkit-appearance: textfield;
+ /* 1 */
+ box-sizing: content-box;
+ /* 2 */
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+legend {
+ border: 0;
+ /* 1 */
+ padding: 0;
+ /* 2 */
+}
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+textarea {
+ overflow: auto;
+}
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+optgroup {
+ font-weight: bold;
+}
+
+/* Tables
+ ========================================================================== */
+/**
+ * Remove most spacing between table cells.
+ */
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+td,
+th {
+ padding: 0;
+}
+
+html {
+ box-sizing: border-box;
+}
+
+*, *:before, *:after {
+ box-sizing: inherit;
+}
+
+ul:not(.browser-default) {
+ padding-left: 0;
+ list-style-type: none;
+}
+
+ul:not(.browser-default) li {
+ list-style-type: none;
+}
+
+a {
+ color: #039be5;
+ text-decoration: none;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.valign-wrapper {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.valign-wrapper .valign {
+ display: block;
+}
+
+.clearfix {
+ clear: both;
+}
+
+.z-depth-0 {
+ box-shadow: none !important;
+}
+
+.z-depth-1, nav, .card-panel, .card, .toast, .btn, .btn-large, .btn-floating, .dropdown-content, .collapsible, .side-nav {
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
+}
+
+.z-depth-1-half, .btn:hover, .btn-large:hover, .btn-floating:hover {
+ box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14), 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2);
+}
+
+.z-depth-2 {
+ box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
+}
+
+.z-depth-3 {
+ box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.3);
+}
+
+.z-depth-4, .modal {
+ box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.3);
+}
+
+.z-depth-5 {
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.3);
+}
+
+.hoverable {
+ transition: box-shadow .25s;
+ box-shadow: 0;
+}
+
+.hoverable:hover {
+ transition: box-shadow .25s;
+ box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+}
+
+.divider {
+ height: 1px;
+ overflow: hidden;
+ background-color: #e0e0e0;
+}
+
+blockquote {
+ margin: 20px 0;
+ padding-left: 1.5rem;
+ border-left: 5px solid #ee6e73;
+}
+
+i {
+ line-height: inherit;
+}
+
+i.left {
+ float: left;
+ margin-right: 15px;
+}
+
+i.right {
+ float: right;
+ margin-left: 15px;
+}
+
+i.tiny {
+ font-size: 1rem;
+}
+
+i.small {
+ font-size: 2rem;
+}
+
+i.medium {
+ font-size: 4rem;
+}
+
+i.large {
+ font-size: 6rem;
+}
+
+img.responsive-img,
+video.responsive-video {
+ max-width: 100%;
+ height: auto;
+}
+
+.pagination li {
+ display: inline-block;
+ border-radius: 2px;
+ text-align: center;
+ vertical-align: top;
+ height: 30px;
+}
+
+.pagination li a {
+ color: #444;
+ display: inline-block;
+ font-size: 1.2rem;
+ padding: 0 10px;
+ line-height: 30px;
+}
+
+.pagination li.active a {
+ color: #fff;
+}
+
+.pagination li.active {
+ background-color: #ee6e73;
+}
+
+.pagination li.disabled a {
+ cursor: default;
+ color: #999;
+}
+
+.pagination li i {
+ font-size: 2rem;
+}
+
+.pagination li.pages ul li {
+ display: inline-block;
+ float: none;
+}
+
+@media only screen and (max-width: 992px) {
+ .pagination {
+ width: 100%;
+ }
+ .pagination li.prev,
+ .pagination li.next {
+ width: 10%;
+ }
+ .pagination li.pages {
+ width: 80%;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+}
+
+.breadcrumb {
+ font-size: 18px;
+ color: rgba(255, 255, 255, 0.7);
+}
+
+.breadcrumb i,
+.breadcrumb [class^="mdi-"], .breadcrumb [class*="mdi-"],
+.breadcrumb i.material-icons {
+ display: inline-block;
+ float: left;
+ font-size: 24px;
+}
+
+.breadcrumb:before {
+ content: '\E5CC';
+ color: rgba(255, 255, 255, 0.7);
+ vertical-align: top;
+ display: inline-block;
+ font-family: 'Material Icons';
+ font-weight: normal;
+ font-style: normal;
+ font-size: 25px;
+ margin: 0 10px 0 8px;
+ -webkit-font-smoothing: antialiased;
+}
+
+.breadcrumb:first-child:before {
+ display: none;
+}
+
+.breadcrumb:last-child {
+ color: #fff;
+}
+
+.parallax-container {
+ position: relative;
+ overflow: hidden;
+ height: 500px;
+}
+
+.parallax {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+}
+
+.parallax img {
+ display: none;
+ position: absolute;
+ left: 50%;
+ bottom: 0;
+ min-width: 100%;
+ min-height: 100%;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+}
+
+.pin-top, .pin-bottom {
+ position: relative;
+}
+
+.pinned {
+ position: fixed !important;
+}
+
+/*********************
+ Transition Classes
+**********************/
+ul.staggered-list li {
+ opacity: 0;
+}
+
+.fade-in {
+ opacity: 0;
+ -webkit-transform-origin: 0 50%;
+ transform-origin: 0 50%;
+}
+
+/*********************
+ Media Query Classes
+**********************/
+@media only screen and (max-width: 600px) {
+ .hide-on-small-only, .hide-on-small-and-down {
+ display: none !important;
+ }
+}
+
+@media only screen and (max-width: 992px) {
+ .hide-on-med-and-down {
+ display: none !important;
+ }
+}
+
+@media only screen and (min-width: 601px) {
+ .hide-on-med-and-up {
+ display: none !important;
+ }
+}
+
+@media only screen and (min-width: 600px) and (max-width: 992px) {
+ .hide-on-med-only {
+ display: none !important;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ .hide-on-large-only {
+ display: none !important;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ .show-on-large {
+ display: block !important;
+ }
+}
+
+@media only screen and (min-width: 600px) and (max-width: 992px) {
+ .show-on-medium {
+ display: block !important;
+ }
+}
+
+@media only screen and (max-width: 600px) {
+ .show-on-small {
+ display: block !important;
+ }
+}
+
+@media only screen and (min-width: 601px) {
+ .show-on-medium-and-up {
+ display: block !important;
+ }
+}
+
+@media only screen and (max-width: 992px) {
+ .show-on-medium-and-down {
+ display: block !important;
+ }
+}
+
+@media only screen and (max-width: 600px) {
+ .center-on-small-only {
+ text-align: center;
+ }
+}
+
+footer.page-footer {
+ padding-top: 20px;
+ background-color: #ee6e73;
+}
+
+footer.page-footer .footer-copyright {
+ overflow: hidden;
+ min-height: 50px;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -ms-flex-align: center;
+ align-items: center;
+ padding: 10px 0px;
+ color: rgba(255, 255, 255, 0.8);
+ background-color: rgba(51, 51, 51, 0.08);
+}
+
+table, th, td {
+ border: none;
+}
+
+table {
+ width: 100%;
+ display: table;
+}
+
+table.bordered > thead > tr,
+table.bordered > tbody > tr {
+ border-bottom: 1px solid #d0d0d0;
+}
+
+table.striped > tbody > tr:nth-child(odd) {
+ background-color: #f2f2f2;
+}
+
+table.striped > tbody > tr > td {
+ border-radius: 0;
+}
+
+table.highlight > tbody > tr {
+ transition: background-color .25s ease;
+}
+
+table.highlight > tbody > tr:hover {
+ background-color: #f2f2f2;
+}
+
+table.centered thead tr th, table.centered tbody tr td {
+ text-align: center;
+}
+
+thead {
+ border-bottom: 1px solid #d0d0d0;
+}
+
+td, th {
+ padding: 15px 5px;
+ display: table-cell;
+ text-align: left;
+ vertical-align: middle;
+ border-radius: 2px;
+}
+
+@media only screen and (max-width: 992px) {
+ table.responsive-table {
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ display: block;
+ position: relative;
+ /* sort out borders */
+ }
+ table.responsive-table td:empty:before {
+ content: '\00a0';
+ }
+ table.responsive-table th,
+ table.responsive-table td {
+ margin: 0;
+ vertical-align: top;
+ }
+ table.responsive-table th {
+ text-align: left;
+ }
+ table.responsive-table thead {
+ display: block;
+ float: left;
+ }
+ table.responsive-table thead tr {
+ display: block;
+ padding: 0 10px 0 0;
+ }
+ table.responsive-table thead tr th::before {
+ content: "\00a0";
+ }
+ table.responsive-table tbody {
+ display: block;
+ width: auto;
+ position: relative;
+ overflow-x: auto;
+ white-space: nowrap;
+ }
+ table.responsive-table tbody tr {
+ display: inline-block;
+ vertical-align: top;
+ }
+ table.responsive-table th {
+ display: block;
+ text-align: right;
+ }
+ table.responsive-table td {
+ display: block;
+ min-height: 1.25em;
+ text-align: left;
+ }
+ table.responsive-table tr {
+ padding: 0 10px;
+ }
+ table.responsive-table thead {
+ border: 0;
+ border-right: 1px solid #d0d0d0;
+ }
+ table.responsive-table.bordered th {
+ border-bottom: 0;
+ border-left: 0;
+ }
+ table.responsive-table.bordered td {
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+ }
+ table.responsive-table.bordered tr {
+ border: 0;
+ }
+ table.responsive-table.bordered tbody tr {
+ border-right: 1px solid #d0d0d0;
+ }
+}
+
+.collection {
+ margin: 0.5rem 0 1rem 0;
+ border: 1px solid #e0e0e0;
+ border-radius: 2px;
+ overflow: hidden;
+ position: relative;
+}
+
+.collection .collection-item {
+ background-color: #fff;
+ line-height: 1.5rem;
+ padding: 10px 20px;
+ margin: 0;
+ border-bottom: 1px solid #e0e0e0;
+}
+
+.collection .collection-item.avatar {
+ min-height: 84px;
+ padding-left: 72px;
+ position: relative;
+}
+
+.collection .collection-item.avatar .circle {
+ position: absolute;
+ width: 42px;
+ height: 42px;
+ overflow: hidden;
+ left: 15px;
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.collection .collection-item.avatar i.circle {
+ font-size: 18px;
+ line-height: 42px;
+ color: #fff;
+ background-color: #999;
+ text-align: center;
+}
+
+.collection .collection-item.avatar .title {
+ font-size: 16px;
+}
+
+.collection .collection-item.avatar p {
+ margin: 0;
+}
+
+.collection .collection-item.avatar .secondary-content {
+ position: absolute;
+ top: 16px;
+ right: 16px;
+}
+
+.collection .collection-item:last-child {
+ border-bottom: none;
+}
+
+.collection .collection-item.active {
+ background-color: #26a69a;
+ color: #eafaf9;
+}
+
+.collection .collection-item.active .secondary-content {
+ color: #fff;
+}
+
+.collection a.collection-item {
+ display: block;
+ transition: .25s;
+ color: #26a69a;
+}
+
+.collection a.collection-item:not(.active):hover {
+ background-color: #ddd;
+}
+
+.collection.with-header .collection-header {
+ background-color: #fff;
+ border-bottom: 1px solid #e0e0e0;
+ padding: 10px 20px;
+}
+
+.collection.with-header .collection-item {
+ padding-left: 30px;
+}
+
+.collection.with-header .collection-item.avatar {
+ padding-left: 72px;
+}
+
+.secondary-content {
+ float: right;
+ color: #26a69a;
+}
+
+.collapsible .collection {
+ margin: 0;
+ border: none;
+}
+
+.video-container {
+ position: relative;
+ padding-bottom: 56.25%;
+ height: 0;
+ overflow: hidden;
+}
+
+.video-container iframe, .video-container object, .video-container embed {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.progress {
+ position: relative;
+ height: 4px;
+ display: block;
+ width: 100%;
+ background-color: #acece6;
+ border-radius: 2px;
+ margin: 0.5rem 0 1rem 0;
+ overflow: hidden;
+}
+
+.progress .determinate {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ background-color: #26a69a;
+ transition: width .3s linear;
+}
+
+.progress .indeterminate {
+ background-color: #26a69a;
+}
+
+.progress .indeterminate:before {
+ content: '';
+ position: absolute;
+ background-color: inherit;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ will-change: left, right;
+ -webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
+ animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
+}
+
+.progress .indeterminate:after {
+ content: '';
+ position: absolute;
+ background-color: inherit;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ will-change: left, right;
+ -webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
+ animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
+ -webkit-animation-delay: 1.15s;
+ animation-delay: 1.15s;
+}
+
+@-webkit-keyframes indeterminate {
+ 0% {
+ left: -35%;
+ right: 100%;
+ }
+ 60% {
+ left: 100%;
+ right: -90%;
+ }
+ 100% {
+ left: 100%;
+ right: -90%;
+ }
+}
+
+@keyframes indeterminate {
+ 0% {
+ left: -35%;
+ right: 100%;
+ }
+ 60% {
+ left: 100%;
+ right: -90%;
+ }
+ 100% {
+ left: 100%;
+ right: -90%;
+ }
+}
+
+@-webkit-keyframes indeterminate-short {
+ 0% {
+ left: -200%;
+ right: 100%;
+ }
+ 60% {
+ left: 107%;
+ right: -8%;
+ }
+ 100% {
+ left: 107%;
+ right: -8%;
+ }
+}
+
+@keyframes indeterminate-short {
+ 0% {
+ left: -200%;
+ right: 100%;
+ }
+ 60% {
+ left: 107%;
+ right: -8%;
+ }
+ 100% {
+ left: 107%;
+ right: -8%;
+ }
+}
+
+/*******************
+ Utility Classes
+*******************/
+.hide {
+ display: none !important;
+}
+
+.left-align {
+ text-align: left;
+}
+
+.right-align {
+ text-align: right;
+}
+
+.center, .center-align {
+ text-align: center;
+}
+
+.left {
+ float: left !important;
+}
+
+.right {
+ float: right !important;
+}
+
+.no-select, input[type=range],
+input[type=range] + .thumb {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.circle {
+ border-radius: 50%;
+}
+
+.center-block {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.truncate {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.no-padding {
+ padding: 0 !important;
+}
+
+span.badge {
+ min-width: 3rem;
+ padding: 0 6px;
+ margin-left: 14px;
+ text-align: center;
+ font-size: 1rem;
+ line-height: 22px;
+ height: 22px;
+ color: #757575;
+ float: right;
+ box-sizing: border-box;
+}
+
+span.badge.new {
+ font-weight: 300;
+ font-size: 0.8rem;
+ color: #fff;
+ background-color: #26a69a;
+ border-radius: 2px;
+}
+
+span.badge.new:after {
+ content: " new";
+}
+
+span.badge[data-badge-caption]::after {
+ content: " " attr(data-badge-caption);
+}
+
+nav ul a span.badge {
+ display: inline-block;
+ float: none;
+ margin-left: 4px;
+ line-height: 22px;
+ height: 22px;
+}
+
+.collection-item span.badge {
+ margin-top: calc(0.75rem - 11px);
+}
+
+.collapsible span.badge {
+ margin-top: calc(1.5rem - 11px);
+}
+
+.side-nav span.badge {
+ margin-top: calc(24px - 11px);
+}
+
+/* This is needed for some mobile phones to display the Google Icon font properly */
+.material-icons {
+ text-rendering: optimizeLegibility;
+ -webkit-font-feature-settings: 'liga';
+ -moz-font-feature-settings: 'liga';
+ font-feature-settings: 'liga';
+}
+
+.container {
+ margin: 0 auto;
+ max-width: 1280px;
+ width: 90%;
+}
+
+@media only screen and (min-width: 601px) {
+ .container {
+ width: 85%;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ .container {
+ width: 70%;
+ }
+}
+
+.container .row {
+ margin-left: -0.75rem;
+ margin-right: -0.75rem;
+}
+
+.section {
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+}
+
+.section.no-pad {
+ padding: 0;
+}
+
+.section.no-pad-bot {
+ padding-bottom: 0;
+}
+
+.section.no-pad-top {
+ padding-top: 0;
+}
+
+.row {
+ margin-left: auto;
+ margin-right: auto;
+ margin-bottom: 20px;
+}
+
+.row:after {
+ content: "";
+ display: table;
+ clear: both;
+}
+
+.row .col {
+ float: left;
+ box-sizing: border-box;
+ padding: 0 0.75rem;
+ min-height: 1px;
+}
+
+.row .col[class*="push-"], .row .col[class*="pull-"] {
+ position: relative;
+}
+
+.row .col.s1 {
+ width: 8.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s2 {
+ width: 16.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s3 {
+ width: 25%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s4 {
+ width: 33.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s5 {
+ width: 41.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s6 {
+ width: 50%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s7 {
+ width: 58.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s8 {
+ width: 66.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s9 {
+ width: 75%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s10 {
+ width: 83.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s11 {
+ width: 91.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.s12 {
+ width: 100%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+}
+
+.row .col.offset-s1 {
+ margin-left: 8.3333333333%;
+}
+
+.row .col.pull-s1 {
+ right: 8.3333333333%;
+}
+
+.row .col.push-s1 {
+ left: 8.3333333333%;
+}
+
+.row .col.offset-s2 {
+ margin-left: 16.6666666667%;
+}
+
+.row .col.pull-s2 {
+ right: 16.6666666667%;
+}
+
+.row .col.push-s2 {
+ left: 16.6666666667%;
+}
+
+.row .col.offset-s3 {
+ margin-left: 25%;
+}
+
+.row .col.pull-s3 {
+ right: 25%;
+}
+
+.row .col.push-s3 {
+ left: 25%;
+}
+
+.row .col.offset-s4 {
+ margin-left: 33.3333333333%;
+}
+
+.row .col.pull-s4 {
+ right: 33.3333333333%;
+}
+
+.row .col.push-s4 {
+ left: 33.3333333333%;
+}
+
+.row .col.offset-s5 {
+ margin-left: 41.6666666667%;
+}
+
+.row .col.pull-s5 {
+ right: 41.6666666667%;
+}
+
+.row .col.push-s5 {
+ left: 41.6666666667%;
+}
+
+.row .col.offset-s6 {
+ margin-left: 50%;
+}
+
+.row .col.pull-s6 {
+ right: 50%;
+}
+
+.row .col.push-s6 {
+ left: 50%;
+}
+
+.row .col.offset-s7 {
+ margin-left: 58.3333333333%;
+}
+
+.row .col.pull-s7 {
+ right: 58.3333333333%;
+}
+
+.row .col.push-s7 {
+ left: 58.3333333333%;
+}
+
+.row .col.offset-s8 {
+ margin-left: 66.6666666667%;
+}
+
+.row .col.pull-s8 {
+ right: 66.6666666667%;
+}
+
+.row .col.push-s8 {
+ left: 66.6666666667%;
+}
+
+.row .col.offset-s9 {
+ margin-left: 75%;
+}
+
+.row .col.pull-s9 {
+ right: 75%;
+}
+
+.row .col.push-s9 {
+ left: 75%;
+}
+
+.row .col.offset-s10 {
+ margin-left: 83.3333333333%;
+}
+
+.row .col.pull-s10 {
+ right: 83.3333333333%;
+}
+
+.row .col.push-s10 {
+ left: 83.3333333333%;
+}
+
+.row .col.offset-s11 {
+ margin-left: 91.6666666667%;
+}
+
+.row .col.pull-s11 {
+ right: 91.6666666667%;
+}
+
+.row .col.push-s11 {
+ left: 91.6666666667%;
+}
+
+.row .col.offset-s12 {
+ margin-left: 100%;
+}
+
+.row .col.pull-s12 {
+ right: 100%;
+}
+
+.row .col.push-s12 {
+ left: 100%;
+}
+
+@media only screen and (min-width: 601px) {
+ .row .col.m1 {
+ width: 8.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m2 {
+ width: 16.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m3 {
+ width: 25%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m4 {
+ width: 33.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m5 {
+ width: 41.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m6 {
+ width: 50%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m7 {
+ width: 58.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m8 {
+ width: 66.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m9 {
+ width: 75%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m10 {
+ width: 83.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m11 {
+ width: 91.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.m12 {
+ width: 100%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.offset-m1 {
+ margin-left: 8.3333333333%;
+ }
+ .row .col.pull-m1 {
+ right: 8.3333333333%;
+ }
+ .row .col.push-m1 {
+ left: 8.3333333333%;
+ }
+ .row .col.offset-m2 {
+ margin-left: 16.6666666667%;
+ }
+ .row .col.pull-m2 {
+ right: 16.6666666667%;
+ }
+ .row .col.push-m2 {
+ left: 16.6666666667%;
+ }
+ .row .col.offset-m3 {
+ margin-left: 25%;
+ }
+ .row .col.pull-m3 {
+ right: 25%;
+ }
+ .row .col.push-m3 {
+ left: 25%;
+ }
+ .row .col.offset-m4 {
+ margin-left: 33.3333333333%;
+ }
+ .row .col.pull-m4 {
+ right: 33.3333333333%;
+ }
+ .row .col.push-m4 {
+ left: 33.3333333333%;
+ }
+ .row .col.offset-m5 {
+ margin-left: 41.6666666667%;
+ }
+ .row .col.pull-m5 {
+ right: 41.6666666667%;
+ }
+ .row .col.push-m5 {
+ left: 41.6666666667%;
+ }
+ .row .col.offset-m6 {
+ margin-left: 50%;
+ }
+ .row .col.pull-m6 {
+ right: 50%;
+ }
+ .row .col.push-m6 {
+ left: 50%;
+ }
+ .row .col.offset-m7 {
+ margin-left: 58.3333333333%;
+ }
+ .row .col.pull-m7 {
+ right: 58.3333333333%;
+ }
+ .row .col.push-m7 {
+ left: 58.3333333333%;
+ }
+ .row .col.offset-m8 {
+ margin-left: 66.6666666667%;
+ }
+ .row .col.pull-m8 {
+ right: 66.6666666667%;
+ }
+ .row .col.push-m8 {
+ left: 66.6666666667%;
+ }
+ .row .col.offset-m9 {
+ margin-left: 75%;
+ }
+ .row .col.pull-m9 {
+ right: 75%;
+ }
+ .row .col.push-m9 {
+ left: 75%;
+ }
+ .row .col.offset-m10 {
+ margin-left: 83.3333333333%;
+ }
+ .row .col.pull-m10 {
+ right: 83.3333333333%;
+ }
+ .row .col.push-m10 {
+ left: 83.3333333333%;
+ }
+ .row .col.offset-m11 {
+ margin-left: 91.6666666667%;
+ }
+ .row .col.pull-m11 {
+ right: 91.6666666667%;
+ }
+ .row .col.push-m11 {
+ left: 91.6666666667%;
+ }
+ .row .col.offset-m12 {
+ margin-left: 100%;
+ }
+ .row .col.pull-m12 {
+ right: 100%;
+ }
+ .row .col.push-m12 {
+ left: 100%;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ .row .col.l1 {
+ width: 8.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l2 {
+ width: 16.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l3 {
+ width: 25%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l4 {
+ width: 33.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l5 {
+ width: 41.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l6 {
+ width: 50%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l7 {
+ width: 58.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l8 {
+ width: 66.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l9 {
+ width: 75%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l10 {
+ width: 83.3333333333%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l11 {
+ width: 91.6666666667%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.l12 {
+ width: 100%;
+ margin-left: auto;
+ left: auto;
+ right: auto;
+ }
+ .row .col.offset-l1 {
+ margin-left: 8.3333333333%;
+ }
+ .row .col.pull-l1 {
+ right: 8.3333333333%;
+ }
+ .row .col.push-l1 {
+ left: 8.3333333333%;
+ }
+ .row .col.offset-l2 {
+ margin-left: 16.6666666667%;
+ }
+ .row .col.pull-l2 {
+ right: 16.6666666667%;
+ }
+ .row .col.push-l2 {
+ left: 16.6666666667%;
+ }
+ .row .col.offset-l3 {
+ margin-left: 25%;
+ }
+ .row .col.pull-l3 {
+ right: 25%;
+ }
+ .row .col.push-l3 {
+ left: 25%;
+ }
+ .row .col.offset-l4 {
+ margin-left: 33.3333333333%;
+ }
+ .row .col.pull-l4 {
+ right: 33.3333333333%;
+ }
+ .row .col.push-l4 {
+ left: 33.3333333333%;
+ }
+ .row .col.offset-l5 {
+ margin-left: 41.6666666667%;
+ }
+ .row .col.pull-l5 {
+ right: 41.6666666667%;
+ }
+ .row .col.push-l5 {
+ left: 41.6666666667%;
+ }
+ .row .col.offset-l6 {
+ margin-left: 50%;
+ }
+ .row .col.pull-l6 {
+ right: 50%;
+ }
+ .row .col.push-l6 {
+ left: 50%;
+ }
+ .row .col.offset-l7 {
+ margin-left: 58.3333333333%;
+ }
+ .row .col.pull-l7 {
+ right: 58.3333333333%;
+ }
+ .row .col.push-l7 {
+ left: 58.3333333333%;
+ }
+ .row .col.offset-l8 {
+ margin-left: 66.6666666667%;
+ }
+ .row .col.pull-l8 {
+ right: 66.6666666667%;
+ }
+ .row .col.push-l8 {
+ left: 66.6666666667%;
+ }
+ .row .col.offset-l9 {
+ margin-left: 75%;
+ }
+ .row .col.pull-l9 {
+ right: 75%;
+ }
+ .row .col.push-l9 {
+ left: 75%;
+ }
+ .row .col.offset-l10 {
+ margin-left: 83.3333333333%;
+ }
+ .row .col.pull-l10 {
+ right: 83.3333333333%;
+ }
+ .row .col.push-l10 {
+ left: 83.3333333333%;
+ }
+ .row .col.offset-l11 {
+ margin-left: 91.6666666667%;
+ }
+ .row .col.pull-l11 {
+ right: 91.6666666667%;
+ }
+ .row .col.push-l11 {
+ left: 91.6666666667%;
+ }
+ .row .col.offset-l12 {
+ margin-left: 100%;
+ }
+ .row .col.pull-l12 {
+ right: 100%;
+ }
+ .row .col.push-l12 {
+ left: 100%;
+ }
+}
+
+nav {
+ color: #fff;
+ background-color: #ee6e73;
+ width: 100%;
+ height: 56px;
+ line-height: 56px;
+}
+
+nav.nav-extended {
+ height: auto;
+}
+
+nav.nav-extended .nav-wrapper {
+ min-height: 56px;
+ height: auto;
+}
+
+nav.nav-extended .nav-content {
+ position: relative;
+ line-height: normal;
+}
+
+nav a {
+ color: #fff;
+}
+
+nav i,
+nav [class^="mdi-"], nav [class*="mdi-"],
+nav i.material-icons {
+ display: block;
+ font-size: 24px;
+ height: 56px;
+ line-height: 56px;
+}
+
+nav .nav-wrapper {
+ position: relative;
+ height: 100%;
+}
+
+@media only screen and (min-width: 993px) {
+ nav a.button-collapse {
+ display: none;
+ }
+}
+
+nav .button-collapse {
+ float: left;
+ position: relative;
+ z-index: 1;
+ height: 56px;
+ margin: 0 18px;
+}
+
+nav .button-collapse i {
+ height: 56px;
+ line-height: 56px;
+}
+
+nav .brand-logo {
+ position: absolute;
+ color: #fff;
+ display: inline-block;
+ font-size: 2.1rem;
+ padding: 0;
+ white-space: nowrap;
+}
+
+nav .brand-logo.center {
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+}
+
+@media only screen and (max-width: 992px) {
+ nav .brand-logo {
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ }
+ nav .brand-logo.left, nav .brand-logo.right {
+ padding: 0;
+ -webkit-transform: none;
+ transform: none;
+ }
+ nav .brand-logo.left {
+ left: 0.5rem;
+ }
+ nav .brand-logo.right {
+ right: 0.5rem;
+ left: auto;
+ }
+}
+
+nav .brand-logo.right {
+ right: 0.5rem;
+ padding: 0;
+}
+
+nav .brand-logo i,
+nav .brand-logo [class^="mdi-"], nav .brand-logo [class*="mdi-"],
+nav .brand-logo i.material-icons {
+ float: left;
+ margin-right: 15px;
+}
+
+nav .nav-title {
+ display: inline-block;
+ font-size: 32px;
+ padding: 28px 0;
+}
+
+nav ul {
+ margin: 0;
+}
+
+nav ul li {
+ transition: background-color .3s;
+ float: left;
+ padding: 0;
+}
+
+nav ul li.active {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+nav ul a {
+ transition: background-color .3s;
+ font-size: 1rem;
+ color: #fff;
+ display: block;
+ padding: 0 15px;
+ cursor: pointer;
+}
+
+nav ul a.btn, nav ul a.btn-large, nav ul a.btn-large, nav ul a.btn-flat, nav ul a.btn-floating {
+ margin-top: -2px;
+ margin-left: 15px;
+ margin-right: 15px;
+}
+
+nav ul a.btn > .material-icons, nav ul a.btn-large > .material-icons, nav ul a.btn-large > .material-icons, nav ul a.btn-flat > .material-icons, nav ul a.btn-floating > .material-icons {
+ height: inherit;
+ line-height: inherit;
+}
+
+nav ul a:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+nav ul.left {
+ float: left;
+}
+
+nav form {
+ height: 100%;
+}
+
+nav .input-field {
+ margin: 0;
+ height: 100%;
+}
+
+nav .input-field input {
+ height: 100%;
+ font-size: 1.2rem;
+ border: none;
+ padding-left: 2rem;
+}
+
+nav .input-field input:focus, nav .input-field input[type=text]:valid, nav .input-field input[type=password]:valid, nav .input-field input[type=email]:valid, nav .input-field input[type=url]:valid, nav .input-field input[type=date]:valid {
+ border: none;
+ box-shadow: none;
+}
+
+nav .input-field label {
+ top: 0;
+ left: 0;
+}
+
+nav .input-field label i {
+ color: rgba(255, 255, 255, 0.7);
+ transition: color .3s;
+}
+
+nav .input-field label.active i {
+ color: #fff;
+}
+
+.navbar-fixed {
+ position: relative;
+ height: 56px;
+ z-index: 997;
+}
+
+.navbar-fixed nav {
+ position: fixed;
+}
+
+@media only screen and (min-width: 601px) {
+ nav.nav-extended .nav-wrapper {
+ min-height: 64px;
+ }
+ nav, nav .nav-wrapper i, nav a.button-collapse, nav a.button-collapse i {
+ height: 64px;
+ line-height: 64px;
+ }
+ .navbar-fixed {
+ height: 64px;
+ }
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Thin), url("../fonts/roboto/Roboto-Thin.eot");
+ src: url("../fonts/roboto/Roboto-Thin.eot?#iefix") format("embedded-opentype"), url("../fonts/roboto/Roboto-Thin.woff2") format("woff2"), url("../fonts/roboto/Roboto-Thin.woff") format("woff"), url("../fonts/roboto/Roboto-Thin.ttf") format("truetype");
+ font-weight: 200;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Light), url("../fonts/roboto/Roboto-Light.eot");
+ src: url("../fonts/roboto/Roboto-Light.eot?#iefix") format("embedded-opentype"), url("../fonts/roboto/Roboto-Light.woff2") format("woff2"), url("../fonts/roboto/Roboto-Light.woff") format("woff"), url("../fonts/roboto/Roboto-Light.ttf") format("truetype");
+ font-weight: 300;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: local(Roboto Regular), url("../fonts/roboto/Roboto-Regular.eot");
+ src: url("../fonts/roboto/Roboto-Regular.eot?#iefix") format("embedded-opentype"), url("../fonts/roboto/Roboto-Regular.woff2") format("woff2"), url("../fonts/roboto/Roboto-Regular.woff") format("woff"), url("../fonts/roboto/Roboto-Regular.ttf") format("truetype");
+ font-weight: 400;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: url("../fonts/roboto/Roboto-Medium.eot");
+ src: url("../fonts/roboto/Roboto-Medium.eot?#iefix") format("embedded-opentype"), url("../fonts/roboto/Roboto-Medium.woff2") format("woff2"), url("../fonts/roboto/Roboto-Medium.woff") format("woff"), url("../fonts/roboto/Roboto-Medium.ttf") format("truetype");
+ font-weight: 500;
+}
+
+@font-face {
+ font-family: "Roboto";
+ src: url("../fonts/roboto/Roboto-Bold.eot");
+ src: url("../fonts/roboto/Roboto-Bold.eot?#iefix") format("embedded-opentype"), url("../fonts/roboto/Roboto-Bold.woff2") format("woff2"), url("../fonts/roboto/Roboto-Bold.woff") format("woff"), url("../fonts/roboto/Roboto-Bold.ttf") format("truetype");
+ font-weight: 700;
+}
+
+a {
+ text-decoration: none;
+}
+
+html {
+ line-height: 1.5;
+ font-family: "Roboto", sans-serif;
+ font-weight: normal;
+ color: rgba(0, 0, 0, 0.87);
+}
+
+@media only screen and (min-width: 0) {
+ html {
+ font-size: 14px;
+ }
+}
+
+@media only screen and (min-width: 992px) {
+ html {
+ font-size: 14.5px;
+ }
+}
+
+@media only screen and (min-width: 1200px) {
+ html {
+ font-size: 15px;
+ }
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: 400;
+ line-height: 1.1;
+}
+
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ font-weight: inherit;
+}
+
+h1 {
+ font-size: 4.2rem;
+ line-height: 110%;
+ margin: 2.1rem 0 1.68rem 0;
+}
+
+h2 {
+ font-size: 3.56rem;
+ line-height: 110%;
+ margin: 1.78rem 0 1.424rem 0;
+}
+
+h3 {
+ font-size: 2.92rem;
+ line-height: 110%;
+ margin: 1.46rem 0 1.168rem 0;
+}
+
+h4 {
+ font-size: 2.28rem;
+ line-height: 110%;
+ margin: 1.14rem 0 0.912rem 0;
+}
+
+h5 {
+ font-size: 1.64rem;
+ line-height: 110%;
+ margin: 0.82rem 0 0.656rem 0;
+}
+
+h6 {
+ font-size: 1rem;
+ line-height: 110%;
+ margin: 0.5rem 0 0.4rem 0;
+}
+
+em {
+ font-style: italic;
+}
+
+strong {
+ font-weight: 500;
+}
+
+small {
+ font-size: 75%;
+}
+
+.light, footer.page-footer .footer-copyright {
+ font-weight: 300;
+}
+
+.thin {
+ font-weight: 200;
+}
+
+.flow-text {
+ font-weight: 300;
+}
+
+@media only screen and (min-width: 360px) {
+ .flow-text {
+ font-size: 1.2rem;
+ }
+}
+
+@media only screen and (min-width: 390px) {
+ .flow-text {
+ font-size: 1.224rem;
+ }
+}
+
+@media only screen and (min-width: 420px) {
+ .flow-text {
+ font-size: 1.248rem;
+ }
+}
+
+@media only screen and (min-width: 450px) {
+ .flow-text {
+ font-size: 1.272rem;
+ }
+}
+
+@media only screen and (min-width: 480px) {
+ .flow-text {
+ font-size: 1.296rem;
+ }
+}
+
+@media only screen and (min-width: 510px) {
+ .flow-text {
+ font-size: 1.32rem;
+ }
+}
+
+@media only screen and (min-width: 540px) {
+ .flow-text {
+ font-size: 1.344rem;
+ }
+}
+
+@media only screen and (min-width: 570px) {
+ .flow-text {
+ font-size: 1.368rem;
+ }
+}
+
+@media only screen and (min-width: 600px) {
+ .flow-text {
+ font-size: 1.392rem;
+ }
+}
+
+@media only screen and (min-width: 630px) {
+ .flow-text {
+ font-size: 1.416rem;
+ }
+}
+
+@media only screen and (min-width: 660px) {
+ .flow-text {
+ font-size: 1.44rem;
+ }
+}
+
+@media only screen and (min-width: 690px) {
+ .flow-text {
+ font-size: 1.464rem;
+ }
+}
+
+@media only screen and (min-width: 720px) {
+ .flow-text {
+ font-size: 1.488rem;
+ }
+}
+
+@media only screen and (min-width: 750px) {
+ .flow-text {
+ font-size: 1.512rem;
+ }
+}
+
+@media only screen and (min-width: 780px) {
+ .flow-text {
+ font-size: 1.536rem;
+ }
+}
+
+@media only screen and (min-width: 810px) {
+ .flow-text {
+ font-size: 1.56rem;
+ }
+}
+
+@media only screen and (min-width: 840px) {
+ .flow-text {
+ font-size: 1.584rem;
+ }
+}
+
+@media only screen and (min-width: 870px) {
+ .flow-text {
+ font-size: 1.608rem;
+ }
+}
+
+@media only screen and (min-width: 900px) {
+ .flow-text {
+ font-size: 1.632rem;
+ }
+}
+
+@media only screen and (min-width: 930px) {
+ .flow-text {
+ font-size: 1.656rem;
+ }
+}
+
+@media only screen and (min-width: 960px) {
+ .flow-text {
+ font-size: 1.68rem;
+ }
+}
+
+@media only screen and (max-width: 360px) {
+ .flow-text {
+ font-size: 1.2rem;
+ }
+}
+
+.scale-transition {
+ transition: -webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;
+ transition: transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;
+ transition: transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63), -webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;
+}
+
+.scale-transition.scale-out {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ transition: -webkit-transform .2s !important;
+ transition: transform .2s !important;
+ transition: transform .2s, -webkit-transform .2s !important;
+}
+
+.scale-transition.scale-in {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+}
+
+.card-panel {
+ transition: box-shadow .25s;
+ padding: 24px;
+ margin: 0.5rem 0 1rem 0;
+ border-radius: 2px;
+ background-color: #fff;
+}
+
+.card {
+ position: relative;
+ margin: 0.5rem 0 1rem 0;
+ background-color: #fff;
+ transition: box-shadow .25s;
+ border-radius: 2px;
+}
+
+.card .card-title {
+ font-size: 24px;
+ font-weight: 300;
+}
+
+.card .card-title.activator {
+ cursor: pointer;
+}
+
+.card.small, .card.medium, .card.large {
+ position: relative;
+}
+
+.card.small .card-image, .card.medium .card-image, .card.large .card-image {
+ max-height: 60%;
+ overflow: hidden;
+}
+
+.card.small .card-image + .card-content, .card.medium .card-image + .card-content, .card.large .card-image + .card-content {
+ max-height: 40%;
+}
+
+.card.small .card-content, .card.medium .card-content, .card.large .card-content {
+ max-height: 100%;
+ overflow: hidden;
+}
+
+.card.small .card-action, .card.medium .card-action, .card.large .card-action {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+.card.small {
+ height: 300px;
+}
+
+.card.medium {
+ height: 400px;
+}
+
+.card.large {
+ height: 500px;
+}
+
+.card.horizontal {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+.card.horizontal.small .card-image, .card.horizontal.medium .card-image, .card.horizontal.large .card-image {
+ height: 100%;
+ max-height: none;
+ overflow: visible;
+}
+
+.card.horizontal.small .card-image img, .card.horizontal.medium .card-image img, .card.horizontal.large .card-image img {
+ height: 100%;
+}
+
+.card.horizontal .card-image {
+ max-width: 50%;
+}
+
+.card.horizontal .card-image img {
+ border-radius: 2px 0 0 2px;
+ max-width: 100%;
+ width: auto;
+}
+
+.card.horizontal .card-stacked {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -webkit-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+ position: relative;
+}
+
+.card.horizontal .card-stacked .card-content {
+ -webkit-flex-grow: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+}
+
+.card.sticky-action .card-action {
+ z-index: 2;
+}
+
+.card.sticky-action .card-reveal {
+ z-index: 1;
+ padding-bottom: 64px;
+}
+
+.card .card-image {
+ position: relative;
+}
+
+.card .card-image img {
+ display: block;
+ border-radius: 2px 2px 0 0;
+ position: relative;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+}
+
+.card .card-image .card-title {
+ color: #fff;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ max-width: 100%;
+ padding: 24px;
+}
+
+.card .card-content {
+ padding: 24px;
+ border-radius: 0 0 2px 2px;
+}
+
+.card .card-content p {
+ margin: 0;
+ color: inherit;
+}
+
+.card .card-content .card-title {
+ display: block;
+ line-height: 32px;
+ margin-bottom: 8px;
+}
+
+.card .card-content .card-title i {
+ line-height: 32px;
+}
+
+.card .card-action {
+ position: relative;
+ background-color: inherit;
+ border-top: 1px solid rgba(160, 160, 160, 0.2);
+ padding: 16px 24px;
+}
+
+.card .card-action a:not(.btn):not(.btn-large):not(.btn-large):not(.btn-floating) {
+ color: #ffab40;
+ margin-right: 24px;
+ transition: color .3s ease;
+ text-transform: uppercase;
+}
+
+.card .card-action a:not(.btn):not(.btn-large):not(.btn-large):not(.btn-floating):hover {
+ color: #ffd8a6;
+}
+
+.card .card-reveal {
+ padding: 24px;
+ position: absolute;
+ background-color: #fff;
+ width: 100%;
+ overflow-y: auto;
+ left: 0;
+ top: 100%;
+ height: 100%;
+ z-index: 3;
+ display: none;
+}
+
+.card .card-reveal .card-title {
+ cursor: pointer;
+ display: block;
+}
+
+#toast-container {
+ display: block;
+ position: fixed;
+ z-index: 10000;
+}
+
+@media only screen and (max-width: 600px) {
+ #toast-container {
+ min-width: 100%;
+ bottom: 0%;
+ }
+}
+
+@media only screen and (min-width: 601px) and (max-width: 992px) {
+ #toast-container {
+ left: 5%;
+ bottom: 7%;
+ max-width: 90%;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ #toast-container {
+ top: 10%;
+ right: 7%;
+ max-width: 86%;
+ }
+}
+
+.toast {
+ border-radius: 2px;
+ top: 35px;
+ width: auto;
+ clear: both;
+ margin-top: 10px;
+ position: relative;
+ max-width: 100%;
+ height: auto;
+ min-height: 48px;
+ line-height: 1.5em;
+ word-break: break-all;
+ background-color: #323232;
+ padding: 10px 25px;
+ font-size: 1.1rem;
+ font-weight: 300;
+ color: #fff;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-justify-content: space-between;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+}
+
+.toast .btn, .toast .btn-large, .toast .btn-flat {
+ margin: 0;
+ margin-left: 3rem;
+}
+
+.toast.rounded {
+ border-radius: 24px;
+}
+
+@media only screen and (max-width: 600px) {
+ .toast {
+ width: 100%;
+ border-radius: 0;
+ }
+}
+
+@media only screen and (min-width: 601px) and (max-width: 992px) {
+ .toast {
+ float: left;
+ }
+}
+
+@media only screen and (min-width: 993px) {
+ .toast {
+ float: right;
+ }
+}
+
+.tabs {
+ position: relative;
+ overflow-x: auto;
+ overflow-y: hidden;
+ height: 48px;
+ width: 100%;
+ background-color: #fff;
+ margin: 0 auto;
+ white-space: nowrap;
+}
+
+.tabs.tabs-transparent {
+ background-color: transparent;
+}
+
+.tabs.tabs-transparent .tab a,
+.tabs.tabs-transparent .tab.disabled a,
+.tabs.tabs-transparent .tab.disabled a:hover {
+ color: rgba(255, 255, 255, 0.7);
+}
+
+.tabs.tabs-transparent .tab a:hover,
+.tabs.tabs-transparent .tab a.active {
+ color: #fff;
+}
+
+.tabs.tabs-transparent .indicator {
+ background-color: #fff;
+}
+
+.tabs.tabs-fixed-width {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+.tabs.tabs-fixed-width .tab {
+ -webkit-flex-grow: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+}
+
+.tabs .tab {
+ display: inline-block;
+ text-align: center;
+ line-height: 48px;
+ height: 48px;
+ padding: 0;
+ margin: 0;
+ text-transform: uppercase;
+}
+
+.tabs .tab a {
+ color: rgba(238, 110, 115, 0.7);
+ display: block;
+ width: 100%;
+ height: 100%;
+ padding: 0 24px;
+ font-size: 14px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ transition: color .28s ease;
+}
+
+.tabs .tab a:hover, .tabs .tab a.active {
+ background-color: transparent;
+ color: #ee6e73;
+}
+
+.tabs .tab.disabled a,
+.tabs .tab.disabled a:hover {
+ color: rgba(238, 110, 115, 0.7);
+ cursor: default;
+}
+
+.tabs .indicator {
+ position: absolute;
+ bottom: 0;
+ height: 2px;
+ background-color: #f6b2b5;
+ will-change: left, right;
+}
+
+@media only screen and (max-width: 992px) {
+ .tabs {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ }
+ .tabs .tab {
+ -webkit-flex-grow: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ }
+ .tabs .tab a {
+ padding: 0 12px;
+ }
+}
+
+.material-tooltip {
+ padding: 10px 8px;
+ font-size: 1rem;
+ z-index: 2000;
+ background-color: transparent;
+ border-radius: 2px;
+ color: #fff;
+ min-height: 36px;
+ line-height: 120%;
+ opacity: 0;
+ position: absolute;
+ text-align: center;
+ max-width: calc(100% - 4px);
+ overflow: hidden;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ visibility: hidden;
+}
+
+.backdrop {
+ position: absolute;
+ opacity: 0;
+ height: 7px;
+ width: 14px;
+ border-radius: 0 0 50% 50%;
+ background-color: #323232;
+ z-index: -1;
+ -webkit-transform-origin: 50% 0%;
+ transform-origin: 50% 0%;
+ visibility: hidden;
+}
+
+.btn, .btn-large,
+.btn-flat {
+ border: none;
+ border-radius: 2px;
+ display: inline-block;
+ height: 36px;
+ line-height: 36px;
+ padding: 0 2rem;
+ text-transform: uppercase;
+ vertical-align: middle;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.btn.disabled, .disabled.btn-large,
+.btn-floating.disabled,
+.btn-large.disabled,
+.btn-flat.disabled,
+.btn:disabled,
+.btn-large:disabled,
+.btn-floating:disabled,
+.btn-large:disabled,
+.btn-flat:disabled,
+.btn[disabled],
+[disabled].btn-large,
+.btn-floating[disabled],
+.btn-large[disabled],
+.btn-flat[disabled] {
+ pointer-events: none;
+ background-color: #DFDFDF !important;
+ box-shadow: none;
+ color: #9F9F9F !important;
+ cursor: default;
+}
+
+.btn.disabled:hover, .disabled.btn-large:hover,
+.btn-floating.disabled:hover,
+.btn-large.disabled:hover,
+.btn-flat.disabled:hover,
+.btn:disabled:hover,
+.btn-large:disabled:hover,
+.btn-floating:disabled:hover,
+.btn-large:disabled:hover,
+.btn-flat:disabled:hover,
+.btn[disabled]:hover,
+[disabled].btn-large:hover,
+.btn-floating[disabled]:hover,
+.btn-large[disabled]:hover,
+.btn-flat[disabled]:hover {
+ background-color: #DFDFDF !important;
+ color: #9F9F9F !important;
+}
+
+.btn, .btn-large,
+.btn-floating,
+.btn-large,
+.btn-flat {
+ outline: 0;
+}
+
+.btn i, .btn-large i,
+.btn-floating i,
+.btn-large i,
+.btn-flat i {
+ font-size: 1.3rem;
+ line-height: inherit;
+}
+
+.btn:focus, .btn-large:focus,
+.btn-floating:focus {
+ background-color: #1d7d74;
+}
+
+.btn, .btn-large {
+ text-decoration: none;
+ color: #fff;
+ background-color: #26a69a;
+ text-align: center;
+ letter-spacing: .5px;
+ transition: .2s ease-out;
+ cursor: pointer;
+}
+
+.btn:hover, .btn-large:hover {
+ background-color: #2bbbad;
+}
+
+.btn-floating {
+ display: inline-block;
+ color: #fff;
+ position: relative;
+ overflow: hidden;
+ z-index: 1;
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+ padding: 0;
+ background-color: #26a69a;
+ border-radius: 50%;
+ transition: .3s;
+ cursor: pointer;
+ vertical-align: middle;
+}
+
+.btn-floating:hover {
+ background-color: #26a69a;
+}
+
+.btn-floating:before {
+ border-radius: 0;
+}
+
+.btn-floating.btn-large {
+ width: 56px;
+ height: 56px;
+}
+
+.btn-floating.btn-large i {
+ line-height: 56px;
+}
+
+.btn-floating.halfway-fab {
+ position: absolute;
+ right: 24px;
+ bottom: 0;
+ -webkit-transform: translateY(50%);
+ transform: translateY(50%);
+}
+
+.btn-floating.halfway-fab.left {
+ right: auto;
+ left: 24px;
+}
+
+.btn-floating i {
+ width: inherit;
+ display: inline-block;
+ text-align: center;
+ color: #fff;
+ font-size: 1.6rem;
+ line-height: 40px;
+}
+
+button.btn-floating {
+ border: none;
+}
+
+.fixed-action-btn {
+ position: fixed;
+ right: 23px;
+ bottom: 23px;
+ padding-top: 15px;
+ margin-bottom: 0;
+ z-index: 998;
+}
+
+.fixed-action-btn.active ul {
+ visibility: visible;
+}
+
+.fixed-action-btn.horizontal {
+ padding: 0 0 0 15px;
+}
+
+.fixed-action-btn.horizontal ul {
+ text-align: right;
+ right: 64px;
+ top: 50%;
+ -webkit-transform: translateY(-50%);
+ transform: translateY(-50%);
+ height: 100%;
+ left: auto;
+ width: 500px;
+ /*width 100% only goes to width of button container */
+}
+
+.fixed-action-btn.horizontal ul li {
+ display: inline-block;
+ margin: 15px 15px 0 0;
+}
+
+.fixed-action-btn.toolbar {
+ padding: 0;
+ height: 56px;
+}
+
+.fixed-action-btn.toolbar.active > a i {
+ opacity: 0;
+}
+
+.fixed-action-btn.toolbar ul {
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ top: 0;
+ bottom: 0;
+}
+
+.fixed-action-btn.toolbar ul li {
+ -webkit-flex: 1;
+ -ms-flex: 1;
+ flex: 1;
+ display: inline-block;
+ margin: 0;
+ height: 100%;
+ transition: none;
+}
+
+.fixed-action-btn.toolbar ul li a {
+ display: block;
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background-color: transparent;
+ box-shadow: none;
+ color: #fff;
+ line-height: 56px;
+ z-index: 1;
+}
+
+.fixed-action-btn.toolbar ul li a i {
+ line-height: inherit;
+}
+
+.fixed-action-btn ul {
+ left: 0;
+ right: 0;
+ text-align: center;
+ position: absolute;
+ bottom: 64px;
+ margin: 0;
+ visibility: hidden;
+}
+
+.fixed-action-btn ul li {
+ margin-bottom: 15px;
+}
+
+.fixed-action-btn ul a.btn-floating {
+ opacity: 0;
+}
+
+.fixed-action-btn .fab-backdrop {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: -1;
+ width: 40px;
+ height: 40px;
+ background-color: #26a69a;
+ border-radius: 50%;
+ -webkit-transform: scale(0);
+ transform: scale(0);
+}
+
+.btn-flat {
+ box-shadow: none;
+ background-color: transparent;
+ color: #343434;
+ cursor: pointer;
+ transition: background-color .2s;
+}
+
+.btn-flat:focus, .btn-flat:active {
+ background-color: transparent;
+}
+
+.btn-flat:focus, .btn-flat:hover {
+ background-color: rgba(0, 0, 0, 0.1);
+ box-shadow: none;
+}
+
+.btn-flat:active {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+.btn-flat.disabled {
+ background-color: transparent !important;
+ color: #b3b3b3 !important;
+ cursor: default;
+}
+
+.btn-large {
+ height: 54px;
+ line-height: 54px;
+}
+
+.btn-large i {
+ font-size: 1.6rem;
+}
+
+.btn-block {
+ display: block;
+}
+
+.dropdown-content {
+ background-color: #fff;
+ margin: 0;
+ display: none;
+ min-width: 100px;
+ max-height: 650px;
+ overflow-y: auto;
+ opacity: 0;
+ position: absolute;
+ z-index: 999;
+ will-change: width, height;
+}
+
+.dropdown-content li {
+ clear: both;
+ color: rgba(0, 0, 0, 0.87);
+ cursor: pointer;
+ min-height: 50px;
+ line-height: 1.5rem;
+ width: 100%;
+ text-align: left;
+ text-transform: none;
+}
+
+.dropdown-content li:hover, .dropdown-content li.active, .dropdown-content li.selected {
+ background-color: #eee;
+}
+
+.dropdown-content li.active.selected {
+ background-color: #e1e1e1;
+}
+
+.dropdown-content li.divider {
+ min-height: 0;
+ height: 1px;
+}
+
+.dropdown-content li > a, .dropdown-content li > span {
+ font-size: 16px;
+ color: #26a69a;
+ display: block;
+ line-height: 22px;
+ padding: 14px 16px;
+}
+
+.dropdown-content li > span > label {
+ top: 1px;
+ left: 0;
+ height: 18px;
+}
+
+.dropdown-content li > a > i {
+ height: inherit;
+ line-height: inherit;
+}
+
+.input-field.col .dropdown-content [type="checkbox"] + label {
+ top: 1px;
+ left: 0;
+ height: 18px;
+}
+
+/*!
+ * Waves v0.6.0
+ * http://fian.my.id/Waves
+ *
+ * Copyright 2014 Alfiana E. Sibuea and other contributors
+ * Released under the MIT license
+ * https://github.com/fians/Waves/blob/master/LICENSE
+ */
+.waves-effect {
+ position: relative;
+ cursor: pointer;
+ display: inline-block;
+ overflow: hidden;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-tap-highlight-color: transparent;
+ vertical-align: middle;
+ z-index: 1;
+ transition: .3s ease-out;
+}
+
+.waves-effect .waves-ripple {
+ position: absolute;
+ border-radius: 50%;
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ margin-left: -10px;
+ opacity: 0;
+ background: rgba(0, 0, 0, 0.2);
+ transition: all 0.7s ease-out;
+ transition-property: opacity, -webkit-transform;
+ transition-property: transform, opacity;
+ transition-property: transform, opacity, -webkit-transform;
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ pointer-events: none;
+}
+
+.waves-effect.waves-light .waves-ripple {
+ background-color: rgba(255, 255, 255, 0.45);
+}
+
+.waves-effect.waves-red .waves-ripple {
+ background-color: rgba(244, 67, 54, 0.7);
+}
+
+.waves-effect.waves-yellow .waves-ripple {
+ background-color: rgba(255, 235, 59, 0.7);
+}
+
+.waves-effect.waves-orange .waves-ripple {
+ background-color: rgba(255, 152, 0, 0.7);
+}
+
+.waves-effect.waves-purple .waves-ripple {
+ background-color: rgba(156, 39, 176, 0.7);
+}
+
+.waves-effect.waves-green .waves-ripple {
+ background-color: rgba(76, 175, 80, 0.7);
+}
+
+.waves-effect.waves-teal .waves-ripple {
+ background-color: rgba(0, 150, 136, 0.7);
+}
+
+.waves-effect input[type="button"], .waves-effect input[type="reset"], .waves-effect input[type="submit"] {
+ border: 0;
+ font-style: normal;
+ font-size: inherit;
+ text-transform: inherit;
+ background: none;
+}
+
+.waves-effect img {
+ position: relative;
+ z-index: -1;
+}
+
+.waves-notransition {
+ transition: none !important;
+}
+
+.waves-circle {
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
+}
+
+.waves-input-wrapper {
+ border-radius: 0.2em;
+ vertical-align: bottom;
+}
+
+.waves-input-wrapper .waves-button-input {
+ position: relative;
+ top: 0;
+ left: 0;
+ z-index: 1;
+}
+
+.waves-circle {
+ text-align: center;
+ width: 2.5em;
+ height: 2.5em;
+ line-height: 2.5em;
+ border-radius: 50%;
+ -webkit-mask-image: none;
+}
+
+.waves-block {
+ display: block;
+}
+
+/* Firefox Bug: link not triggered */
+.waves-effect .waves-ripple {
+ z-index: -1;
+}
+
+.modal {
+ display: none;
+ position: fixed;
+ left: 0;
+ right: 0;
+ background-color: #fafafa;
+ padding: 0;
+ max-height: 70%;
+ width: 55%;
+ margin: auto;
+ overflow-y: auto;
+ border-radius: 2px;
+ will-change: top, opacity;
+}
+
+@media only screen and (max-width: 992px) {
+ .modal {
+ width: 80%;
+ }
+}
+
+.modal h1, .modal h2, .modal h3, .modal h4 {
+ margin-top: 0;
+}
+
+.modal .modal-content {
+ padding: 24px;
+}
+
+.modal .modal-close {
+ cursor: pointer;
+}
+
+.modal .modal-footer {
+ border-radius: 0 0 2px 2px;
+ background-color: #fafafa;
+ padding: 4px 6px;
+ height: 56px;
+ width: 100%;
+}
+
+.modal .modal-footer .btn, .modal .modal-footer .btn-large, .modal .modal-footer .btn-flat {
+ float: right;
+ margin: 6px 0;
+}
+
+.modal-overlay {
+ position: fixed;
+ z-index: 999;
+ top: -100px;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ height: 125%;
+ width: 100%;
+ background: #000;
+ display: none;
+ will-change: opacity;
+}
+
+.modal.modal-fixed-footer {
+ padding: 0;
+ height: 70%;
+}
+
+.modal.modal-fixed-footer .modal-content {
+ position: absolute;
+ height: calc(100% - 56px);
+ max-height: 100%;
+ width: 100%;
+ overflow-y: auto;
+}
+
+.modal.modal-fixed-footer .modal-footer {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ position: absolute;
+ bottom: 0;
+}
+
+.modal.bottom-sheet {
+ top: auto;
+ bottom: -100%;
+ margin: 0;
+ width: 100%;
+ max-height: 45%;
+ border-radius: 0;
+ will-change: bottom, opacity;
+}
+
+.collapsible {
+ border-top: 1px solid #ddd;
+ border-right: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+ margin: 0.5rem 0 1rem 0;
+}
+
+.collapsible-header {
+ display: block;
+ cursor: pointer;
+ min-height: 3rem;
+ line-height: 3rem;
+ padding: 0 1rem;
+ background-color: #fff;
+ border-bottom: 1px solid #ddd;
+}
+
+.collapsible-header i {
+ width: 2rem;
+ font-size: 1.6rem;
+ line-height: 3rem;
+ display: block;
+ float: left;
+ text-align: center;
+ margin-right: 1rem;
+}
+
+.collapsible-body {
+ display: none;
+ border-bottom: 1px solid #ddd;
+ box-sizing: border-box;
+ padding: 2rem;
+}
+
+.side-nav .collapsible,
+.side-nav.fixed .collapsible {
+ border: none;
+ box-shadow: none;
+}
+
+.side-nav .collapsible li,
+.side-nav.fixed .collapsible li {
+ padding: 0;
+}
+
+.side-nav .collapsible-header,
+.side-nav.fixed .collapsible-header {
+ background-color: transparent;
+ border: none;
+ line-height: inherit;
+ height: inherit;
+ padding: 0 16px;
+}
+
+.side-nav .collapsible-header:hover,
+.side-nav.fixed .collapsible-header:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.side-nav .collapsible-header i,
+.side-nav.fixed .collapsible-header i {
+ line-height: inherit;
+}
+
+.side-nav .collapsible-body,
+.side-nav.fixed .collapsible-body {
+ border: 0;
+ background-color: #fff;
+}
+
+.side-nav .collapsible-body li a,
+.side-nav.fixed .collapsible-body li a {
+ padding: 0 23.5px 0 31px;
+}
+
+.collapsible.popout {
+ border: none;
+ box-shadow: none;
+}
+
+.collapsible.popout > li {
+ box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
+ margin: 0 24px;
+ transition: margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+}
+
+.collapsible.popout > li.active {
+ box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15);
+ margin: 16px 0;
+}
+
+.chip {
+ display: inline-block;
+ height: 32px;
+ font-size: 13px;
+ font-weight: 500;
+ color: rgba(0, 0, 0, 0.6);
+ line-height: 32px;
+ padding: 0 12px;
+ border-radius: 16px;
+ background-color: #e4e4e4;
+ margin-bottom: 5px;
+ margin-right: 5px;
+}
+
+.chip img {
+ float: left;
+ margin: 0 8px 0 -12px;
+ height: 32px;
+ width: 32px;
+ border-radius: 50%;
+}
+
+.chip .close {
+ cursor: pointer;
+ float: right;
+ font-size: 16px;
+ line-height: 32px;
+ padding-left: 8px;
+}
+
+.chips {
+ border: none;
+ border-bottom: 1px solid #9e9e9e;
+ box-shadow: none;
+ margin: 0 0 20px 0;
+ min-height: 45px;
+ outline: none;
+ transition: all .3s;
+}
+
+.chips.focus {
+ border-bottom: 1px solid #26a69a;
+ box-shadow: 0 1px 0 0 #26a69a;
+}
+
+.chips:hover {
+ cursor: text;
+}
+
+.chips .chip.selected {
+ background-color: #26a69a;
+ color: #fff;
+}
+
+.chips .input {
+ background: none;
+ border: 0;
+ color: rgba(0, 0, 0, 0.6);
+ display: inline-block;
+ font-size: 1rem;
+ height: 3rem;
+ line-height: 32px;
+ outline: 0;
+ margin: 0;
+ padding: 0 !important;
+ width: 120px !important;
+}
+
+.chips .input:focus {
+ border: 0 !important;
+ box-shadow: none !important;
+}
+
+.prefix ~ .chips {
+ margin-left: 3rem;
+ width: 92%;
+ width: calc(100% - 3rem);
+}
+
+.chips:empty ~ label {
+ font-size: 0.8rem;
+ -webkit-transform: translateY(-140%);
+ transform: translateY(-140%);
+}
+
+.materialboxed {
+ display: block;
+ cursor: -webkit-zoom-in;
+ cursor: zoom-in;
+ position: relative;
+ transition: opacity .4s;
+ -webkit-backface-visibility: hidden;
+}
+
+.materialboxed:hover:not(.active) {
+ opacity: .8;
+}
+
+.materialboxed.active {
+ cursor: -webkit-zoom-out;
+ cursor: zoom-out;
+}
+
+#materialbox-overlay {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: #292929;
+ z-index: 1000;
+ will-change: opacity;
+}
+
+.materialbox-caption {
+ position: fixed;
+ display: none;
+ color: #fff;
+ line-height: 50px;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ text-align: center;
+ padding: 0% 15%;
+ height: 50px;
+ z-index: 1000;
+ -webkit-font-smoothing: antialiased;
+}
+
+select:focus {
+ outline: 1px solid #c9f3ef;
+}
+
+button:focus {
+ outline: none;
+ background-color: #2ab7a9;
+}
+
+label {
+ font-size: 0.8rem;
+ color: #9e9e9e;
+}
+
+/* Text Inputs + Textarea
+ ========================================================================== */
+/* Style Placeholders */
+::-webkit-input-placeholder {
+ color: #d1d1d1;
+}
+
+:-moz-placeholder {
+ /* Firefox 18- */
+ color: #d1d1d1;
+}
+
+::-moz-placeholder {
+ /* Firefox 19+ */
+ color: #d1d1d1;
+}
+
+:-ms-input-placeholder {
+ color: #d1d1d1;
+}
+
+/* Text inputs */
+input:not([type]),
+input[type=text],
+input[type=password],
+input[type=email],
+input[type=url],
+input[type=time],
+input[type=date],
+input[type=datetime],
+input[type=datetime-local],
+input[type=tel],
+input[type=number],
+input[type=search],
+textarea.materialize-textarea {
+ background-color: transparent;
+ border: none;
+ border-bottom: 1px solid #9e9e9e;
+ border-radius: 0;
+ outline: none;
+ height: 3rem;
+ width: 100%;
+ font-size: 1rem;
+ margin: 0 0 20px 0;
+ padding: 0;
+ box-shadow: none;
+ box-sizing: content-box;
+ transition: all 0.3s;
+}
+
+input:not([type]):disabled, input:not([type])[readonly="readonly"],
+input[type=text]:disabled,
+input[type=text][readonly="readonly"],
+input[type=password]:disabled,
+input[type=password][readonly="readonly"],
+input[type=email]:disabled,
+input[type=email][readonly="readonly"],
+input[type=url]:disabled,
+input[type=url][readonly="readonly"],
+input[type=time]:disabled,
+input[type=time][readonly="readonly"],
+input[type=date]:disabled,
+input[type=date][readonly="readonly"],
+input[type=datetime]:disabled,
+input[type=datetime][readonly="readonly"],
+input[type=datetime-local]:disabled,
+input[type=datetime-local][readonly="readonly"],
+input[type=tel]:disabled,
+input[type=tel][readonly="readonly"],
+input[type=number]:disabled,
+input[type=number][readonly="readonly"],
+input[type=search]:disabled,
+input[type=search][readonly="readonly"],
+textarea.materialize-textarea:disabled,
+textarea.materialize-textarea[readonly="readonly"] {
+ color: rgba(0, 0, 0, 0.26);
+ border-bottom: 1px dotted rgba(0, 0, 0, 0.26);
+}
+
+input:not([type]):disabled + label,
+input:not([type])[readonly="readonly"] + label,
+input[type=text]:disabled + label,
+input[type=text][readonly="readonly"] + label,
+input[type=password]:disabled + label,
+input[type=password][readonly="readonly"] + label,
+input[type=email]:disabled + label,
+input[type=email][readonly="readonly"] + label,
+input[type=url]:disabled + label,
+input[type=url][readonly="readonly"] + label,
+input[type=time]:disabled + label,
+input[type=time][readonly="readonly"] + label,
+input[type=date]:disabled + label,
+input[type=date][readonly="readonly"] + label,
+input[type=datetime]:disabled + label,
+input[type=datetime][readonly="readonly"] + label,
+input[type=datetime-local]:disabled + label,
+input[type=datetime-local][readonly="readonly"] + label,
+input[type=tel]:disabled + label,
+input[type=tel][readonly="readonly"] + label,
+input[type=number]:disabled + label,
+input[type=number][readonly="readonly"] + label,
+input[type=search]:disabled + label,
+input[type=search][readonly="readonly"] + label,
+textarea.materialize-textarea:disabled + label,
+textarea.materialize-textarea[readonly="readonly"] + label {
+ color: rgba(0, 0, 0, 0.26);
+}
+
+input:not([type]):focus:not([readonly]),
+input[type=text]:focus:not([readonly]),
+input[type=password]:focus:not([readonly]),
+input[type=email]:focus:not([readonly]),
+input[type=url]:focus:not([readonly]),
+input[type=time]:focus:not([readonly]),
+input[type=date]:focus:not([readonly]),
+input[type=datetime]:focus:not([readonly]),
+input[type=datetime-local]:focus:not([readonly]),
+input[type=tel]:focus:not([readonly]),
+input[type=number]:focus:not([readonly]),
+input[type=search]:focus:not([readonly]),
+textarea.materialize-textarea:focus:not([readonly]) {
+ border-bottom: 1px solid #26a69a;
+ box-shadow: 0 1px 0 0 #26a69a;
+}
+
+input:not([type]):focus:not([readonly]) + label,
+input[type=text]:focus:not([readonly]) + label,
+input[type=password]:focus:not([readonly]) + label,
+input[type=email]:focus:not([readonly]) + label,
+input[type=url]:focus:not([readonly]) + label,
+input[type=time]:focus:not([readonly]) + label,
+input[type=date]:focus:not([readonly]) + label,
+input[type=datetime]:focus:not([readonly]) + label,
+input[type=datetime-local]:focus:not([readonly]) + label,
+input[type=tel]:focus:not([readonly]) + label,
+input[type=number]:focus:not([readonly]) + label,
+input[type=search]:focus:not([readonly]) + label,
+textarea.materialize-textarea:focus:not([readonly]) + label {
+ color: #26a69a;
+}
+
+input:not([type]).valid, input:not([type]):focus.valid,
+input[type=text].valid,
+input[type=text]:focus.valid,
+input[type=password].valid,
+input[type=password]:focus.valid,
+input[type=email].valid,
+input[type=email]:focus.valid,
+input[type=url].valid,
+input[type=url]:focus.valid,
+input[type=time].valid,
+input[type=time]:focus.valid,
+input[type=date].valid,
+input[type=date]:focus.valid,
+input[type=datetime].valid,
+input[type=datetime]:focus.valid,
+input[type=datetime-local].valid,
+input[type=datetime-local]:focus.valid,
+input[type=tel].valid,
+input[type=tel]:focus.valid,
+input[type=number].valid,
+input[type=number]:focus.valid,
+input[type=search].valid,
+input[type=search]:focus.valid,
+textarea.materialize-textarea.valid,
+textarea.materialize-textarea:focus.valid {
+ border-bottom: 1px solid #4CAF50;
+ box-shadow: 0 1px 0 0 #4CAF50;
+}
+
+input:not([type]).valid + label:after,
+input:not([type]):focus.valid + label:after,
+input[type=text].valid + label:after,
+input[type=text]:focus.valid + label:after,
+input[type=password].valid + label:after,
+input[type=password]:focus.valid + label:after,
+input[type=email].valid + label:after,
+input[type=email]:focus.valid + label:after,
+input[type=url].valid + label:after,
+input[type=url]:focus.valid + label:after,
+input[type=time].valid + label:after,
+input[type=time]:focus.valid + label:after,
+input[type=date].valid + label:after,
+input[type=date]:focus.valid + label:after,
+input[type=datetime].valid + label:after,
+input[type=datetime]:focus.valid + label:after,
+input[type=datetime-local].valid + label:after,
+input[type=datetime-local]:focus.valid + label:after,
+input[type=tel].valid + label:after,
+input[type=tel]:focus.valid + label:after,
+input[type=number].valid + label:after,
+input[type=number]:focus.valid + label:after,
+input[type=search].valid + label:after,
+input[type=search]:focus.valid + label:after,
+textarea.materialize-textarea.valid + label:after,
+textarea.materialize-textarea:focus.valid + label:after {
+ content: attr(data-success);
+ color: #4CAF50;
+ opacity: 1;
+}
+
+input:not([type]).invalid, input:not([type]):focus.invalid,
+input[type=text].invalid,
+input[type=text]:focus.invalid,
+input[type=password].invalid,
+input[type=password]:focus.invalid,
+input[type=email].invalid,
+input[type=email]:focus.invalid,
+input[type=url].invalid,
+input[type=url]:focus.invalid,
+input[type=time].invalid,
+input[type=time]:focus.invalid,
+input[type=date].invalid,
+input[type=date]:focus.invalid,
+input[type=datetime].invalid,
+input[type=datetime]:focus.invalid,
+input[type=datetime-local].invalid,
+input[type=datetime-local]:focus.invalid,
+input[type=tel].invalid,
+input[type=tel]:focus.invalid,
+input[type=number].invalid,
+input[type=number]:focus.invalid,
+input[type=search].invalid,
+input[type=search]:focus.invalid,
+textarea.materialize-textarea.invalid,
+textarea.materialize-textarea:focus.invalid {
+ border-bottom: 1px solid #F44336;
+ box-shadow: 0 1px 0 0 #F44336;
+}
+
+input:not([type]).invalid + label:after,
+input:not([type]):focus.invalid + label:after,
+input[type=text].invalid + label:after,
+input[type=text]:focus.invalid + label:after,
+input[type=password].invalid + label:after,
+input[type=password]:focus.invalid + label:after,
+input[type=email].invalid + label:after,
+input[type=email]:focus.invalid + label:after,
+input[type=url].invalid + label:after,
+input[type=url]:focus.invalid + label:after,
+input[type=time].invalid + label:after,
+input[type=time]:focus.invalid + label:after,
+input[type=date].invalid + label:after,
+input[type=date]:focus.invalid + label:after,
+input[type=datetime].invalid + label:after,
+input[type=datetime]:focus.invalid + label:after,
+input[type=datetime-local].invalid + label:after,
+input[type=datetime-local]:focus.invalid + label:after,
+input[type=tel].invalid + label:after,
+input[type=tel]:focus.invalid + label:after,
+input[type=number].invalid + label:after,
+input[type=number]:focus.invalid + label:after,
+input[type=search].invalid + label:after,
+input[type=search]:focus.invalid + label:after,
+textarea.materialize-textarea.invalid + label:after,
+textarea.materialize-textarea:focus.invalid + label:after {
+ content: attr(data-error);
+ color: #F44336;
+ opacity: 1;
+}
+
+input:not([type]).validate + label,
+input[type=text].validate + label,
+input[type=password].validate + label,
+input[type=email].validate + label,
+input[type=url].validate + label,
+input[type=time].validate + label,
+input[type=date].validate + label,
+input[type=datetime].validate + label,
+input[type=datetime-local].validate + label,
+input[type=tel].validate + label,
+input[type=number].validate + label,
+input[type=search].validate + label,
+textarea.materialize-textarea.validate + label {
+ width: 100%;
+ pointer-events: none;
+}
+
+input:not([type]) + label:after,
+input[type=text] + label:after,
+input[type=password] + label:after,
+input[type=email] + label:after,
+input[type=url] + label:after,
+input[type=time] + label:after,
+input[type=date] + label:after,
+input[type=datetime] + label:after,
+input[type=datetime-local] + label:after,
+input[type=tel] + label:after,
+input[type=number] + label:after,
+input[type=search] + label:after,
+textarea.materialize-textarea + label:after {
+ display: block;
+ content: "";
+ position: absolute;
+ top: 60px;
+ opacity: 0;
+ transition: .2s opacity ease-out, .2s color ease-out;
+}
+
+.input-field {
+ position: relative;
+ margin-top: 1rem;
+}
+
+.input-field.inline {
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 5px;
+}
+
+.input-field.inline input,
+.input-field.inline .select-dropdown {
+ margin-bottom: 1rem;
+}
+
+.input-field.col label {
+ left: 0.75rem;
+}
+
+.input-field.col .prefix ~ label,
+.input-field.col .prefix ~ .validate ~ label {
+ width: calc(100% - 3rem - 1.5rem);
+}
+
+.input-field label {
+ color: #9e9e9e;
+ position: absolute;
+ top: 0.8rem;
+ left: 0;
+ font-size: 1rem;
+ cursor: text;
+ transition: .2s ease-out;
+}
+
+.input-field label:not(.label-icon).active {
+ font-size: 0.8rem;
+ -webkit-transform: translateY(-140%);
+ transform: translateY(-140%);
+}
+
+.input-field .prefix {
+ position: absolute;
+ width: 3rem;
+ font-size: 2rem;
+ transition: color .2s;
+}
+
+.input-field .prefix.active {
+ color: #26a69a;
+}
+
+.input-field .prefix ~ input,
+.input-field .prefix ~ textarea,
+.input-field .prefix ~ label,
+.input-field .prefix ~ .validate ~ label,
+.input-field .prefix ~ .autocomplete-content {
+ margin-left: 3rem;
+ width: 92%;
+ width: calc(100% - 3rem);
+}
+
+.input-field .prefix ~ label {
+ margin-left: 3rem;
+}
+
+@media only screen and (max-width: 992px) {
+ .input-field .prefix ~ input {
+ width: 86%;
+ width: calc(100% - 3rem);
+ }
+}
+
+@media only screen and (max-width: 600px) {
+ .input-field .prefix ~ input {
+ width: 80%;
+ width: calc(100% - 3rem);
+ }
+}
+
+/* Search Field */
+.input-field input[type=search] {
+ display: block;
+ line-height: inherit;
+ padding-left: 4rem;
+ width: calc(100% - 4rem);
+}
+
+.input-field input[type=search]:focus {
+ background-color: #fff;
+ border: 0;
+ box-shadow: none;
+ color: #444;
+}
+
+.input-field input[type=search]:focus + label i,
+.input-field input[type=search]:focus ~ .mdi-navigation-close,
+.input-field input[type=search]:focus ~ .material-icons {
+ color: #444;
+}
+
+.input-field input[type=search] + label {
+ left: 1rem;
+}
+
+.input-field input[type=search] ~ .mdi-navigation-close,
+.input-field input[type=search] ~ .material-icons {
+ position: absolute;
+ top: 0;
+ right: 1rem;
+ color: transparent;
+ cursor: pointer;
+ font-size: 2rem;
+ transition: .3s color;
+}
+
+/* Textarea */
+textarea {
+ width: 100%;
+ height: 3rem;
+ background-color: transparent;
+}
+
+textarea.materialize-textarea {
+ overflow-y: hidden;
+ /* prevents scroll bar flash */
+ padding: .8rem 0 1.6rem 0;
+ /* prevents text jump on Enter keypress */
+ resize: none;
+ min-height: 3rem;
+}
+
+.hiddendiv {
+ display: none;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ /* future version of deprecated 'word-wrap' */
+ padding-top: 1.2rem;
+ /* prevents text jump on Enter keypress */
+}
+
+/* Autocomplete */
+.autocomplete-content {
+ margin-top: -15px;
+ display: block;
+ opacity: 1;
+ position: static;
+}
+
+.autocomplete-content li .highlight {
+ color: #444;
+}
+
+.autocomplete-content li img {
+ height: 40px;
+ width: 40px;
+ margin: 5px 15px;
+}
+
+/* Radio Buttons
+ ========================================================================== */
+[type="radio"]:not(:checked),
+[type="radio"]:checked {
+ position: absolute;
+ left: -9999px;
+ opacity: 0;
+}
+
+[type="radio"]:not(:checked) + label,
+[type="radio"]:checked + label {
+ position: relative;
+ padding-left: 35px;
+ cursor: pointer;
+ display: inline-block;
+ height: 25px;
+ line-height: 25px;
+ font-size: 1rem;
+ transition: .28s ease;
+ /* webkit (konqueror) browsers */
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+[type="radio"] + label:before,
+[type="radio"] + label:after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin: 4px;
+ width: 16px;
+ height: 16px;
+ z-index: 0;
+ transition: .28s ease;
+}
+
+/* Unchecked styles */
+[type="radio"]:not(:checked) + label:before,
+[type="radio"]:not(:checked) + label:after,
+[type="radio"]:checked + label:before,
+[type="radio"]:checked + label:after,
+[type="radio"].with-gap:checked + label:before,
+[type="radio"].with-gap:checked + label:after {
+ border-radius: 50%;
+}
+
+[type="radio"]:not(:checked) + label:before,
+[type="radio"]:not(:checked) + label:after {
+ border: 2px solid #5a5a5a;
+}
+
+[type="radio"]:not(:checked) + label:after {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+}
+
+/* Checked styles */
+[type="radio"]:checked + label:before {
+ border: 2px solid transparent;
+}
+
+[type="radio"]:checked + label:after,
+[type="radio"].with-gap:checked + label:before,
+[type="radio"].with-gap:checked + label:after {
+ border: 2px solid #26a69a;
+}
+
+[type="radio"]:checked + label:after,
+[type="radio"].with-gap:checked + label:after {
+ background-color: #26a69a;
+}
+
+[type="radio"]:checked + label:after {
+ -webkit-transform: scale(1.02);
+ transform: scale(1.02);
+}
+
+/* Radio With gap */
+[type="radio"].with-gap:checked + label:after {
+ -webkit-transform: scale(0.5);
+ transform: scale(0.5);
+}
+
+/* Focused styles */
+[type="radio"].tabbed:focus + label:before {
+ box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
+}
+
+/* Disabled Radio With gap */
+[type="radio"].with-gap:disabled:checked + label:before {
+ border: 2px solid rgba(0, 0, 0, 0.26);
+}
+
+[type="radio"].with-gap:disabled:checked + label:after {
+ border: none;
+ background-color: rgba(0, 0, 0, 0.26);
+}
+
+/* Disabled style */
+[type="radio"]:disabled:not(:checked) + label:before,
+[type="radio"]:disabled:checked + label:before {
+ background-color: transparent;
+ border-color: rgba(0, 0, 0, 0.26);
+}
+
+[type="radio"]:disabled + label {
+ color: rgba(0, 0, 0, 0.26);
+}
+
+[type="radio"]:disabled:not(:checked) + label:before {
+ border-color: rgba(0, 0, 0, 0.26);
+}
+
+[type="radio"]:disabled:checked + label:after {
+ background-color: rgba(0, 0, 0, 0.26);
+ border-color: #BDBDBD;
+}
+
+/* Checkboxes
+ ========================================================================== */
+/* CUSTOM CSS CHECKBOXES */
+form p {
+ margin-bottom: 10px;
+ text-align: left;
+}
+
+form p:last-child {
+ margin-bottom: 0;
+}
+
+/* Remove default checkbox */
+[type="checkbox"]:not(:checked),
+[type="checkbox"]:checked {
+ position: absolute;
+ left: -9999px;
+ opacity: 0;
+}
+
+[type="checkbox"] {
+ /* checkbox aspect */
+}
+
+[type="checkbox"] + label {
+ position: relative;
+ padding-left: 35px;
+ cursor: pointer;
+ display: inline-block;
+ height: 25px;
+ line-height: 25px;
+ font-size: 1rem;
+ -webkit-user-select: none;
+ /* webkit (safari, chrome) browsers */
+ -moz-user-select: none;
+ /* mozilla browsers */
+ -khtml-user-select: none;
+ /* webkit (konqueror) browsers */
+ -ms-user-select: none;
+ /* IE10+ */
+}
+
+[type="checkbox"] + label:before,
+[type="checkbox"]:not(.filled-in) + label:after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 18px;
+ height: 18px;
+ z-index: 0;
+ border: 2px solid #5a5a5a;
+ border-radius: 1px;
+ margin-top: 2px;
+ transition: .2s;
+}
+
+[type="checkbox"]:not(.filled-in) + label:after {
+ border: 0;
+ -webkit-transform: scale(0);
+ transform: scale(0);
+}
+
+[type="checkbox"]:not(:checked):disabled + label:before {
+ border: none;
+ background-color: rgba(0, 0, 0, 0.26);
+}
+
+[type="checkbox"].tabbed:focus + label:after {
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ border: 0;
+ border-radius: 50%;
+ box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+[type="checkbox"]:checked + label:before {
+ top: -4px;
+ left: -5px;
+ width: 12px;
+ height: 22px;
+ border-top: 2px solid transparent;
+ border-left: 2px solid transparent;
+ border-right: 2px solid #26a69a;
+ border-bottom: 2px solid #26a69a;
+ -webkit-transform: rotate(40deg);
+ transform: rotate(40deg);
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-transform-origin: 100% 100%;
+ transform-origin: 100% 100%;
+}
+
+[type="checkbox"]:checked:disabled + label:before {
+ border-right: 2px solid rgba(0, 0, 0, 0.26);
+ border-bottom: 2px solid rgba(0, 0, 0, 0.26);
+}
+
+/* Indeterminate checkbox */
+[type="checkbox"]:indeterminate + label:before {
+ top: -11px;
+ left: -12px;
+ width: 10px;
+ height: 22px;
+ border-top: none;
+ border-left: none;
+ border-right: 2px solid #26a69a;
+ border-bottom: none;
+ -webkit-transform: rotate(90deg);
+ transform: rotate(90deg);
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-transform-origin: 100% 100%;
+ transform-origin: 100% 100%;
+}
+
+[type="checkbox"]:indeterminate:disabled + label:before {
+ border-right: 2px solid rgba(0, 0, 0, 0.26);
+ background-color: transparent;
+}
+
+[type="checkbox"].filled-in + label:after {
+ border-radius: 2px;
+}
+
+[type="checkbox"].filled-in + label:before,
+[type="checkbox"].filled-in + label:after {
+ content: '';
+ left: 0;
+ position: absolute;
+ /* .1s delay is for check animation */
+ transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;
+ z-index: 1;
+}
+
+[type="checkbox"].filled-in:not(:checked) + label:before {
+ width: 0;
+ height: 0;
+ border: 3px solid transparent;
+ left: 6px;
+ top: 10px;
+ -webkit-transform: rotateZ(37deg);
+ transform: rotateZ(37deg);
+ -webkit-transform-origin: 20% 40%;
+ transform-origin: 100% 100%;
+}
+
+[type="checkbox"].filled-in:not(:checked) + label:after {
+ height: 20px;
+ width: 20px;
+ background-color: transparent;
+ border: 2px solid #5a5a5a;
+ top: 0px;
+ z-index: 0;
+}
+
+[type="checkbox"].filled-in:checked + label:before {
+ top: 0;
+ left: 1px;
+ width: 8px;
+ height: 13px;
+ border-top: 2px solid transparent;
+ border-left: 2px solid transparent;
+ border-right: 2px solid #fff;
+ border-bottom: 2px solid #fff;
+ -webkit-transform: rotateZ(37deg);
+ transform: rotateZ(37deg);
+ -webkit-transform-origin: 100% 100%;
+ transform-origin: 100% 100%;
+}
+
+[type="checkbox"].filled-in:checked + label:after {
+ top: 0;
+ width: 20px;
+ height: 20px;
+ border: 2px solid #26a69a;
+ background-color: #26a69a;
+ z-index: 0;
+}
+
+[type="checkbox"].filled-in.tabbed:focus + label:after {
+ border-radius: 2px;
+ border-color: #5a5a5a;
+ background-color: rgba(0, 0, 0, 0.1);
+}
+
+[type="checkbox"].filled-in.tabbed:checked:focus + label:after {
+ border-radius: 2px;
+ background-color: #26a69a;
+ border-color: #26a69a;
+}
+
+[type="checkbox"].filled-in:disabled:not(:checked) + label:before {
+ background-color: transparent;
+ border: 2px solid transparent;
+}
+
+[type="checkbox"].filled-in:disabled:not(:checked) + label:after {
+ border-color: transparent;
+ background-color: #BDBDBD;
+}
+
+[type="checkbox"].filled-in:disabled:checked + label:before {
+ background-color: transparent;
+}
+
+[type="checkbox"].filled-in:disabled:checked + label:after {
+ background-color: #BDBDBD;
+ border-color: #BDBDBD;
+}
+
+/* Switch
+ ========================================================================== */
+.switch,
+.switch * {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -ms-user-select: none;
+}
+
+.switch label {
+ cursor: pointer;
+}
+
+.switch label input[type=checkbox] {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.switch label input[type=checkbox]:checked + .lever {
+ background-color: #84c7c1;
+}
+
+.switch label input[type=checkbox]:checked + .lever:after {
+ background-color: #26a69a;
+ left: 24px;
+}
+
+.switch label .lever {
+ content: "";
+ display: inline-block;
+ position: relative;
+ width: 40px;
+ height: 15px;
+ background-color: #818181;
+ border-radius: 15px;
+ margin-right: 10px;
+ transition: background 0.3s ease;
+ vertical-align: middle;
+ margin: 0 16px;
+}
+
+.switch label .lever:after {
+ content: "";
+ position: absolute;
+ display: inline-block;
+ width: 21px;
+ height: 21px;
+ background-color: #F1F1F1;
+ border-radius: 21px;
+ box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4);
+ left: -5px;
+ top: -3px;
+ transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease;
+}
+
+input[type=checkbox]:checked:not(:disabled) ~ .lever:active::after,
+input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::after {
+ box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(38, 166, 154, 0.1);
+}
+
+input[type=checkbox]:not(:disabled) ~ .lever:active:after,
+input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::after {
+ box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 0, 0, 0.08);
+}
+
+.switch input[type=checkbox][disabled] + .lever {
+ cursor: default;
+}
+
+.switch label input[type=checkbox][disabled] + .lever:after,
+.switch label input[type=checkbox][disabled]:checked + .lever:after {
+ background-color: #BDBDBD;
+}
+
+/* Select Field
+ ========================================================================== */
+select {
+ display: none;
+}
+
+select.browser-default {
+ display: block;
+}
+
+select {
+ background-color: rgba(255, 255, 255, 0.9);
+ width: 100%;
+ padding: 5px;
+ border: 1px solid #f2f2f2;
+ border-radius: 2px;
+ height: 3rem;
+}
+
+.select-label {
+ position: absolute;
+}
+
+.select-wrapper {
+ position: relative;
+}
+
+.select-wrapper input.select-dropdown {
+ position: relative;
+ cursor: pointer;
+ background-color: transparent;
+ border: none;
+ border-bottom: 1px solid #9e9e9e;
+ outline: none;
+ height: 3rem;
+ line-height: 3rem;
+ width: 100%;
+ font-size: 1rem;
+ margin: 0 0 20px 0;
+ padding: 0;
+ display: block;
+}
+
+.select-wrapper span.caret {
+ color: initial;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ height: 10px;
+ margin: auto 0;
+ font-size: 10px;
+ line-height: 10px;
+}
+
+.select-wrapper span.caret.disabled {
+ color: rgba(0, 0, 0, 0.26);
+}
+
+.select-wrapper + label {
+ position: absolute;
+ top: -14px;
+ font-size: 0.8rem;
+}
+
+select:disabled {
+ color: rgba(0, 0, 0, 0.3);
+}
+
+.select-wrapper input.select-dropdown:disabled {
+ color: rgba(0, 0, 0, 0.3);
+ cursor: default;
+ -webkit-user-select: none;
+ /* webkit (safari, chrome) browsers */
+ -moz-user-select: none;
+ /* mozilla browsers */
+ -ms-user-select: none;
+ /* IE10+ */
+ border-bottom: 1px solid rgba(0, 0, 0, 0.3);
+}
+
+.select-wrapper i {
+ color: rgba(0, 0, 0, 0.3);
+}
+
+.select-dropdown li.disabled,
+.select-dropdown li.disabled > span,
+.select-dropdown li.optgroup {
+ color: rgba(0, 0, 0, 0.3);
+ background-color: transparent;
+}
+
+.prefix ~ .select-wrapper {
+ margin-left: 3rem;
+ width: 92%;
+ width: calc(100% - 3rem);
+}
+
+.prefix ~ label {
+ margin-left: 3rem;
+}
+
+.select-dropdown li img {
+ height: 40px;
+ width: 40px;
+ margin: 5px 15px;
+ float: right;
+}
+
+.select-dropdown li.optgroup {
+ border-top: 1px solid #eee;
+}
+
+.select-dropdown li.optgroup.selected > span {
+ color: rgba(0, 0, 0, 0.7);
+}
+
+.select-dropdown li.optgroup > span {
+ color: rgba(0, 0, 0, 0.4);
+}
+
+.select-dropdown li.optgroup ~ li.optgroup-option {
+ padding-left: 1rem;
+}
+
+/* File Input
+ ========================================================================== */
+.file-field {
+ position: relative;
+}
+
+.file-field .file-path-wrapper {
+ overflow: hidden;
+ padding-left: 10px;
+}
+
+.file-field input.file-path {
+ width: 100%;
+}
+
+.file-field .btn, .file-field .btn-large {
+ float: left;
+ height: 3rem;
+ line-height: 3rem;
+}
+
+.file-field span {
+ cursor: pointer;
+}
+
+.file-field input[type=file] {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ font-size: 20px;
+ cursor: pointer;
+ opacity: 0;
+ filter: alpha(opacity=0);
+}
+
+/* Range
+ ========================================================================== */
+.range-field {
+ position: relative;
+}
+
+input[type=range],
+input[type=range] + .thumb {
+ cursor: pointer;
+}
+
+input[type=range] {
+ position: relative;
+ background-color: transparent;
+ border: none;
+ outline: none;
+ width: 100%;
+ margin: 15px 0;
+ padding: 0;
+}
+
+input[type=range]:focus {
+ outline: none;
+}
+
+input[type=range] + .thumb {
+ position: absolute;
+ border: none;
+ height: 0;
+ width: 0;
+ border-radius: 50%;
+ background-color: #26a69a;
+ top: 10px;
+ margin-left: -6px;
+ -webkit-transform-origin: 50% 50%;
+ transform-origin: 50% 50%;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+
+input[type=range] + .thumb .value {
+ display: block;
+ width: 30px;
+ text-align: center;
+ color: #26a69a;
+ font-size: 0;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+input[type=range] + .thumb.active {
+ border-radius: 50% 50% 50% 0;
+}
+
+input[type=range] + .thumb.active .value {
+ color: #fff;
+ margin-left: -1px;
+ margin-top: 8px;
+ font-size: 10px;
+}
+
+input[type=range] {
+ -webkit-appearance: none;
+}
+
+input[type=range]::-webkit-slider-runnable-track {
+ height: 3px;
+ background: #c2c0c2;
+ border: none;
+}
+
+input[type=range]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ border: none;
+ height: 14px;
+ width: 14px;
+ border-radius: 50%;
+ background-color: #26a69a;
+ -webkit-transform-origin: 50% 50%;
+ transform-origin: 50% 50%;
+ margin: -5px 0 0 0;
+ transition: .3s;
+}
+
+input[type=range]:focus::-webkit-slider-runnable-track {
+ background: #ccc;
+}
+
+input[type=range] {
+ /* fix for FF unable to apply focus style bug */
+ border: 1px solid white;
+ /*required for proper track sizing in FF*/
+}
+
+input[type=range]::-moz-range-track {
+ height: 3px;
+ background: #ddd;
+ border: none;
+}
+
+input[type=range]::-moz-range-thumb {
+ border: none;
+ height: 14px;
+ width: 14px;
+ border-radius: 50%;
+ background: #26a69a;
+ margin-top: -5px;
+}
+
+input[type=range]:-moz-focusring {
+ outline: 1px solid #fff;
+ outline-offset: -1px;
+}
+
+input[type=range]:focus::-moz-range-track {
+ background: #ccc;
+}
+
+input[type=range]::-ms-track {
+ height: 3px;
+ background: transparent;
+ border-color: transparent;
+ border-width: 6px 0;
+ /*remove default tick marks*/
+ color: transparent;
+}
+
+input[type=range]::-ms-fill-lower {
+ background: #777;
+}
+
+input[type=range]::-ms-fill-upper {
+ background: #ddd;
+}
+
+input[type=range]::-ms-thumb {
+ border: none;
+ height: 14px;
+ width: 14px;
+ border-radius: 50%;
+ background: #26a69a;
+}
+
+input[type=range]:focus::-ms-fill-lower {
+ background: #888;
+}
+
+input[type=range]:focus::-ms-fill-upper {
+ background: #ccc;
+}
+
+/***************
+ Nav List
+***************/
+.table-of-contents.fixed {
+ position: fixed;
+}
+
+.table-of-contents li {
+ padding: 2px 0;
+}
+
+.table-of-contents a {
+ display: inline-block;
+ font-weight: 300;
+ color: #757575;
+ padding-left: 20px;
+ height: 1.5rem;
+ line-height: 1.5rem;
+ letter-spacing: .4;
+ display: inline-block;
+}
+
+.table-of-contents a:hover {
+ color: #a8a8a8;
+ padding-left: 19px;
+ border-left: 1px solid #ee6e73;
+}
+
+.table-of-contents a.active {
+ font-weight: 500;
+ padding-left: 18px;
+ border-left: 2px solid #ee6e73;
+}
+
+.side-nav {
+ position: fixed;
+ width: 300px;
+ left: 0;
+ top: 0;
+ margin: 0;
+ -webkit-transform: translateX(-100%);
+ transform: translateX(-100%);
+ height: 100%;
+ height: calc(100% + 60px);
+ height: -moz-calc(100%);
+ padding-bottom: 60px;
+ background-color: #fff;
+ z-index: 999;
+ overflow-y: auto;
+ will-change: transform;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-transform: translateX(-105%);
+ transform: translateX(-105%);
+}
+
+.side-nav.right-aligned {
+ right: 0;
+ -webkit-transform: translateX(105%);
+ transform: translateX(105%);
+ left: auto;
+ -webkit-transform: translateX(100%);
+ transform: translateX(100%);
+}
+
+.side-nav .collapsible {
+ margin: 0;
+}
+
+.side-nav li {
+ float: none;
+ line-height: 48px;
+}
+
+.side-nav li.active {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.side-nav a {
+ color: rgba(0, 0, 0, 0.87);
+ display: block;
+ font-size: 14px;
+ font-weight: 500;
+ height: 48px;
+ line-height: 48px;
+ padding: 0 32px;
+}
+
+.side-nav a:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.side-nav a.btn, .side-nav a.btn-large, .side-nav a.btn-large, .side-nav a.btn-flat, .side-nav a.btn-floating {
+ margin: 10px 15px;
+}
+
+.side-nav a.btn, .side-nav a.btn-large, .side-nav a.btn-large, .side-nav a.btn-floating {
+ color: #fff;
+}
+
+.side-nav a.btn-flat {
+ color: #343434;
+}
+
+.side-nav a.btn:hover, .side-nav a.btn-large:hover, .side-nav a.btn-large:hover {
+ background-color: #2bbbad;
+}
+
+.side-nav a.btn-floating:hover {
+ background-color: #26a69a;
+}
+
+.side-nav li > a > i,
+.side-nav li > a > [class^="mdi-"], .side-nav li > a > [class*="mdi-"],
+.side-nav li > a > i.material-icons {
+ float: left;
+ height: 48px;
+ line-height: 48px;
+ margin: 0 32px 0 0;
+ width: 24px;
+ color: rgba(0, 0, 0, 0.54);
+}
+
+.side-nav .divider {
+ margin: 8px 0 0 0;
+}
+
+.side-nav .subheader {
+ cursor: initial;
+ pointer-events: none;
+ color: rgba(0, 0, 0, 0.54);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 48px;
+}
+
+.side-nav .subheader:hover {
+ background-color: transparent;
+}
+
+.side-nav .userView {
+ position: relative;
+ padding: 32px 32px 0;
+ margin-bottom: 8px;
+}
+
+.side-nav .userView > a {
+ height: auto;
+ padding: 0;
+}
+
+.side-nav .userView > a:hover {
+ background-color: transparent;
+}
+
+.side-nav .userView .background {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: -1;
+}
+
+.side-nav .userView .circle, .side-nav .userView .name, .side-nav .userView .email {
+ display: block;
+}
+
+.side-nav .userView .circle {
+ height: 64px;
+ width: 64px;
+}
+
+.side-nav .userView .name,
+.side-nav .userView .email {
+ font-size: 14px;
+ line-height: 24px;
+}
+
+.side-nav .userView .name {
+ margin-top: 16px;
+ font-weight: 500;
+}
+
+.side-nav .userView .email {
+ padding-bottom: 16px;
+ font-weight: 400;
+}
+
+.drag-target {
+ height: 100%;
+ width: 10px;
+ position: fixed;
+ top: 0;
+ z-index: 998;
+}
+
+.side-nav.fixed {
+ left: 0;
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ position: fixed;
+}
+
+.side-nav.fixed.right-aligned {
+ right: 0;
+ left: auto;
+}
+
+@media only screen and (max-width: 992px) {
+ .side-nav.fixed {
+ -webkit-transform: translateX(-105%);
+ transform: translateX(-105%);
+ }
+ .side-nav.fixed.right-aligned {
+ -webkit-transform: translateX(105%);
+ transform: translateX(105%);
+ }
+ .side-nav a {
+ padding: 0 16px;
+ }
+ .side-nav .userView {
+ padding: 16px 16px 0;
+ }
+}
+
+.side-nav .collapsible-body > ul:not(.collapsible) > li.active,
+.side-nav.fixed .collapsible-body > ul:not(.collapsible) > li.active {
+ background-color: #ee6e73;
+}
+
+.side-nav .collapsible-body > ul:not(.collapsible) > li.active a,
+.side-nav.fixed .collapsible-body > ul:not(.collapsible) > li.active a {
+ color: #fff;
+}
+
+#sidenav-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 120vh;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 997;
+ will-change: opacity;
+}
+
+/*
+ @license
+ Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ Code distributed by Google as part of the polymer project is also
+ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+/**************************/
+/* STYLES FOR THE SPINNER */
+/**************************/
+/*
+ * Constants:
+ * STROKEWIDTH = 3px
+ * ARCSIZE = 270 degrees (amount of circle the arc takes up)
+ * ARCTIME = 1333ms (time it takes to expand and contract arc)
+ * ARCSTARTROT = 216 degrees (how much the start location of the arc
+ * should rotate each time, 216 gives us a
+ * 5 pointed star shape (it's 360/5 * 3).
+ * For a 7 pointed star, we might do
+ * 360/7 * 3 = 154.286)
+ * CONTAINERWIDTH = 28px
+ * SHRINK_TIME = 400ms
+ */
+.preloader-wrapper {
+ display: inline-block;
+ position: relative;
+ width: 48px;
+ height: 48px;
+}
+
+.preloader-wrapper.small {
+ width: 36px;
+ height: 36px;
+}
+
+.preloader-wrapper.big {
+ width: 64px;
+ height: 64px;
+}
+
+.preloader-wrapper.active {
+ /* duration: 360 * ARCTIME / (ARCSTARTROT + (360-ARCSIZE)) */
+ -webkit-animation: container-rotate 1568ms linear infinite;
+ animation: container-rotate 1568ms linear infinite;
+}
+
+@-webkit-keyframes container-rotate {
+ to {
+ -webkit-transform: rotate(360deg);
+ }
+}
+
+@keyframes container-rotate {
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.spinner-layer {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ border-color: #26a69a;
+}
+
+.spinner-blue,
+.spinner-blue-only {
+ border-color: #4285f4;
+}
+
+.spinner-red,
+.spinner-red-only {
+ border-color: #db4437;
+}
+
+.spinner-yellow,
+.spinner-yellow-only {
+ border-color: #f4b400;
+}
+
+.spinner-green,
+.spinner-green-only {
+ border-color: #0f9d58;
+}
+
+/**
+ * IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
+ *
+ * iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
+ * guarantee that the animation will start _exactly_ after that value. So we avoid using
+ * animation-delay and instead set custom keyframes for each color (as redundant as it
+ * seems).
+ *
+ * We write out each animation in full (instead of separating animation-name,
+ * animation-duration, etc.) because under the polyfill, Safari does not recognize those
+ * specific properties properly, treats them as -webkit-animation, and overrides the
+ * other animation rules. See https://github.com/Polymer/platform/issues/53.
+ */
+.active .spinner-layer.spinner-blue {
+ /* durations: 4 * ARCTIME */
+ -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+.active .spinner-layer.spinner-red {
+ /* durations: 4 * ARCTIME */
+ -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+.active .spinner-layer.spinner-yellow {
+ /* durations: 4 * ARCTIME */
+ -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+.active .spinner-layer.spinner-green {
+ /* durations: 4 * ARCTIME */
+ -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both, green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+.active .spinner-layer,
+.active .spinner-layer.spinner-blue-only,
+.active .spinner-layer.spinner-red-only,
+.active .spinner-layer.spinner-yellow-only,
+.active .spinner-layer.spinner-green-only {
+ /* durations: 4 * ARCTIME */
+ opacity: 1;
+ -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+@-webkit-keyframes fill-unfill-rotate {
+ 12.5% {
+ -webkit-transform: rotate(135deg);
+ }
+ /* 0.5 * ARCSIZE */
+ 25% {
+ -webkit-transform: rotate(270deg);
+ }
+ /* 1 * ARCSIZE */
+ 37.5% {
+ -webkit-transform: rotate(405deg);
+ }
+ /* 1.5 * ARCSIZE */
+ 50% {
+ -webkit-transform: rotate(540deg);
+ }
+ /* 2 * ARCSIZE */
+ 62.5% {
+ -webkit-transform: rotate(675deg);
+ }
+ /* 2.5 * ARCSIZE */
+ 75% {
+ -webkit-transform: rotate(810deg);
+ }
+ /* 3 * ARCSIZE */
+ 87.5% {
+ -webkit-transform: rotate(945deg);
+ }
+ /* 3.5 * ARCSIZE */
+ to {
+ -webkit-transform: rotate(1080deg);
+ }
+ /* 4 * ARCSIZE */
+}
+
+@keyframes fill-unfill-rotate {
+ 12.5% {
+ -webkit-transform: rotate(135deg);
+ transform: rotate(135deg);
+ }
+ /* 0.5 * ARCSIZE */
+ 25% {
+ -webkit-transform: rotate(270deg);
+ transform: rotate(270deg);
+ }
+ /* 1 * ARCSIZE */
+ 37.5% {
+ -webkit-transform: rotate(405deg);
+ transform: rotate(405deg);
+ }
+ /* 1.5 * ARCSIZE */
+ 50% {
+ -webkit-transform: rotate(540deg);
+ transform: rotate(540deg);
+ }
+ /* 2 * ARCSIZE */
+ 62.5% {
+ -webkit-transform: rotate(675deg);
+ transform: rotate(675deg);
+ }
+ /* 2.5 * ARCSIZE */
+ 75% {
+ -webkit-transform: rotate(810deg);
+ transform: rotate(810deg);
+ }
+ /* 3 * ARCSIZE */
+ 87.5% {
+ -webkit-transform: rotate(945deg);
+ transform: rotate(945deg);
+ }
+ /* 3.5 * ARCSIZE */
+ to {
+ -webkit-transform: rotate(1080deg);
+ transform: rotate(1080deg);
+ }
+ /* 4 * ARCSIZE */
+}
+
+@-webkit-keyframes blue-fade-in-out {
+ from {
+ opacity: 1;
+ }
+ 25% {
+ opacity: 1;
+ }
+ 26% {
+ opacity: 0;
+ }
+ 89% {
+ opacity: 0;
+ }
+ 90% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes blue-fade-in-out {
+ from {
+ opacity: 1;
+ }
+ 25% {
+ opacity: 1;
+ }
+ 26% {
+ opacity: 0;
+ }
+ 89% {
+ opacity: 0;
+ }
+ 90% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@-webkit-keyframes red-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 15% {
+ opacity: 0;
+ }
+ 25% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 51% {
+ opacity: 0;
+ }
+}
+
+@keyframes red-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 15% {
+ opacity: 0;
+ }
+ 25% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 51% {
+ opacity: 0;
+ }
+}
+
+@-webkit-keyframes yellow-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 40% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 75% {
+ opacity: 1;
+ }
+ 76% {
+ opacity: 0;
+ }
+}
+
+@keyframes yellow-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 40% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 75% {
+ opacity: 1;
+ }
+ 76% {
+ opacity: 0;
+ }
+}
+
+@-webkit-keyframes green-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 65% {
+ opacity: 0;
+ }
+ 75% {
+ opacity: 1;
+ }
+ 90% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes green-fade-in-out {
+ from {
+ opacity: 0;
+ }
+ 65% {
+ opacity: 0;
+ }
+ 75% {
+ opacity: 1;
+ }
+ 90% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+/**
+ * Patch the gap that appear between the two adjacent div.circle-clipper while the
+ * spinner is rotating (appears on Chrome 38, Safari 7.1, and IE 11).
+ */
+.gap-patch {
+ position: absolute;
+ top: 0;
+ left: 45%;
+ width: 10%;
+ height: 100%;
+ overflow: hidden;
+ border-color: inherit;
+}
+
+.gap-patch .circle {
+ width: 1000%;
+ left: -450%;
+}
+
+.circle-clipper {
+ display: inline-block;
+ position: relative;
+ width: 50%;
+ height: 100%;
+ overflow: hidden;
+ border-color: inherit;
+}
+
+.circle-clipper .circle {
+ width: 200%;
+ height: 100%;
+ border-width: 3px;
+ /* STROKEWIDTH */
+ border-style: solid;
+ border-color: inherit;
+ border-bottom-color: transparent !important;
+ border-radius: 50%;
+ -webkit-animation: none;
+ animation: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.circle-clipper.left .circle {
+ left: 0;
+ border-right-color: transparent !important;
+ -webkit-transform: rotate(129deg);
+ transform: rotate(129deg);
+}
+
+.circle-clipper.right .circle {
+ left: -100%;
+ border-left-color: transparent !important;
+ -webkit-transform: rotate(-129deg);
+ transform: rotate(-129deg);
+}
+
+.active .circle-clipper.left .circle {
+ /* duration: ARCTIME */
+ -webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+.active .circle-clipper.right .circle {
+ /* duration: ARCTIME */
+ -webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+ animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;
+}
+
+@-webkit-keyframes left-spin {
+ from {
+ -webkit-transform: rotate(130deg);
+ }
+ 50% {
+ -webkit-transform: rotate(-5deg);
+ }
+ to {
+ -webkit-transform: rotate(130deg);
+ }
+}
+
+@keyframes left-spin {
+ from {
+ -webkit-transform: rotate(130deg);
+ transform: rotate(130deg);
+ }
+ 50% {
+ -webkit-transform: rotate(-5deg);
+ transform: rotate(-5deg);
+ }
+ to {
+ -webkit-transform: rotate(130deg);
+ transform: rotate(130deg);
+ }
+}
+
+@-webkit-keyframes right-spin {
+ from {
+ -webkit-transform: rotate(-130deg);
+ }
+ 50% {
+ -webkit-transform: rotate(5deg);
+ }
+ to {
+ -webkit-transform: rotate(-130deg);
+ }
+}
+
+@keyframes right-spin {
+ from {
+ -webkit-transform: rotate(-130deg);
+ transform: rotate(-130deg);
+ }
+ 50% {
+ -webkit-transform: rotate(5deg);
+ transform: rotate(5deg);
+ }
+ to {
+ -webkit-transform: rotate(-130deg);
+ transform: rotate(-130deg);
+ }
+}
+
+#spinnerContainer.cooldown {
+ /* duration: SHRINK_TIME */
+ -webkit-animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1);
+ animation: container-rotate 1568ms linear infinite, fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+@-webkit-keyframes fade-out {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+@keyframes fade-out {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+.slider {
+ position: relative;
+ height: 400px;
+ width: 100%;
+}
+
+.slider.fullscreen {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.slider.fullscreen ul.slides {
+ height: 100%;
+}
+
+.slider.fullscreen ul.indicators {
+ z-index: 2;
+ bottom: 30px;
+}
+
+.slider .slides {
+ background-color: #9e9e9e;
+ margin: 0;
+ height: 400px;
+}
+
+.slider .slides li {
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ width: 100%;
+ height: inherit;
+ overflow: hidden;
+}
+
+.slider .slides li img {
+ height: 100%;
+ width: 100%;
+ background-size: cover;
+ background-position: center;
+}
+
+.slider .slides li .caption {
+ color: #fff;
+ position: absolute;
+ top: 15%;
+ left: 15%;
+ width: 70%;
+ opacity: 0;
+}
+
+.slider .slides li .caption p {
+ color: #e0e0e0;
+}
+
+.slider .slides li.active {
+ z-index: 2;
+}
+
+.slider .indicators {
+ position: absolute;
+ text-align: center;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ margin: 0;
+}
+
+.slider .indicators .indicator-item {
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+ height: 16px;
+ width: 16px;
+ margin: 0 12px;
+ background-color: #e0e0e0;
+ transition: background-color .3s;
+ border-radius: 50%;
+}
+
+.slider .indicators .indicator-item.active {
+ background-color: #4CAF50;
+}
+
+.carousel {
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+ height: 400px;
+ -webkit-perspective: 500px;
+ perspective: 500px;
+ -webkit-transform-style: preserve-3d;
+ transform-style: preserve-3d;
+ -webkit-transform-origin: 0% 50%;
+ transform-origin: 0% 50%;
+}
+
+.carousel.carousel-slider {
+ top: 0;
+ left: 0;
+ height: 0;
+}
+
+.carousel.carousel-slider .carousel-fixed-item {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 20px;
+ z-index: 1;
+}
+
+.carousel.carousel-slider .carousel-fixed-item.with-indicators {
+ bottom: 68px;
+}
+
+.carousel.carousel-slider .carousel-item {
+ width: 100%;
+ height: 100%;
+ min-height: 400px;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.carousel.carousel-slider .carousel-item h2 {
+ font-size: 24px;
+ font-weight: 500;
+ line-height: 32px;
+}
+
+.carousel.carousel-slider .carousel-item p {
+ font-size: 15px;
+}
+
+.carousel .carousel-item {
+ display: none;
+ width: 200px;
+ height: 200px;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.carousel .carousel-item img {
+ width: 100%;
+}
+
+.carousel .indicators {
+ position: absolute;
+ text-align: center;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ margin: 0;
+}
+
+.carousel .indicators .indicator-item {
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+ height: 8px;
+ width: 8px;
+ margin: 24px 4px;
+ background-color: rgba(255, 255, 255, 0.5);
+ transition: background-color .3s;
+ border-radius: 50%;
+}
+
+.carousel .indicators .indicator-item.active {
+ background-color: #fff;
+}
+
+/* ==========================================================================
+ $BASE-PICKER
+ ========================================================================== */
+/**
+ * Note: the root picker element should *NOT* be styled more than what's here.
+ */
+.picker {
+ font-size: 16px;
+ text-align: left;
+ line-height: 1.2;
+ color: #000000;
+ position: absolute;
+ z-index: 10000;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/**
+ * The picker input element.
+ */
+.picker__input {
+ cursor: default;
+}
+
+/**
+ * When the picker is opened, the input element is "activated".
+ */
+.picker__input.picker__input--active {
+ border-color: #0089ec;
+}
+
+/**
+ * The holder is the only "scrollable" top-level container element.
+ */
+.picker__holder {
+ width: 100%;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+/*!
+ * Default mobile-first, responsive styling for pickadate.js
+ * Demo: http://amsul.github.io/pickadate.js
+ */
+/**
+ * Note: the root picker element should *NOT* be styled more than what's here.
+ */
+/**
+ * Make the holder and frame fullscreen.
+ */
+.picker__holder,
+.picker__frame {
+ bottom: 0;
+ left: 0;
+ right: 0;
+ top: 100%;
+}
+
+/**
+ * The holder should overlay the entire screen.
+ */
+.picker__holder {
+ position: fixed;
+ transition: background 0.15s ease-out, top 0s 0.15s;
+ -webkit-backface-visibility: hidden;
+}
+
+/**
+ * The frame that bounds the box contents of the picker.
+ */
+.picker__frame {
+ position: absolute;
+ margin: 0 auto;
+ min-width: 256px;
+ width: 300px;
+ max-height: 350px;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: alpha(opacity=0);
+ -moz-opacity: 0;
+ opacity: 0;
+ transition: all 0.15s ease-out;
+}
+
+@media (min-height: 28.875em) {
+ .picker__frame {
+ overflow: visible;
+ top: auto;
+ bottom: -100%;
+ max-height: 80%;
+ }
+}
+
+@media (min-height: 40.125em) {
+ .picker__frame {
+ margin-bottom: 7.5%;
+ }
+}
+
+/**
+ * The wrapper sets the stage to vertically align the box contents.
+ */
+.picker__wrap {
+ display: table;
+ width: 100%;
+ height: 100%;
+}
+
+@media (min-height: 28.875em) {
+ .picker__wrap {
+ display: block;
+ }
+}
+
+/**
+ * The box contains all the picker contents.
+ */
+.picker__box {
+ background: #ffffff;
+ display: table-cell;
+ vertical-align: middle;
+}
+
+@media (min-height: 28.875em) {
+ .picker__box {
+ display: block;
+ border: 1px solid #777777;
+ border-top-color: #898989;
+ border-bottom-width: 0;
+ border-radius: 5px 5px 0 0;
+ box-shadow: 0 12px 36px 16px rgba(0, 0, 0, 0.24);
+ }
+}
+
+/**
+ * When the picker opens...
+ */
+.picker--opened .picker__holder {
+ top: 0;
+ background: transparent;
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E000000,endColorstr=#1E000000)";
+ zoom: 1;
+ background: rgba(0, 0, 0, 0.32);
+ transition: background 0.15s ease-out;
+}
+
+.picker--opened .picker__frame {
+ top: 0;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ filter: alpha(opacity=100);
+ -moz-opacity: 1;
+ opacity: 1;
+}
+
+@media (min-height: 35.875em) {
+ .picker--opened .picker__frame {
+ top: 10%;
+ bottom: auto;
+ }
+}
+
+/**
+ * For `large` screens, transform into an inline picker.
+ */
+/* ==========================================================================
+ CUSTOM MATERIALIZE STYLES
+ ========================================================================== */
+.picker__input.picker__input--active {
+ border-color: #E3F2FD;
+}
+
+.picker__frame {
+ margin: 0 auto;
+ max-width: 325px;
+}
+
+@media (min-height: 38.875em) {
+ .picker--opened .picker__frame {
+ top: 10%;
+ bottom: auto;
+ }
+}
+
+/* ==========================================================================
+ $BASE-DATE-PICKER
+ ========================================================================== */
+/**
+ * The picker box.
+ */
+.picker__box {
+ padding: 0 1em;
+}
+
+/**
+ * The header containing the month and year stuff.
+ */
+.picker__header {
+ text-align: center;
+ position: relative;
+ margin-top: .75em;
+}
+
+/**
+ * The month and year labels.
+ */
+.picker__month,
+.picker__year {
+ display: inline-block;
+ margin-left: .25em;
+ margin-right: .25em;
+}
+
+/**
+ * The month and year selectors.
+ */
+.picker__select--month,
+.picker__select--year {
+ height: 2em;
+ padding: 0;
+ margin-left: .25em;
+ margin-right: .25em;
+}
+
+.picker__select--month.browser-default {
+ display: inline;
+ background-color: #FFFFFF;
+ width: 40%;
+}
+
+.picker__select--year.browser-default {
+ display: inline;
+ background-color: #FFFFFF;
+ width: 26%;
+}
+
+.picker__select--month:focus,
+.picker__select--year:focus {
+ border-color: rgba(0, 0, 0, 0.05);
+}
+
+/**
+ * The month navigation buttons.
+ */
+.picker__nav--prev,
+.picker__nav--next {
+ position: absolute;
+ padding: .5em 1.25em;
+ width: 1em;
+ height: 1em;
+ box-sizing: content-box;
+ top: -0.25em;
+}
+
+.picker__nav--prev {
+ left: -1em;
+ padding-right: 1.25em;
+}
+
+.picker__nav--next {
+ right: -1em;
+ padding-left: 1.25em;
+}
+
+.picker__nav--disabled,
+.picker__nav--disabled:hover,
+.picker__nav--disabled:before,
+.picker__nav--disabled:before:hover {
+ cursor: default;
+ background: none;
+ border-right-color: #f5f5f5;
+ border-left-color: #f5f5f5;
+}
+
+/**
+ * The calendar table of dates
+ */
+.picker__table {
+ text-align: center;
+ border-collapse: collapse;
+ border-spacing: 0;
+ table-layout: fixed;
+ font-size: 1rem;
+ width: 100%;
+ margin-top: .75em;
+ margin-bottom: .5em;
+}
+
+.picker__table th, .picker__table td {
+ text-align: center;
+}
+
+.picker__table td {
+ margin: 0;
+ padding: 0;
+}
+
+/**
+ * The weekday labels
+ */
+.picker__weekday {
+ width: 14.285714286%;
+ font-size: .75em;
+ padding-bottom: .25em;
+ color: #999999;
+ font-weight: 500;
+ /* Increase the spacing a tad */
+}
+
+@media (min-height: 33.875em) {
+ .picker__weekday {
+ padding-bottom: .5em;
+ }
+}
+
+/**
+ * The days on the calendar
+ */
+.picker__day--today {
+ position: relative;
+ color: #595959;
+ letter-spacing: -.3;
+ padding: .75rem 0;
+ font-weight: 400;
+ border: 1px solid transparent;
+}
+
+.picker__day--disabled:before {
+ border-top-color: #aaaaaa;
+}
+
+.picker__day--infocus:hover {
+ cursor: pointer;
+ color: #000;
+ font-weight: 500;
+}
+
+.picker__day--outfocus {
+ display: none;
+ padding: .75rem 0;
+ color: #fff;
+}
+
+.picker__day--outfocus:hover {
+ cursor: pointer;
+ color: #dddddd;
+ font-weight: 500;
+}
+
+.picker__day--highlighted:hover,
+.picker--focused .picker__day--highlighted {
+ cursor: pointer;
+}
+
+.picker__day--selected,
+.picker__day--selected:hover,
+.picker--focused .picker__day--selected {
+ border-radius: 50%;
+ -webkit-transform: scale(0.75);
+ transform: scale(0.75);
+ background: #0089ec;
+ color: #ffffff;
+}
+
+.picker__day--disabled,
+.picker__day--disabled:hover,
+.picker--focused .picker__day--disabled {
+ background: #f5f5f5;
+ border-color: #f5f5f5;
+ color: #dddddd;
+ cursor: default;
+}
+
+.picker__day--highlighted.picker__day--disabled,
+.picker__day--highlighted.picker__day--disabled:hover {
+ background: #bbbbbb;
+}
+
+/**
+ * The footer containing the "today", "clear", and "close" buttons.
+ */
+.picker__footer {
+ text-align: center;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-justify-content: space-between;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+}
+
+.picker__button--today,
+.picker__button--clear,
+.picker__button--close {
+ border: 1px solid #ffffff;
+ background: #ffffff;
+ font-size: .8em;
+ padding: .66em 0;
+ font-weight: bold;
+ width: 33%;
+ display: inline-block;
+ vertical-align: bottom;
+}
+
+.picker__button--today:hover,
+.picker__button--clear:hover,
+.picker__button--close:hover {
+ cursor: pointer;
+ color: #000000;
+ background: #b1dcfb;
+ border-bottom-color: #b1dcfb;
+}
+
+.picker__button--today:focus,
+.picker__button--clear:focus,
+.picker__button--close:focus {
+ background: #b1dcfb;
+ border-color: rgba(0, 0, 0, 0.05);
+ outline: none;
+}
+
+.picker__button--today:before,
+.picker__button--clear:before,
+.picker__button--close:before {
+ position: relative;
+ display: inline-block;
+ height: 0;
+}
+
+.picker__button--today:before,
+.picker__button--clear:before {
+ content: " ";
+ margin-right: .45em;
+}
+
+.picker__button--today:before {
+ top: -0.05em;
+ width: 0;
+ border-top: 0.66em solid #0059bc;
+ border-left: .66em solid transparent;
+}
+
+.picker__button--clear:before {
+ top: -0.25em;
+ width: .66em;
+ border-top: 3px solid #ee2200;
+}
+
+.picker__button--close:before {
+ content: "\D7";
+ top: -0.1em;
+ vertical-align: top;
+ font-size: 1.1em;
+ margin-right: .35em;
+ color: #777777;
+}
+
+.picker__button--today[disabled],
+.picker__button--today[disabled]:hover {
+ background: #f5f5f5;
+ border-color: #f5f5f5;
+ color: #dddddd;
+ cursor: default;
+}
+
+.picker__button--today[disabled]:before {
+ border-top-color: #aaaaaa;
+}
+
+/* ==========================================================================
+ CUSTOM MATERIALIZE STYLES
+ ========================================================================== */
+.picker__box {
+ border-radius: 2px;
+ overflow: hidden;
+}
+
+.picker__date-display {
+ text-align: center;
+ background-color: #26a69a;
+ color: #fff;
+ padding-bottom: 15px;
+ font-weight: 300;
+}
+
+.picker__nav--prev:hover,
+.picker__nav--next:hover {
+ cursor: pointer;
+ color: #000000;
+ background: #a1ded8;
+}
+
+.picker__weekday-display {
+ background-color: #1f897f;
+ padding: 10px;
+ font-weight: 200;
+ letter-spacing: .5;
+ font-size: 1rem;
+ margin-bottom: 15px;
+}
+
+.picker__month-display {
+ text-transform: uppercase;
+ font-size: 2rem;
+}
+
+.picker__day-display {
+ font-size: 4.5rem;
+ font-weight: 400;
+}
+
+.picker__year-display {
+ font-size: 1.8rem;
+ color: rgba(255, 255, 255, 0.4);
+}
+
+.picker__box {
+ padding: 0;
+}
+
+.picker__calendar-container {
+ padding: 0 1rem;
+}
+
+.picker__calendar-container thead {
+ border: none;
+}
+
+.picker__table {
+ margin-top: 0;
+ margin-bottom: .5em;
+}
+
+.picker__day--infocus {
+ color: #595959;
+ letter-spacing: -.3;
+ padding: .75rem 0;
+ font-weight: 400;
+ border: 1px solid transparent;
+}
+
+.picker__day.picker__day--today {
+ color: #26a69a;
+}
+
+.picker__day.picker__day--today.picker__day--selected {
+ color: #fff;
+}
+
+.picker__weekday {
+ font-size: .9rem;
+}
+
+.picker__day--selected,
+.picker__day--selected:hover,
+.picker--focused .picker__day--selected {
+ border-radius: 50%;
+ -webkit-transform: scale(0.9);
+ transform: scale(0.9);
+ background-color: #26a69a;
+ color: #ffffff;
+}
+
+.picker__day--selected.picker__day--outfocus,
+.picker__day--selected:hover.picker__day--outfocus,
+.picker--focused .picker__day--selected.picker__day--outfocus {
+ background-color: #a1ded8;
+}
+
+.picker__footer {
+ text-align: right;
+ padding: 5px 10px;
+}
+
+.picker__close, .picker__today {
+ font-size: 1.1rem;
+ padding: 0 1rem;
+ color: #26a69a;
+}
+
+.picker__nav--prev:before,
+.picker__nav--next:before {
+ content: " ";
+ border-top: .5em solid transparent;
+ border-bottom: .5em solid transparent;
+ border-right: 0.75em solid #676767;
+ width: 0;
+ height: 0;
+ display: block;
+ margin: 0 auto;
+}
+
+.picker__nav--next:before {
+ border-right: 0;
+ border-left: 0.75em solid #676767;
+}
+
+button.picker__today:focus, button.picker__clear:focus, button.picker__close:focus {
+ background-color: #a1ded8;
+}
+
+/* ==========================================================================
+ $BASE-TIME-PICKER
+ ========================================================================== */
+/**
+ * The list of times.
+ */
+.picker__list {
+ list-style: none;
+ padding: 0.75em 0 4.2em;
+ margin: 0;
+}
+
+/**
+ * The times on the clock.
+ */
+.picker__list-item {
+ border-bottom: 1px solid #dddddd;
+ border-top: 1px solid #dddddd;
+ margin-bottom: -1px;
+ position: relative;
+ background: #ffffff;
+ padding: .75em 1.25em;
+}
+
+@media (min-height: 46.75em) {
+ .picker__list-item {
+ padding: .5em 1em;
+ }
+}
+
+/* Hovered time */
+.picker__list-item:hover {
+ cursor: pointer;
+ color: #000000;
+ background: #b1dcfb;
+ border-color: #0089ec;
+ z-index: 10;
+}
+
+/* Highlighted and hovered/focused time */
+.picker__list-item--highlighted {
+ border-color: #0089ec;
+ z-index: 10;
+}
+
+.picker__list-item--highlighted:hover,
+.picker--focused .picker__list-item--highlighted {
+ cursor: pointer;
+ color: #000000;
+ background: #b1dcfb;
+}
+
+/* Selected and hovered/focused time */
+.picker__list-item--selected,
+.picker__list-item--selected:hover,
+.picker--focused .picker__list-item--selected {
+ background: #0089ec;
+ color: #ffffff;
+ z-index: 10;
+}
+
+/* Disabled time */
+.picker__list-item--disabled,
+.picker__list-item--disabled:hover,
+.picker--focused .picker__list-item--disabled {
+ background: #f5f5f5;
+ border-color: #f5f5f5;
+ color: #dddddd;
+ cursor: default;
+ border-color: #dddddd;
+ z-index: auto;
+}
+
+/**
+ * The clear button
+ */
+.picker--time .picker__button--clear {
+ display: block;
+ width: 80%;
+ margin: 1em auto 0;
+ padding: 1em 1.25em;
+ background: none;
+ border: 0;
+ font-weight: 500;
+ font-size: .67em;
+ text-align: center;
+ text-transform: uppercase;
+ color: #666;
+}
+
+.picker--time .picker__button--clear:hover,
+.picker--time .picker__button--clear:focus {
+ color: #000000;
+ background: #b1dcfb;
+ background: #ee2200;
+ border-color: #ee2200;
+ cursor: pointer;
+ color: #ffffff;
+ outline: none;
+}
+
+.picker--time .picker__button--clear:before {
+ top: -0.25em;
+ color: #666;
+ font-size: 1.25em;
+ font-weight: bold;
+}
+
+.picker--time .picker__button--clear:hover:before,
+.picker--time .picker__button--clear:focus:before {
+ color: #ffffff;
+}
+
+/* ==========================================================================
+ $DEFAULT-TIME-PICKER
+ ========================================================================== */
+/**
+ * The frame the bounds the time picker.
+ */
+.picker--time .picker__frame {
+ min-width: 256px;
+ max-width: 320px;
+}
+
+/**
+ * The picker box.
+ */
+.picker--time .picker__box {
+ font-size: 1em;
+ background: #f2f2f2;
+ padding: 0;
+}
+
+@media (min-height: 40.125em) {
+ .picker--time .picker__box {
+ margin-bottom: 5em;
+ }
+}
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.min.css b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.min.css
new file mode 100644
index 000000000..910474550
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/css/materialize.min.css
@@ -0,0 +1,16 @@
+/*!
+ * Materialize v0.98.0 (http://materializecss.com)
+ * Copyright 2014-2015 Materialize
+ * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
+ */
+.materialize-red{background-color:#e51c23 !important}.materialize-red-text{color:#e51c23 !important}.materialize-red.lighten-5{background-color:#fdeaeb !important}.materialize-red-text.text-lighten-5{color:#fdeaeb !important}.materialize-red.lighten-4{background-color:#f8c1c3 !important}.materialize-red-text.text-lighten-4{color:#f8c1c3 !important}.materialize-red.lighten-3{background-color:#f3989b !important}.materialize-red-text.text-lighten-3{color:#f3989b !important}.materialize-red.lighten-2{background-color:#ee6e73 !important}.materialize-red-text.text-lighten-2{color:#ee6e73 !important}.materialize-red.lighten-1{background-color:#ea454b !important}.materialize-red-text.text-lighten-1{color:#ea454b !important}.materialize-red.darken-1{background-color:#d0181e !important}.materialize-red-text.text-darken-1{color:#d0181e !important}.materialize-red.darken-2{background-color:#b9151b !important}.materialize-red-text.text-darken-2{color:#b9151b !important}.materialize-red.darken-3{background-color:#a21318 !important}.materialize-red-text.text-darken-3{color:#a21318 !important}.materialize-red.darken-4{background-color:#8b1014 !important}.materialize-red-text.text-darken-4{color:#8b1014 !important}.red{background-color:#F44336 !important}.red-text{color:#F44336 !important}.red.lighten-5{background-color:#FFEBEE !important}.red-text.text-lighten-5{color:#FFEBEE !important}.red.lighten-4{background-color:#FFCDD2 !important}.red-text.text-lighten-4{color:#FFCDD2 !important}.red.lighten-3{background-color:#EF9A9A !important}.red-text.text-lighten-3{color:#EF9A9A !important}.red.lighten-2{background-color:#E57373 !important}.red-text.text-lighten-2{color:#E57373 !important}.red.lighten-1{background-color:#EF5350 !important}.red-text.text-lighten-1{color:#EF5350 !important}.red.darken-1{background-color:#E53935 !important}.red-text.text-darken-1{color:#E53935 !important}.red.darken-2{background-color:#D32F2F !important}.red-text.text-darken-2{color:#D32F2F !important}.red.darken-3{background-color:#C62828 !important}.red-text.text-darken-3{color:#C62828 !important}.red.darken-4{background-color:#B71C1C !important}.red-text.text-darken-4{color:#B71C1C !important}.red.accent-1{background-color:#FF8A80 !important}.red-text.text-accent-1{color:#FF8A80 !important}.red.accent-2{background-color:#FF5252 !important}.red-text.text-accent-2{color:#FF5252 !important}.red.accent-3{background-color:#FF1744 !important}.red-text.text-accent-3{color:#FF1744 !important}.red.accent-4{background-color:#D50000 !important}.red-text.text-accent-4{color:#D50000 !important}.pink{background-color:#e91e63 !important}.pink-text{color:#e91e63 !important}.pink.lighten-5{background-color:#fce4ec !important}.pink-text.text-lighten-5{color:#fce4ec !important}.pink.lighten-4{background-color:#f8bbd0 !important}.pink-text.text-lighten-4{color:#f8bbd0 !important}.pink.lighten-3{background-color:#f48fb1 !important}.pink-text.text-lighten-3{color:#f48fb1 !important}.pink.lighten-2{background-color:#f06292 !important}.pink-text.text-lighten-2{color:#f06292 !important}.pink.lighten-1{background-color:#ec407a !important}.pink-text.text-lighten-1{color:#ec407a !important}.pink.darken-1{background-color:#d81b60 !important}.pink-text.text-darken-1{color:#d81b60 !important}.pink.darken-2{background-color:#c2185b !important}.pink-text.text-darken-2{color:#c2185b !important}.pink.darken-3{background-color:#ad1457 !important}.pink-text.text-darken-3{color:#ad1457 !important}.pink.darken-4{background-color:#880e4f !important}.pink-text.text-darken-4{color:#880e4f !important}.pink.accent-1{background-color:#ff80ab !important}.pink-text.text-accent-1{color:#ff80ab !important}.pink.accent-2{background-color:#ff4081 !important}.pink-text.text-accent-2{color:#ff4081 !important}.pink.accent-3{background-color:#f50057 !important}.pink-text.text-accent-3{color:#f50057 !important}.pink.accent-4{background-color:#c51162 !important}.pink-text.text-accent-4{color:#c51162 !important}.purple{background-color:#9c27b0 !important}.purple-text{color:#9c27b0 !important}.purple.lighten-5{background-color:#f3e5f5 !important}.purple-text.text-lighten-5{color:#f3e5f5 !important}.purple.lighten-4{background-color:#e1bee7 !important}.purple-text.text-lighten-4{color:#e1bee7 !important}.purple.lighten-3{background-color:#ce93d8 !important}.purple-text.text-lighten-3{color:#ce93d8 !important}.purple.lighten-2{background-color:#ba68c8 !important}.purple-text.text-lighten-2{color:#ba68c8 !important}.purple.lighten-1{background-color:#ab47bc !important}.purple-text.text-lighten-1{color:#ab47bc !important}.purple.darken-1{background-color:#8e24aa !important}.purple-text.text-darken-1{color:#8e24aa !important}.purple.darken-2{background-color:#7b1fa2 !important}.purple-text.text-darken-2{color:#7b1fa2 !important}.purple.darken-3{background-color:#6a1b9a !important}.purple-text.text-darken-3{color:#6a1b9a !important}.purple.darken-4{background-color:#4a148c !important}.purple-text.text-darken-4{color:#4a148c !important}.purple.accent-1{background-color:#ea80fc !important}.purple-text.text-accent-1{color:#ea80fc !important}.purple.accent-2{background-color:#e040fb !important}.purple-text.text-accent-2{color:#e040fb !important}.purple.accent-3{background-color:#d500f9 !important}.purple-text.text-accent-3{color:#d500f9 !important}.purple.accent-4{background-color:#a0f !important}.purple-text.text-accent-4{color:#a0f !important}.deep-purple{background-color:#673ab7 !important}.deep-purple-text{color:#673ab7 !important}.deep-purple.lighten-5{background-color:#ede7f6 !important}.deep-purple-text.text-lighten-5{color:#ede7f6 !important}.deep-purple.lighten-4{background-color:#d1c4e9 !important}.deep-purple-text.text-lighten-4{color:#d1c4e9 !important}.deep-purple.lighten-3{background-color:#b39ddb !important}.deep-purple-text.text-lighten-3{color:#b39ddb !important}.deep-purple.lighten-2{background-color:#9575cd !important}.deep-purple-text.text-lighten-2{color:#9575cd !important}.deep-purple.lighten-1{background-color:#7e57c2 !important}.deep-purple-text.text-lighten-1{color:#7e57c2 !important}.deep-purple.darken-1{background-color:#5e35b1 !important}.deep-purple-text.text-darken-1{color:#5e35b1 !important}.deep-purple.darken-2{background-color:#512da8 !important}.deep-purple-text.text-darken-2{color:#512da8 !important}.deep-purple.darken-3{background-color:#4527a0 !important}.deep-purple-text.text-darken-3{color:#4527a0 !important}.deep-purple.darken-4{background-color:#311b92 !important}.deep-purple-text.text-darken-4{color:#311b92 !important}.deep-purple.accent-1{background-color:#b388ff !important}.deep-purple-text.text-accent-1{color:#b388ff !important}.deep-purple.accent-2{background-color:#7c4dff !important}.deep-purple-text.text-accent-2{color:#7c4dff !important}.deep-purple.accent-3{background-color:#651fff !important}.deep-purple-text.text-accent-3{color:#651fff !important}.deep-purple.accent-4{background-color:#6200ea !important}.deep-purple-text.text-accent-4{color:#6200ea !important}.indigo{background-color:#3f51b5 !important}.indigo-text{color:#3f51b5 !important}.indigo.lighten-5{background-color:#e8eaf6 !important}.indigo-text.text-lighten-5{color:#e8eaf6 !important}.indigo.lighten-4{background-color:#c5cae9 !important}.indigo-text.text-lighten-4{color:#c5cae9 !important}.indigo.lighten-3{background-color:#9fa8da !important}.indigo-text.text-lighten-3{color:#9fa8da !important}.indigo.lighten-2{background-color:#7986cb !important}.indigo-text.text-lighten-2{color:#7986cb !important}.indigo.lighten-1{background-color:#5c6bc0 !important}.indigo-text.text-lighten-1{color:#5c6bc0 !important}.indigo.darken-1{background-color:#3949ab !important}.indigo-text.text-darken-1{color:#3949ab !important}.indigo.darken-2{background-color:#303f9f !important}.indigo-text.text-darken-2{color:#303f9f !important}.indigo.darken-3{background-color:#283593 !important}.indigo-text.text-darken-3{color:#283593 !important}.indigo.darken-4{background-color:#1a237e !important}.indigo-text.text-darken-4{color:#1a237e !important}.indigo.accent-1{background-color:#8c9eff !important}.indigo-text.text-accent-1{color:#8c9eff !important}.indigo.accent-2{background-color:#536dfe !important}.indigo-text.text-accent-2{color:#536dfe !important}.indigo.accent-3{background-color:#3d5afe !important}.indigo-text.text-accent-3{color:#3d5afe !important}.indigo.accent-4{background-color:#304ffe !important}.indigo-text.text-accent-4{color:#304ffe !important}.blue{background-color:#2196F3 !important}.blue-text{color:#2196F3 !important}.blue.lighten-5{background-color:#E3F2FD !important}.blue-text.text-lighten-5{color:#E3F2FD !important}.blue.lighten-4{background-color:#BBDEFB !important}.blue-text.text-lighten-4{color:#BBDEFB !important}.blue.lighten-3{background-color:#90CAF9 !important}.blue-text.text-lighten-3{color:#90CAF9 !important}.blue.lighten-2{background-color:#64B5F6 !important}.blue-text.text-lighten-2{color:#64B5F6 !important}.blue.lighten-1{background-color:#42A5F5 !important}.blue-text.text-lighten-1{color:#42A5F5 !important}.blue.darken-1{background-color:#1E88E5 !important}.blue-text.text-darken-1{color:#1E88E5 !important}.blue.darken-2{background-color:#1976D2 !important}.blue-text.text-darken-2{color:#1976D2 !important}.blue.darken-3{background-color:#1565C0 !important}.blue-text.text-darken-3{color:#1565C0 !important}.blue.darken-4{background-color:#0D47A1 !important}.blue-text.text-darken-4{color:#0D47A1 !important}.blue.accent-1{background-color:#82B1FF !important}.blue-text.text-accent-1{color:#82B1FF !important}.blue.accent-2{background-color:#448AFF !important}.blue-text.text-accent-2{color:#448AFF !important}.blue.accent-3{background-color:#2979FF !important}.blue-text.text-accent-3{color:#2979FF !important}.blue.accent-4{background-color:#2962FF !important}.blue-text.text-accent-4{color:#2962FF !important}.light-blue{background-color:#03a9f4 !important}.light-blue-text{color:#03a9f4 !important}.light-blue.lighten-5{background-color:#e1f5fe !important}.light-blue-text.text-lighten-5{color:#e1f5fe !important}.light-blue.lighten-4{background-color:#b3e5fc !important}.light-blue-text.text-lighten-4{color:#b3e5fc !important}.light-blue.lighten-3{background-color:#81d4fa !important}.light-blue-text.text-lighten-3{color:#81d4fa !important}.light-blue.lighten-2{background-color:#4fc3f7 !important}.light-blue-text.text-lighten-2{color:#4fc3f7 !important}.light-blue.lighten-1{background-color:#29b6f6 !important}.light-blue-text.text-lighten-1{color:#29b6f6 !important}.light-blue.darken-1{background-color:#039be5 !important}.light-blue-text.text-darken-1{color:#039be5 !important}.light-blue.darken-2{background-color:#0288d1 !important}.light-blue-text.text-darken-2{color:#0288d1 !important}.light-blue.darken-3{background-color:#0277bd !important}.light-blue-text.text-darken-3{color:#0277bd !important}.light-blue.darken-4{background-color:#01579b !important}.light-blue-text.text-darken-4{color:#01579b !important}.light-blue.accent-1{background-color:#80d8ff !important}.light-blue-text.text-accent-1{color:#80d8ff !important}.light-blue.accent-2{background-color:#40c4ff !important}.light-blue-text.text-accent-2{color:#40c4ff !important}.light-blue.accent-3{background-color:#00b0ff !important}.light-blue-text.text-accent-3{color:#00b0ff !important}.light-blue.accent-4{background-color:#0091ea !important}.light-blue-text.text-accent-4{color:#0091ea !important}.cyan{background-color:#00bcd4 !important}.cyan-text{color:#00bcd4 !important}.cyan.lighten-5{background-color:#e0f7fa !important}.cyan-text.text-lighten-5{color:#e0f7fa !important}.cyan.lighten-4{background-color:#b2ebf2 !important}.cyan-text.text-lighten-4{color:#b2ebf2 !important}.cyan.lighten-3{background-color:#80deea !important}.cyan-text.text-lighten-3{color:#80deea !important}.cyan.lighten-2{background-color:#4dd0e1 !important}.cyan-text.text-lighten-2{color:#4dd0e1 !important}.cyan.lighten-1{background-color:#26c6da !important}.cyan-text.text-lighten-1{color:#26c6da !important}.cyan.darken-1{background-color:#00acc1 !important}.cyan-text.text-darken-1{color:#00acc1 !important}.cyan.darken-2{background-color:#0097a7 !important}.cyan-text.text-darken-2{color:#0097a7 !important}.cyan.darken-3{background-color:#00838f !important}.cyan-text.text-darken-3{color:#00838f !important}.cyan.darken-4{background-color:#006064 !important}.cyan-text.text-darken-4{color:#006064 !important}.cyan.accent-1{background-color:#84ffff !important}.cyan-text.text-accent-1{color:#84ffff !important}.cyan.accent-2{background-color:#18ffff !important}.cyan-text.text-accent-2{color:#18ffff !important}.cyan.accent-3{background-color:#00e5ff !important}.cyan-text.text-accent-3{color:#00e5ff !important}.cyan.accent-4{background-color:#00b8d4 !important}.cyan-text.text-accent-4{color:#00b8d4 !important}.teal{background-color:#009688 !important}.teal-text{color:#009688 !important}.teal.lighten-5{background-color:#e0f2f1 !important}.teal-text.text-lighten-5{color:#e0f2f1 !important}.teal.lighten-4{background-color:#b2dfdb !important}.teal-text.text-lighten-4{color:#b2dfdb !important}.teal.lighten-3{background-color:#80cbc4 !important}.teal-text.text-lighten-3{color:#80cbc4 !important}.teal.lighten-2{background-color:#4db6ac !important}.teal-text.text-lighten-2{color:#4db6ac !important}.teal.lighten-1{background-color:#26a69a !important}.teal-text.text-lighten-1{color:#26a69a !important}.teal.darken-1{background-color:#00897b !important}.teal-text.text-darken-1{color:#00897b !important}.teal.darken-2{background-color:#00796b !important}.teal-text.text-darken-2{color:#00796b !important}.teal.darken-3{background-color:#00695c !important}.teal-text.text-darken-3{color:#00695c !important}.teal.darken-4{background-color:#004d40 !important}.teal-text.text-darken-4{color:#004d40 !important}.teal.accent-1{background-color:#a7ffeb !important}.teal-text.text-accent-1{color:#a7ffeb !important}.teal.accent-2{background-color:#64ffda !important}.teal-text.text-accent-2{color:#64ffda !important}.teal.accent-3{background-color:#1de9b6 !important}.teal-text.text-accent-3{color:#1de9b6 !important}.teal.accent-4{background-color:#00bfa5 !important}.teal-text.text-accent-4{color:#00bfa5 !important}.green{background-color:#4CAF50 !important}.green-text{color:#4CAF50 !important}.green.lighten-5{background-color:#E8F5E9 !important}.green-text.text-lighten-5{color:#E8F5E9 !important}.green.lighten-4{background-color:#C8E6C9 !important}.green-text.text-lighten-4{color:#C8E6C9 !important}.green.lighten-3{background-color:#A5D6A7 !important}.green-text.text-lighten-3{color:#A5D6A7 !important}.green.lighten-2{background-color:#81C784 !important}.green-text.text-lighten-2{color:#81C784 !important}.green.lighten-1{background-color:#66BB6A !important}.green-text.text-lighten-1{color:#66BB6A !important}.green.darken-1{background-color:#43A047 !important}.green-text.text-darken-1{color:#43A047 !important}.green.darken-2{background-color:#388E3C !important}.green-text.text-darken-2{color:#388E3C !important}.green.darken-3{background-color:#2E7D32 !important}.green-text.text-darken-3{color:#2E7D32 !important}.green.darken-4{background-color:#1B5E20 !important}.green-text.text-darken-4{color:#1B5E20 !important}.green.accent-1{background-color:#B9F6CA !important}.green-text.text-accent-1{color:#B9F6CA !important}.green.accent-2{background-color:#69F0AE !important}.green-text.text-accent-2{color:#69F0AE !important}.green.accent-3{background-color:#00E676 !important}.green-text.text-accent-3{color:#00E676 !important}.green.accent-4{background-color:#00C853 !important}.green-text.text-accent-4{color:#00C853 !important}.light-green{background-color:#8bc34a !important}.light-green-text{color:#8bc34a !important}.light-green.lighten-5{background-color:#f1f8e9 !important}.light-green-text.text-lighten-5{color:#f1f8e9 !important}.light-green.lighten-4{background-color:#dcedc8 !important}.light-green-text.text-lighten-4{color:#dcedc8 !important}.light-green.lighten-3{background-color:#c5e1a5 !important}.light-green-text.text-lighten-3{color:#c5e1a5 !important}.light-green.lighten-2{background-color:#aed581 !important}.light-green-text.text-lighten-2{color:#aed581 !important}.light-green.lighten-1{background-color:#9ccc65 !important}.light-green-text.text-lighten-1{color:#9ccc65 !important}.light-green.darken-1{background-color:#7cb342 !important}.light-green-text.text-darken-1{color:#7cb342 !important}.light-green.darken-2{background-color:#689f38 !important}.light-green-text.text-darken-2{color:#689f38 !important}.light-green.darken-3{background-color:#558b2f !important}.light-green-text.text-darken-3{color:#558b2f !important}.light-green.darken-4{background-color:#33691e !important}.light-green-text.text-darken-4{color:#33691e !important}.light-green.accent-1{background-color:#ccff90 !important}.light-green-text.text-accent-1{color:#ccff90 !important}.light-green.accent-2{background-color:#b2ff59 !important}.light-green-text.text-accent-2{color:#b2ff59 !important}.light-green.accent-3{background-color:#76ff03 !important}.light-green-text.text-accent-3{color:#76ff03 !important}.light-green.accent-4{background-color:#64dd17 !important}.light-green-text.text-accent-4{color:#64dd17 !important}.lime{background-color:#cddc39 !important}.lime-text{color:#cddc39 !important}.lime.lighten-5{background-color:#f9fbe7 !important}.lime-text.text-lighten-5{color:#f9fbe7 !important}.lime.lighten-4{background-color:#f0f4c3 !important}.lime-text.text-lighten-4{color:#f0f4c3 !important}.lime.lighten-3{background-color:#e6ee9c !important}.lime-text.text-lighten-3{color:#e6ee9c !important}.lime.lighten-2{background-color:#dce775 !important}.lime-text.text-lighten-2{color:#dce775 !important}.lime.lighten-1{background-color:#d4e157 !important}.lime-text.text-lighten-1{color:#d4e157 !important}.lime.darken-1{background-color:#c0ca33 !important}.lime-text.text-darken-1{color:#c0ca33 !important}.lime.darken-2{background-color:#afb42b !important}.lime-text.text-darken-2{color:#afb42b !important}.lime.darken-3{background-color:#9e9d24 !important}.lime-text.text-darken-3{color:#9e9d24 !important}.lime.darken-4{background-color:#827717 !important}.lime-text.text-darken-4{color:#827717 !important}.lime.accent-1{background-color:#f4ff81 !important}.lime-text.text-accent-1{color:#f4ff81 !important}.lime.accent-2{background-color:#eeff41 !important}.lime-text.text-accent-2{color:#eeff41 !important}.lime.accent-3{background-color:#c6ff00 !important}.lime-text.text-accent-3{color:#c6ff00 !important}.lime.accent-4{background-color:#aeea00 !important}.lime-text.text-accent-4{color:#aeea00 !important}.yellow{background-color:#ffeb3b !important}.yellow-text{color:#ffeb3b !important}.yellow.lighten-5{background-color:#fffde7 !important}.yellow-text.text-lighten-5{color:#fffde7 !important}.yellow.lighten-4{background-color:#fff9c4 !important}.yellow-text.text-lighten-4{color:#fff9c4 !important}.yellow.lighten-3{background-color:#fff59d !important}.yellow-text.text-lighten-3{color:#fff59d !important}.yellow.lighten-2{background-color:#fff176 !important}.yellow-text.text-lighten-2{color:#fff176 !important}.yellow.lighten-1{background-color:#ffee58 !important}.yellow-text.text-lighten-1{color:#ffee58 !important}.yellow.darken-1{background-color:#fdd835 !important}.yellow-text.text-darken-1{color:#fdd835 !important}.yellow.darken-2{background-color:#fbc02d !important}.yellow-text.text-darken-2{color:#fbc02d !important}.yellow.darken-3{background-color:#f9a825 !important}.yellow-text.text-darken-3{color:#f9a825 !important}.yellow.darken-4{background-color:#f57f17 !important}.yellow-text.text-darken-4{color:#f57f17 !important}.yellow.accent-1{background-color:#ffff8d !important}.yellow-text.text-accent-1{color:#ffff8d !important}.yellow.accent-2{background-color:#ff0 !important}.yellow-text.text-accent-2{color:#ff0 !important}.yellow.accent-3{background-color:#ffea00 !important}.yellow-text.text-accent-3{color:#ffea00 !important}.yellow.accent-4{background-color:#ffd600 !important}.yellow-text.text-accent-4{color:#ffd600 !important}.amber{background-color:#ffc107 !important}.amber-text{color:#ffc107 !important}.amber.lighten-5{background-color:#fff8e1 !important}.amber-text.text-lighten-5{color:#fff8e1 !important}.amber.lighten-4{background-color:#ffecb3 !important}.amber-text.text-lighten-4{color:#ffecb3 !important}.amber.lighten-3{background-color:#ffe082 !important}.amber-text.text-lighten-3{color:#ffe082 !important}.amber.lighten-2{background-color:#ffd54f !important}.amber-text.text-lighten-2{color:#ffd54f !important}.amber.lighten-1{background-color:#ffca28 !important}.amber-text.text-lighten-1{color:#ffca28 !important}.amber.darken-1{background-color:#ffb300 !important}.amber-text.text-darken-1{color:#ffb300 !important}.amber.darken-2{background-color:#ffa000 !important}.amber-text.text-darken-2{color:#ffa000 !important}.amber.darken-3{background-color:#ff8f00 !important}.amber-text.text-darken-3{color:#ff8f00 !important}.amber.darken-4{background-color:#ff6f00 !important}.amber-text.text-darken-4{color:#ff6f00 !important}.amber.accent-1{background-color:#ffe57f !important}.amber-text.text-accent-1{color:#ffe57f !important}.amber.accent-2{background-color:#ffd740 !important}.amber-text.text-accent-2{color:#ffd740 !important}.amber.accent-3{background-color:#ffc400 !important}.amber-text.text-accent-3{color:#ffc400 !important}.amber.accent-4{background-color:#ffab00 !important}.amber-text.text-accent-4{color:#ffab00 !important}.orange{background-color:#ff9800 !important}.orange-text{color:#ff9800 !important}.orange.lighten-5{background-color:#fff3e0 !important}.orange-text.text-lighten-5{color:#fff3e0 !important}.orange.lighten-4{background-color:#ffe0b2 !important}.orange-text.text-lighten-4{color:#ffe0b2 !important}.orange.lighten-3{background-color:#ffcc80 !important}.orange-text.text-lighten-3{color:#ffcc80 !important}.orange.lighten-2{background-color:#ffb74d !important}.orange-text.text-lighten-2{color:#ffb74d !important}.orange.lighten-1{background-color:#ffa726 !important}.orange-text.text-lighten-1{color:#ffa726 !important}.orange.darken-1{background-color:#fb8c00 !important}.orange-text.text-darken-1{color:#fb8c00 !important}.orange.darken-2{background-color:#f57c00 !important}.orange-text.text-darken-2{color:#f57c00 !important}.orange.darken-3{background-color:#ef6c00 !important}.orange-text.text-darken-3{color:#ef6c00 !important}.orange.darken-4{background-color:#e65100 !important}.orange-text.text-darken-4{color:#e65100 !important}.orange.accent-1{background-color:#ffd180 !important}.orange-text.text-accent-1{color:#ffd180 !important}.orange.accent-2{background-color:#ffab40 !important}.orange-text.text-accent-2{color:#ffab40 !important}.orange.accent-3{background-color:#ff9100 !important}.orange-text.text-accent-3{color:#ff9100 !important}.orange.accent-4{background-color:#ff6d00 !important}.orange-text.text-accent-4{color:#ff6d00 !important}.deep-orange{background-color:#ff5722 !important}.deep-orange-text{color:#ff5722 !important}.deep-orange.lighten-5{background-color:#fbe9e7 !important}.deep-orange-text.text-lighten-5{color:#fbe9e7 !important}.deep-orange.lighten-4{background-color:#ffccbc !important}.deep-orange-text.text-lighten-4{color:#ffccbc !important}.deep-orange.lighten-3{background-color:#ffab91 !important}.deep-orange-text.text-lighten-3{color:#ffab91 !important}.deep-orange.lighten-2{background-color:#ff8a65 !important}.deep-orange-text.text-lighten-2{color:#ff8a65 !important}.deep-orange.lighten-1{background-color:#ff7043 !important}.deep-orange-text.text-lighten-1{color:#ff7043 !important}.deep-orange.darken-1{background-color:#f4511e !important}.deep-orange-text.text-darken-1{color:#f4511e !important}.deep-orange.darken-2{background-color:#e64a19 !important}.deep-orange-text.text-darken-2{color:#e64a19 !important}.deep-orange.darken-3{background-color:#d84315 !important}.deep-orange-text.text-darken-3{color:#d84315 !important}.deep-orange.darken-4{background-color:#bf360c !important}.deep-orange-text.text-darken-4{color:#bf360c !important}.deep-orange.accent-1{background-color:#ff9e80 !important}.deep-orange-text.text-accent-1{color:#ff9e80 !important}.deep-orange.accent-2{background-color:#ff6e40 !important}.deep-orange-text.text-accent-2{color:#ff6e40 !important}.deep-orange.accent-3{background-color:#ff3d00 !important}.deep-orange-text.text-accent-3{color:#ff3d00 !important}.deep-orange.accent-4{background-color:#dd2c00 !important}.deep-orange-text.text-accent-4{color:#dd2c00 !important}.brown{background-color:#795548 !important}.brown-text{color:#795548 !important}.brown.lighten-5{background-color:#efebe9 !important}.brown-text.text-lighten-5{color:#efebe9 !important}.brown.lighten-4{background-color:#d7ccc8 !important}.brown-text.text-lighten-4{color:#d7ccc8 !important}.brown.lighten-3{background-color:#bcaaa4 !important}.brown-text.text-lighten-3{color:#bcaaa4 !important}.brown.lighten-2{background-color:#a1887f !important}.brown-text.text-lighten-2{color:#a1887f !important}.brown.lighten-1{background-color:#8d6e63 !important}.brown-text.text-lighten-1{color:#8d6e63 !important}.brown.darken-1{background-color:#6d4c41 !important}.brown-text.text-darken-1{color:#6d4c41 !important}.brown.darken-2{background-color:#5d4037 !important}.brown-text.text-darken-2{color:#5d4037 !important}.brown.darken-3{background-color:#4e342e !important}.brown-text.text-darken-3{color:#4e342e !important}.brown.darken-4{background-color:#3e2723 !important}.brown-text.text-darken-4{color:#3e2723 !important}.blue-grey{background-color:#607d8b !important}.blue-grey-text{color:#607d8b !important}.blue-grey.lighten-5{background-color:#eceff1 !important}.blue-grey-text.text-lighten-5{color:#eceff1 !important}.blue-grey.lighten-4{background-color:#cfd8dc !important}.blue-grey-text.text-lighten-4{color:#cfd8dc !important}.blue-grey.lighten-3{background-color:#b0bec5 !important}.blue-grey-text.text-lighten-3{color:#b0bec5 !important}.blue-grey.lighten-2{background-color:#90a4ae !important}.blue-grey-text.text-lighten-2{color:#90a4ae !important}.blue-grey.lighten-1{background-color:#78909c !important}.blue-grey-text.text-lighten-1{color:#78909c !important}.blue-grey.darken-1{background-color:#546e7a !important}.blue-grey-text.text-darken-1{color:#546e7a !important}.blue-grey.darken-2{background-color:#455a64 !important}.blue-grey-text.text-darken-2{color:#455a64 !important}.blue-grey.darken-3{background-color:#37474f !important}.blue-grey-text.text-darken-3{color:#37474f !important}.blue-grey.darken-4{background-color:#263238 !important}.blue-grey-text.text-darken-4{color:#263238 !important}.grey{background-color:#9e9e9e !important}.grey-text{color:#9e9e9e !important}.grey.lighten-5{background-color:#fafafa !important}.grey-text.text-lighten-5{color:#fafafa !important}.grey.lighten-4{background-color:#f5f5f5 !important}.grey-text.text-lighten-4{color:#f5f5f5 !important}.grey.lighten-3{background-color:#eee !important}.grey-text.text-lighten-3{color:#eee !important}.grey.lighten-2{background-color:#e0e0e0 !important}.grey-text.text-lighten-2{color:#e0e0e0 !important}.grey.lighten-1{background-color:#bdbdbd !important}.grey-text.text-lighten-1{color:#bdbdbd !important}.grey.darken-1{background-color:#757575 !important}.grey-text.text-darken-1{color:#757575 !important}.grey.darken-2{background-color:#616161 !important}.grey-text.text-darken-2{color:#616161 !important}.grey.darken-3{background-color:#424242 !important}.grey-text.text-darken-3{color:#424242 !important}.grey.darken-4{background-color:#212121 !important}.grey-text.text-darken-4{color:#212121 !important}.black{background-color:#000 !important}.black-text{color:#000 !important}.white{background-color:#fff !important}.white-text{color:#fff !important}.transparent{background-color:transparent !important}.transparent-text{color:transparent !important}/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-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:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}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{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{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-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}ul:not(.browser-default){padding-left:0;list-style-type:none}ul:not(.browser-default) li{list-style-type:none}a{color:#039be5;text-decoration:none;-webkit-tap-highlight-color:transparent}.valign-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.valign-wrapper .valign{display:block}.clearfix{clear:both}.z-depth-0{box-shadow:none !important}.z-depth-1,nav,.card-panel,.card,.toast,.btn,.btn-large,.btn-floating,.dropdown-content,.collapsible,.side-nav{box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 1px 5px 0 rgba(0,0,0,0.12),0 3px 1px -2px rgba(0,0,0,0.2)}.z-depth-1-half,.btn:hover,.btn-large:hover,.btn-floating:hover{box-shadow:0 3px 3px 0 rgba(0,0,0,0.14),0 1px 7px 0 rgba(0,0,0,0.12),0 3px 1px -1px rgba(0,0,0,0.2)}.z-depth-2{box-shadow:0 4px 5px 0 rgba(0,0,0,0.14),0 1px 10px 0 rgba(0,0,0,0.12),0 2px 4px -1px rgba(0,0,0,0.3)}.z-depth-3{box-shadow:0 6px 10px 0 rgba(0,0,0,0.14),0 1px 18px 0 rgba(0,0,0,0.12),0 3px 5px -1px rgba(0,0,0,0.3)}.z-depth-4,.modal{box-shadow:0 8px 10px 1px rgba(0,0,0,0.14),0 3px 14px 2px rgba(0,0,0,0.12),0 5px 5px -3px rgba(0,0,0,0.3)}.z-depth-5{box-shadow:0 16px 24px 2px rgba(0,0,0,0.14),0 6px 30px 5px rgba(0,0,0,0.12),0 8px 10px -5px rgba(0,0,0,0.3)}.hoverable{transition:box-shadow .25s;box-shadow:0}.hoverable:hover{transition:box-shadow .25s;box-shadow:0 8px 17px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}.divider{height:1px;overflow:hidden;background-color:#e0e0e0}blockquote{margin:20px 0;padding-left:1.5rem;border-left:5px solid #ee6e73}i{line-height:inherit}i.left{float:left;margin-right:15px}i.right{float:right;margin-left:15px}i.tiny{font-size:1rem}i.small{font-size:2rem}i.medium{font-size:4rem}i.large{font-size:6rem}img.responsive-img,video.responsive-video{max-width:100%;height:auto}.pagination li{display:inline-block;border-radius:2px;text-align:center;vertical-align:top;height:30px}.pagination li a{color:#444;display:inline-block;font-size:1.2rem;padding:0 10px;line-height:30px}.pagination li.active a{color:#fff}.pagination li.active{background-color:#ee6e73}.pagination li.disabled a{cursor:default;color:#999}.pagination li i{font-size:2rem}.pagination li.pages ul li{display:inline-block;float:none}@media only screen and (max-width: 992px){.pagination{width:100%}.pagination li.prev,.pagination li.next{width:10%}.pagination li.pages{width:80%;overflow:hidden;white-space:nowrap}}.breadcrumb{font-size:18px;color:rgba(255,255,255,0.7)}.breadcrumb i,.breadcrumb [class^="mdi-"],.breadcrumb [class*="mdi-"],.breadcrumb i.material-icons{display:inline-block;float:left;font-size:24px}.breadcrumb:before{content:'\E5CC';color:rgba(255,255,255,0.7);vertical-align:top;display:inline-block;font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:25px;margin:0 10px 0 8px;-webkit-font-smoothing:antialiased}.breadcrumb:first-child:before{display:none}.breadcrumb:last-child{color:#fff}.parallax-container{position:relative;overflow:hidden;height:500px}.parallax{position:absolute;top:0;left:0;right:0;bottom:0;z-index:-1}.parallax img{display:none;position:absolute;left:50%;bottom:0;min-width:100%;min-height:100%;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);-webkit-transform:translateX(-50%);transform:translateX(-50%)}.pin-top,.pin-bottom{position:relative}.pinned{position:fixed !important}ul.staggered-list li{opacity:0}.fade-in{opacity:0;-webkit-transform-origin:0 50%;transform-origin:0 50%}@media only screen and (max-width: 600px){.hide-on-small-only,.hide-on-small-and-down{display:none !important}}@media only screen and (max-width: 992px){.hide-on-med-and-down{display:none !important}}@media only screen and (min-width: 601px){.hide-on-med-and-up{display:none !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.hide-on-med-only{display:none !important}}@media only screen and (min-width: 993px){.hide-on-large-only{display:none !important}}@media only screen and (min-width: 993px){.show-on-large{display:block !important}}@media only screen and (min-width: 600px) and (max-width: 992px){.show-on-medium{display:block !important}}@media only screen and (max-width: 600px){.show-on-small{display:block !important}}@media only screen and (min-width: 601px){.show-on-medium-and-up{display:block !important}}@media only screen and (max-width: 992px){.show-on-medium-and-down{display:block !important}}@media only screen and (max-width: 600px){.center-on-small-only{text-align:center}}footer.page-footer{padding-top:20px;background-color:#ee6e73}footer.page-footer .footer-copyright{overflow:hidden;min-height:50px;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;padding:10px 0px;color:rgba(255,255,255,0.8);background-color:rgba(51,51,51,0.08)}table,th,td{border:none}table{width:100%;display:table}table.bordered>thead>tr,table.bordered>tbody>tr{border-bottom:1px solid #d0d0d0}table.striped>tbody>tr:nth-child(odd){background-color:#f2f2f2}table.striped>tbody>tr>td{border-radius:0}table.highlight>tbody>tr{transition:background-color .25s ease}table.highlight>tbody>tr:hover{background-color:#f2f2f2}table.centered thead tr th,table.centered tbody tr td{text-align:center}thead{border-bottom:1px solid #d0d0d0}td,th{padding:15px 5px;display:table-cell;text-align:left;vertical-align:middle;border-radius:2px}@media only screen and (max-width: 992px){table.responsive-table{width:100%;border-collapse:collapse;border-spacing:0;display:block;position:relative}table.responsive-table td:empty:before{content:'\00a0'}table.responsive-table th,table.responsive-table td{margin:0;vertical-align:top}table.responsive-table th{text-align:left}table.responsive-table thead{display:block;float:left}table.responsive-table thead tr{display:block;padding:0 10px 0 0}table.responsive-table thead tr th::before{content:"\00a0"}table.responsive-table tbody{display:block;width:auto;position:relative;overflow-x:auto;white-space:nowrap}table.responsive-table tbody tr{display:inline-block;vertical-align:top}table.responsive-table th{display:block;text-align:right}table.responsive-table td{display:block;min-height:1.25em;text-align:left}table.responsive-table tr{padding:0 10px}table.responsive-table thead{border:0;border-right:1px solid #d0d0d0}table.responsive-table.bordered th{border-bottom:0;border-left:0}table.responsive-table.bordered td{border-left:0;border-right:0;border-bottom:0}table.responsive-table.bordered tr{border:0}table.responsive-table.bordered tbody tr{border-right:1px solid #d0d0d0}}.collection{margin:.5rem 0 1rem 0;border:1px solid #e0e0e0;border-radius:2px;overflow:hidden;position:relative}.collection .collection-item{background-color:#fff;line-height:1.5rem;padding:10px 20px;margin:0;border-bottom:1px solid #e0e0e0}.collection .collection-item.avatar{min-height:84px;padding-left:72px;position:relative}.collection .collection-item.avatar .circle{position:absolute;width:42px;height:42px;overflow:hidden;left:15px;display:inline-block;vertical-align:middle}.collection .collection-item.avatar i.circle{font-size:18px;line-height:42px;color:#fff;background-color:#999;text-align:center}.collection .collection-item.avatar .title{font-size:16px}.collection .collection-item.avatar p{margin:0}.collection .collection-item.avatar .secondary-content{position:absolute;top:16px;right:16px}.collection .collection-item:last-child{border-bottom:none}.collection .collection-item.active{background-color:#26a69a;color:#eafaf9}.collection .collection-item.active .secondary-content{color:#fff}.collection a.collection-item{display:block;transition:.25s;color:#26a69a}.collection a.collection-item:not(.active):hover{background-color:#ddd}.collection.with-header .collection-header{background-color:#fff;border-bottom:1px solid #e0e0e0;padding:10px 20px}.collection.with-header .collection-item{padding-left:30px}.collection.with-header .collection-item.avatar{padding-left:72px}.secondary-content{float:right;color:#26a69a}.collapsible .collection{margin:0;border:none}.video-container{position:relative;padding-bottom:56.25%;height:0;overflow:hidden}.video-container iframe,.video-container object,.video-container embed{position:absolute;top:0;left:0;width:100%;height:100%}.progress{position:relative;height:4px;display:block;width:100%;background-color:#acece6;border-radius:2px;margin:.5rem 0 1rem 0;overflow:hidden}.progress .determinate{position:absolute;top:0;left:0;bottom:0;background-color:#26a69a;transition:width .3s linear}.progress .indeterminate{background-color:#26a69a}.progress .indeterminate:before{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;animation:indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite}.progress .indeterminate:after{content:'';position:absolute;background-color:inherit;top:0;left:0;bottom:0;will-change:left, right;-webkit-animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;animation:indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;-webkit-animation-delay:1.15s;animation-delay:1.15s}@-webkit-keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@keyframes indeterminate{0%{left:-35%;right:100%}60%{left:100%;right:-90%}100%{left:100%;right:-90%}}@-webkit-keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}@keyframes indeterminate-short{0%{left:-200%;right:100%}60%{left:107%;right:-8%}100%{left:107%;right:-8%}}.hide{display:none !important}.left-align{text-align:left}.right-align{text-align:right}.center,.center-align{text-align:center}.left{float:left !important}.right{float:right !important}.no-select,input[type=range],input[type=range]+.thumb{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.circle{border-radius:50%}.center-block{display:block;margin-left:auto;margin-right:auto}.truncate{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.no-padding{padding:0 !important}span.badge{min-width:3rem;padding:0 6px;margin-left:14px;text-align:center;font-size:1rem;line-height:22px;height:22px;color:#757575;float:right;box-sizing:border-box}span.badge.new{font-weight:300;font-size:0.8rem;color:#fff;background-color:#26a69a;border-radius:2px}span.badge.new:after{content:" new"}span.badge[data-badge-caption]::after{content:" " attr(data-badge-caption)}nav ul a span.badge{display:inline-block;float:none;margin-left:4px;line-height:22px;height:22px}.collection-item span.badge{margin-top:calc(.75rem - 11px)}.collapsible span.badge{margin-top:calc(1.5rem - 11px)}.side-nav span.badge{margin-top:calc(24px - 11px)}.material-icons{text-rendering:optimizeLegibility;-webkit-font-feature-settings:'liga';-moz-font-feature-settings:'liga';font-feature-settings:'liga'}.container{margin:0 auto;max-width:1280px;width:90%}@media only screen and (min-width: 601px){.container{width:85%}}@media only screen and (min-width: 993px){.container{width:70%}}.container .row{margin-left:-.75rem;margin-right:-.75rem}.section{padding-top:1rem;padding-bottom:1rem}.section.no-pad{padding:0}.section.no-pad-bot{padding-bottom:0}.section.no-pad-top{padding-top:0}.row{margin-left:auto;margin-right:auto;margin-bottom:20px}.row:after{content:"";display:table;clear:both}.row .col{float:left;box-sizing:border-box;padding:0 .75rem;min-height:1px}.row .col[class*="push-"],.row .col[class*="pull-"]{position:relative}.row .col.s1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.s4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.s7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.s10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.s11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.s12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-s1{margin-left:8.3333333333%}.row .col.pull-s1{right:8.3333333333%}.row .col.push-s1{left:8.3333333333%}.row .col.offset-s2{margin-left:16.6666666667%}.row .col.pull-s2{right:16.6666666667%}.row .col.push-s2{left:16.6666666667%}.row .col.offset-s3{margin-left:25%}.row .col.pull-s3{right:25%}.row .col.push-s3{left:25%}.row .col.offset-s4{margin-left:33.3333333333%}.row .col.pull-s4{right:33.3333333333%}.row .col.push-s4{left:33.3333333333%}.row .col.offset-s5{margin-left:41.6666666667%}.row .col.pull-s5{right:41.6666666667%}.row .col.push-s5{left:41.6666666667%}.row .col.offset-s6{margin-left:50%}.row .col.pull-s6{right:50%}.row .col.push-s6{left:50%}.row .col.offset-s7{margin-left:58.3333333333%}.row .col.pull-s7{right:58.3333333333%}.row .col.push-s7{left:58.3333333333%}.row .col.offset-s8{margin-left:66.6666666667%}.row .col.pull-s8{right:66.6666666667%}.row .col.push-s8{left:66.6666666667%}.row .col.offset-s9{margin-left:75%}.row .col.pull-s9{right:75%}.row .col.push-s9{left:75%}.row .col.offset-s10{margin-left:83.3333333333%}.row .col.pull-s10{right:83.3333333333%}.row .col.push-s10{left:83.3333333333%}.row .col.offset-s11{margin-left:91.6666666667%}.row .col.pull-s11{right:91.6666666667%}.row .col.push-s11{left:91.6666666667%}.row .col.offset-s12{margin-left:100%}.row .col.pull-s12{right:100%}.row .col.push-s12{left:100%}@media only screen and (min-width: 601px){.row .col.m1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.m4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.m7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.m10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.m11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.m12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-m1{margin-left:8.3333333333%}.row .col.pull-m1{right:8.3333333333%}.row .col.push-m1{left:8.3333333333%}.row .col.offset-m2{margin-left:16.6666666667%}.row .col.pull-m2{right:16.6666666667%}.row .col.push-m2{left:16.6666666667%}.row .col.offset-m3{margin-left:25%}.row .col.pull-m3{right:25%}.row .col.push-m3{left:25%}.row .col.offset-m4{margin-left:33.3333333333%}.row .col.pull-m4{right:33.3333333333%}.row .col.push-m4{left:33.3333333333%}.row .col.offset-m5{margin-left:41.6666666667%}.row .col.pull-m5{right:41.6666666667%}.row .col.push-m5{left:41.6666666667%}.row .col.offset-m6{margin-left:50%}.row .col.pull-m6{right:50%}.row .col.push-m6{left:50%}.row .col.offset-m7{margin-left:58.3333333333%}.row .col.pull-m7{right:58.3333333333%}.row .col.push-m7{left:58.3333333333%}.row .col.offset-m8{margin-left:66.6666666667%}.row .col.pull-m8{right:66.6666666667%}.row .col.push-m8{left:66.6666666667%}.row .col.offset-m9{margin-left:75%}.row .col.pull-m9{right:75%}.row .col.push-m9{left:75%}.row .col.offset-m10{margin-left:83.3333333333%}.row .col.pull-m10{right:83.3333333333%}.row .col.push-m10{left:83.3333333333%}.row .col.offset-m11{margin-left:91.6666666667%}.row .col.pull-m11{right:91.6666666667%}.row .col.push-m11{left:91.6666666667%}.row .col.offset-m12{margin-left:100%}.row .col.pull-m12{right:100%}.row .col.push-m12{left:100%}}@media only screen and (min-width: 993px){.row .col.l1{width:8.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l2{width:16.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l3{width:25%;margin-left:auto;left:auto;right:auto}.row .col.l4{width:33.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l5{width:41.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l6{width:50%;margin-left:auto;left:auto;right:auto}.row .col.l7{width:58.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l8{width:66.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l9{width:75%;margin-left:auto;left:auto;right:auto}.row .col.l10{width:83.3333333333%;margin-left:auto;left:auto;right:auto}.row .col.l11{width:91.6666666667%;margin-left:auto;left:auto;right:auto}.row .col.l12{width:100%;margin-left:auto;left:auto;right:auto}.row .col.offset-l1{margin-left:8.3333333333%}.row .col.pull-l1{right:8.3333333333%}.row .col.push-l1{left:8.3333333333%}.row .col.offset-l2{margin-left:16.6666666667%}.row .col.pull-l2{right:16.6666666667%}.row .col.push-l2{left:16.6666666667%}.row .col.offset-l3{margin-left:25%}.row .col.pull-l3{right:25%}.row .col.push-l3{left:25%}.row .col.offset-l4{margin-left:33.3333333333%}.row .col.pull-l4{right:33.3333333333%}.row .col.push-l4{left:33.3333333333%}.row .col.offset-l5{margin-left:41.6666666667%}.row .col.pull-l5{right:41.6666666667%}.row .col.push-l5{left:41.6666666667%}.row .col.offset-l6{margin-left:50%}.row .col.pull-l6{right:50%}.row .col.push-l6{left:50%}.row .col.offset-l7{margin-left:58.3333333333%}.row .col.pull-l7{right:58.3333333333%}.row .col.push-l7{left:58.3333333333%}.row .col.offset-l8{margin-left:66.6666666667%}.row .col.pull-l8{right:66.6666666667%}.row .col.push-l8{left:66.6666666667%}.row .col.offset-l9{margin-left:75%}.row .col.pull-l9{right:75%}.row .col.push-l9{left:75%}.row .col.offset-l10{margin-left:83.3333333333%}.row .col.pull-l10{right:83.3333333333%}.row .col.push-l10{left:83.3333333333%}.row .col.offset-l11{margin-left:91.6666666667%}.row .col.pull-l11{right:91.6666666667%}.row .col.push-l11{left:91.6666666667%}.row .col.offset-l12{margin-left:100%}.row .col.pull-l12{right:100%}.row .col.push-l12{left:100%}}nav{color:#fff;background-color:#ee6e73;width:100%;height:56px;line-height:56px}nav.nav-extended{height:auto}nav.nav-extended .nav-wrapper{min-height:56px;height:auto}nav.nav-extended .nav-content{position:relative;line-height:normal}nav a{color:#fff}nav i,nav [class^="mdi-"],nav [class*="mdi-"],nav i.material-icons{display:block;font-size:24px;height:56px;line-height:56px}nav .nav-wrapper{position:relative;height:100%}@media only screen and (min-width: 993px){nav a.button-collapse{display:none}}nav .button-collapse{float:left;position:relative;z-index:1;height:56px;margin:0 18px}nav .button-collapse i{height:56px;line-height:56px}nav .brand-logo{position:absolute;color:#fff;display:inline-block;font-size:2.1rem;padding:0;white-space:nowrap}nav .brand-logo.center{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}@media only screen and (max-width: 992px){nav .brand-logo{left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}nav .brand-logo.left,nav .brand-logo.right{padding:0;-webkit-transform:none;transform:none}nav .brand-logo.left{left:0.5rem}nav .brand-logo.right{right:0.5rem;left:auto}}nav .brand-logo.right{right:0.5rem;padding:0}nav .brand-logo i,nav .brand-logo [class^="mdi-"],nav .brand-logo [class*="mdi-"],nav .brand-logo i.material-icons{float:left;margin-right:15px}nav .nav-title{display:inline-block;font-size:32px;padding:28px 0}nav ul{margin:0}nav ul li{transition:background-color .3s;float:left;padding:0}nav ul li.active{background-color:rgba(0,0,0,0.1)}nav ul a{transition:background-color .3s;font-size:1rem;color:#fff;display:block;padding:0 15px;cursor:pointer}nav ul a.btn,nav ul a.btn-large,nav ul a.btn-large,nav ul a.btn-flat,nav ul a.btn-floating{margin-top:-2px;margin-left:15px;margin-right:15px}nav ul a.btn>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-large>.material-icons,nav ul a.btn-flat>.material-icons,nav ul a.btn-floating>.material-icons{height:inherit;line-height:inherit}nav ul a:hover{background-color:rgba(0,0,0,0.1)}nav ul.left{float:left}nav form{height:100%}nav .input-field{margin:0;height:100%}nav .input-field input{height:100%;font-size:1.2rem;border:none;padding-left:2rem}nav .input-field input:focus,nav .input-field input[type=text]:valid,nav .input-field input[type=password]:valid,nav .input-field input[type=email]:valid,nav .input-field input[type=url]:valid,nav .input-field input[type=date]:valid{border:none;box-shadow:none}nav .input-field label{top:0;left:0}nav .input-field label i{color:rgba(255,255,255,0.7);transition:color .3s}nav .input-field label.active i{color:#fff}.navbar-fixed{position:relative;height:56px;z-index:997}.navbar-fixed nav{position:fixed}@media only screen and (min-width: 601px){nav.nav-extended .nav-wrapper{min-height:64px}nav,nav .nav-wrapper i,nav a.button-collapse,nav a.button-collapse i{height:64px;line-height:64px}.navbar-fixed{height:64px}}@font-face{font-family:"Roboto";src:local(Roboto Thin),url("../fonts/roboto/Roboto-Thin.eot");src:url("../fonts/roboto/Roboto-Thin.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto/Roboto-Thin.woff2") format("woff2"),url("../fonts/roboto/Roboto-Thin.woff") format("woff"),url("../fonts/roboto/Roboto-Thin.ttf") format("truetype");font-weight:200}@font-face{font-family:"Roboto";src:local(Roboto Light),url("../fonts/roboto/Roboto-Light.eot");src:url("../fonts/roboto/Roboto-Light.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto/Roboto-Light.woff2") format("woff2"),url("../fonts/roboto/Roboto-Light.woff") format("woff"),url("../fonts/roboto/Roboto-Light.ttf") format("truetype");font-weight:300}@font-face{font-family:"Roboto";src:local(Roboto Regular),url("../fonts/roboto/Roboto-Regular.eot");src:url("../fonts/roboto/Roboto-Regular.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto/Roboto-Regular.woff2") format("woff2"),url("../fonts/roboto/Roboto-Regular.woff") format("woff"),url("../fonts/roboto/Roboto-Regular.ttf") format("truetype");font-weight:400}@font-face{font-family:"Roboto";src:url("../fonts/roboto/Roboto-Medium.eot");src:url("../fonts/roboto/Roboto-Medium.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto/Roboto-Medium.woff2") format("woff2"),url("../fonts/roboto/Roboto-Medium.woff") format("woff"),url("../fonts/roboto/Roboto-Medium.ttf") format("truetype");font-weight:500}@font-face{font-family:"Roboto";src:url("../fonts/roboto/Roboto-Bold.eot");src:url("../fonts/roboto/Roboto-Bold.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto/Roboto-Bold.woff2") format("woff2"),url("../fonts/roboto/Roboto-Bold.woff") format("woff"),url("../fonts/roboto/Roboto-Bold.ttf") format("truetype");font-weight:700}a{text-decoration:none}html{line-height:1.5;font-family:"Roboto", sans-serif;font-weight:normal;color:rgba(0,0,0,0.87)}@media only screen and (min-width: 0){html{font-size:14px}}@media only screen and (min-width: 992px){html{font-size:14.5px}}@media only screen and (min-width: 1200px){html{font-size:15px}}h1,h2,h3,h4,h5,h6{font-weight:400;line-height:1.1}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{font-weight:inherit}h1{font-size:4.2rem;line-height:110%;margin:2.1rem 0 1.68rem 0}h2{font-size:3.56rem;line-height:110%;margin:1.78rem 0 1.424rem 0}h3{font-size:2.92rem;line-height:110%;margin:1.46rem 0 1.168rem 0}h4{font-size:2.28rem;line-height:110%;margin:1.14rem 0 .912rem 0}h5{font-size:1.64rem;line-height:110%;margin:.82rem 0 .656rem 0}h6{font-size:1rem;line-height:110%;margin:.5rem 0 .4rem 0}em{font-style:italic}strong{font-weight:500}small{font-size:75%}.light,footer.page-footer .footer-copyright{font-weight:300}.thin{font-weight:200}.flow-text{font-weight:300}@media only screen and (min-width: 360px){.flow-text{font-size:1.2rem}}@media only screen and (min-width: 390px){.flow-text{font-size:1.224rem}}@media only screen and (min-width: 420px){.flow-text{font-size:1.248rem}}@media only screen and (min-width: 450px){.flow-text{font-size:1.272rem}}@media only screen and (min-width: 480px){.flow-text{font-size:1.296rem}}@media only screen and (min-width: 510px){.flow-text{font-size:1.32rem}}@media only screen and (min-width: 540px){.flow-text{font-size:1.344rem}}@media only screen and (min-width: 570px){.flow-text{font-size:1.368rem}}@media only screen and (min-width: 600px){.flow-text{font-size:1.392rem}}@media only screen and (min-width: 630px){.flow-text{font-size:1.416rem}}@media only screen and (min-width: 660px){.flow-text{font-size:1.44rem}}@media only screen and (min-width: 690px){.flow-text{font-size:1.464rem}}@media only screen and (min-width: 720px){.flow-text{font-size:1.488rem}}@media only screen and (min-width: 750px){.flow-text{font-size:1.512rem}}@media only screen and (min-width: 780px){.flow-text{font-size:1.536rem}}@media only screen and (min-width: 810px){.flow-text{font-size:1.56rem}}@media only screen and (min-width: 840px){.flow-text{font-size:1.584rem}}@media only screen and (min-width: 870px){.flow-text{font-size:1.608rem}}@media only screen and (min-width: 900px){.flow-text{font-size:1.632rem}}@media only screen and (min-width: 930px){.flow-text{font-size:1.656rem}}@media only screen and (min-width: 960px){.flow-text{font-size:1.68rem}}@media only screen and (max-width: 360px){.flow-text{font-size:1.2rem}}.scale-transition{transition:-webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important;transition:transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63), -webkit-transform 0.3s cubic-bezier(0.53, 0.01, 0.36, 1.63) !important}.scale-transition.scale-out{-webkit-transform:scale(0);transform:scale(0);transition:-webkit-transform .2s !important;transition:transform .2s !important;transition:transform .2s, -webkit-transform .2s !important}.scale-transition.scale-in{-webkit-transform:scale(1);transform:scale(1)}.card-panel{transition:box-shadow .25s;padding:24px;margin:.5rem 0 1rem 0;border-radius:2px;background-color:#fff}.card{position:relative;margin:.5rem 0 1rem 0;background-color:#fff;transition:box-shadow .25s;border-radius:2px}.card .card-title{font-size:24px;font-weight:300}.card .card-title.activator{cursor:pointer}.card.small,.card.medium,.card.large{position:relative}.card.small .card-image,.card.medium .card-image,.card.large .card-image{max-height:60%;overflow:hidden}.card.small .card-image+.card-content,.card.medium .card-image+.card-content,.card.large .card-image+.card-content{max-height:40%}.card.small .card-content,.card.medium .card-content,.card.large .card-content{max-height:100%;overflow:hidden}.card.small .card-action,.card.medium .card-action,.card.large .card-action{position:absolute;bottom:0;left:0;right:0}.card.small{height:300px}.card.medium{height:400px}.card.large{height:500px}.card.horizontal{display:-webkit-flex;display:-ms-flexbox;display:flex}.card.horizontal.small .card-image,.card.horizontal.medium .card-image,.card.horizontal.large .card-image{height:100%;max-height:none;overflow:visible}.card.horizontal.small .card-image img,.card.horizontal.medium .card-image img,.card.horizontal.large .card-image img{height:100%}.card.horizontal .card-image{max-width:50%}.card.horizontal .card-image img{border-radius:2px 0 0 2px;max-width:100%;width:auto}.card.horizontal .card-stacked{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex:1;-ms-flex:1;flex:1;position:relative}.card.horizontal .card-stacked .card-content{-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.card.sticky-action .card-action{z-index:2}.card.sticky-action .card-reveal{z-index:1;padding-bottom:64px}.card .card-image{position:relative}.card .card-image img{display:block;border-radius:2px 2px 0 0;position:relative;left:0;right:0;top:0;bottom:0;width:100%}.card .card-image .card-title{color:#fff;position:absolute;bottom:0;left:0;max-width:100%;padding:24px}.card .card-content{padding:24px;border-radius:0 0 2px 2px}.card .card-content p{margin:0;color:inherit}.card .card-content .card-title{display:block;line-height:32px;margin-bottom:8px}.card .card-content .card-title i{line-height:32px}.card .card-action{position:relative;background-color:inherit;border-top:1px solid rgba(160,160,160,0.2);padding:16px 24px}.card .card-action a:not(.btn):not(.btn-large):not(.btn-large):not(.btn-floating){color:#ffab40;margin-right:24px;transition:color .3s ease;text-transform:uppercase}.card .card-action a:not(.btn):not(.btn-large):not(.btn-large):not(.btn-floating):hover{color:#ffd8a6}.card .card-reveal{padding:24px;position:absolute;background-color:#fff;width:100%;overflow-y:auto;left:0;top:100%;height:100%;z-index:3;display:none}.card .card-reveal .card-title{cursor:pointer;display:block}#toast-container{display:block;position:fixed;z-index:10000}@media only screen and (max-width: 600px){#toast-container{min-width:100%;bottom:0%}}@media only screen and (min-width: 601px) and (max-width: 992px){#toast-container{left:5%;bottom:7%;max-width:90%}}@media only screen and (min-width: 993px){#toast-container{top:10%;right:7%;max-width:86%}}.toast{border-radius:2px;top:35px;width:auto;clear:both;margin-top:10px;position:relative;max-width:100%;height:auto;min-height:48px;line-height:1.5em;word-break:break-all;background-color:#323232;padding:10px 25px;font-size:1.1rem;font-weight:300;color:#fff;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.toast .btn,.toast .btn-large,.toast .btn-flat{margin:0;margin-left:3rem}.toast.rounded{border-radius:24px}@media only screen and (max-width: 600px){.toast{width:100%;border-radius:0}}@media only screen and (min-width: 601px) and (max-width: 992px){.toast{float:left}}@media only screen and (min-width: 993px){.toast{float:right}}.tabs{position:relative;overflow-x:auto;overflow-y:hidden;height:48px;width:100%;background-color:#fff;margin:0 auto;white-space:nowrap}.tabs.tabs-transparent{background-color:transparent}.tabs.tabs-transparent .tab a,.tabs.tabs-transparent .tab.disabled a,.tabs.tabs-transparent .tab.disabled a:hover{color:rgba(255,255,255,0.7)}.tabs.tabs-transparent .tab a:hover,.tabs.tabs-transparent .tab a.active{color:#fff}.tabs.tabs-transparent .indicator{background-color:#fff}.tabs.tabs-fixed-width{display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs.tabs-fixed-width .tab{-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab{display:inline-block;text-align:center;line-height:48px;height:48px;padding:0;margin:0;text-transform:uppercase}.tabs .tab a{color:rgba(238,110,115,0.7);display:block;width:100%;height:100%;padding:0 24px;font-size:14px;text-overflow:ellipsis;overflow:hidden;transition:color .28s ease}.tabs .tab a:hover,.tabs .tab a.active{background-color:transparent;color:#ee6e73}.tabs .tab.disabled a,.tabs .tab.disabled a:hover{color:rgba(238,110,115,0.7);cursor:default}.tabs .indicator{position:absolute;bottom:0;height:2px;background-color:#f6b2b5;will-change:left, right}@media only screen and (max-width: 992px){.tabs{display:-webkit-flex;display:-ms-flexbox;display:flex}.tabs .tab{-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1}.tabs .tab a{padding:0 12px}}.material-tooltip{padding:10px 8px;font-size:1rem;z-index:2000;background-color:transparent;border-radius:2px;color:#fff;min-height:36px;line-height:120%;opacity:0;position:absolute;text-align:center;max-width:calc(100% - 4px);overflow:hidden;left:0;top:0;pointer-events:none;visibility:hidden}.backdrop{position:absolute;opacity:0;height:7px;width:14px;border-radius:0 0 50% 50%;background-color:#323232;z-index:-1;-webkit-transform-origin:50% 0%;transform-origin:50% 0%;visibility:hidden}.btn,.btn-large,.btn-flat{border:none;border-radius:2px;display:inline-block;height:36px;line-height:36px;padding:0 2rem;text-transform:uppercase;vertical-align:middle;-webkit-tap-highlight-color:transparent}.btn.disabled,.disabled.btn-large,.btn-floating.disabled,.btn-large.disabled,.btn-flat.disabled,.btn:disabled,.btn-large:disabled,.btn-floating:disabled,.btn-large:disabled,.btn-flat:disabled,.btn[disabled],[disabled].btn-large,.btn-floating[disabled],.btn-large[disabled],.btn-flat[disabled]{pointer-events:none;background-color:#DFDFDF !important;box-shadow:none;color:#9F9F9F !important;cursor:default}.btn.disabled:hover,.disabled.btn-large:hover,.btn-floating.disabled:hover,.btn-large.disabled:hover,.btn-flat.disabled:hover,.btn:disabled:hover,.btn-large:disabled:hover,.btn-floating:disabled:hover,.btn-large:disabled:hover,.btn-flat:disabled:hover,.btn[disabled]:hover,[disabled].btn-large:hover,.btn-floating[disabled]:hover,.btn-large[disabled]:hover,.btn-flat[disabled]:hover{background-color:#DFDFDF !important;color:#9F9F9F !important}.btn,.btn-large,.btn-floating,.btn-large,.btn-flat{outline:0}.btn i,.btn-large i,.btn-floating i,.btn-large i,.btn-flat i{font-size:1.3rem;line-height:inherit}.btn:focus,.btn-large:focus,.btn-floating:focus{background-color:#1d7d74}.btn,.btn-large{text-decoration:none;color:#fff;background-color:#26a69a;text-align:center;letter-spacing:.5px;transition:.2s ease-out;cursor:pointer}.btn:hover,.btn-large:hover{background-color:#2bbbad}.btn-floating{display:inline-block;color:#fff;position:relative;overflow:hidden;z-index:1;width:40px;height:40px;line-height:40px;padding:0;background-color:#26a69a;border-radius:50%;transition:.3s;cursor:pointer;vertical-align:middle}.btn-floating:hover{background-color:#26a69a}.btn-floating:before{border-radius:0}.btn-floating.btn-large{width:56px;height:56px}.btn-floating.btn-large i{line-height:56px}.btn-floating.halfway-fab{position:absolute;right:24px;bottom:0;-webkit-transform:translateY(50%);transform:translateY(50%)}.btn-floating.halfway-fab.left{right:auto;left:24px}.btn-floating i{width:inherit;display:inline-block;text-align:center;color:#fff;font-size:1.6rem;line-height:40px}button.btn-floating{border:none}.fixed-action-btn{position:fixed;right:23px;bottom:23px;padding-top:15px;margin-bottom:0;z-index:998}.fixed-action-btn.active ul{visibility:visible}.fixed-action-btn.horizontal{padding:0 0 0 15px}.fixed-action-btn.horizontal ul{text-align:right;right:64px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);height:100%;left:auto;width:500px}.fixed-action-btn.horizontal ul li{display:inline-block;margin:15px 15px 0 0}.fixed-action-btn.toolbar{padding:0;height:56px}.fixed-action-btn.toolbar.active>a i{opacity:0}.fixed-action-btn.toolbar ul{display:-webkit-flex;display:-ms-flexbox;display:flex;top:0;bottom:0}.fixed-action-btn.toolbar ul li{-webkit-flex:1;-ms-flex:1;flex:1;display:inline-block;margin:0;height:100%;transition:none}.fixed-action-btn.toolbar ul li a{display:block;overflow:hidden;position:relative;width:100%;height:100%;background-color:transparent;box-shadow:none;color:#fff;line-height:56px;z-index:1}.fixed-action-btn.toolbar ul li a i{line-height:inherit}.fixed-action-btn ul{left:0;right:0;text-align:center;position:absolute;bottom:64px;margin:0;visibility:hidden}.fixed-action-btn ul li{margin-bottom:15px}.fixed-action-btn ul a.btn-floating{opacity:0}.fixed-action-btn .fab-backdrop{position:absolute;top:0;left:0;z-index:-1;width:40px;height:40px;background-color:#26a69a;border-radius:50%;-webkit-transform:scale(0);transform:scale(0)}.btn-flat{box-shadow:none;background-color:transparent;color:#343434;cursor:pointer;transition:background-color .2s}.btn-flat:focus,.btn-flat:active{background-color:transparent}.btn-flat:focus,.btn-flat:hover{background-color:rgba(0,0,0,0.1);box-shadow:none}.btn-flat:active{background-color:rgba(0,0,0,0.2)}.btn-flat.disabled{background-color:transparent !important;color:#b3b3b3 !important;cursor:default}.btn-large{height:54px;line-height:54px}.btn-large i{font-size:1.6rem}.btn-block{display:block}.dropdown-content{background-color:#fff;margin:0;display:none;min-width:100px;max-height:650px;overflow-y:auto;opacity:0;position:absolute;z-index:999;will-change:width, height}.dropdown-content li{clear:both;color:rgba(0,0,0,0.87);cursor:pointer;min-height:50px;line-height:1.5rem;width:100%;text-align:left;text-transform:none}.dropdown-content li:hover,.dropdown-content li.active,.dropdown-content li.selected{background-color:#eee}.dropdown-content li.active.selected{background-color:#e1e1e1}.dropdown-content li.divider{min-height:0;height:1px}.dropdown-content li>a,.dropdown-content li>span{font-size:16px;color:#26a69a;display:block;line-height:22px;padding:14px 16px}.dropdown-content li>span>label{top:1px;left:0;height:18px}.dropdown-content li>a>i{height:inherit;line-height:inherit}.input-field.col .dropdown-content [type="checkbox"]+label{top:1px;left:0;height:18px}/*!
+ * Waves v0.6.0
+ * http://fian.my.id/Waves
+ *
+ * Copyright 2014 Alfiana E. Sibuea and other contributors
+ * Released under the MIT license
+ * https://github.com/fians/Waves/blob/master/LICENSE
+ */.waves-effect{position:relative;cursor:pointer;display:inline-block;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;vertical-align:middle;z-index:1;transition:.3s ease-out}.waves-effect .waves-ripple{position:absolute;border-radius:50%;width:20px;height:20px;margin-top:-10px;margin-left:-10px;opacity:0;background:rgba(0,0,0,0.2);transition:all 0.7s ease-out;transition-property:opacity, -webkit-transform;transition-property:transform, opacity;transition-property:transform, opacity, -webkit-transform;-webkit-transform:scale(0);transform:scale(0);pointer-events:none}.waves-effect.waves-light .waves-ripple{background-color:rgba(255,255,255,0.45)}.waves-effect.waves-red .waves-ripple{background-color:rgba(244,67,54,0.7)}.waves-effect.waves-yellow .waves-ripple{background-color:rgba(255,235,59,0.7)}.waves-effect.waves-orange .waves-ripple{background-color:rgba(255,152,0,0.7)}.waves-effect.waves-purple .waves-ripple{background-color:rgba(156,39,176,0.7)}.waves-effect.waves-green .waves-ripple{background-color:rgba(76,175,80,0.7)}.waves-effect.waves-teal .waves-ripple{background-color:rgba(0,150,136,0.7)}.waves-effect input[type="button"],.waves-effect input[type="reset"],.waves-effect input[type="submit"]{border:0;font-style:normal;font-size:inherit;text-transform:inherit;background:none}.waves-effect img{position:relative;z-index:-1}.waves-notransition{transition:none !important}.waves-circle{-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-mask-image:-webkit-radial-gradient(circle, #fff 100%, #000 100%)}.waves-input-wrapper{border-radius:0.2em;vertical-align:bottom}.waves-input-wrapper .waves-button-input{position:relative;top:0;left:0;z-index:1}.waves-circle{text-align:center;width:2.5em;height:2.5em;line-height:2.5em;border-radius:50%;-webkit-mask-image:none}.waves-block{display:block}.waves-effect .waves-ripple{z-index:-1}.modal{display:none;position:fixed;left:0;right:0;background-color:#fafafa;padding:0;max-height:70%;width:55%;margin:auto;overflow-y:auto;border-radius:2px;will-change:top, opacity}@media only screen and (max-width: 992px){.modal{width:80%}}.modal h1,.modal h2,.modal h3,.modal h4{margin-top:0}.modal .modal-content{padding:24px}.modal .modal-close{cursor:pointer}.modal .modal-footer{border-radius:0 0 2px 2px;background-color:#fafafa;padding:4px 6px;height:56px;width:100%}.modal .modal-footer .btn,.modal .modal-footer .btn-large,.modal .modal-footer .btn-flat{float:right;margin:6px 0}.modal-overlay{position:fixed;z-index:999;top:-100px;left:0;bottom:0;right:0;height:125%;width:100%;background:#000;display:none;will-change:opacity}.modal.modal-fixed-footer{padding:0;height:70%}.modal.modal-fixed-footer .modal-content{position:absolute;height:calc(100% - 56px);max-height:100%;width:100%;overflow-y:auto}.modal.modal-fixed-footer .modal-footer{border-top:1px solid rgba(0,0,0,0.1);position:absolute;bottom:0}.modal.bottom-sheet{top:auto;bottom:-100%;margin:0;width:100%;max-height:45%;border-radius:0;will-change:bottom, opacity}.collapsible{border-top:1px solid #ddd;border-right:1px solid #ddd;border-left:1px solid #ddd;margin:.5rem 0 1rem 0}.collapsible-header{display:block;cursor:pointer;min-height:3rem;line-height:3rem;padding:0 1rem;background-color:#fff;border-bottom:1px solid #ddd}.collapsible-header i{width:2rem;font-size:1.6rem;line-height:3rem;display:block;float:left;text-align:center;margin-right:1rem}.collapsible-body{display:none;border-bottom:1px solid #ddd;box-sizing:border-box;padding:2rem}.side-nav .collapsible,.side-nav.fixed .collapsible{border:none;box-shadow:none}.side-nav .collapsible li,.side-nav.fixed .collapsible li{padding:0}.side-nav .collapsible-header,.side-nav.fixed .collapsible-header{background-color:transparent;border:none;line-height:inherit;height:inherit;padding:0 16px}.side-nav .collapsible-header:hover,.side-nav.fixed .collapsible-header:hover{background-color:rgba(0,0,0,0.05)}.side-nav .collapsible-header i,.side-nav.fixed .collapsible-header i{line-height:inherit}.side-nav .collapsible-body,.side-nav.fixed .collapsible-body{border:0;background-color:#fff}.side-nav .collapsible-body li a,.side-nav.fixed .collapsible-body li a{padding:0 23.5px 0 31px}.collapsible.popout{border:none;box-shadow:none}.collapsible.popout>li{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);margin:0 24px;transition:margin 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)}.collapsible.popout>li.active{box-shadow:0 5px 11px 0 rgba(0,0,0,0.18),0 4px 15px 0 rgba(0,0,0,0.15);margin:16px 0}.chip{display:inline-block;height:32px;font-size:13px;font-weight:500;color:rgba(0,0,0,0.6);line-height:32px;padding:0 12px;border-radius:16px;background-color:#e4e4e4;margin-bottom:5px;margin-right:5px}.chip img{float:left;margin:0 8px 0 -12px;height:32px;width:32px;border-radius:50%}.chip .close{cursor:pointer;float:right;font-size:16px;line-height:32px;padding-left:8px}.chips{border:none;border-bottom:1px solid #9e9e9e;box-shadow:none;margin:0 0 20px 0;min-height:45px;outline:none;transition:all .3s}.chips.focus{border-bottom:1px solid #26a69a;box-shadow:0 1px 0 0 #26a69a}.chips:hover{cursor:text}.chips .chip.selected{background-color:#26a69a;color:#fff}.chips .input{background:none;border:0;color:rgba(0,0,0,0.6);display:inline-block;font-size:1rem;height:3rem;line-height:32px;outline:0;margin:0;padding:0 !important;width:120px !important}.chips .input:focus{border:0 !important;box-shadow:none !important}.prefix ~ .chips{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.chips:empty ~ label{font-size:0.8rem;-webkit-transform:translateY(-140%);transform:translateY(-140%)}.materialboxed{display:block;cursor:-webkit-zoom-in;cursor:zoom-in;position:relative;transition:opacity .4s;-webkit-backface-visibility:hidden}.materialboxed:hover:not(.active){opacity:.8}.materialboxed.active{cursor:-webkit-zoom-out;cursor:zoom-out}#materialbox-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#292929;z-index:1000;will-change:opacity}.materialbox-caption{position:fixed;display:none;color:#fff;line-height:50px;bottom:0;left:0;width:100%;text-align:center;padding:0% 15%;height:50px;z-index:1000;-webkit-font-smoothing:antialiased}select:focus{outline:1px solid #c9f3ef}button:focus{outline:none;background-color:#2ab7a9}label{font-size:.8rem;color:#9e9e9e}::-webkit-input-placeholder{color:#d1d1d1}:-moz-placeholder{color:#d1d1d1}::-moz-placeholder{color:#d1d1d1}:-ms-input-placeholder{color:#d1d1d1}input:not([type]),input[type=text],input[type=password],input[type=email],input[type=url],input[type=time],input[type=date],input[type=datetime],input[type=datetime-local],input[type=tel],input[type=number],input[type=search],textarea.materialize-textarea{background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;border-radius:0;outline:none;height:3rem;width:100%;font-size:1rem;margin:0 0 20px 0;padding:0;box-shadow:none;box-sizing:content-box;transition:all 0.3s}input:not([type]):disabled,input:not([type])[readonly="readonly"],input[type=text]:disabled,input[type=text][readonly="readonly"],input[type=password]:disabled,input[type=password][readonly="readonly"],input[type=email]:disabled,input[type=email][readonly="readonly"],input[type=url]:disabled,input[type=url][readonly="readonly"],input[type=time]:disabled,input[type=time][readonly="readonly"],input[type=date]:disabled,input[type=date][readonly="readonly"],input[type=datetime]:disabled,input[type=datetime][readonly="readonly"],input[type=datetime-local]:disabled,input[type=datetime-local][readonly="readonly"],input[type=tel]:disabled,input[type=tel][readonly="readonly"],input[type=number]:disabled,input[type=number][readonly="readonly"],input[type=search]:disabled,input[type=search][readonly="readonly"],textarea.materialize-textarea:disabled,textarea.materialize-textarea[readonly="readonly"]{color:rgba(0,0,0,0.26);border-bottom:1px dotted rgba(0,0,0,0.26)}input:not([type]):disabled+label,input:not([type])[readonly="readonly"]+label,input[type=text]:disabled+label,input[type=text][readonly="readonly"]+label,input[type=password]:disabled+label,input[type=password][readonly="readonly"]+label,input[type=email]:disabled+label,input[type=email][readonly="readonly"]+label,input[type=url]:disabled+label,input[type=url][readonly="readonly"]+label,input[type=time]:disabled+label,input[type=time][readonly="readonly"]+label,input[type=date]:disabled+label,input[type=date][readonly="readonly"]+label,input[type=datetime]:disabled+label,input[type=datetime][readonly="readonly"]+label,input[type=datetime-local]:disabled+label,input[type=datetime-local][readonly="readonly"]+label,input[type=tel]:disabled+label,input[type=tel][readonly="readonly"]+label,input[type=number]:disabled+label,input[type=number][readonly="readonly"]+label,input[type=search]:disabled+label,input[type=search][readonly="readonly"]+label,textarea.materialize-textarea:disabled+label,textarea.materialize-textarea[readonly="readonly"]+label{color:rgba(0,0,0,0.26)}input:not([type]):focus:not([readonly]),input[type=text]:focus:not([readonly]),input[type=password]:focus:not([readonly]),input[type=email]:focus:not([readonly]),input[type=url]:focus:not([readonly]),input[type=time]:focus:not([readonly]),input[type=date]:focus:not([readonly]),input[type=datetime]:focus:not([readonly]),input[type=datetime-local]:focus:not([readonly]),input[type=tel]:focus:not([readonly]),input[type=number]:focus:not([readonly]),input[type=search]:focus:not([readonly]),textarea.materialize-textarea:focus:not([readonly]){border-bottom:1px solid #26a69a;box-shadow:0 1px 0 0 #26a69a}input:not([type]):focus:not([readonly])+label,input[type=text]:focus:not([readonly])+label,input[type=password]:focus:not([readonly])+label,input[type=email]:focus:not([readonly])+label,input[type=url]:focus:not([readonly])+label,input[type=time]:focus:not([readonly])+label,input[type=date]:focus:not([readonly])+label,input[type=datetime]:focus:not([readonly])+label,input[type=datetime-local]:focus:not([readonly])+label,input[type=tel]:focus:not([readonly])+label,input[type=number]:focus:not([readonly])+label,input[type=search]:focus:not([readonly])+label,textarea.materialize-textarea:focus:not([readonly])+label{color:#26a69a}input:not([type]).valid,input:not([type]):focus.valid,input[type=text].valid,input[type=text]:focus.valid,input[type=password].valid,input[type=password]:focus.valid,input[type=email].valid,input[type=email]:focus.valid,input[type=url].valid,input[type=url]:focus.valid,input[type=time].valid,input[type=time]:focus.valid,input[type=date].valid,input[type=date]:focus.valid,input[type=datetime].valid,input[type=datetime]:focus.valid,input[type=datetime-local].valid,input[type=datetime-local]:focus.valid,input[type=tel].valid,input[type=tel]:focus.valid,input[type=number].valid,input[type=number]:focus.valid,input[type=search].valid,input[type=search]:focus.valid,textarea.materialize-textarea.valid,textarea.materialize-textarea:focus.valid{border-bottom:1px solid #4CAF50;box-shadow:0 1px 0 0 #4CAF50}input:not([type]).valid+label:after,input:not([type]):focus.valid+label:after,input[type=text].valid+label:after,input[type=text]:focus.valid+label:after,input[type=password].valid+label:after,input[type=password]:focus.valid+label:after,input[type=email].valid+label:after,input[type=email]:focus.valid+label:after,input[type=url].valid+label:after,input[type=url]:focus.valid+label:after,input[type=time].valid+label:after,input[type=time]:focus.valid+label:after,input[type=date].valid+label:after,input[type=date]:focus.valid+label:after,input[type=datetime].valid+label:after,input[type=datetime]:focus.valid+label:after,input[type=datetime-local].valid+label:after,input[type=datetime-local]:focus.valid+label:after,input[type=tel].valid+label:after,input[type=tel]:focus.valid+label:after,input[type=number].valid+label:after,input[type=number]:focus.valid+label:after,input[type=search].valid+label:after,input[type=search]:focus.valid+label:after,textarea.materialize-textarea.valid+label:after,textarea.materialize-textarea:focus.valid+label:after{content:attr(data-success);color:#4CAF50;opacity:1}input:not([type]).invalid,input:not([type]):focus.invalid,input[type=text].invalid,input[type=text]:focus.invalid,input[type=password].invalid,input[type=password]:focus.invalid,input[type=email].invalid,input[type=email]:focus.invalid,input[type=url].invalid,input[type=url]:focus.invalid,input[type=time].invalid,input[type=time]:focus.invalid,input[type=date].invalid,input[type=date]:focus.invalid,input[type=datetime].invalid,input[type=datetime]:focus.invalid,input[type=datetime-local].invalid,input[type=datetime-local]:focus.invalid,input[type=tel].invalid,input[type=tel]:focus.invalid,input[type=number].invalid,input[type=number]:focus.invalid,input[type=search].invalid,input[type=search]:focus.invalid,textarea.materialize-textarea.invalid,textarea.materialize-textarea:focus.invalid{border-bottom:1px solid #F44336;box-shadow:0 1px 0 0 #F44336}input:not([type]).invalid+label:after,input:not([type]):focus.invalid+label:after,input[type=text].invalid+label:after,input[type=text]:focus.invalid+label:after,input[type=password].invalid+label:after,input[type=password]:focus.invalid+label:after,input[type=email].invalid+label:after,input[type=email]:focus.invalid+label:after,input[type=url].invalid+label:after,input[type=url]:focus.invalid+label:after,input[type=time].invalid+label:after,input[type=time]:focus.invalid+label:after,input[type=date].invalid+label:after,input[type=date]:focus.invalid+label:after,input[type=datetime].invalid+label:after,input[type=datetime]:focus.invalid+label:after,input[type=datetime-local].invalid+label:after,input[type=datetime-local]:focus.invalid+label:after,input[type=tel].invalid+label:after,input[type=tel]:focus.invalid+label:after,input[type=number].invalid+label:after,input[type=number]:focus.invalid+label:after,input[type=search].invalid+label:after,input[type=search]:focus.invalid+label:after,textarea.materialize-textarea.invalid+label:after,textarea.materialize-textarea:focus.invalid+label:after{content:attr(data-error);color:#F44336;opacity:1}input:not([type]).validate+label,input[type=text].validate+label,input[type=password].validate+label,input[type=email].validate+label,input[type=url].validate+label,input[type=time].validate+label,input[type=date].validate+label,input[type=datetime].validate+label,input[type=datetime-local].validate+label,input[type=tel].validate+label,input[type=number].validate+label,input[type=search].validate+label,textarea.materialize-textarea.validate+label{width:100%;pointer-events:none}input:not([type])+label:after,input[type=text]+label:after,input[type=password]+label:after,input[type=email]+label:after,input[type=url]+label:after,input[type=time]+label:after,input[type=date]+label:after,input[type=datetime]+label:after,input[type=datetime-local]+label:after,input[type=tel]+label:after,input[type=number]+label:after,input[type=search]+label:after,textarea.materialize-textarea+label:after{display:block;content:"";position:absolute;top:60px;opacity:0;transition:.2s opacity ease-out, .2s color ease-out}.input-field{position:relative;margin-top:1rem}.input-field.inline{display:inline-block;vertical-align:middle;margin-left:5px}.input-field.inline input,.input-field.inline .select-dropdown{margin-bottom:1rem}.input-field.col label{left:.75rem}.input-field.col .prefix ~ label,.input-field.col .prefix ~ .validate ~ label{width:calc(100% - 3rem - 1.5rem)}.input-field label{color:#9e9e9e;position:absolute;top:0.8rem;left:0;font-size:1rem;cursor:text;transition:.2s ease-out}.input-field label:not(.label-icon).active{font-size:.8rem;-webkit-transform:translateY(-140%);transform:translateY(-140%)}.input-field .prefix{position:absolute;width:3rem;font-size:2rem;transition:color .2s}.input-field .prefix.active{color:#26a69a}.input-field .prefix ~ input,.input-field .prefix ~ textarea,.input-field .prefix ~ label,.input-field .prefix ~ .validate ~ label,.input-field .prefix ~ .autocomplete-content{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.input-field .prefix ~ label{margin-left:3rem}@media only screen and (max-width: 992px){.input-field .prefix ~ input{width:86%;width:calc(100% - 3rem)}}@media only screen and (max-width: 600px){.input-field .prefix ~ input{width:80%;width:calc(100% - 3rem)}}.input-field input[type=search]{display:block;line-height:inherit;padding-left:4rem;width:calc(100% - 4rem)}.input-field input[type=search]:focus{background-color:#fff;border:0;box-shadow:none;color:#444}.input-field input[type=search]:focus+label i,.input-field input[type=search]:focus ~ .mdi-navigation-close,.input-field input[type=search]:focus ~ .material-icons{color:#444}.input-field input[type=search]+label{left:1rem}.input-field input[type=search] ~ .mdi-navigation-close,.input-field input[type=search] ~ .material-icons{position:absolute;top:0;right:1rem;color:transparent;cursor:pointer;font-size:2rem;transition:.3s color}textarea{width:100%;height:3rem;background-color:transparent}textarea.materialize-textarea{overflow-y:hidden;padding:.8rem 0 1.6rem 0;resize:none;min-height:3rem}.hiddendiv{display:none;white-space:pre-wrap;word-wrap:break-word;overflow-wrap:break-word;padding-top:1.2rem}.autocomplete-content{margin-top:-15px;display:block;opacity:1;position:static}.autocomplete-content li .highlight{color:#444}.autocomplete-content li img{height:40px;width:40px;margin:5px 15px}[type="radio"]:not(:checked),[type="radio"]:checked{position:absolute;left:-9999px;opacity:0}[type="radio"]:not(:checked)+label,[type="radio"]:checked+label{position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;transition:.28s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}[type="radio"]+label:before,[type="radio"]+label:after{content:'';position:absolute;left:0;top:0;margin:4px;width:16px;height:16px;z-index:0;transition:.28s ease}[type="radio"]:not(:checked)+label:before,[type="radio"]:not(:checked)+label:after,[type="radio"]:checked+label:before,[type="radio"]:checked+label:after,[type="radio"].with-gap:checked+label:before,[type="radio"].with-gap:checked+label:after{border-radius:50%}[type="radio"]:not(:checked)+label:before,[type="radio"]:not(:checked)+label:after{border:2px solid #5a5a5a}[type="radio"]:not(:checked)+label:after{-webkit-transform:scale(0);transform:scale(0)}[type="radio"]:checked+label:before{border:2px solid transparent}[type="radio"]:checked+label:after,[type="radio"].with-gap:checked+label:before,[type="radio"].with-gap:checked+label:after{border:2px solid #26a69a}[type="radio"]:checked+label:after,[type="radio"].with-gap:checked+label:after{background-color:#26a69a}[type="radio"]:checked+label:after{-webkit-transform:scale(1.02);transform:scale(1.02)}[type="radio"].with-gap:checked+label:after{-webkit-transform:scale(0.5);transform:scale(0.5)}[type="radio"].tabbed:focus+label:before{box-shadow:0 0 0 10px rgba(0,0,0,0.1)}[type="radio"].with-gap:disabled:checked+label:before{border:2px solid rgba(0,0,0,0.26)}[type="radio"].with-gap:disabled:checked+label:after{border:none;background-color:rgba(0,0,0,0.26)}[type="radio"]:disabled:not(:checked)+label:before,[type="radio"]:disabled:checked+label:before{background-color:transparent;border-color:rgba(0,0,0,0.26)}[type="radio"]:disabled+label{color:rgba(0,0,0,0.26)}[type="radio"]:disabled:not(:checked)+label:before{border-color:rgba(0,0,0,0.26)}[type="radio"]:disabled:checked+label:after{background-color:rgba(0,0,0,0.26);border-color:#BDBDBD}form p{margin-bottom:10px;text-align:left}form p:last-child{margin-bottom:0}[type="checkbox"]:not(:checked),[type="checkbox"]:checked{position:absolute;left:-9999px;opacity:0}[type="checkbox"]+label{position:relative;padding-left:35px;cursor:pointer;display:inline-block;height:25px;line-height:25px;font-size:1rem;-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-ms-user-select:none}[type="checkbox"]+label:before,[type="checkbox"]:not(.filled-in)+label:after{content:'';position:absolute;top:0;left:0;width:18px;height:18px;z-index:0;border:2px solid #5a5a5a;border-radius:1px;margin-top:2px;transition:.2s}[type="checkbox"]:not(.filled-in)+label:after{border:0;-webkit-transform:scale(0);transform:scale(0)}[type="checkbox"]:not(:checked):disabled+label:before{border:none;background-color:rgba(0,0,0,0.26)}[type="checkbox"].tabbed:focus+label:after{-webkit-transform:scale(1);transform:scale(1);border:0;border-radius:50%;box-shadow:0 0 0 10px rgba(0,0,0,0.1);background-color:rgba(0,0,0,0.1)}[type="checkbox"]:checked+label:before{top:-4px;left:-5px;width:12px;height:22px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #26a69a;border-bottom:2px solid #26a69a;-webkit-transform:rotate(40deg);transform:rotate(40deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:checked:disabled+label:before{border-right:2px solid rgba(0,0,0,0.26);border-bottom:2px solid rgba(0,0,0,0.26)}[type="checkbox"]:indeterminate+label:before{top:-11px;left:-12px;width:10px;height:22px;border-top:none;border-left:none;border-right:2px solid #26a69a;border-bottom:none;-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"]:indeterminate:disabled+label:before{border-right:2px solid rgba(0,0,0,0.26);background-color:transparent}[type="checkbox"].filled-in+label:after{border-radius:2px}[type="checkbox"].filled-in+label:before,[type="checkbox"].filled-in+label:after{content:'';left:0;position:absolute;transition:border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s;z-index:1}[type="checkbox"].filled-in:not(:checked)+label:before{width:0;height:0;border:3px solid transparent;left:6px;top:10px;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:20% 40%;transform-origin:100% 100%}[type="checkbox"].filled-in:not(:checked)+label:after{height:20px;width:20px;background-color:transparent;border:2px solid #5a5a5a;top:0px;z-index:0}[type="checkbox"].filled-in:checked+label:before{top:0;left:1px;width:8px;height:13px;border-top:2px solid transparent;border-left:2px solid transparent;border-right:2px solid #fff;border-bottom:2px solid #fff;-webkit-transform:rotateZ(37deg);transform:rotateZ(37deg);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}[type="checkbox"].filled-in:checked+label:after{top:0;width:20px;height:20px;border:2px solid #26a69a;background-color:#26a69a;z-index:0}[type="checkbox"].filled-in.tabbed:focus+label:after{border-radius:2px;border-color:#5a5a5a;background-color:rgba(0,0,0,0.1)}[type="checkbox"].filled-in.tabbed:checked:focus+label:after{border-radius:2px;background-color:#26a69a;border-color:#26a69a}[type="checkbox"].filled-in:disabled:not(:checked)+label:before{background-color:transparent;border:2px solid transparent}[type="checkbox"].filled-in:disabled:not(:checked)+label:after{border-color:transparent;background-color:#BDBDBD}[type="checkbox"].filled-in:disabled:checked+label:before{background-color:transparent}[type="checkbox"].filled-in:disabled:checked+label:after{background-color:#BDBDBD;border-color:#BDBDBD}.switch,.switch *{-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-ms-user-select:none}.switch label{cursor:pointer}.switch label input[type=checkbox]{opacity:0;width:0;height:0}.switch label input[type=checkbox]:checked+.lever{background-color:#84c7c1}.switch label input[type=checkbox]:checked+.lever:after{background-color:#26a69a;left:24px}.switch label .lever{content:"";display:inline-block;position:relative;width:40px;height:15px;background-color:#818181;border-radius:15px;margin-right:10px;transition:background 0.3s ease;vertical-align:middle;margin:0 16px}.switch label .lever:after{content:"";position:absolute;display:inline-block;width:21px;height:21px;background-color:#F1F1F1;border-radius:21px;box-shadow:0 1px 3px 1px rgba(0,0,0,0.4);left:-5px;top:-3px;transition:left 0.3s ease, background .3s ease, box-shadow 0.1s ease}input[type=checkbox]:checked:not(:disabled) ~ .lever:active::after,input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::after{box-shadow:0 1px 3px 1px rgba(0,0,0,0.4),0 0 0 15px rgba(38,166,154,0.1)}input[type=checkbox]:not(:disabled) ~ .lever:active:after,input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::after{box-shadow:0 1px 3px 1px rgba(0,0,0,0.4),0 0 0 15px rgba(0,0,0,0.08)}.switch input[type=checkbox][disabled]+.lever{cursor:default}.switch label input[type=checkbox][disabled]+.lever:after,.switch label input[type=checkbox][disabled]:checked+.lever:after{background-color:#BDBDBD}select{display:none}select.browser-default{display:block}select{background-color:rgba(255,255,255,0.9);width:100%;padding:5px;border:1px solid #f2f2f2;border-radius:2px;height:3rem}.select-label{position:absolute}.select-wrapper{position:relative}.select-wrapper input.select-dropdown{position:relative;cursor:pointer;background-color:transparent;border:none;border-bottom:1px solid #9e9e9e;outline:none;height:3rem;line-height:3rem;width:100%;font-size:1rem;margin:0 0 20px 0;padding:0;display:block}.select-wrapper span.caret{color:initial;position:absolute;right:0;top:0;bottom:0;height:10px;margin:auto 0;font-size:10px;line-height:10px}.select-wrapper span.caret.disabled{color:rgba(0,0,0,0.26)}.select-wrapper+label{position:absolute;top:-14px;font-size:.8rem}select:disabled{color:rgba(0,0,0,0.3)}.select-wrapper input.select-dropdown:disabled{color:rgba(0,0,0,0.3);cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;border-bottom:1px solid rgba(0,0,0,0.3)}.select-wrapper i{color:rgba(0,0,0,0.3)}.select-dropdown li.disabled,.select-dropdown li.disabled>span,.select-dropdown li.optgroup{color:rgba(0,0,0,0.3);background-color:transparent}.prefix ~ .select-wrapper{margin-left:3rem;width:92%;width:calc(100% - 3rem)}.prefix ~ label{margin-left:3rem}.select-dropdown li img{height:40px;width:40px;margin:5px 15px;float:right}.select-dropdown li.optgroup{border-top:1px solid #eee}.select-dropdown li.optgroup.selected>span{color:rgba(0,0,0,0.7)}.select-dropdown li.optgroup>span{color:rgba(0,0,0,0.4)}.select-dropdown li.optgroup ~ li.optgroup-option{padding-left:1rem}.file-field{position:relative}.file-field .file-path-wrapper{overflow:hidden;padding-left:10px}.file-field input.file-path{width:100%}.file-field .btn,.file-field .btn-large{float:left;height:3rem;line-height:3rem}.file-field span{cursor:pointer}.file-field input[type=file]{position:absolute;top:0;right:0;left:0;bottom:0;width:100%;margin:0;padding:0;font-size:20px;cursor:pointer;opacity:0;filter:alpha(opacity=0)}.range-field{position:relative}input[type=range],input[type=range]+.thumb{cursor:pointer}input[type=range]{position:relative;background-color:transparent;border:none;outline:none;width:100%;margin:15px 0;padding:0}input[type=range]:focus{outline:none}input[type=range]+.thumb{position:absolute;border:none;height:0;width:0;border-radius:50%;background-color:#26a69a;top:10px;margin-left:-6px;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}input[type=range]+.thumb .value{display:block;width:30px;text-align:center;color:#26a69a;font-size:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}input[type=range]+.thumb.active{border-radius:50% 50% 50% 0}input[type=range]+.thumb.active .value{color:#fff;margin-left:-1px;margin-top:8px;font-size:10px}input[type=range]{-webkit-appearance:none}input[type=range]::-webkit-slider-runnable-track{height:3px;background:#c2c0c2;border:none}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;border:none;height:14px;width:14px;border-radius:50%;background-color:#26a69a;-webkit-transform-origin:50% 50%;transform-origin:50% 50%;margin:-5px 0 0 0;transition:.3s}input[type=range]:focus::-webkit-slider-runnable-track{background:#ccc}input[type=range]{border:1px solid white}input[type=range]::-moz-range-track{height:3px;background:#ddd;border:none}input[type=range]::-moz-range-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a;margin-top:-5px}input[type=range]:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input[type=range]:focus::-moz-range-track{background:#ccc}input[type=range]::-ms-track{height:3px;background:transparent;border-color:transparent;border-width:6px 0;color:transparent}input[type=range]::-ms-fill-lower{background:#777}input[type=range]::-ms-fill-upper{background:#ddd}input[type=range]::-ms-thumb{border:none;height:14px;width:14px;border-radius:50%;background:#26a69a}input[type=range]:focus::-ms-fill-lower{background:#888}input[type=range]:focus::-ms-fill-upper{background:#ccc}.table-of-contents.fixed{position:fixed}.table-of-contents li{padding:2px 0}.table-of-contents a{display:inline-block;font-weight:300;color:#757575;padding-left:20px;height:1.5rem;line-height:1.5rem;letter-spacing:.4;display:inline-block}.table-of-contents a:hover{color:#a8a8a8;padding-left:19px;border-left:1px solid #ee6e73}.table-of-contents a.active{font-weight:500;padding-left:18px;border-left:2px solid #ee6e73}.side-nav{position:fixed;width:300px;left:0;top:0;margin:0;-webkit-transform:translateX(-100%);transform:translateX(-100%);height:100%;height:calc(100% + 60px);height:-moz-calc(100%);padding-bottom:60px;background-color:#fff;z-index:999;overflow-y:auto;will-change:transform;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateX(-105%);transform:translateX(-105%)}.side-nav.right-aligned{right:0;-webkit-transform:translateX(105%);transform:translateX(105%);left:auto;-webkit-transform:translateX(100%);transform:translateX(100%)}.side-nav .collapsible{margin:0}.side-nav li{float:none;line-height:48px}.side-nav li.active{background-color:rgba(0,0,0,0.05)}.side-nav a{color:rgba(0,0,0,0.87);display:block;font-size:14px;font-weight:500;height:48px;line-height:48px;padding:0 32px}.side-nav a:hover{background-color:rgba(0,0,0,0.05)}.side-nav a.btn,.side-nav a.btn-large,.side-nav a.btn-large,.side-nav a.btn-flat,.side-nav a.btn-floating{margin:10px 15px}.side-nav a.btn,.side-nav a.btn-large,.side-nav a.btn-large,.side-nav a.btn-floating{color:#fff}.side-nav a.btn-flat{color:#343434}.side-nav a.btn:hover,.side-nav a.btn-large:hover,.side-nav a.btn-large:hover{background-color:#2bbbad}.side-nav a.btn-floating:hover{background-color:#26a69a}.side-nav li>a>i,.side-nav li>a>[class^="mdi-"],.side-nav li>a>[class*="mdi-"],.side-nav li>a>i.material-icons{float:left;height:48px;line-height:48px;margin:0 32px 0 0;width:24px;color:rgba(0,0,0,0.54)}.side-nav .divider{margin:8px 0 0 0}.side-nav .subheader{cursor:initial;pointer-events:none;color:rgba(0,0,0,0.54);font-size:14px;font-weight:500;line-height:48px}.side-nav .subheader:hover{background-color:transparent}.side-nav .userView{position:relative;padding:32px 32px 0;margin-bottom:8px}.side-nav .userView>a{height:auto;padding:0}.side-nav .userView>a:hover{background-color:transparent}.side-nav .userView .background{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1}.side-nav .userView .circle,.side-nav .userView .name,.side-nav .userView .email{display:block}.side-nav .userView .circle{height:64px;width:64px}.side-nav .userView .name,.side-nav .userView .email{font-size:14px;line-height:24px}.side-nav .userView .name{margin-top:16px;font-weight:500}.side-nav .userView .email{padding-bottom:16px;font-weight:400}.drag-target{height:100%;width:10px;position:fixed;top:0;z-index:998}.side-nav.fixed{left:0;-webkit-transform:translateX(0);transform:translateX(0);position:fixed}.side-nav.fixed.right-aligned{right:0;left:auto}@media only screen and (max-width: 992px){.side-nav.fixed{-webkit-transform:translateX(-105%);transform:translateX(-105%)}.side-nav.fixed.right-aligned{-webkit-transform:translateX(105%);transform:translateX(105%)}.side-nav a{padding:0 16px}.side-nav .userView{padding:16px 16px 0}}.side-nav .collapsible-body>ul:not(.collapsible)>li.active,.side-nav.fixed .collapsible-body>ul:not(.collapsible)>li.active{background-color:#ee6e73}.side-nav .collapsible-body>ul:not(.collapsible)>li.active a,.side-nav.fixed .collapsible-body>ul:not(.collapsible)>li.active a{color:#fff}#sidenav-overlay{position:fixed;top:0;left:0;right:0;height:120vh;background-color:rgba(0,0,0,0.5);z-index:997;will-change:opacity}.preloader-wrapper{display:inline-block;position:relative;width:48px;height:48px}.preloader-wrapper.small{width:36px;height:36px}.preloader-wrapper.big{width:64px;height:64px}.preloader-wrapper.active{-webkit-animation:container-rotate 1568ms linear infinite;animation:container-rotate 1568ms linear infinite}@-webkit-keyframes container-rotate{to{-webkit-transform:rotate(360deg)}}@keyframes container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-layer{position:absolute;width:100%;height:100%;opacity:0;border-color:#26a69a}.spinner-blue,.spinner-blue-only{border-color:#4285f4}.spinner-red,.spinner-red-only{border-color:#db4437}.spinner-yellow,.spinner-yellow-only{border-color:#f4b400}.spinner-green,.spinner-green-only{border-color:#0f9d58}.active .spinner-layer.spinner-blue{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,blue-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-red{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,red-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-yellow{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,yellow-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer.spinner-green{-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,green-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .spinner-layer,.active .spinner-layer.spinner-blue-only,.active .spinner-layer.spinner-red-only,.active .spinner-layer.spinner-yellow-only,.active .spinner-layer.spinner-green-only{opacity:1;-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg)}}@keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@keyframes blue-fade-in-out{from{opacity:1}25%{opacity:1}26%{opacity:0}89%{opacity:0}90%{opacity:1}100%{opacity:1}}@-webkit-keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@keyframes red-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:1}50%{opacity:1}51%{opacity:0}}@-webkit-keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@keyframes yellow-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:1}75%{opacity:1}76%{opacity:0}}@-webkit-keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}@keyframes green-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:1}90%{opacity:1}100%{opacity:0}}.gap-patch{position:absolute;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.gap-patch .circle{width:1000%;left:-450%}.circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.circle-clipper .circle{width:200%;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent !important;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0}.circle-clipper.left .circle{left:0;border-right-color:transparent !important;-webkit-transform:rotate(129deg);transform:rotate(129deg)}.circle-clipper.right .circle{left:-100%;border-left-color:transparent !important;-webkit-transform:rotate(-129deg);transform:rotate(-129deg)}.active .circle-clipper.left .circle{-webkit-animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.active .circle-clipper.right .circle{-webkit-animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}@-webkit-keyframes left-spin{from{-webkit-transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg)}}@keyframes left-spin{from{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(130deg);transform:rotate(130deg)}}@-webkit-keyframes right-spin{from{-webkit-transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg)}}@keyframes right-spin{from{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}to{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}}#spinnerContainer.cooldown{-webkit-animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1);animation:container-rotate 1568ms linear infinite,fade-out 400ms cubic-bezier(0.4, 0, 0.2, 1)}@-webkit-keyframes fade-out{from{opacity:1}to{opacity:0}}@keyframes fade-out{from{opacity:1}to{opacity:0}}.slider{position:relative;height:400px;width:100%}.slider.fullscreen{height:100%;width:100%;position:absolute;top:0;left:0;right:0;bottom:0}.slider.fullscreen ul.slides{height:100%}.slider.fullscreen ul.indicators{z-index:2;bottom:30px}.slider .slides{background-color:#9e9e9e;margin:0;height:400px}.slider .slides li{opacity:0;position:absolute;top:0;left:0;z-index:1;width:100%;height:inherit;overflow:hidden}.slider .slides li img{height:100%;width:100%;background-size:cover;background-position:center}.slider .slides li .caption{color:#fff;position:absolute;top:15%;left:15%;width:70%;opacity:0}.slider .slides li .caption p{color:#e0e0e0}.slider .slides li.active{z-index:2}.slider .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.slider .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:16px;width:16px;margin:0 12px;background-color:#e0e0e0;transition:background-color .3s;border-radius:50%}.slider .indicators .indicator-item.active{background-color:#4CAF50}.carousel{overflow:hidden;position:relative;width:100%;height:400px;-webkit-perspective:500px;perspective:500px;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-transform-origin:0% 50%;transform-origin:0% 50%}.carousel.carousel-slider{top:0;left:0;height:0}.carousel.carousel-slider .carousel-fixed-item{position:absolute;left:0;right:0;bottom:20px;z-index:1}.carousel.carousel-slider .carousel-fixed-item.with-indicators{bottom:68px}.carousel.carousel-slider .carousel-item{width:100%;height:100%;min-height:400px;position:absolute;top:0;left:0}.carousel.carousel-slider .carousel-item h2{font-size:24px;font-weight:500;line-height:32px}.carousel.carousel-slider .carousel-item p{font-size:15px}.carousel .carousel-item{display:none;width:200px;height:200px;position:absolute;top:0;left:0}.carousel .carousel-item img{width:100%}.carousel .indicators{position:absolute;text-align:center;left:0;right:0;bottom:0;margin:0}.carousel .indicators .indicator-item{display:inline-block;position:relative;cursor:pointer;height:8px;width:8px;margin:24px 4px;background-color:rgba(255,255,255,0.5);transition:background-color .3s;border-radius:50%}.carousel .indicators .indicator-item.active{background-color:#fff}.picker{font-size:16px;text-align:left;line-height:1.2;color:#000000;position:absolute;z-index:10000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.picker__input{cursor:default}.picker__input.picker__input--active{border-color:#0089ec}.picker__holder{width:100%;overflow-y:auto;-webkit-overflow-scrolling:touch}/*!
+ * Default mobile-first, responsive styling for pickadate.js
+ * Demo: http://amsul.github.io/pickadate.js
+ */.picker__holder,.picker__frame{bottom:0;left:0;right:0;top:100%}.picker__holder{position:fixed;transition:background 0.15s ease-out, top 0s 0.15s;-webkit-backface-visibility:hidden}.picker__frame{position:absolute;margin:0 auto;min-width:256px;width:300px;max-height:350px;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);-moz-opacity:0;opacity:0;transition:all 0.15s ease-out}@media (min-height: 28.875em){.picker__frame{overflow:visible;top:auto;bottom:-100%;max-height:80%}}@media (min-height: 40.125em){.picker__frame{margin-bottom:7.5%}}.picker__wrap{display:table;width:100%;height:100%}@media (min-height: 28.875em){.picker__wrap{display:block}}.picker__box{background:#ffffff;display:table-cell;vertical-align:middle}@media (min-height: 28.875em){.picker__box{display:block;border:1px solid #777777;border-top-color:#898989;border-bottom-width:0;border-radius:5px 5px 0 0;box-shadow:0 12px 36px 16px rgba(0,0,0,0.24)}}.picker--opened .picker__holder{top:0;background:transparent;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#1E000000,endColorstr=#1E000000)";zoom:1;background:rgba(0,0,0,0.32);transition:background 0.15s ease-out}.picker--opened .picker__frame{top:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);-moz-opacity:1;opacity:1}@media (min-height: 35.875em){.picker--opened .picker__frame{top:10%;bottom:auto}}.picker__input.picker__input--active{border-color:#E3F2FD}.picker__frame{margin:0 auto;max-width:325px}@media (min-height: 38.875em){.picker--opened .picker__frame{top:10%;bottom:auto}}.picker__box{padding:0 1em}.picker__header{text-align:center;position:relative;margin-top:.75em}.picker__month,.picker__year{display:inline-block;margin-left:.25em;margin-right:.25em}.picker__select--month,.picker__select--year{height:2em;padding:0;margin-left:.25em;margin-right:.25em}.picker__select--month.browser-default{display:inline;background-color:#FFFFFF;width:40%}.picker__select--year.browser-default{display:inline;background-color:#FFFFFF;width:26%}.picker__select--month:focus,.picker__select--year:focus{border-color:rgba(0,0,0,0.05)}.picker__nav--prev,.picker__nav--next{position:absolute;padding:.5em 1.25em;width:1em;height:1em;box-sizing:content-box;top:-0.25em}.picker__nav--prev{left:-1em;padding-right:1.25em}.picker__nav--next{right:-1em;padding-left:1.25em}.picker__nav--disabled,.picker__nav--disabled:hover,.picker__nav--disabled:before,.picker__nav--disabled:before:hover{cursor:default;background:none;border-right-color:#f5f5f5;border-left-color:#f5f5f5}.picker__table{text-align:center;border-collapse:collapse;border-spacing:0;table-layout:fixed;font-size:1rem;width:100%;margin-top:.75em;margin-bottom:.5em}.picker__table th,.picker__table td{text-align:center}.picker__table td{margin:0;padding:0}.picker__weekday{width:14.285714286%;font-size:.75em;padding-bottom:.25em;color:#999999;font-weight:500}@media (min-height: 33.875em){.picker__weekday{padding-bottom:.5em}}.picker__day--today{position:relative;color:#595959;letter-spacing:-.3;padding:.75rem 0;font-weight:400;border:1px solid transparent}.picker__day--disabled:before{border-top-color:#aaaaaa}.picker__day--infocus:hover{cursor:pointer;color:#000;font-weight:500}.picker__day--outfocus{display:none;padding:.75rem 0;color:#fff}.picker__day--outfocus:hover{cursor:pointer;color:#dddddd;font-weight:500}.picker__day--highlighted:hover,.picker--focused .picker__day--highlighted{cursor:pointer}.picker__day--selected,.picker__day--selected:hover,.picker--focused .picker__day--selected{border-radius:50%;-webkit-transform:scale(0.75);transform:scale(0.75);background:#0089ec;color:#ffffff}.picker__day--disabled,.picker__day--disabled:hover,.picker--focused .picker__day--disabled{background:#f5f5f5;border-color:#f5f5f5;color:#dddddd;cursor:default}.picker__day--highlighted.picker__day--disabled,.picker__day--highlighted.picker__day--disabled:hover{background:#bbbbbb}.picker__footer{text-align:center;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.picker__button--today,.picker__button--clear,.picker__button--close{border:1px solid #ffffff;background:#ffffff;font-size:.8em;padding:.66em 0;font-weight:bold;width:33%;display:inline-block;vertical-align:bottom}.picker__button--today:hover,.picker__button--clear:hover,.picker__button--close:hover{cursor:pointer;color:#000000;background:#b1dcfb;border-bottom-color:#b1dcfb}.picker__button--today:focus,.picker__button--clear:focus,.picker__button--close:focus{background:#b1dcfb;border-color:rgba(0,0,0,0.05);outline:none}.picker__button--today:before,.picker__button--clear:before,.picker__button--close:before{position:relative;display:inline-block;height:0}.picker__button--today:before,.picker__button--clear:before{content:" ";margin-right:.45em}.picker__button--today:before{top:-0.05em;width:0;border-top:0.66em solid #0059bc;border-left:.66em solid transparent}.picker__button--clear:before{top:-0.25em;width:.66em;border-top:3px solid #ee2200}.picker__button--close:before{content:"\D7";top:-0.1em;vertical-align:top;font-size:1.1em;margin-right:.35em;color:#777777}.picker__button--today[disabled],.picker__button--today[disabled]:hover{background:#f5f5f5;border-color:#f5f5f5;color:#dddddd;cursor:default}.picker__button--today[disabled]:before{border-top-color:#aaaaaa}.picker__box{border-radius:2px;overflow:hidden}.picker__date-display{text-align:center;background-color:#26a69a;color:#fff;padding-bottom:15px;font-weight:300}.picker__nav--prev:hover,.picker__nav--next:hover{cursor:pointer;color:#000000;background:#a1ded8}.picker__weekday-display{background-color:#1f897f;padding:10px;font-weight:200;letter-spacing:.5;font-size:1rem;margin-bottom:15px}.picker__month-display{text-transform:uppercase;font-size:2rem}.picker__day-display{font-size:4.5rem;font-weight:400}.picker__year-display{font-size:1.8rem;color:rgba(255,255,255,0.4)}.picker__box{padding:0}.picker__calendar-container{padding:0 1rem}.picker__calendar-container thead{border:none}.picker__table{margin-top:0;margin-bottom:.5em}.picker__day--infocus{color:#595959;letter-spacing:-.3;padding:.75rem 0;font-weight:400;border:1px solid transparent}.picker__day.picker__day--today{color:#26a69a}.picker__day.picker__day--today.picker__day--selected{color:#fff}.picker__weekday{font-size:.9rem}.picker__day--selected,.picker__day--selected:hover,.picker--focused .picker__day--selected{border-radius:50%;-webkit-transform:scale(0.9);transform:scale(0.9);background-color:#26a69a;color:#ffffff}.picker__day--selected.picker__day--outfocus,.picker__day--selected:hover.picker__day--outfocus,.picker--focused .picker__day--selected.picker__day--outfocus{background-color:#a1ded8}.picker__footer{text-align:right;padding:5px 10px}.picker__close,.picker__today{font-size:1.1rem;padding:0 1rem;color:#26a69a}.picker__nav--prev:before,.picker__nav--next:before{content:" ";border-top:.5em solid transparent;border-bottom:.5em solid transparent;border-right:0.75em solid #676767;width:0;height:0;display:block;margin:0 auto}.picker__nav--next:before{border-right:0;border-left:0.75em solid #676767}button.picker__today:focus,button.picker__clear:focus,button.picker__close:focus{background-color:#a1ded8}.picker__list{list-style:none;padding:0.75em 0 4.2em;margin:0}.picker__list-item{border-bottom:1px solid #dddddd;border-top:1px solid #dddddd;margin-bottom:-1px;position:relative;background:#ffffff;padding:.75em 1.25em}@media (min-height: 46.75em){.picker__list-item{padding:.5em 1em}}.picker__list-item:hover{cursor:pointer;color:#000000;background:#b1dcfb;border-color:#0089ec;z-index:10}.picker__list-item--highlighted{border-color:#0089ec;z-index:10}.picker__list-item--highlighted:hover,.picker--focused .picker__list-item--highlighted{cursor:pointer;color:#000000;background:#b1dcfb}.picker__list-item--selected,.picker__list-item--selected:hover,.picker--focused .picker__list-item--selected{background:#0089ec;color:#ffffff;z-index:10}.picker__list-item--disabled,.picker__list-item--disabled:hover,.picker--focused .picker__list-item--disabled{background:#f5f5f5;border-color:#f5f5f5;color:#dddddd;cursor:default;border-color:#dddddd;z-index:auto}.picker--time .picker__button--clear{display:block;width:80%;margin:1em auto 0;padding:1em 1.25em;background:none;border:0;font-weight:500;font-size:.67em;text-align:center;text-transform:uppercase;color:#666}.picker--time .picker__button--clear:hover,.picker--time .picker__button--clear:focus{color:#000000;background:#b1dcfb;background:#ee2200;border-color:#ee2200;cursor:pointer;color:#ffffff;outline:none}.picker--time .picker__button--clear:before{top:-0.25em;color:#666;font-size:1.25em;font-weight:bold}.picker--time .picker__button--clear:hover:before,.picker--time .picker__button--clear:focus:before{color:#ffffff}.picker--time .picker__frame{min-width:256px;max-width:320px}.picker--time .picker__box{font-size:1em;background:#f2f2f2;padding:0}@media (min-height: 40.125em){.picker--time .picker__box{margin-bottom:5em}}
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/.DS_Store b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/.DS_Store
new file mode 100644
index 000000000..ae0d28ed8
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/.DS_Store
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.eot b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.eot
new file mode 100644
index 000000000..b73776ee3
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.eot
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.ttf b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.ttf
new file mode 100644
index 000000000..68822caf2
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.ttf
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff
new file mode 100644
index 000000000..1f75afdcc
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff2 b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff2
new file mode 100644
index 000000000..350d1c3a2
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Bold.woff2
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.eot b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.eot
new file mode 100644
index 000000000..072cdc480
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.eot
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.ttf b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.ttf
new file mode 100644
index 000000000..aa4534075
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.ttf
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff
new file mode 100644
index 000000000..3480c6c8b
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff2 b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff2
new file mode 100644
index 000000000..9a4d98c46
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Light.woff2
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.eot b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.eot
new file mode 100644
index 000000000..f9ad99566
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.eot
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.ttf b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.ttf
new file mode 100644
index 000000000..a3c1a1f17
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.ttf
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff
new file mode 100644
index 000000000..1186773fd
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff2 b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff2
new file mode 100644
index 000000000..d10a59261
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Medium.woff2
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.eot b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.eot
new file mode 100644
index 000000000..9b5e8e413
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.eot
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.ttf b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.ttf
new file mode 100644
index 000000000..0e58508a6
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.ttf
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff
new file mode 100644
index 000000000..f823258a4
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff2 b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff2
new file mode 100644
index 000000000..b7082ef31
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Regular.woff2
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.eot b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.eot
new file mode 100644
index 000000000..2284a3b3e
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.eot
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.ttf b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.ttf
new file mode 100644
index 000000000..8779333b1
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.ttf
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff
new file mode 100644
index 000000000..2a98c1e41
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff2 b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff2
new file mode 100644
index 000000000..a38025a15
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/fonts/roboto/Roboto-Thin.woff2
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.js b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.js
new file mode 100644
index 000000000..8c994c467
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.js
@@ -0,0 +1,8031 @@
+/*!
+ * Materialize v0.98.0 (http://materializecss.com)
+ * Copyright 2014-2015 Materialize
+ * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
+ */
+// Check for jQuery.
+if (typeof(jQuery) === 'undefined') {
+ var jQuery;
+ // Check if require is a defined function.
+ if (typeof(require) === 'function') {
+ jQuery = $ = require('jquery');
+ // Else use the dollar sign alias.
+ } else {
+ jQuery = $;
+ }
+}
+;/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+// t: current time, b: begInnIng value, c: change In value, d: duration
+jQuery.easing['jswing'] = jQuery.easing['swing'];
+
+jQuery.extend( jQuery.easing,
+{
+ def: 'easeOutQuad',
+ swing: function (x, t, b, c, d) {
+ //alert(jQuery.easing.default);
+ return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
+ },
+ easeInQuad: function (x, t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+ easeOutQuad: function (x, t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+ easeInOutQuad: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+ easeInCubic: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t + b;
+ },
+ easeOutCubic: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+ },
+ easeInOutCubic: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ },
+ easeInQuart: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+ easeOutQuart: function (x, t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+ easeInOutQuart: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+ easeInQuint: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t*t + b;
+ },
+ easeOutQuint: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ },
+ easeInOutQuint: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
+ },
+ easeInSine: function (x, t, b, c, d) {
+ return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ },
+ easeOutSine: function (x, t, b, c, d) {
+ return c * Math.sin(t/d * (Math.PI/2)) + b;
+ },
+ easeInOutSine: function (x, t, b, c, d) {
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ },
+ easeInExpo: function (x, t, b, c, d) {
+ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ },
+ easeOutExpo: function (x, t, b, c, d) {
+ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ },
+ easeInOutExpo: function (x, t, b, c, d) {
+ if (t==0) return b;
+ if (t==d) return b+c;
+ if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+ return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ },
+ easeInCirc: function (x, t, b, c, d) {
+ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ },
+ easeOutCirc: function (x, t, b, c, d) {
+ return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ },
+ easeInOutCirc: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
+ return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ },
+ easeInElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+ easeOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+ easeInOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+ easeInBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+ easeOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+ easeInOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+ easeInBounce: function (x, t, b, c, d) {
+ return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
+ },
+ easeOutBounce: function (x, t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+ },
+ easeInOutBounce: function (x, t, b, c, d) {
+ if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
+ return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright © 2001 Robert Penner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */;// Custom Easing
+jQuery.extend( jQuery.easing,
+{
+ easeInOutMaterial: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return c/4*((t-=2)*t*t + 2) + b;
+ }
+});;/*! VelocityJS.org (1.2.3). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */
+/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */
+/*! Note that this has been modified by Materialize to confirm that Velocity is not already being imported. */
+jQuery.Velocity?console.log("Velocity is already loaded. You may be needlessly importing Velocity again; note that Materialize includes Velocity."):(!function(e){function t(e){var t=e.length,a=r.type(e);return"function"===a||r.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===a||0===t||"number"==typeof t&&t>0&&t-1 in e}if(!e.jQuery){var r=function(e,t){return new r.fn.init(e,t)};r.isWindow=function(e){return null!=e&&e==e.window},r.type=function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e},r.isArray=Array.isArray||function(e){return"array"===r.type(e)},r.isPlainObject=function(e){var t;if(!e||"object"!==r.type(e)||e.nodeType||r.isWindow(e))return!1;try{if(e.constructor&&!o.call(e,"constructor")&&!o.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(a){return!1}for(t in e);return void 0===t||o.call(e,t)},r.each=function(e,r,a){var n,o=0,i=e.length,s=t(e);if(a){if(s)for(;i>o&&(n=r.apply(e[o],a),n!==!1);o++);else for(o in e)if(n=r.apply(e[o],a),n===!1)break}else if(s)for(;i>o&&(n=r.call(e[o],o,e[o]),n!==!1);o++);else for(o in e)if(n=r.call(e[o],o,e[o]),n===!1)break;return e},r.data=function(e,t,n){if(void 0===n){var o=e[r.expando],i=o&&a[o];if(void 0===t)return i;if(i&&t in i)return i[t]}else if(void 0!==t){var o=e[r.expando]||(e[r.expando]=++r.uuid);return a[o]=a[o]||{},a[o][t]=n,n}},r.removeData=function(e,t){var n=e[r.expando],o=n&&a[n];o&&r.each(t,function(e,t){delete o[t]})},r.extend=function(){var e,t,a,n,o,i,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[l]||{},l++),"object"!=typeof s&&"function"!==r.type(s)&&(s={}),l===u&&(s=this,l--);u>l;l++)if(null!=(o=arguments[l]))for(n in o)e=s[n],a=o[n],s!==a&&(c&&a&&(r.isPlainObject(a)||(t=r.isArray(a)))?(t?(t=!1,i=e&&r.isArray(e)?e:[]):i=e&&r.isPlainObject(e)?e:{},s[n]=r.extend(c,i,a)):void 0!==a&&(s[n]=a));return s},r.queue=function(e,a,n){function o(e,r){var a=r||[];return null!=e&&(t(Object(e))?!function(e,t){for(var r=+t.length,a=0,n=e.length;r>a;)e[n++]=t[a++];if(r!==r)for(;void 0!==t[a];)e[n++]=t[a++];return e.length=n,e}(a,"string"==typeof e?[e]:e):[].push.call(a,e)),a}if(e){a=(a||"fx")+"queue";var i=r.data(e,a);return n?(!i||r.isArray(n)?i=r.data(e,a,o(n)):i.push(n),i):i||[]}},r.dequeue=function(e,t){r.each(e.nodeType?[e]:e,function(e,a){t=t||"fx";var n=r.queue(a,t),o=n.shift();"inprogress"===o&&(o=n.shift()),o&&("fx"===t&&n.unshift("inprogress"),o.call(a,function(){r.dequeue(a,t)}))})},r.fn=r.prototype={init:function(e){if(e.nodeType)return this[0]=e,this;throw new Error("Not a DOM node.")},offset:function(){var t=this[0].getBoundingClientRect?this[0].getBoundingClientRect():{top:0,left:0};return{top:t.top+(e.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:t.left+(e.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function e(){for(var e=this.offsetParent||document;e&&"html"===!e.nodeType.toLowerCase&&"static"===e.style.position;)e=e.offsetParent;return e||document}var t=this[0],e=e.apply(t),a=this.offset(),n=/^(?:body|html)$/i.test(e.nodeName)?{top:0,left:0}:r(e).offset();return a.top-=parseFloat(t.style.marginTop)||0,a.left-=parseFloat(t.style.marginLeft)||0,e.style&&(n.top+=parseFloat(e.style.borderTopWidth)||0,n.left+=parseFloat(e.style.borderLeftWidth)||0),{top:a.top-n.top,left:a.left-n.left}}};var a={};r.expando="velocity"+(new Date).getTime(),r.uuid=0;for(var n={},o=n.hasOwnProperty,i=n.toString,s="Boolean Number String Function Array Date RegExp Object Error".split(" "),l=0;l<s.length;l++)n["[object "+s[l]+"]"]=s[l].toLowerCase();r.fn.init.prototype=r.fn,e.Velocity={Utilities:r}}}(window),function(e){"object"==typeof module&&"object"==typeof module.exports?module.exports=e():"function"==typeof define&&define.amd?define(e):e()}(function(){return function(e,t,r,a){function n(e){for(var t=-1,r=e?e.length:0,a=[];++t<r;){var n=e[t];n&&a.push(n)}return a}function o(e){return m.isWrapped(e)?e=[].slice.call(e):m.isNode(e)&&(e=[e]),e}function i(e){var t=f.data(e,"velocity");return null===t?a:t}function s(e){return function(t){return Math.round(t*e)*(1/e)}}function l(e,r,a,n){function o(e,t){return 1-3*t+3*e}function i(e,t){return 3*t-6*e}function s(e){return 3*e}function l(e,t,r){return((o(t,r)*e+i(t,r))*e+s(t))*e}function u(e,t,r){return 3*o(t,r)*e*e+2*i(t,r)*e+s(t)}function c(t,r){for(var n=0;m>n;++n){var o=u(r,e,a);if(0===o)return r;var i=l(r,e,a)-t;r-=i/o}return r}function p(){for(var t=0;b>t;++t)w[t]=l(t*x,e,a)}function f(t,r,n){var o,i,s=0;do i=r+(n-r)/2,o=l(i,e,a)-t,o>0?n=i:r=i;while(Math.abs(o)>h&&++s<v);return i}function d(t){for(var r=0,n=1,o=b-1;n!=o&&w[n]<=t;++n)r+=x;--n;var i=(t-w[n])/(w[n+1]-w[n]),s=r+i*x,l=u(s,e,a);return l>=y?c(t,s):0==l?s:f(t,r,r+x)}function g(){V=!0,(e!=r||a!=n)&&p()}var m=4,y=.001,h=1e-7,v=10,b=11,x=1/(b-1),S="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;4>P;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),a=Math.min(a,1),e=Math.max(e,0),a=Math.max(a,0);var w=S?new Float32Array(b):new Array(b),V=!1,C=function(t){return V||g(),e===r&&a===n?t:0===t?0:1===t?1:l(d(t),r,n)};C.getControlPoints=function(){return[{x:e,y:r},{x:a,y:n}]};var T="generateBezier("+[e,r,a,n]+")";return C.toString=function(){return T},C}function u(e,t){var r=e;return m.isString(e)?b.Easings[e]||(r=!1):r=m.isArray(e)&&1===e.length?s.apply(null,e):m.isArray(e)&&2===e.length?x.apply(null,e.concat([t])):m.isArray(e)&&4===e.length?l.apply(null,e):!1,r===!1&&(r=b.Easings[b.defaults.easing]?b.defaults.easing:v),r}function c(e){if(e){var t=(new Date).getTime(),r=b.State.calls.length;r>1e4&&(b.State.calls=n(b.State.calls));for(var o=0;r>o;o++)if(b.State.calls[o]){var s=b.State.calls[o],l=s[0],u=s[2],d=s[3],g=!!d,y=null;d||(d=b.State.calls[o][3]=t-16);for(var h=Math.min((t-d)/u.duration,1),v=0,x=l.length;x>v;v++){var P=l[v],V=P.element;if(i(V)){var C=!1;if(u.display!==a&&null!==u.display&&"none"!==u.display){if("flex"===u.display){var T=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];f.each(T,function(e,t){S.setPropertyValue(V,"display",t)})}S.setPropertyValue(V,"display",u.display)}u.visibility!==a&&"hidden"!==u.visibility&&S.setPropertyValue(V,"visibility",u.visibility);for(var k in P)if("element"!==k){var A,F=P[k],j=m.isString(F.easing)?b.Easings[F.easing]:F.easing;if(1===h)A=F.endValue;else{var E=F.endValue-F.startValue;if(A=F.startValue+E*j(h,u,E),!g&&A===F.currentValue)continue}if(F.currentValue=A,"tween"===k)y=A;else{if(S.Hooks.registered[k]){var H=S.Hooks.getRoot(k),N=i(V).rootPropertyValueCache[H];N&&(F.rootPropertyValue=N)}var L=S.setPropertyValue(V,k,F.currentValue+(0===parseFloat(A)?"":F.unitType),F.rootPropertyValue,F.scrollData);S.Hooks.registered[k]&&(i(V).rootPropertyValueCache[H]=S.Normalizations.registered[H]?S.Normalizations.registered[H]("extract",null,L[1]):L[1]),"transform"===L[0]&&(C=!0)}}u.mobileHA&&i(V).transformCache.translate3d===a&&(i(V).transformCache.translate3d="(0px, 0px, 0px)",C=!0),C&&S.flushTransformCache(V)}}u.display!==a&&"none"!==u.display&&(b.State.calls[o][2].display=!1),u.visibility!==a&&"hidden"!==u.visibility&&(b.State.calls[o][2].visibility=!1),u.progress&&u.progress.call(s[1],s[1],h,Math.max(0,d+u.duration-t),d,y),1===h&&p(o)}}b.State.isTicking&&w(c)}function p(e,t){if(!b.State.calls[e])return!1;for(var r=b.State.calls[e][0],n=b.State.calls[e][1],o=b.State.calls[e][2],s=b.State.calls[e][4],l=!1,u=0,c=r.length;c>u;u++){var p=r[u].element;if(t||o.loop||("none"===o.display&&S.setPropertyValue(p,"display",o.display),"hidden"===o.visibility&&S.setPropertyValue(p,"visibility",o.visibility)),o.loop!==!0&&(f.queue(p)[1]===a||!/\.velocityQueueEntryFlag/i.test(f.queue(p)[1]))&&i(p)){i(p).isAnimating=!1,i(p).rootPropertyValueCache={};var d=!1;f.each(S.Lists.transforms3D,function(e,t){var r=/^scale/.test(t)?1:0,n=i(p).transformCache[t];i(p).transformCache[t]!==a&&new RegExp("^\\("+r+"[^.]").test(n)&&(d=!0,delete i(p).transformCache[t])}),o.mobileHA&&(d=!0,delete i(p).transformCache.translate3d),d&&S.flushTransformCache(p),S.Values.removeClass(p,"velocity-animating")}if(!t&&o.complete&&!o.loop&&u===c-1)try{o.complete.call(n,n)}catch(g){setTimeout(function(){throw g},1)}s&&o.loop!==!0&&s(n),i(p)&&o.loop===!0&&!t&&(f.each(i(p).tweensContainer,function(e,t){/^rotate/.test(e)&&360===parseFloat(t.endValue)&&(t.endValue=0,t.startValue=360),/^backgroundPosition/.test(e)&&100===parseFloat(t.endValue)&&"%"===t.unitType&&(t.endValue=0,t.startValue=100)}),b(p,"reverse",{loop:!0,delay:o.delay})),o.queue!==!1&&f.dequeue(p,o.queue)}b.State.calls[e]=!1;for(var m=0,y=b.State.calls.length;y>m;m++)if(b.State.calls[m]!==!1){l=!0;break}l===!1&&(b.State.isTicking=!1,delete b.State.calls,b.State.calls=[])}var f,d=function(){if(r.documentMode)return r.documentMode;for(var e=7;e>4;e--){var t=r.createElement("div");if(t.innerHTML="<!--[if IE "+e+"]><span></span><![endif]-->",t.getElementsByTagName("span").length)return t=null,e}return a}(),g=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r,a=(new Date).getTime();return r=Math.max(0,16-(a-e)),e=a+r,setTimeout(function(){t(a+r)},r)}}(),m={isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"==typeof e&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==a&&(0===e.length||"object"==typeof e[0]&&e[0].nodeType>0)},isWrapped:function(e){return e&&(e.jquery||t.Zepto&&t.Zepto.zepto.isZ(e))},isSVG:function(e){return t.SVGElement&&e instanceof t.SVGElement},isEmptyObject:function(e){for(var t in e)return!1;return!0}},y=!1;if(e.fn&&e.fn.jquery?(f=e,y=!0):f=t.Velocity.Utilities,8>=d&&!y)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=d)return void(jQuery.fn.velocity=jQuery.fn.animate);var h=400,v="swing",b={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:f,Redirects:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:h,easing:v,begin:a,complete:a,progress:a,display:a,visibility:a,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(e){f.data(e,"velocity",{isSVG:m.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:2,patch:2},debug:!1};t.pageYOffset!==a?(b.State.scrollAnchor=t,b.State.scrollPropertyLeft="pageXOffset",b.State.scrollPropertyTop="pageYOffset"):(b.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,b.State.scrollPropertyLeft="scrollLeft",b.State.scrollPropertyTop="scrollTop");var x=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,a){var n={x:t.x+a.dx*r,v:t.v+a.dv*r,tension:t.tension,friction:t.friction};return{dx:n.v,dv:e(n)}}function r(r,a){var n={dx:r.v,dv:e(r)},o=t(r,.5*a,n),i=t(r,.5*a,o),s=t(r,a,i),l=1/6*(n.dx+2*(o.dx+i.dx)+s.dx),u=1/6*(n.dv+2*(o.dv+i.dv)+s.dv);return r.x=r.x+l*a,r.v=r.v+u*a,r}return function a(e,t,n){var o,i,s,l={x:-1,v:0,tension:null,friction:null},u=[0],c=0,p=1e-4,f=.016;for(e=parseFloat(e)||500,t=parseFloat(t)||20,n=n||null,l.tension=e,l.friction=t,o=null!==n,o?(c=a(e,t),i=c/n*f):i=f;s=r(s||l,i),u.push(1+s.x),c+=16,Math.abs(s.x)>p&&Math.abs(s.v)>p;);return o?function(e){return u[e*(u.length-1)|0]}:c}}();b.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},f.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){b.Easings[t[0]]=l.apply(null,t[1])});var S=b.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e<S.Lists.colors.length;e++){var t="color"===S.Lists.colors[e]?"0 0 0 1":"255 255 255 1";S.Hooks.templates[S.Lists.colors[e]]=["Red Green Blue Alpha",t]}var r,a,n;if(d)for(r in S.Hooks.templates){a=S.Hooks.templates[r],n=a[0].split(" ");var o=a[1].match(S.RegEx.valueSplit);"Color"===n[0]&&(n.push(n.shift()),o.push(o.shift()),S.Hooks.templates[r]=[n.join(" "),o.join(" ")])}for(r in S.Hooks.templates){a=S.Hooks.templates[r],n=a[0].split(" ");for(var e in n){var i=r+n[e],s=e;S.Hooks.registered[i]=[r,s]}}},getRoot:function(e){var t=S.Hooks.registered[e];return t?t[0]:e},cleanRootPropertyValue:function(e,t){return S.RegEx.valueUnwrap.test(t)&&(t=t.match(S.RegEx.valueUnwrap)[1]),S.Values.isCSSNullValue(t)&&(t=S.Hooks.templates[e][1]),t},extractValue:function(e,t){var r=S.Hooks.registered[e];if(r){var a=r[0],n=r[1];return t=S.Hooks.cleanRootPropertyValue(a,t),t.toString().match(S.RegEx.valueSplit)[n]}return t},injectValue:function(e,t,r){var a=S.Hooks.registered[e];if(a){var n,o,i=a[0],s=a[1];return r=S.Hooks.cleanRootPropertyValue(i,r),n=r.toString().match(S.RegEx.valueSplit),n[s]=t,o=n.join(" ")}return r}},Normalizations:{registered:{clip:function(e,t,r){switch(e){case"name":return"clip";case"extract":var a;return S.RegEx.wrappedValueAlreadyExtracted.test(r)?a=r:(a=r.toString().match(S.RegEx.valueUnwrap),a=a?a[1].replace(/,(\s+)?/g," "):r),a;case"inject":return"rect("+r+")"}},blur:function(e,t,r){switch(e){case"name":return b.State.isFirefox?"filter":"-webkit-filter";case"extract":var a=parseFloat(r);if(!a&&0!==a){var n=r.toString().match(/blur\(([0-9]+[A-z]+)\)/i);a=n?n[1]:0}return a;case"inject":return parseFloat(r)?"blur("+r+")":"none"}},opacity:function(e,t,r){if(8>=d)switch(e){case"name":return"filter";case"extract":var a=r.toString().match(/alpha\(opacity=(.*)\)/i);return r=a?a[1]/100:1;case"inject":return t.style.zoom=1,parseFloat(r)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){9>=d||b.State.isGingerbread||(S.Lists.transformsBase=S.Lists.transformsBase.concat(S.Lists.transforms3D));for(var e=0;e<S.Lists.transformsBase.length;e++)!function(){var t=S.Lists.transformsBase[e];S.Normalizations.registered[t]=function(e,r,n){switch(e){case"name":return"transform";case"extract":return i(r)===a||i(r).transformCache[t]===a?/^scale/i.test(t)?1:0:i(r).transformCache[t].replace(/[()]/g,"");case"inject":var o=!1;switch(t.substr(0,t.length-1)){case"translate":o=!/(%|px|em|rem|vw|vh|\d)$/i.test(n);break;case"scal":case"scale":b.State.isAndroid&&i(r).transformCache[t]===a&&1>n&&(n=1),o=!/(\d)$/i.test(n);break;case"skew":o=!/(deg|\d)$/i.test(n);break;case"rotate":o=!/(deg|\d)$/i.test(n)}return o||(i(r).transformCache[t]="("+n+")"),i(r).transformCache[t]}}}();for(var e=0;e<S.Lists.colors.length;e++)!function(){var t=S.Lists.colors[e];S.Normalizations.registered[t]=function(e,r,n){switch(e){case"name":return t;case"extract":var o;if(S.RegEx.wrappedValueAlreadyExtracted.test(n))o=n;else{var i,s={black:"rgb(0, 0, 0)",blue:"rgb(0, 0, 255)",gray:"rgb(128, 128, 128)",green:"rgb(0, 128, 0)",red:"rgb(255, 0, 0)",white:"rgb(255, 255, 255)"};/^[A-z]+$/i.test(n)?i=s[n]!==a?s[n]:s.black:S.RegEx.isHex.test(n)?i="rgb("+S.Values.hexToRgb(n).join(" ")+")":/^rgba?\(/i.test(n)||(i=s.black),o=(i||n).toString().match(S.RegEx.valueUnwrap)[1].replace(/,(\s+)?/g," ")}return 8>=d||3!==o.split(" ").length||(o+=" 1"),o;case"inject":return 8>=d?4===n.split(" ").length&&(n=n.split(/\s+/).slice(0,3).join(" ")):3===n.split(" ").length&&(n+=" 1"),(8>=d?"rgb":"rgba")+"("+n.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(d||b.State.isAndroid&&!b.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(b.State.prefixMatches[e])return[b.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,a=t.length;a>r;r++){var n;if(n=0===r?e:t[r]+e.replace(/^\w/,function(e){return e.toUpperCase()}),m.isString(b.State.prefixElement.style[n]))return b.State.prefixMatches[e]=n,[n,!0]}return[e,!1]}},Values:{hexToRgb:function(e){var t,r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;return e=e.replace(r,function(e,t,r,a){return t+t+r+r+a+a}),t=a.exec(e),t?[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]:[0,0,0]},isCSSNullValue:function(e){return 0==e||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(e)},getUnitType:function(e){return/^(rotate|skew)/i.test(e)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(e)?"":"px"},getDisplayType:function(e){var t=e&&e.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(t)?"inline":/^(li)$/i.test(t)?"list-item":/^(tr)$/i.test(t)?"table-row":/^(table)$/i.test(t)?"table":/^(tbody)$/i.test(t)?"table-row-group":"block"},addClass:function(e,t){e.classList?e.classList.add(t):e.className+=(e.className.length?" ":"")+t},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.toString().replace(new RegExp("(^|\\s)"+t.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(e,r,n,o){function s(e,r){function n(){u&&S.setPropertyValue(e,"display","none")}var l=0;if(8>=d)l=f.css(e,r);else{var u=!1;if(/^(width|height)$/.test(r)&&0===S.getPropertyValue(e,"display")&&(u=!0,S.setPropertyValue(e,"display",S.Values.getDisplayType(e))),!o){if("height"===r&&"border-box"!==S.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var c=e.offsetHeight-(parseFloat(S.getPropertyValue(e,"borderTopWidth"))||0)-(parseFloat(S.getPropertyValue(e,"borderBottomWidth"))||0)-(parseFloat(S.getPropertyValue(e,"paddingTop"))||0)-(parseFloat(S.getPropertyValue(e,"paddingBottom"))||0);return n(),c}if("width"===r&&"border-box"!==S.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var p=e.offsetWidth-(parseFloat(S.getPropertyValue(e,"borderLeftWidth"))||0)-(parseFloat(S.getPropertyValue(e,"borderRightWidth"))||0)-(parseFloat(S.getPropertyValue(e,"paddingLeft"))||0)-(parseFloat(S.getPropertyValue(e,"paddingRight"))||0);return n(),p}}var g;g=i(e)===a?t.getComputedStyle(e,null):i(e).computedStyle?i(e).computedStyle:i(e).computedStyle=t.getComputedStyle(e,null),"borderColor"===r&&(r="borderTopColor"),l=9===d&&"filter"===r?g.getPropertyValue(r):g[r],(""===l||null===l)&&(l=e.style[r]),n()}if("auto"===l&&/^(top|right|bottom|left)$/i.test(r)){var m=s(e,"position");("fixed"===m||"absolute"===m&&/top|left/i.test(r))&&(l=f(e).position()[r]+"px")}return l}var l;if(S.Hooks.registered[r]){var u=r,c=S.Hooks.getRoot(u);n===a&&(n=S.getPropertyValue(e,S.Names.prefixCheck(c)[0])),S.Normalizations.registered[c]&&(n=S.Normalizations.registered[c]("extract",e,n)),l=S.Hooks.extractValue(u,n)}else if(S.Normalizations.registered[r]){var p,g;p=S.Normalizations.registered[r]("name",e),"transform"!==p&&(g=s(e,S.Names.prefixCheck(p)[0]),S.Values.isCSSNullValue(g)&&S.Hooks.templates[r]&&(g=S.Hooks.templates[r][1])),l=S.Normalizations.registered[r]("extract",e,g)}if(!/^[\d-]/.test(l))if(i(e)&&i(e).isSVG&&S.Names.SVGAttribute(r))if(/^(height|width)$/i.test(r))try{l=e.getBBox()[r]}catch(m){l=0}else l=e.getAttribute(r);else l=s(e,S.Names.prefixCheck(r)[0]);return S.Values.isCSSNullValue(l)&&(l=0),b.debug>=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,a,n,o){var s=r;if("scroll"===r)o.container?o.container["scroll"+o.direction]=a:"Left"===o.direction?t.scrollTo(a,o.alternateValue):t.scrollTo(o.alternateValue,a);else if(S.Normalizations.registered[r]&&"transform"===S.Normalizations.registered[r]("name",e))S.Normalizations.registered[r]("inject",e,a),s="transform",a=i(e).transformCache[r];else{if(S.Hooks.registered[r]){var l=r,u=S.Hooks.getRoot(r);n=n||S.getPropertyValue(e,u),a=S.Hooks.injectValue(l,a,n),r=u}if(S.Normalizations.registered[r]&&(a=S.Normalizations.registered[r]("inject",e,a),r=S.Normalizations.registered[r]("name",e)),s=S.Names.prefixCheck(r)[0],8>=d)try{e.style[s]=a}catch(c){b.debug&&console.log("Browser does not support ["+a+"] for ["+s+"]")}else i(e)&&i(e).isSVG&&S.Names.SVGAttribute(r)?e.setAttribute(r,a):e.style[s]=a;b.debug>=2&&console.log("Set "+r+" ("+s+"): "+a)}return[s,a]},flushTransformCache:function(e){function t(t){return parseFloat(S.getPropertyValue(e,t))}var r="";if((d||b.State.isAndroid&&!b.State.isChrome)&&i(e).isSVG){var a={translate:[t("translateX"),t("translateY")],skewX:[t("skewX")],skewY:[t("skewY")],scale:1!==t("scale")?[t("scale"),t("scale")]:[t("scaleX"),t("scaleY")],rotate:[t("rotateZ"),0,0]};f.each(i(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),a[e]&&(r+=e+"("+a[e].join(" ")+") ",delete a[e])})}else{var n,o;f.each(i(e).transformCache,function(t){return n=i(e).transformCache[t],"transformPerspective"===t?(o=n,!0):(9===d&&"rotateZ"===t&&(t="rotate"),void(r+=t+n+" "))}),o&&(r="perspective"+o+" "+r)}S.setPropertyValue(e,"transform",r)}};S.Hooks.register(),S.Normalizations.register(),b.hook=function(e,t,r){var n=a;return e=o(e),f.each(e,function(e,o){if(i(o)===a&&b.init(o),r===a)n===a&&(n=b.CSS.getPropertyValue(o,t));else{var s=b.CSS.setPropertyValue(o,t,r);"transform"===s[0]&&b.CSS.flushTransformCache(o),n=s}}),n};var P=function(){function e(){return s?k.promise||null:l}function n(){function e(e){function p(e,t){var r=a,n=a,i=a;return m.isArray(e)?(r=e[0],!m.isArray(e[1])&&/^[\d-]/.test(e[1])||m.isFunction(e[1])||S.RegEx.isHex.test(e[1])?i=e[1]:(m.isString(e[1])&&!S.RegEx.isHex.test(e[1])||m.isArray(e[1]))&&(n=t?e[1]:u(e[1],s.duration),e[2]!==a&&(i=e[2]))):r=e,t||(n=n||s.easing),m.isFunction(r)&&(r=r.call(o,V,w)),m.isFunction(i)&&(i=i.call(o,V,w)),[r||0,n,i]}function d(e,t){var r,a;return a=(t||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=S.Values.getUnitType(e)),[a,r]}function h(){var e={myParent:o.parentNode||r.body,position:S.getPropertyValue(o,"position"),fontSize:S.getPropertyValue(o,"fontSize")},a=e.position===L.lastPosition&&e.myParent===L.lastParent,n=e.fontSize===L.lastFontSize;L.lastParent=e.myParent,L.lastPosition=e.position,L.lastFontSize=e.fontSize;var s=100,l={};if(n&&a)l.emToPx=L.lastEmToPx,l.percentToPxWidth=L.lastPercentToPxWidth,l.percentToPxHeight=L.lastPercentToPxHeight;else{var u=i(o).isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");b.init(u),e.myParent.appendChild(u),f.each(["overflow","overflowX","overflowY"],function(e,t){b.CSS.setPropertyValue(u,t,"hidden")}),b.CSS.setPropertyValue(u,"position",e.position),b.CSS.setPropertyValue(u,"fontSize",e.fontSize),b.CSS.setPropertyValue(u,"boxSizing","content-box"),f.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){b.CSS.setPropertyValue(u,t,s+"%")}),b.CSS.setPropertyValue(u,"paddingLeft",s+"em"),l.percentToPxWidth=L.lastPercentToPxWidth=(parseFloat(S.getPropertyValue(u,"width",null,!0))||1)/s,l.percentToPxHeight=L.lastPercentToPxHeight=(parseFloat(S.getPropertyValue(u,"height",null,!0))||1)/s,l.emToPx=L.lastEmToPx=(parseFloat(S.getPropertyValue(u,"paddingLeft"))||1)/s,e.myParent.removeChild(u)}return null===L.remToPx&&(L.remToPx=parseFloat(S.getPropertyValue(r.body,"fontSize"))||16),null===L.vwToPx&&(L.vwToPx=parseFloat(t.innerWidth)/100,L.vhToPx=parseFloat(t.innerHeight)/100),l.remToPx=L.remToPx,l.vwToPx=L.vwToPx,l.vhToPx=L.vhToPx,b.debug>=1&&console.log("Unit ratios: "+JSON.stringify(l),o),l}if(s.begin&&0===V)try{s.begin.call(g,g)}catch(x){setTimeout(function(){throw x},1)}if("scroll"===A){var P,C,T,F=/^x$/i.test(s.axis)?"Left":"Top",j=parseFloat(s.offset)||0;s.container?m.isWrapped(s.container)||m.isNode(s.container)?(s.container=s.container[0]||s.container,P=s.container["scroll"+F],T=P+f(o).position()[F.toLowerCase()]+j):s.container=null:(P=b.State.scrollAnchor[b.State["scrollProperty"+F]],C=b.State.scrollAnchor[b.State["scrollProperty"+("Left"===F?"Top":"Left")]],T=f(o).offset()[F.toLowerCase()]+j),l={scroll:{rootPropertyValue:!1,startValue:P,currentValue:P,endValue:T,unitType:"",easing:s.easing,scrollData:{container:s.container,direction:F,alternateValue:C}},element:o},b.debug&&console.log("tweensContainer (scroll): ",l.scroll,o)}else if("reverse"===A){if(!i(o).tweensContainer)return void f.dequeue(o,s.queue);"none"===i(o).opts.display&&(i(o).opts.display="auto"),"hidden"===i(o).opts.visibility&&(i(o).opts.visibility="visible"),i(o).opts.loop=!1,i(o).opts.begin=null,i(o).opts.complete=null,v.easing||delete s.easing,v.duration||delete s.duration,s=f.extend({},i(o).opts,s);var E=f.extend(!0,{},i(o).tweensContainer);for(var H in E)if("element"!==H){var N=E[H].startValue;E[H].startValue=E[H].currentValue=E[H].endValue,E[H].endValue=N,m.isEmptyObject(v)||(E[H].easing=s.easing),b.debug&&console.log("reverse tweensContainer ("+H+"): "+JSON.stringify(E[H]),o)}l=E}else if("start"===A){var E;i(o).tweensContainer&&i(o).isAnimating===!0&&(E=i(o).tweensContainer),f.each(y,function(e,t){if(RegExp("^"+S.Lists.colors.join("$|^")+"$").test(e)){var r=p(t,!0),n=r[0],o=r[1],i=r[2];if(S.RegEx.isHex.test(n)){for(var s=["Red","Green","Blue"],l=S.Values.hexToRgb(n),u=i?S.Values.hexToRgb(i):a,c=0;c<s.length;c++){var f=[l[c]];o&&f.push(o),u!==a&&f.push(u[c]),y[e+s[c]]=f}delete y[e]}}});for(var z in y){var O=p(y[z]),q=O[0],$=O[1],M=O[2];z=S.Names.camelCase(z);var I=S.Hooks.getRoot(z),B=!1;if(i(o).isSVG||"tween"===I||S.Names.prefixCheck(I)[1]!==!1||S.Normalizations.registered[I]!==a){(s.display!==a&&null!==s.display&&"none"!==s.display||s.visibility!==a&&"hidden"!==s.visibility)&&/opacity|filter/.test(z)&&!M&&0!==q&&(M=0),s._cacheValues&&E&&E[z]?(M===a&&(M=E[z].endValue+E[z].unitType),B=i(o).rootPropertyValueCache[I]):S.Hooks.registered[z]?M===a?(B=S.getPropertyValue(o,I),M=S.getPropertyValue(o,z,B)):B=S.Hooks.templates[I][1]:M===a&&(M=S.getPropertyValue(o,z));var W,G,Y,D=!1;if(W=d(z,M),M=W[0],Y=W[1],W=d(z,q),q=W[0].replace(/^([+-\/*])=/,function(e,t){return D=t,""}),G=W[1],M=parseFloat(M)||0,q=parseFloat(q)||0,"%"===G&&(/^(fontSize|lineHeight)$/.test(z)?(q/=100,G="em"):/^scale/.test(z)?(q/=100,G=""):/(Red|Green|Blue)$/i.test(z)&&(q=q/100*255,G="")),/[\/*]/.test(D))G=Y;else if(Y!==G&&0!==M)if(0===q)G=Y;else{n=n||h();var Q=/margin|padding|left|right|width|text|word|letter/i.test(z)||/X$/.test(z)||"x"===z?"x":"y";switch(Y){case"%":M*="x"===Q?n.percentToPxWidth:n.percentToPxHeight;break;case"px":break;default:M*=n[Y+"ToPx"]}switch(G){case"%":M*=1/("x"===Q?n.percentToPxWidth:n.percentToPxHeight);break;case"px":break;default:M*=1/n[G+"ToPx"]}}switch(D){case"+":q=M+q;break;case"-":q=M-q;break;case"*":q=M*q;break;case"/":q=M/q}l[z]={rootPropertyValue:B,startValue:M,currentValue:M,endValue:q,unitType:G,easing:$},b.debug&&console.log("tweensContainer ("+z+"): "+JSON.stringify(l[z]),o)}else b.debug&&console.log("Skipping ["+I+"] due to a lack of browser support.")}l.element=o}l.element&&(S.Values.addClass(o,"velocity-animating"),R.push(l),""===s.queue&&(i(o).tweensContainer=l,i(o).opts=s),i(o).isAnimating=!0,V===w-1?(b.State.calls.push([R,g,s,null,k.resolver]),b.State.isTicking===!1&&(b.State.isTicking=!0,c())):V++)}var n,o=this,s=f.extend({},b.defaults,v),l={};switch(i(o)===a&&b.init(o),parseFloat(s.delay)&&s.queue!==!1&&f.queue(o,s.queue,function(e){b.velocityQueueEntryFlag=!0,i(o).delayTimer={setTimeout:setTimeout(e,parseFloat(s.delay)),next:e}}),s.duration.toString().toLowerCase()){case"fast":s.duration=200;break;case"normal":s.duration=h;break;case"slow":s.duration=600;break;default:s.duration=parseFloat(s.duration)||1}b.mock!==!1&&(b.mock===!0?s.duration=s.delay=1:(s.duration*=parseFloat(b.mock)||1,s.delay*=parseFloat(b.mock)||1)),s.easing=u(s.easing,s.duration),s.begin&&!m.isFunction(s.begin)&&(s.begin=null),s.progress&&!m.isFunction(s.progress)&&(s.progress=null),s.complete&&!m.isFunction(s.complete)&&(s.complete=null),s.display!==a&&null!==s.display&&(s.display=s.display.toString().toLowerCase(),"auto"===s.display&&(s.display=b.CSS.Values.getDisplayType(o))),s.visibility!==a&&null!==s.visibility&&(s.visibility=s.visibility.toString().toLowerCase()),s.mobileHA=s.mobileHA&&b.State.isMobile&&!b.State.isGingerbread,s.queue===!1?s.delay?setTimeout(e,s.delay):e():f.queue(o,s.queue,function(t,r){return r===!0?(k.promise&&k.resolver(g),!0):(b.velocityQueueEntryFlag=!0,void e(t))}),""!==s.queue&&"fx"!==s.queue||"inprogress"===f.queue(o)[0]||f.dequeue(o)}var s,l,d,g,y,v,x=arguments[0]&&(arguments[0].p||f.isPlainObject(arguments[0].properties)&&!arguments[0].properties.names||m.isString(arguments[0].properties));if(m.isWrapped(this)?(s=!1,d=0,g=this,l=this):(s=!0,d=1,g=x?arguments[0].elements||arguments[0].e:arguments[0]),g=o(g)){x?(y=arguments[0].properties||arguments[0].p,v=arguments[0].options||arguments[0].o):(y=arguments[d],v=arguments[d+1]);var w=g.length,V=0;if(!/^(stop|finish)$/i.test(y)&&!f.isPlainObject(v)){var C=d+1;v={};for(var T=C;T<arguments.length;T++)m.isArray(arguments[T])||!/^(fast|normal|slow)$/i.test(arguments[T])&&!/^\d/.test(arguments[T])?m.isString(arguments[T])||m.isArray(arguments[T])?v.easing=arguments[T]:m.isFunction(arguments[T])&&(v.complete=arguments[T]):v.duration=arguments[T]}var k={promise:null,resolver:null,rejecter:null};s&&b.Promise&&(k.promise=new b.Promise(function(e,t){k.resolver=e,k.rejecter=t}));var A;switch(y){case"scroll":A="scroll";break;case"reverse":A="reverse";break;case"finish":case"stop":f.each(g,function(e,t){i(t)&&i(t).delayTimer&&(clearTimeout(i(t).delayTimer.setTimeout),i(t).delayTimer.next&&i(t).delayTimer.next(),delete i(t).delayTimer)});var F=[];return f.each(b.State.calls,function(e,t){t&&f.each(t[1],function(r,n){var o=v===a?"":v;return o===!0||t[2].queue===o||v===a&&t[2].queue===!1?void f.each(g,function(r,a){a===n&&((v===!0||m.isString(v))&&(f.each(f.queue(a,m.isString(v)?v:""),function(e,t){
+m.isFunction(t)&&t(null,!0)}),f.queue(a,m.isString(v)?v:"",[])),"stop"===y?(i(a)&&i(a).tweensContainer&&o!==!1&&f.each(i(a).tweensContainer,function(e,t){t.endValue=t.currentValue}),F.push(e)):"finish"===y&&(t[2].duration=1))}):!0})}),"stop"===y&&(f.each(F,function(e,t){p(t,!0)}),k.promise&&k.resolver(g)),e();default:if(!f.isPlainObject(y)||m.isEmptyObject(y)){if(m.isString(y)&&b.Redirects[y]){var j=f.extend({},v),E=j.duration,H=j.delay||0;return j.backwards===!0&&(g=f.extend(!0,[],g).reverse()),f.each(g,function(e,t){parseFloat(j.stagger)?j.delay=H+parseFloat(j.stagger)*e:m.isFunction(j.stagger)&&(j.delay=H+j.stagger.call(t,e,w)),j.drag&&(j.duration=parseFloat(E)||(/^(callout|transition)/.test(y)?1e3:h),j.duration=Math.max(j.duration*(j.backwards?1-e/w:(e+1)/w),.75*j.duration,200)),b.Redirects[y].call(t,t,j||{},e,w,g,k.promise?k:a)}),e()}var N="Velocity: First argument ("+y+") was not a property map, a known action, or a registered redirect. Aborting.";return k.promise?k.rejecter(new Error(N)):console.log(N),e()}A="start"}var L={lastParent:null,lastPosition:null,lastFontSize:null,lastPercentToPxWidth:null,lastPercentToPxHeight:null,lastEmToPx:null,remToPx:null,vwToPx:null,vhToPx:null},R=[];f.each(g,function(e,t){m.isNode(t)&&n.call(t)});var z,j=f.extend({},b.defaults,v);if(j.loop=parseInt(j.loop),z=2*j.loop-1,j.loop)for(var O=0;z>O;O++){var q={delay:j.delay,progress:j.progress};O===z-1&&(q.display=j.display,q.visibility=j.visibility,q.complete=j.complete),P(g,"reverse",q)}return e()}};b=f.extend(P,b),b.animate=P;var w=t.requestAnimationFrame||g;return b.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(w=function(e){return setTimeout(function(){e(!0)},16)},c()):w=t.requestAnimationFrame||g}),e.Velocity=b,e!==t&&(e.fn.velocity=P,e.fn.velocity.defaults=b.defaults),f.each(["Down","Up"],function(e,t){b.Redirects["slide"+t]=function(e,r,n,o,i,s){var l=f.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},d={};l.display===a&&(l.display="Down"===t?"inline"===b.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i);for(var r in p){d[r]=e.style[r];var a=b.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}d.overflow=e.style.overflow,e.style.overflow="hidden"},l.complete=function(){for(var t in d)e.style[t]=d[t];c&&c.call(i,i),s&&s.resolver(i)},b(e,p,l)}}),f.each(["In","Out"],function(e,t){b.Redirects["fade"+t]=function(e,r,n,o,i,s){var l=f.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),b(this,u,l)}}),b}(window.jQuery||window.Zepto||window,window,document)}));
+;!function(a,b,c,d){"use strict";function k(a,b,c){return setTimeout(q(a,c),b)}function l(a,b,c){return Array.isArray(a)?(m(a,c[b],c),!0):!1}function m(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e<a.length;)b.call(c,a[e],e,a),e++;else for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)}function n(a,b,c){for(var e=Object.keys(b),f=0;f<e.length;)(!c||c&&a[e[f]]===d)&&(a[e[f]]=b[e[f]]),f++;return a}function o(a,b){return n(a,b,!0)}function p(a,b,c){var e,d=b.prototype;e=a.prototype=Object.create(d),e.constructor=a,e._super=d,c&&n(e,c)}function q(a,b){return function(){return a.apply(b,arguments)}}function r(a,b){return typeof a==g?a.apply(b?b[0]||d:d,b):a}function s(a,b){return a===d?b:a}function t(a,b,c){m(x(b),function(b){a.addEventListener(b,c,!1)})}function u(a,b,c){m(x(b),function(b){a.removeEventListener(b,c,!1)})}function v(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function w(a,b){return a.indexOf(b)>-1}function x(a){return a.trim().split(/\s+/g)}function y(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;d<a.length;){if(c&&a[d][c]==b||!c&&a[d]===b)return d;d++}return-1}function z(a){return Array.prototype.slice.call(a,0)}function A(a,b,c){for(var d=[],e=[],f=0;f<a.length;){var g=b?a[f][b]:a[f];y(e,g)<0&&d.push(a[f]),e[f]=g,f++}return c&&(d=b?d.sort(function(a,c){return a[b]>c[b]}):d.sort()),d}function B(a,b){for(var c,f,g=b[0].toUpperCase()+b.slice(1),h=0;h<e.length;){if(c=e[h],f=c?c+g:b,f in a)return f;h++}return d}function D(){return C++}function E(a){var b=a.ownerDocument;return b.defaultView||b.parentWindow}function ab(a,b){var c=this;this.manager=a,this.callback=b,this.element=a.element,this.target=a.options.inputTarget,this.domHandler=function(b){r(a.options.enable,[a])&&c.handler(b)},this.init()}function bb(a){var b,c=a.options.inputClass;return b=c?c:H?wb:I?Eb:G?Gb:rb,new b(a,cb)}function cb(a,b,c){var d=c.pointers.length,e=c.changedPointers.length,f=b&O&&0===d-e,g=b&(Q|R)&&0===d-e;c.isFirst=!!f,c.isFinal=!!g,f&&(a.session={}),c.eventType=b,db(a,c),a.emit("hammer.input",c),a.recognize(c),a.session.prevInput=c}function db(a,b){var c=a.session,d=b.pointers,e=d.length;c.firstInput||(c.firstInput=gb(b)),e>1&&!c.firstMultiple?c.firstMultiple=gb(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=hb(d);b.timeStamp=j(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=lb(h,i),b.distance=kb(h,i),eb(c,b),b.offsetDirection=jb(b.deltaX,b.deltaY),b.scale=g?nb(g.pointers,d):1,b.rotation=g?mb(g.pointers,d):0,fb(c,b);var k=a.element;v(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function eb(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===O||f.eventType===Q)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function fb(a,b){var f,g,h,j,c=a.lastInterval||b,e=b.timeStamp-c.timeStamp;if(b.eventType!=R&&(e>N||c.velocity===d)){var k=c.deltaX-b.deltaX,l=c.deltaY-b.deltaY,m=ib(e,k,l);g=m.x,h=m.y,f=i(m.x)>i(m.y)?m.x:m.y,j=jb(k,l),a.lastInterval=b}else f=c.velocity,g=c.velocityX,h=c.velocityY,j=c.direction;b.velocity=f,b.velocityX=g,b.velocityY=h,b.direction=j}function gb(a){for(var b=[],c=0;c<a.pointers.length;)b[c]={clientX:h(a.pointers[c].clientX),clientY:h(a.pointers[c].clientY)},c++;return{timeStamp:j(),pointers:b,center:hb(b),deltaX:a.deltaX,deltaY:a.deltaY}}function hb(a){var b=a.length;if(1===b)return{x:h(a[0].clientX),y:h(a[0].clientY)};for(var c=0,d=0,e=0;b>e;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:h(c/b),y:h(d/b)}}function ib(a,b,c){return{x:b/a||0,y:c/a||0}}function jb(a,b){return a===b?S:i(a)>=i(b)?a>0?T:U:b>0?V:W}function kb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function lb(a,b,c){c||(c=$);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function mb(a,b){return lb(b[1],b[0],_)-lb(a[1],a[0],_)}function nb(a,b){return kb(b[0],b[1],_)/kb(a[0],a[1],_)}function rb(){this.evEl=pb,this.evWin=qb,this.allow=!0,this.pressed=!1,ab.apply(this,arguments)}function wb(){this.evEl=ub,this.evWin=vb,ab.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function Ab(){this.evTarget=yb,this.evWin=zb,this.started=!1,ab.apply(this,arguments)}function Bb(a,b){var c=z(a.touches),d=z(a.changedTouches);return b&(Q|R)&&(c=A(c.concat(d),"identifier",!0)),[c,d]}function Eb(){this.evTarget=Db,this.targetIds={},ab.apply(this,arguments)}function Fb(a,b){var c=z(a.touches),d=this.targetIds;if(b&(O|P)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=z(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return v(a.target,i)}),b===O)for(e=0;e<f.length;)d[f[e].identifier]=!0,e++;for(e=0;e<g.length;)d[g[e].identifier]&&h.push(g[e]),b&(Q|R)&&delete d[g[e].identifier],e++;return h.length?[A(f.concat(h),"identifier",!0),h]:void 0}function Gb(){ab.apply(this,arguments);var a=q(this.handler,this);this.touch=new Eb(this.manager,a),this.mouse=new rb(this.manager,a)}function Pb(a,b){this.manager=a,this.set(b)}function Qb(a){if(w(a,Mb))return Mb;var b=w(a,Nb),c=w(a,Ob);return b&&c?Nb+" "+Ob:b||c?b?Nb:Ob:w(a,Lb)?Lb:Kb}function Yb(a){this.id=D(),this.manager=null,this.options=o(a||{},this.defaults),this.options.enable=s(this.options.enable,!0),this.state=Rb,this.simultaneous={},this.requireFail=[]}function Zb(a){return a&Wb?"cancel":a&Ub?"end":a&Tb?"move":a&Sb?"start":""}function $b(a){return a==W?"down":a==V?"up":a==T?"left":a==U?"right":""}function _b(a,b){var c=b.manager;return c?c.get(a):a}function ac(){Yb.apply(this,arguments)}function bc(){ac.apply(this,arguments),this.pX=null,this.pY=null}function cc(){ac.apply(this,arguments)}function dc(){Yb.apply(this,arguments),this._timer=null,this._input=null}function ec(){ac.apply(this,arguments)}function fc(){ac.apply(this,arguments)}function gc(){Yb.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function hc(a,b){return b=b||{},b.recognizers=s(b.recognizers,hc.defaults.preset),new kc(a,b)}function kc(a,b){b=b||{},this.options=o(b,hc.defaults),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.element=a,this.input=bb(this),this.touchAction=new Pb(this,this.options.touchAction),lc(this,!0),m(b.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function lc(a,b){var c=a.element;m(a.options.cssProps,function(a,d){c.style[B(c.style,d)]=b?a:""})}function mc(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var e=["","webkit","moz","MS","ms","o"],f=b.createElement("div"),g="function",h=Math.round,i=Math.abs,j=Date.now,C=1,F=/mobile|tablet|ip(ad|hone|od)|android/i,G="ontouchstart"in a,H=B(a,"PointerEvent")!==d,I=G&&F.test(navigator.userAgent),J="touch",K="pen",L="mouse",M="kinect",N=25,O=1,P=2,Q=4,R=8,S=1,T=2,U=4,V=8,W=16,X=T|U,Y=V|W,Z=X|Y,$=["x","y"],_=["clientX","clientY"];ab.prototype={handler:function(){},init:function(){this.evEl&&t(this.element,this.evEl,this.domHandler),this.evTarget&&t(this.target,this.evTarget,this.domHandler),this.evWin&&t(E(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&u(this.element,this.evEl,this.domHandler),this.evTarget&&u(this.target,this.evTarget,this.domHandler),this.evWin&&u(E(this.element),this.evWin,this.domHandler)}};var ob={mousedown:O,mousemove:P,mouseup:Q},pb="mousedown",qb="mousemove mouseup";p(rb,ab,{handler:function(a){var b=ob[a.type];b&O&&0===a.button&&(this.pressed=!0),b&P&&1!==a.which&&(b=Q),this.pressed&&this.allow&&(b&Q&&(this.pressed=!1),this.callback(this.manager,b,{pointers:[a],changedPointers:[a],pointerType:L,srcEvent:a}))}});var sb={pointerdown:O,pointermove:P,pointerup:Q,pointercancel:R,pointerout:R},tb={2:J,3:K,4:L,5:M},ub="pointerdown",vb="pointermove pointerup pointercancel";a.MSPointerEvent&&(ub="MSPointerDown",vb="MSPointerMove MSPointerUp MSPointerCancel"),p(wb,ab,{handler:function(a){var b=this.store,c=!1,d=a.type.toLowerCase().replace("ms",""),e=sb[d],f=tb[a.pointerType]||a.pointerType,g=f==J,h=y(b,a.pointerId,"pointerId");e&O&&(0===a.button||g)?0>h&&(b.push(a),h=b.length-1):e&(Q|R)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var xb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},yb="touchstart",zb="touchstart touchmove touchend touchcancel";p(Ab,ab,{handler:function(a){var b=xb[a.type];if(b===O&&(this.started=!0),this.started){var c=Bb.call(this,a,b);b&(Q|R)&&0===c[0].length-c[1].length&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}});var Cb={touchstart:O,touchmove:P,touchend:Q,touchcancel:R},Db="touchstart touchmove touchend touchcancel";p(Eb,ab,{handler:function(a){var b=Cb[a.type],c=Fb.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:J,srcEvent:a})}}),p(Gb,ab,{handler:function(a,b,c){var d=c.pointerType==J,e=c.pointerType==L;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Q|R)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var Hb=B(f.style,"touchAction"),Ib=Hb!==d,Jb="compute",Kb="auto",Lb="manipulation",Mb="none",Nb="pan-x",Ob="pan-y";Pb.prototype={set:function(a){a==Jb&&(a=this.compute()),Ib&&(this.manager.element.style[Hb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return m(this.manager.recognizers,function(b){r(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),Qb(a.join(" "))},preventDefaults:function(a){if(!Ib){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return b.preventDefault(),void 0;var d=this.actions,e=w(d,Mb),f=w(d,Ob),g=w(d,Nb);return e||f&&c&X||g&&c&Y?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var Rb=1,Sb=2,Tb=4,Ub=8,Vb=Ub,Wb=16,Xb=32;Yb.prototype={defaults:{},set:function(a){return n(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(l(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_b(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return l(a,"dropRecognizeWith",this)?this:(a=_b(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(l(a,"requireFailure",this))return this;var b=this.requireFail;return a=_b(a,this),-1===y(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(l(a,"dropRequireFailure",this))return this;a=_b(a,this);var b=y(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function d(d){b.manager.emit(b.options.event+(d?Zb(c):""),a)}var b=this,c=this.state;Ub>c&&d(!0),d(),c>=Ub&&d(!0)},tryEmit:function(a){return this.canEmit()?this.emit(a):(this.state=Xb,void 0)},canEmit:function(){for(var a=0;a<this.requireFail.length;){if(!(this.requireFail[a].state&(Xb|Rb)))return!1;a++}return!0},recognize:function(a){var b=n({},a);return r(this.options.enable,[this,b])?(this.state&(Vb|Wb|Xb)&&(this.state=Rb),this.state=this.process(b),this.state&(Sb|Tb|Ub|Wb)&&this.tryEmit(b),void 0):(this.reset(),this.state=Xb,void 0)},process:function(){},getTouchAction:function(){},reset:function(){}},p(ac,Yb,{defaults:{pointers:1},attrTest:function(a){var b=this.options.pointers;return 0===b||a.pointers.length===b},process:function(a){var b=this.state,c=a.eventType,d=b&(Sb|Tb),e=this.attrTest(a);return d&&(c&R||!e)?b|Wb:d||e?c&Q?b|Ub:b&Sb?b|Tb:Sb:Xb}}),p(bc,ac,{defaults:{event:"pan",threshold:10,pointers:1,direction:Z},getTouchAction:function(){var a=this.options.direction,b=[];return a&X&&b.push(Ob),a&Y&&b.push(Nb),b},directionTest:function(a){var b=this.options,c=!0,d=a.distance,e=a.direction,f=a.deltaX,g=a.deltaY;return e&b.direction||(b.direction&X?(e=0===f?S:0>f?T:U,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?S:0>g?V:W,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return ac.prototype.attrTest.call(this,a)&&(this.state&Sb||!(this.state&Sb)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this._super.emit.call(this,a)}}),p(cc,ac,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&Sb)},emit:function(a){if(this._super.emit.call(this,a),1!==a.scale){var b=a.scale<1?"in":"out";this.manager.emit(this.options.event+b,a)}}}),p(dc,Yb,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return[Kb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,e=a.deltaTime>b.time;if(this._input=a,!d||!c||a.eventType&(Q|R)&&!e)this.reset();else if(a.eventType&O)this.reset(),this._timer=k(function(){this.state=Vb,this.tryEmit()},b.time,this);else if(a.eventType&Q)return Vb;return Xb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===Vb&&(a&&a.eventType&Q?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=j(),this.manager.emit(this.options.event,this._input)))}}),p(ec,ac,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[Mb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&Sb)}}),p(fc,ac,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:X|Y,pointers:1},getTouchAction:function(){return bc.prototype.getTouchAction.call(this)},attrTest:function(a){var c,b=this.options.direction;return b&(X|Y)?c=a.velocity:b&X?c=a.velocityX:b&Y&&(c=a.velocityY),this._super.attrTest.call(this,a)&&b&a.direction&&a.distance>this.options.threshold&&i(c)>this.options.velocity&&a.eventType&Q},emit:function(a){var b=$b(a.direction);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),p(gc,Yb,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[Lb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,e=a.deltaTime<b.time;if(this.reset(),a.eventType&O&&0===this.count)return this.failTimeout();if(d&&e&&c){if(a.eventType!=Q)return this.failTimeout();var f=this.pTime?a.timeStamp-this.pTime<b.interval:!0,g=!this.pCenter||kb(this.pCenter,a.center)<b.posThreshold;this.pTime=a.timeStamp,this.pCenter=a.center,g&&f?this.count+=1:this.count=1,this._input=a;var h=this.count%b.taps;if(0===h)return this.hasRequireFailures()?(this._timer=k(function(){this.state=Vb,this.tryEmit()},b.interval,this),Sb):Vb}return Xb},failTimeout:function(){return this._timer=k(function(){this.state=Xb},this.options.interval,this),Xb},reset:function(){clearTimeout(this._timer)},emit:function(){this.state==Vb&&(this._input.tapCount=this.count,this.manager.emit(this.options.event,this._input))}}),hc.VERSION="2.0.4",hc.defaults={domEvents:!1,touchAction:Jb,enable:!0,inputTarget:null,inputClass:null,preset:[[ec,{enable:!1}],[cc,{enable:!1},["rotate"]],[fc,{direction:X}],[bc,{direction:X},["swipe"]],[gc],[gc,{event:"doubletap",taps:2},["tap"]],[dc]],cssProps:{userSelect:"default",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}};var ic=1,jc=2;kc.prototype={set:function(a){return n(this.options,a),a.touchAction&&this.touchAction.update(),a.inputTarget&&(this.input.destroy(),this.input.target=a.inputTarget,this.input.init()),this},stop:function(a){this.session.stopped=a?jc:ic},recognize:function(a){var b=this.session;if(!b.stopped){this.touchAction.preventDefaults(a);var c,d=this.recognizers,e=b.curRecognizer;(!e||e&&e.state&Vb)&&(e=b.curRecognizer=null);for(var f=0;f<d.length;)c=d[f],b.stopped===jc||e&&c!=e&&!c.canRecognizeWith(e)?c.reset():c.recognize(a),!e&&c.state&(Sb|Tb|Ub)&&(e=b.curRecognizer=c),f++}},get:function(a){if(a instanceof Yb)return a;for(var b=this.recognizers,c=0;c<b.length;c++)if(b[c].options.event==a)return b[c];return null},add:function(a){if(l(a,"add",this))return this;var b=this.get(a.options.event);return b&&this.remove(b),this.recognizers.push(a),a.manager=this,this.touchAction.update(),a},remove:function(a){if(l(a,"remove",this))return this;var b=this.recognizers;return a=this.get(a),b.splice(y(b,a),1),this.touchAction.update(),this},on:function(a,b){var c=this.handlers;return m(x(a),function(a){c[a]=c[a]||[],c[a].push(b)}),this},off:function(a,b){var c=this.handlers;return m(x(a),function(a){b?c[a].splice(y(c[a],b),1):delete c[a]}),this},emit:function(a,b){this.options.domEvents&&mc(a,b);var c=this.handlers[a]&&this.handlers[a].slice();if(c&&c.length){b.type=a,b.preventDefault=function(){b.srcEvent.preventDefault()};for(var d=0;d<c.length;)c[d](b),d++}},destroy:function(){this.element&&lc(this,!1),this.handlers={},this.session={},this.input.destroy(),this.element=null}},n(hc,{INPUT_START:O,INPUT_MOVE:P,INPUT_END:Q,INPUT_CANCEL:R,STATE_POSSIBLE:Rb,STATE_BEGAN:Sb,STATE_CHANGED:Tb,STATE_ENDED:Ub,STATE_RECOGNIZED:Vb,STATE_CANCELLED:Wb,STATE_FAILED:Xb,DIRECTION_NONE:S,DIRECTION_LEFT:T,DIRECTION_RIGHT:U,DIRECTION_UP:V,DIRECTION_DOWN:W,DIRECTION_HORIZONTAL:X,DIRECTION_VERTICAL:Y,DIRECTION_ALL:Z,Manager:kc,Input:ab,TouchAction:Pb,TouchInput:Eb,MouseInput:rb,PointerEventInput:wb,TouchMouseInput:Gb,SingleTouchInput:Ab,Recognizer:Yb,AttrRecognizer:ac,Tap:gc,Pan:bc,Swipe:fc,Pinch:cc,Rotate:ec,Press:dc,on:t,off:u,each:m,merge:o,extend:n,inherit:p,bindFn:q,prefixed:B}),typeof define==g&&define.amd?define(function(){return hc}):"undefined"!=typeof module&&module.exports?module.exports=hc:a[c]=hc}(window,document,"Hammer");;(function(factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery', 'hammerjs'], factory);
+ } else if (typeof exports === 'object') {
+ factory(require('jquery'), require('hammerjs'));
+ } else {
+ factory(jQuery, Hammer);
+ }
+}(function($, Hammer) {
+ function hammerify(el, options) {
+ var $el = $(el);
+ if(!$el.data("hammer")) {
+ $el.data("hammer", new Hammer($el[0], options));
+ }
+ }
+
+ $.fn.hammer = function(options) {
+ return this.each(function() {
+ hammerify(this, options);
+ });
+ };
+
+ // extend the emit method to also trigger jQuery events
+ Hammer.Manager.prototype.emit = (function(originalEmit) {
+ return function(type, data) {
+ originalEmit.call(this, type, data);
+ $(this.element).trigger({
+ type: type,
+ gesture: data
+ });
+ };
+ })(Hammer.Manager.prototype.emit);
+}));
+;// Required for Meteor package, the use of window prevents export by Meteor
+(function(window){
+ if(window.Package){
+ Materialize = {};
+ } else {
+ window.Materialize = {};
+ }
+})(window);
+
+
+/*
+ * raf.js
+ * https://github.com/ngryman/raf.js
+ *
+ * original requestAnimationFrame polyfill by Erik Möller
+ * inspired from paul_irish gist and post
+ *
+ * Copyright (c) 2013 ngryman
+ * Licensed under the MIT license.
+ */
+(function(window) {
+ var lastTime = 0,
+ vendors = ['webkit', 'moz'],
+ requestAnimationFrame = window.requestAnimationFrame,
+ cancelAnimationFrame = window.cancelAnimationFrame,
+ i = vendors.length;
+
+ // try to un-prefix existing raf
+ while (--i >= 0 && !requestAnimationFrame) {
+ requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame'];
+ cancelAnimationFrame = window[vendors[i] + 'CancelRequestAnimationFrame'];
+ }
+
+ // polyfill with setTimeout fallback
+ // heavily inspired from @darius gist mod: https://gist.github.com/paulirish/1579671#comment-837945
+ if (!requestAnimationFrame || !cancelAnimationFrame) {
+ requestAnimationFrame = function(callback) {
+ var now = +Date.now(),
+ nextTime = Math.max(lastTime + 16, now);
+ return setTimeout(function() {
+ callback(lastTime = nextTime);
+ }, nextTime - now);
+ };
+
+ cancelAnimationFrame = clearTimeout;
+ }
+
+ // export to window
+ window.requestAnimationFrame = requestAnimationFrame;
+ window.cancelAnimationFrame = cancelAnimationFrame;
+}(window));
+
+
+// Unique ID
+Materialize.guid = (function() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return function() {
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+ };
+})();
+
+/**
+ * Escapes hash from special characters
+ * @param {string} hash String returned from this.hash
+ * @returns {string}
+ */
+Materialize.escapeHash = function(hash) {
+ return hash.replace( /(:|\.|\[|\]|,|=)/g, "\\$1" );
+};
+
+Materialize.elementOrParentIsFixed = function(element) {
+ var $element = $(element);
+ var $checkElements = $element.add($element.parents());
+ var isFixed = false;
+ $checkElements.each(function(){
+ if ($(this).css("position") === "fixed") {
+ isFixed = true;
+ return false;
+ }
+ });
+ return isFixed;
+};
+
+
+/**
+ * Get time in ms
+ * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
+ * @type {function}
+ * @return {number}
+ */
+var getTime = (Date.now || function () {
+ return new Date().getTime();
+});
+
+
+/**
+ * Returns a function, that, when invoked, will only be triggered at most once
+ * during a given window of time. Normally, the throttled function will run
+ * as much as it can, without ever going more than once per `wait` duration;
+ * but if you'd like to disable the execution on the leading edge, pass
+ * `{leading: false}`. To disable execution on the trailing edge, ditto.
+ * @license https://raw.github.com/jashkenas/underscore/master/LICENSE
+ * @param {function} func
+ * @param {number} wait
+ * @param {Object=} options
+ * @returns {Function}
+ */
+Materialize.throttle = function(func, wait, options) {
+ var context, args, result;
+ var timeout = null;
+ var previous = 0;
+ options || (options = {});
+ var later = function () {
+ previous = options.leading === false ? 0 : getTime();
+ timeout = null;
+ result = func.apply(context, args);
+ context = args = null;
+ };
+ return function () {
+ var now = getTime();
+ if (!previous && options.leading === false) previous = now;
+ var remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0) {
+ clearTimeout(timeout);
+ timeout = null;
+ previous = now;
+ result = func.apply(context, args);
+ context = args = null;
+ } else if (!timeout && options.trailing !== false) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+};
+
+
+// Velocity has conflicts when loaded with jQuery, this will check for it
+// First, check if in noConflict mode
+var Vel;
+if (jQuery) {
+ Vel = jQuery.Velocity;
+} else if ($) {
+ Vel = $.Velocity;
+} else {
+ Vel = Velocity;
+}
+;(function ($) {
+ $.fn.collapsible = function(options) {
+ var defaults = {
+ accordion: undefined,
+ onOpen: undefined,
+ onClose: undefined
+ };
+
+ options = $.extend(defaults, options);
+
+
+ return this.each(function() {
+
+ var $this = $(this);
+
+ var $panel_headers = $(this).find('> li > .collapsible-header');
+
+ var collapsible_type = $this.data("collapsible");
+
+ // Turn off any existing event handlers
+ $this.off('click.collapse', '> li > .collapsible-header');
+ $panel_headers.off('click.collapse');
+
+
+ /****************
+ Helper Functions
+ ****************/
+
+ // Accordion Open
+ function accordionOpen(object) {
+ $panel_headers = $this.find('> li > .collapsible-header');
+ if (object.hasClass('active')) {
+ object.parent().addClass('active');
+ }
+ else {
+ object.parent().removeClass('active');
+ }
+ if (object.parent().hasClass('active')){
+ object.siblings('.collapsible-body').stop(true,false).slideDown({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
+ }
+ else{
+ object.siblings('.collapsible-body').stop(true,false).slideUp({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
+ }
+
+ $panel_headers.not(object).removeClass('active').parent().removeClass('active');
+
+ // Close previously open accordion elements.
+ $panel_headers.not(object).parent().children('.collapsible-body').stop(true,false).each(function() {
+ if ($(this).is(':visible')) {
+ $(this).slideUp({
+ duration: 350,
+ easing: "easeOutQuart",
+ queue: false,
+ complete:
+ function() {
+ $(this).css('height', '');
+ execCallbacks($(this).siblings('.collapsible-header'));
+ }
+ });
+ }
+ });
+ }
+
+ // Expandable Open
+ function expandableOpen(object) {
+ if (object.hasClass('active')) {
+ object.parent().addClass('active');
+ }
+ else {
+ object.parent().removeClass('active');
+ }
+ if (object.parent().hasClass('active')){
+ object.siblings('.collapsible-body').stop(true,false).slideDown({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
+ }
+ else {
+ object.siblings('.collapsible-body').stop(true,false).slideUp({ duration: 350, easing: "easeOutQuart", queue: false, complete: function() {$(this).css('height', '');}});
+ }
+ }
+
+ // Open collapsible. object: .collapsible-header
+ function collapsibleOpen(object) {
+ if (options.accordion || collapsible_type === "accordion" || collapsible_type === undefined) { // Handle Accordion
+ accordionOpen(object);
+ } else { // Handle Expandables
+ expandableOpen(object);
+ }
+
+ execCallbacks(object);
+ }
+
+ // Handle callbacks
+ function execCallbacks(object) {
+ if (object.hasClass('active')) {
+ if (typeof(options.onOpen) === "function") {
+ options.onOpen.call(this, object.parent());
+ }
+ } else {
+ if (typeof(options.onClose) === "function") {
+ options.onClose.call(this, object.parent());
+ }
+ }
+ }
+
+ /**
+ * Check if object is children of panel header
+ * @param {Object} object Jquery object
+ * @return {Boolean} true if it is children
+ */
+ function isChildrenOfPanelHeader(object) {
+
+ var panelHeader = getPanelHeader(object);
+
+ return panelHeader.length > 0;
+ }
+
+ /**
+ * Get panel header from a children element
+ * @param {Object} object Jquery object
+ * @return {Object} panel header object
+ */
+ function getPanelHeader(object) {
+
+ return object.closest('li > .collapsible-header');
+ }
+
+ /***** End Helper Functions *****/
+
+
+
+ // Add click handler to only direct collapsible header children
+ $this.on('click.collapse', '> li > .collapsible-header', function(e) {
+ var element = $(e.target);
+
+ if (isChildrenOfPanelHeader(element)) {
+ element = getPanelHeader(element);
+ }
+
+ element.toggleClass('active');
+
+ collapsibleOpen(element);
+ });
+
+
+ // Open first active
+ if (options.accordion || collapsible_type === "accordion" || collapsible_type === undefined) { // Handle Accordion
+ collapsibleOpen($panel_headers.filter('.active').first());
+
+ } else { // Handle Expandables
+ $panel_headers.filter('.active').each(function() {
+ collapsibleOpen($(this));
+ });
+ }
+
+ });
+ };
+
+ $(document).ready(function(){
+ $('.collapsible').collapsible();
+ });
+}( jQuery ));;(function ($) {
+
+ // Add posibility to scroll to selected option
+ // usefull for select for example
+ $.fn.scrollTo = function(elem) {
+ $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top);
+ return this;
+ };
+
+ $.fn.dropdown = function (options) {
+ var defaults = {
+ inDuration: 300,
+ outDuration: 225,
+ constrainWidth: true, // Constrains width of dropdown to the activator
+ hover: false,
+ gutter: 0, // Spacing from edge
+ belowOrigin: false,
+ alignment: 'left',
+ stopPropagation: false
+ };
+
+ // Open dropdown.
+ if (options === "open") {
+ this.each(function() {
+ $(this).trigger('open');
+ });
+ return false;
+ }
+
+ // Close dropdown.
+ if (options === "close") {
+ this.each(function() {
+ $(this).trigger('close');
+ });
+ return false;
+ }
+
+ this.each(function(){
+ var origin = $(this);
+ var curr_options = $.extend({}, defaults, options);
+ var isFocused = false;
+
+ // Dropdown menu
+ var activates = $("#"+ origin.attr('data-activates'));
+
+ function updateOptions() {
+ if (origin.data('induration') !== undefined)
+ curr_options.inDuration = origin.data('induration');
+ if (origin.data('outduration') !== undefined)
+ curr_options.outDuration = origin.data('outduration');
+ if (origin.data('constrainwidth') !== undefined)
+ curr_options.constrainWidth = origin.data('constrainwidth');
+ if (origin.data('hover') !== undefined)
+ curr_options.hover = origin.data('hover');
+ if (origin.data('gutter') !== undefined)
+ curr_options.gutter = origin.data('gutter');
+ if (origin.data('beloworigin') !== undefined)
+ curr_options.belowOrigin = origin.data('beloworigin');
+ if (origin.data('alignment') !== undefined)
+ curr_options.alignment = origin.data('alignment');
+ if (origin.data('stoppropagation') !== undefined)
+ curr_options.stopPropagation = origin.data('stoppropagation');
+ }
+
+ updateOptions();
+
+ // Attach dropdown to its activator
+ origin.after(activates);
+
+ /*
+ Helper function to position and resize dropdown.
+ Used in hover and click handler.
+ */
+ function placeDropdown(eventType) {
+ // Check for simultaneous focus and click events.
+ if (eventType === 'focus') {
+ isFocused = true;
+ }
+
+ // Check html data attributes
+ updateOptions();
+
+ // Set Dropdown state
+ activates.addClass('active');
+ origin.addClass('active');
+
+ // Constrain width
+ if (curr_options.constrainWidth === true) {
+ activates.css('width', origin.outerWidth());
+
+ } else {
+ activates.css('white-space', 'nowrap');
+ }
+
+ // Offscreen detection
+ var windowHeight = window.innerHeight;
+ var originHeight = origin.innerHeight();
+ var offsetLeft = origin.offset().left;
+ var offsetTop = origin.offset().top - $(window).scrollTop();
+ var currAlignment = curr_options.alignment;
+ var gutterSpacing = 0;
+ var leftPosition = 0;
+
+ // Below Origin
+ var verticalOffset = 0;
+ if (curr_options.belowOrigin === true) {
+ verticalOffset = originHeight;
+ }
+
+ // Check for scrolling positioned container.
+ var scrollYOffset = 0;
+ var scrollXOffset = 0;
+ var wrapper = origin.parent();
+ if (!wrapper.is('body')) {
+ if (wrapper[0].scrollHeight > wrapper[0].clientHeight) {
+ scrollYOffset = wrapper[0].scrollTop;
+ }
+ if (wrapper[0].scrollWidth > wrapper[0].clientWidth) {
+ scrollXOffset = wrapper[0].scrollLeft;
+ }
+ }
+
+
+ if (offsetLeft + activates.innerWidth() > $(window).width()) {
+ // Dropdown goes past screen on right, force right alignment
+ currAlignment = 'right';
+
+ } else if (offsetLeft - activates.innerWidth() + origin.innerWidth() < 0) {
+ // Dropdown goes past screen on left, force left alignment
+ currAlignment = 'left';
+ }
+ // Vertical bottom offscreen detection
+ if (offsetTop + activates.innerHeight() > windowHeight) {
+ // If going upwards still goes offscreen, just crop height of dropdown.
+ if (offsetTop + originHeight - activates.innerHeight() < 0) {
+ var adjustedHeight = windowHeight - offsetTop - verticalOffset;
+ activates.css('max-height', adjustedHeight);
+ } else {
+ // Flow upwards.
+ if (!verticalOffset) {
+ verticalOffset += originHeight;
+ }
+ verticalOffset -= activates.innerHeight();
+ }
+ }
+
+ // Handle edge alignment
+ if (currAlignment === 'left') {
+ gutterSpacing = curr_options.gutter;
+ leftPosition = origin.position().left + gutterSpacing;
+ }
+ else if (currAlignment === 'right') {
+ var offsetRight = origin.position().left + origin.outerWidth() - activates.outerWidth();
+ gutterSpacing = -curr_options.gutter;
+ leftPosition = offsetRight + gutterSpacing;
+ }
+
+ // Position dropdown
+ activates.css({
+ position: 'absolute',
+ top: origin.position().top + verticalOffset + scrollYOffset,
+ left: leftPosition + scrollXOffset
+ });
+
+
+ // Show dropdown
+ activates.stop(true, true).css('opacity', 0)
+ .slideDown({
+ queue: false,
+ duration: curr_options.inDuration,
+ easing: 'easeOutCubic',
+ complete: function() {
+ $(this).css('height', '');
+ }
+ })
+ .animate( {opacity: 1}, {queue: false, duration: curr_options.inDuration, easing: 'easeOutSine'});
+
+ // Add click close handler to document
+ $(document).bind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'), function (e) {
+ if (!activates.is(e.target) && !origin.is(e.target) && (!origin.find(e.target).length) ) {
+ hideDropdown();
+ $(document).unbind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'));
+ }
+ });
+ }
+
+ function hideDropdown() {
+ // Check for simultaneous focus and click events.
+ isFocused = false;
+ activates.fadeOut(curr_options.outDuration);
+ activates.removeClass('active');
+ origin.removeClass('active');
+ $(document).unbind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'));
+ setTimeout(function() { activates.css('max-height', ''); }, curr_options.outDuration);
+ }
+
+ // Hover
+ if (curr_options.hover) {
+ var open = false;
+ origin.unbind('click.' + origin.attr('id'));
+ // Hover handler to show dropdown
+ origin.on('mouseenter', function(e){ // Mouse over
+ if (open === false) {
+ placeDropdown();
+ open = true;
+ }
+ });
+ origin.on('mouseleave', function(e){
+ // If hover on origin then to something other than dropdown content, then close
+ var toEl = e.toElement || e.relatedTarget; // added browser compatibility for target element
+ if(!$(toEl).closest('.dropdown-content').is(activates)) {
+ activates.stop(true, true);
+ hideDropdown();
+ open = false;
+ }
+ });
+
+ activates.on('mouseleave', function(e){ // Mouse out
+ var toEl = e.toElement || e.relatedTarget;
+ if(!$(toEl).closest('.dropdown-button').is(origin)) {
+ activates.stop(true, true);
+ hideDropdown();
+ open = false;
+ }
+ });
+
+ // Click
+ } else {
+ // Click handler to show dropdown
+ origin.unbind('click.' + origin.attr('id'));
+ origin.bind('click.'+origin.attr('id'), function(e){
+ if (!isFocused) {
+ if ( origin[0] == e.currentTarget &&
+ !origin.hasClass('active') &&
+ ($(e.target).closest('.dropdown-content').length === 0)) {
+ e.preventDefault(); // Prevents button click from moving window
+ if (curr_options.stopPropagation) {
+ e.stopPropagation();
+ }
+ placeDropdown('click');
+ }
+ // If origin is clicked and menu is open, close menu
+ else if (origin.hasClass('active')) {
+ hideDropdown();
+ $(document).unbind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'));
+ }
+ }
+ });
+
+ } // End else
+
+ // Listen to open and close event - useful for select component
+ origin.on('open', function(e, eventType) {
+ placeDropdown(eventType);
+ });
+ origin.on('close', hideDropdown);
+
+
+ });
+ }; // End dropdown plugin
+
+ $(document).ready(function(){
+ $('.dropdown-button').dropdown();
+ });
+}( jQuery ));
+;(function($) {
+ var _stack = 0,
+ _lastID = 0,
+ _generateID = function() {
+ _lastID++;
+ return 'materialize-modal-overlay-' + _lastID;
+ };
+
+ var methods = {
+ init : function(options) {
+ var defaults = {
+ opacity: 0.5,
+ inDuration: 350,
+ outDuration: 250,
+ ready: undefined,
+ complete: undefined,
+ dismissible: true,
+ startingTop: '4%',
+ endingTop: '10%'
+ };
+
+ // Override defaults
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+ var $modal = $(this);
+ var modal_id = $(this).attr("id") || '#' + $(this).data('target');
+
+ var closeModal = function() {
+ var overlayID = $modal.data('overlay-id');
+ var $overlay = $('#' + overlayID);
+ $modal.removeClass('open');
+
+ // Enable scrolling
+ $('body').css({
+ overflow: '',
+ width: ''
+ });
+
+ $modal.find('.modal-close').off('click.close');
+ $(document).off('keyup.modal' + overlayID);
+
+ $overlay.velocity( { opacity: 0}, {duration: options.outDuration, queue: false, ease: "easeOutQuart"});
+
+
+ // Define Bottom Sheet animation
+ var exitVelocityOptions = {
+ duration: options.outDuration,
+ queue: false,
+ ease: "easeOutCubic",
+ // Handle modal ready callback
+ complete: function() {
+ $(this).css({display:"none"});
+
+ // Call complete callback
+ if (typeof(options.complete) === "function") {
+ options.complete.call(this, $modal);
+ }
+ $overlay.remove();
+ _stack--;
+ }
+ };
+ if ($modal.hasClass('bottom-sheet')) {
+ $modal.velocity({bottom: "-100%", opacity: 0}, exitVelocityOptions);
+ }
+ else {
+ $modal.velocity(
+ { top: options.startingTop, opacity: 0, scaleX: 0.7},
+ exitVelocityOptions
+ );
+ }
+ };
+
+ var openModal = function($trigger) {
+ var $body = $('body');
+ var oldWidth = $body.innerWidth();
+ $body.css('overflow', 'hidden');
+ $body.width(oldWidth);
+
+ if ($modal.hasClass('open')) {
+ return;
+ }
+
+ var overlayID = _generateID();
+ var $overlay = $('<div class="modal-overlay"></div>');
+ lStack = (++_stack);
+
+ // Store a reference of the overlay
+ $overlay.attr('id', overlayID).css('z-index', 1000 + lStack * 2);
+ $modal.data('overlay-id', overlayID).css('z-index', 1000 + lStack * 2 + 1);
+ $modal.addClass('open');
+
+ $("body").append($overlay);
+
+ if (options.dismissible) {
+ $overlay.click(function() {
+ closeModal();
+ });
+ // Return on ESC
+ $(document).on('keyup.modal' + overlayID, function(e) {
+ if (e.keyCode === 27) { // ESC key
+ closeModal();
+ }
+ });
+ }
+
+ $modal.find(".modal-close").on('click.close', function(e) {
+ closeModal();
+ });
+
+ $overlay.css({ display : "block", opacity : 0 });
+
+ $modal.css({
+ display : "block",
+ opacity: 0
+ });
+
+ $overlay.velocity({opacity: options.opacity}, {duration: options.inDuration, queue: false, ease: "easeOutCubic"});
+ $modal.data('associated-overlay', $overlay[0]);
+
+ // Define Bottom Sheet animation
+ var enterVelocityOptions = {
+ duration: options.inDuration,
+ queue: false,
+ ease: "easeOutCubic",
+ // Handle modal ready callback
+ complete: function() {
+ if (typeof(options.ready) === "function") {
+ options.ready.call(this, $modal, $trigger);
+ }
+ }
+ };
+ if ($modal.hasClass('bottom-sheet')) {
+ $modal.velocity({bottom: "0", opacity: 1}, enterVelocityOptions);
+ }
+ else {
+ $.Velocity.hook($modal, "scaleX", 0.7);
+ $modal.css({ top: options.startingTop });
+ $modal.velocity({top: options.endingTop, opacity: 1, scaleX: '1'}, enterVelocityOptions);
+ }
+
+ };
+
+ // Reset handlers
+ $(document).off('click.modalTrigger', 'a[href="#' + modal_id + '"], [data-target="' + modal_id + '"]');
+ $(this).off('openModal');
+ $(this).off('closeModal');
+
+ // Close Handlers
+ $(document).on('click.modalTrigger', 'a[href="#' + modal_id + '"], [data-target="' + modal_id + '"]', function(e) {
+ options.startingTop = ($(this).offset().top - $(window).scrollTop()) /1.15;
+ openModal($(this));
+ e.preventDefault();
+ }); // done set on click
+
+ $(this).on('openModal', function() {
+ var modal_id = $(this).attr("href") || '#' + $(this).data('target');
+ openModal();
+ });
+
+ $(this).on('closeModal', function() {
+ closeModal();
+ });
+ }); // done return
+ },
+ open : function() {
+ $(this).trigger('openModal');
+ },
+ close : function() {
+ $(this).trigger('closeModal');
+ }
+ };
+
+ $.fn.modal = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.modal' );
+ }
+ };
+})(jQuery);
+;(function ($) {
+
+ $.fn.materialbox = function () {
+
+ return this.each(function() {
+
+ if ($(this).hasClass('initialized')) {
+ return;
+ }
+
+ $(this).addClass('initialized');
+
+ var overlayActive = false;
+ var doneAnimating = true;
+ var inDuration = 275;
+ var outDuration = 200;
+ var origin = $(this);
+ var placeholder = $('<div></div>').addClass('material-placeholder');
+ var originalWidth = 0;
+ var originalHeight = 0;
+ var ancestorsChanged;
+ var ancestor;
+ origin.wrap(placeholder);
+
+
+ origin.on('click', function(){
+ var placeholder = origin.parent('.material-placeholder');
+ var windowWidth = window.innerWidth;
+ var windowHeight = window.innerHeight;
+ var originalWidth = origin.width();
+ var originalHeight = origin.height();
+
+
+ // If already modal, return to original
+ if (doneAnimating === false) {
+ returnToOriginal();
+ return false;
+ }
+ else if (overlayActive && doneAnimating===true) {
+ returnToOriginal();
+ return false;
+ }
+
+
+ // Set states
+ doneAnimating = false;
+ origin.addClass('active');
+ overlayActive = true;
+
+ // Set positioning for placeholder
+ placeholder.css({
+ width: placeholder[0].getBoundingClientRect().width,
+ height: placeholder[0].getBoundingClientRect().height,
+ position: 'relative',
+ top: 0,
+ left: 0
+ });
+
+ // Find ancestor with overflow: hidden; and remove it
+ ancestorsChanged = undefined;
+ ancestor = placeholder[0].parentNode;
+ var count = 0;
+ while (ancestor !== null && !$(ancestor).is(document)) {
+ var curr = $(ancestor);
+ if (curr.css('overflow') !== 'visible') {
+ curr.css('overflow', 'visible');
+ if (ancestorsChanged === undefined) {
+ ancestorsChanged = curr;
+ }
+ else {
+ ancestorsChanged = ancestorsChanged.add(curr);
+ }
+ }
+ ancestor = ancestor.parentNode;
+ }
+
+ // Set css on origin
+ origin.css({
+ position: 'absolute',
+ 'z-index': 1000,
+ 'will-change': 'left, top, width, height'
+ })
+ .data('width', originalWidth)
+ .data('height', originalHeight);
+
+ // Add overlay
+ var overlay = $('<div id="materialbox-overlay"></div>')
+ .css({
+ opacity: 0
+ })
+ .click(function(){
+ if (doneAnimating === true)
+ returnToOriginal();
+ });
+
+ // Put before in origin image to preserve z-index layering.
+ origin.before(overlay);
+
+ // Set dimensions if needed
+ var overlayOffset = overlay[0].getBoundingClientRect();
+ overlay.css({
+ width: windowWidth,
+ height: windowHeight,
+ left: -1 * overlayOffset.left,
+ top: -1 * overlayOffset.top
+ })
+
+ // Animate Overlay
+ overlay.velocity({opacity: 1},
+ {duration: inDuration, queue: false, easing: 'easeOutQuad'} );
+
+ // Add and animate caption if it exists
+ if (origin.data('caption') !== "") {
+ var $photo_caption = $('<div class="materialbox-caption"></div>');
+ $photo_caption.text(origin.data('caption'));
+ $('body').append($photo_caption);
+ $photo_caption.css({ "display": "inline" });
+ $photo_caption.velocity({opacity: 1}, {duration: inDuration, queue: false, easing: 'easeOutQuad'});
+ }
+
+ // Resize Image
+ var ratio = 0;
+ var widthPercent = originalWidth / windowWidth;
+ var heightPercent = originalHeight / windowHeight;
+ var newWidth = 0;
+ var newHeight = 0;
+
+ if (widthPercent > heightPercent) {
+ ratio = originalHeight / originalWidth;
+ newWidth = windowWidth * 0.9;
+ newHeight = windowWidth * 0.9 * ratio;
+ }
+ else {
+ ratio = originalWidth / originalHeight;
+ newWidth = (windowHeight * 0.9) * ratio;
+ newHeight = windowHeight * 0.9;
+ }
+
+ // Animate image + set z-index
+ if(origin.hasClass('responsive-img')) {
+ origin.velocity({'max-width': newWidth, 'width': originalWidth}, {duration: 0, queue: false,
+ complete: function(){
+ origin.css({left: 0, top: 0})
+ .velocity(
+ {
+ height: newHeight,
+ width: newWidth,
+ left: $(document).scrollLeft() + windowWidth/2 - origin.parent('.material-placeholder').offset().left - newWidth/2,
+ top: $(document).scrollTop() + windowHeight/2 - origin.parent('.material-placeholder').offset().top - newHeight/ 2
+ },
+ {
+ duration: inDuration,
+ queue: false,
+ easing: 'easeOutQuad',
+ complete: function(){doneAnimating = true;}
+ }
+ );
+ } // End Complete
+ }); // End Velocity
+ }
+ else {
+ origin.css('left', 0)
+ .css('top', 0)
+ .velocity(
+ {
+ height: newHeight,
+ width: newWidth,
+ left: $(document).scrollLeft() + windowWidth/2 - origin.parent('.material-placeholder').offset().left - newWidth/2,
+ top: $(document).scrollTop() + windowHeight/2 - origin.parent('.material-placeholder').offset().top - newHeight/ 2
+ },
+ {
+ duration: inDuration,
+ queue: false,
+ easing: 'easeOutQuad',
+ complete: function(){doneAnimating = true;}
+ }
+ ); // End Velocity
+ }
+
+ }); // End origin on click
+
+
+ // Return on scroll
+ $(window).scroll(function() {
+ if (overlayActive) {
+ returnToOriginal();
+ }
+ });
+
+ // Return on ESC
+ $(document).keyup(function(e) {
+
+ if (e.keyCode === 27 && doneAnimating === true) { // ESC key
+ if (overlayActive) {
+ returnToOriginal();
+ }
+ }
+ });
+
+
+ // This function returns the modaled image to the original spot
+ function returnToOriginal() {
+
+ doneAnimating = false;
+
+ var placeholder = origin.parent('.material-placeholder');
+ var windowWidth = window.innerWidth;
+ var windowHeight = window.innerHeight;
+ var originalWidth = origin.data('width');
+ var originalHeight = origin.data('height');
+
+ origin.velocity("stop", true);
+ $('#materialbox-overlay').velocity("stop", true);
+ $('.materialbox-caption').velocity("stop", true);
+
+
+ $('#materialbox-overlay').velocity({opacity: 0}, {
+ duration: outDuration, // Delay prevents animation overlapping
+ queue: false, easing: 'easeOutQuad',
+ complete: function(){
+ // Remove Overlay
+ overlayActive = false;
+ $(this).remove();
+ }
+ });
+
+ // Resize Image
+ origin.velocity(
+ {
+ width: originalWidth,
+ height: originalHeight,
+ left: 0,
+ top: 0
+ },
+ {
+ duration: outDuration,
+ queue: false, easing: 'easeOutQuad'
+ }
+ );
+
+ // Remove Caption + reset css settings on image
+ $('.materialbox-caption').velocity({opacity: 0}, {
+ duration: outDuration, // Delay prevents animation overlapping
+ queue: false, easing: 'easeOutQuad',
+ complete: function(){
+ placeholder.css({
+ height: '',
+ width: '',
+ position: '',
+ top: '',
+ left: ''
+ });
+
+ origin.css({
+ height: '',
+ top: '',
+ left: '',
+ width: '',
+ 'max-width': '',
+ position: '',
+ 'z-index': '',
+ 'will-change': ''
+ });
+
+ // Remove class
+ origin.removeClass('active');
+ doneAnimating = true;
+ $(this).remove();
+
+ // Remove overflow overrides on ancestors
+ if (ancestorsChanged) {
+ ancestorsChanged.css('overflow', '');
+ }
+ }
+ });
+
+ }
+ });
+ };
+
+ $(document).ready(function(){
+ $('.materialboxed').materialbox();
+ });
+
+}( jQuery ));
+;(function ($) {
+
+ $.fn.parallax = function () {
+ var window_width = $(window).width();
+ // Parallax Scripts
+ return this.each(function(i) {
+ var $this = $(this);
+ $this.addClass('parallax');
+
+ function updateParallax(initial) {
+ var container_height;
+ if (window_width < 601) {
+ container_height = ($this.height() > 0) ? $this.height() : $this.children("img").height();
+ }
+ else {
+ container_height = ($this.height() > 0) ? $this.height() : 500;
+ }
+ var $img = $this.children("img").first();
+ var img_height = $img.height();
+ var parallax_dist = img_height - container_height;
+ var bottom = $this.offset().top + container_height;
+ var top = $this.offset().top;
+ var scrollTop = $(window).scrollTop();
+ var windowHeight = window.innerHeight;
+ var windowBottom = scrollTop + windowHeight;
+ var percentScrolled = (windowBottom - top) / (container_height + windowHeight);
+ var parallax = Math.round((parallax_dist * percentScrolled));
+
+ if (initial) {
+ $img.css('display', 'block');
+ }
+ if ((bottom > scrollTop) && (top < (scrollTop + windowHeight))) {
+ $img.css('transform', "translate3D(-50%," + parallax + "px, 0)");
+ }
+
+ }
+
+ // Wait for image load
+ $this.children("img").one("load", function() {
+ updateParallax(true);
+ }).each(function() {
+ if (this.complete) $(this).trigger("load");
+ });
+
+ $(window).scroll(function() {
+ window_width = $(window).width();
+ updateParallax(false);
+ });
+
+ $(window).resize(function() {
+ window_width = $(window).width();
+ updateParallax(false);
+ });
+
+ });
+
+ };
+}( jQuery ));
+;(function ($) {
+
+ var methods = {
+ init : function(options) {
+ var defaults = {
+ onShow: null,
+ swipeable: false,
+ responsiveThreshold: Infinity, // breakpoint for swipeable
+ };
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+
+ // For each set of tabs, we want to keep track of
+ // which tab is active and its associated content
+ var $this = $(this),
+ window_width = $(window).width();
+
+ var $active, $content, $links = $this.find('li.tab a'),
+ $tabs_width = $this.width(),
+ $tabs_content = $(),
+ $tabs_wrapper,
+ $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length,
+ $indicator,
+ index = prev_index = 0,
+ clicked = false,
+ clickedTimeout,
+ transition = 300;
+
+
+ // Finds right attribute for indicator based on active tab.
+ // el: jQuery Object
+ var calcRightPos = function(el) {
+ return $tabs_width - el.position().left - el.outerWidth() - $this.scrollLeft();
+ };
+
+ // Finds left attribute for indicator based on active tab.
+ // el: jQuery Object
+ var calcLeftPos = function(el) {
+ return el.position().left + $this.scrollLeft();
+ };
+
+ // Animates Indicator to active tab.
+ // prev_index: Number
+ var animateIndicator = function(prev_index) {
+ if ((index - prev_index) >= 0) {
+ $indicator.velocity({"right": calcRightPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
+ $indicator.velocity({"left": calcLeftPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
+
+ } else {
+ $indicator.velocity({"left": calcLeftPos($active) }, { duration: transition, queue: false, easing: 'easeOutQuad'});
+ $indicator.velocity({"right": calcRightPos($active) }, {duration: transition, queue: false, easing: 'easeOutQuad', delay: 90});
+ }
+ };
+
+ // Change swipeable according to responsive threshold
+ if (options.swipeable) {
+ if (window_width > options.responsiveThreshold) {
+ options.swipeable = false;
+ }
+ }
+
+
+ // If the location.hash matches one of the links, use that as the active tab.
+ $active = $($links.filter('[href="'+location.hash+'"]'));
+
+ // If no match is found, use the first link or any with class 'active' as the initial active tab.
+ if ($active.length === 0) {
+ $active = $(this).find('li.tab a.active').first();
+ }
+ if ($active.length === 0) {
+ $active = $(this).find('li.tab a').first();
+ }
+
+ $active.addClass('active');
+ index = $links.index($active);
+ if (index < 0) {
+ index = 0;
+ }
+
+ if ($active[0] !== undefined) {
+ $content = $($active[0].hash);
+ $content.addClass('active');
+ }
+
+ // append indicator then set indicator width to tab width
+ if (!$this.find('.indicator').length) {
+ $this.append('<div class="indicator"></div>');
+ }
+ $indicator = $this.find('.indicator');
+
+ // we make sure that the indicator is at the end of the tabs
+ $this.append($indicator);
+
+ if ($this.is(":visible")) {
+ // $indicator.css({"right": $tabs_width - ((index + 1) * $tab_width)});
+ // $indicator.css({"left": index * $tab_width});
+ setTimeout(function() {
+ $indicator.css({"right": calcRightPos($active) });
+ $indicator.css({"left": calcLeftPos($active) });
+ }, 0);
+ }
+ $(window).resize(function () {
+ $tabs_width = $this.width();
+ $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
+ if (index < 0) {
+ index = 0;
+ }
+ if ($tab_width !== 0 && $tabs_width !== 0) {
+ $indicator.css({"right": calcRightPos($active) });
+ $indicator.css({"left": calcLeftPos($active) });
+ }
+ });
+
+ // Initialize Tabs Content.
+ if (options.swipeable) {
+ // TODO: Duplicate calls with swipeable? handle multiple div wrapping.
+ $links.each(function () {
+ var $curr_content = $(Materialize.escapeHash(this.hash));
+ $curr_content.addClass('carousel-item');
+ $tabs_content = $tabs_content.add($curr_content);
+ });
+ $tabs_wrapper = $tabs_content.wrapAll('<div class="tabs-content carousel"></div>');
+ $tabs_content.css('display', '');
+ $('.tabs-content.carousel').carousel({
+ fullWidth: true,
+ noWrap: true,
+ onCycleTo: function(item) {
+ if (!clicked) {
+ var prev_index = index;
+ index = $tabs_wrapper.index(item);
+ $active = $links.eq(index);
+ animateIndicator(prev_index);
+ }
+ },
+ });
+ } else {
+ // Hide the remaining content
+ $links.not($active).each(function () {
+ $(Materialize.escapeHash(this.hash)).hide();
+ });
+ }
+
+
+ // Bind the click event handler
+ $this.on('click', 'a', function(e) {
+ if ($(this).parent().hasClass('disabled')) {
+ e.preventDefault();
+ return;
+ }
+
+ // Act as regular link if target attribute is specified.
+ if (!!$(this).attr("target")) {
+ return;
+ }
+
+ clicked = true;
+ $tabs_width = $this.width();
+ $tab_width = Math.max($tabs_width, $this[0].scrollWidth) / $links.length;
+
+ // Make the old tab inactive.
+ $active.removeClass('active');
+ var $oldContent = $content
+
+ // Update the variables with the new link and content
+ $active = $(this);
+ $content = $(Materialize.escapeHash(this.hash));
+ $links = $this.find('li.tab a');
+ var activeRect = $active.position();
+
+ // Make the tab active.
+ $active.addClass('active');
+ prev_index = index;
+ index = $links.index($(this));
+ if (index < 0) {
+ index = 0;
+ }
+ // Change url to current tab
+ // window.location.hash = $active.attr('href');
+
+ // Swap content
+ if (options.swipeable) {
+ if ($tabs_content.length) {
+ $tabs_content.carousel('set', index);
+ }
+ } else {
+ if ($content !== undefined) {
+ $content.show();
+ $content.addClass('active');
+ if (typeof(options.onShow) === "function") {
+ options.onShow.call(this, $content);
+ }
+ }
+
+ if ($oldContent !== undefined &&
+ !$oldContent.is($content)) {
+ $oldContent.hide();
+ $oldContent.removeClass('active');
+ }
+ }
+
+ // Reset clicked state
+ clickedTimeout = setTimeout(function(){ clicked = false; }, transition);
+
+ // Update indicator
+ animateIndicator(prev_index);
+
+ // Prevent the anchor's default click action
+ e.preventDefault();
+ });
+ });
+
+ },
+ select_tab : function( id ) {
+ this.find('a[href="#' + id + '"]').trigger('click');
+ }
+ };
+
+ $.fn.tabs = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tabs' );
+ }
+ };
+
+ $(document).ready(function(){
+ $('ul.tabs').tabs();
+ });
+}( jQuery ));
+;(function ($) {
+ $.fn.tooltip = function (options) {
+ var timeout = null,
+ margin = 5;
+
+ // Defaults
+ var defaults = {
+ delay: 350,
+ tooltip: '',
+ position: 'bottom',
+ html: false
+ };
+
+ // Remove tooltip from the activator
+ if (options === "remove") {
+ this.each(function() {
+ $('#' + $(this).attr('data-tooltip-id')).remove();
+ $(this).off('mouseenter.tooltip mouseleave.tooltip');
+ });
+ return false;
+ }
+
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+ var tooltipId = Materialize.guid();
+ var origin = $(this);
+
+ // Destroy old tooltip
+ if (origin.attr('data-tooltip-id')) {
+ $('#' + origin.attr('data-tooltip-id')).remove();
+ }
+
+ origin.attr('data-tooltip-id', tooltipId);
+
+ // Get attributes.
+ var allowHtml,
+ tooltipDelay,
+ tooltipPosition,
+ tooltipText,
+ tooltipEl,
+ backdrop;
+ var setAttributes = function() {
+ allowHtml = origin.attr('data-html') ? origin.attr('data-html') === 'true' : options.html;
+ tooltipDelay = origin.attr('data-delay');
+ tooltipDelay = (tooltipDelay === undefined || tooltipDelay === '') ?
+ options.delay : tooltipDelay;
+ tooltipPosition = origin.attr('data-position');
+ tooltipPosition = (tooltipPosition === undefined || tooltipPosition === '') ?
+ options.position : tooltipPosition;
+ tooltipText = origin.attr('data-tooltip');
+ tooltipText = (tooltipText === undefined || tooltipText === '') ?
+ options.tooltip : tooltipText;
+ };
+ setAttributes();
+
+ var renderTooltipEl = function() {
+ var tooltip = $('<div class="material-tooltip"></div>');
+
+ // Create Text span
+ if (allowHtml) {
+ tooltipText = $('<span></span>').html(tooltipText);
+ } else{
+ tooltipText = $('<span></span>').text(tooltipText);
+ }
+
+ // Create tooltip
+ tooltip.append(tooltipText)
+ .appendTo($('body'))
+ .attr('id', tooltipId);
+
+ // Create backdrop
+ backdrop = $('<div class="backdrop"></div>');
+ backdrop.appendTo(tooltip);
+ return tooltip;
+ };
+ tooltipEl = renderTooltipEl();
+
+ // Destroy previously binded events
+ origin.off('mouseenter.tooltip mouseleave.tooltip');
+ // Mouse In
+ var started = false, timeoutRef;
+ origin.on({'mouseenter.tooltip': function(e) {
+ var showTooltip = function() {
+ setAttributes();
+ started = true;
+ tooltipEl.velocity('stop');
+ backdrop.velocity('stop');
+ tooltipEl.css({ visibility: 'visible', left: '0px', top: '0px' });
+
+ // Tooltip positioning
+ var originWidth = origin.outerWidth();
+ var originHeight = origin.outerHeight();
+ var tooltipHeight = tooltipEl.outerHeight();
+ var tooltipWidth = tooltipEl.outerWidth();
+ var tooltipVerticalMovement = '0px';
+ var tooltipHorizontalMovement = '0px';
+ var backdropOffsetWidth = backdrop[0].offsetWidth;
+ var backdropOffsetHeight = backdrop[0].offsetHeight;
+ var scaleXFactor = 8;
+ var scaleYFactor = 8;
+ var scaleFactor = 0;
+ var targetTop, targetLeft, newCoordinates;
+
+ if (tooltipPosition === "top") {
+ // Top Position
+ targetTop = origin.offset().top - tooltipHeight - margin;
+ targetLeft = origin.offset().left + originWidth/2 - tooltipWidth/2;
+ newCoordinates = repositionWithinScreen(targetLeft, targetTop, tooltipWidth, tooltipHeight);
+ tooltipVerticalMovement = '-10px';
+ backdrop.css({
+ bottom: 0,
+ left: 0,
+ borderRadius: '14px 14px 0 0',
+ transformOrigin: '50% 100%',
+ marginTop: tooltipHeight,
+ marginLeft: (tooltipWidth/2) - (backdropOffsetWidth/2)
+ });
+ }
+ // Left Position
+ else if (tooltipPosition === "left") {
+ targetTop = origin.offset().top + originHeight/2 - tooltipHeight/2;
+ targetLeft = origin.offset().left - tooltipWidth - margin;
+ newCoordinates = repositionWithinScreen(targetLeft, targetTop, tooltipWidth, tooltipHeight);
+
+ tooltipHorizontalMovement = '-10px';
+ backdrop.css({
+ top: '-7px',
+ right: 0,
+ width: '14px',
+ height: '14px',
+ borderRadius: '14px 0 0 14px',
+ transformOrigin: '95% 50%',
+ marginTop: tooltipHeight/2,
+ marginLeft: tooltipWidth
+ });
+ }
+ // Right Position
+ else if (tooltipPosition === "right") {
+ targetTop = origin.offset().top + originHeight/2 - tooltipHeight/2;
+ targetLeft = origin.offset().left + originWidth + margin;
+ newCoordinates = repositionWithinScreen(targetLeft, targetTop, tooltipWidth, tooltipHeight);
+
+ tooltipHorizontalMovement = '+10px';
+ backdrop.css({
+ top: '-7px',
+ left: 0,
+ width: '14px',
+ height: '14px',
+ borderRadius: '0 14px 14px 0',
+ transformOrigin: '5% 50%',
+ marginTop: tooltipHeight/2,
+ marginLeft: '0px'
+ });
+ }
+ else {
+ // Bottom Position
+ targetTop = origin.offset().top + origin.outerHeight() + margin;
+ targetLeft = origin.offset().left + originWidth/2 - tooltipWidth/2;
+ newCoordinates = repositionWithinScreen(targetLeft, targetTop, tooltipWidth, tooltipHeight);
+ tooltipVerticalMovement = '+10px';
+ backdrop.css({
+ top: 0,
+ left: 0,
+ marginLeft: (tooltipWidth/2) - (backdropOffsetWidth/2)
+ });
+ }
+
+ // Set tooptip css placement
+ tooltipEl.css({
+ top: newCoordinates.y,
+ left: newCoordinates.x
+ });
+
+ // Calculate Scale to fill
+ scaleXFactor = Math.SQRT2 * tooltipWidth / parseInt(backdropOffsetWidth);
+ scaleYFactor = Math.SQRT2 * tooltipHeight / parseInt(backdropOffsetHeight);
+ scaleFactor = Math.max(scaleXFactor, scaleYFactor);
+
+ tooltipEl.velocity({ translateY: tooltipVerticalMovement, translateX: tooltipHorizontalMovement}, { duration: 350, queue: false })
+ .velocity({opacity: 1}, {duration: 300, delay: 50, queue: false});
+ backdrop.css({ visibility: 'visible' })
+ .velocity({opacity:1},{duration: 55, delay: 0, queue: false})
+ .velocity({scaleX: scaleFactor, scaleY: scaleFactor}, {duration: 300, delay: 0, queue: false, easing: 'easeInOutQuad'});
+ };
+
+ timeoutRef = setTimeout(showTooltip, tooltipDelay); // End Interval
+
+ // Mouse Out
+ },
+ 'mouseleave.tooltip': function(){
+ // Reset State
+ started = false;
+ clearTimeout(timeoutRef);
+
+ // Animate back
+ setTimeout(function() {
+ if (started !== true) {
+ tooltipEl.velocity({
+ opacity: 0, translateY: 0, translateX: 0}, { duration: 225, queue: false});
+ backdrop.velocity({opacity: 0, scaleX: 1, scaleY: 1}, {
+ duration:225,
+ queue: false,
+ complete: function(){
+ backdrop.css({ visibility: 'hidden' });
+ tooltipEl.css({ visibility: 'hidden' });
+ started = false;}
+ });
+ }
+ },225);
+ }
+ });
+ });
+ };
+
+ var repositionWithinScreen = function(x, y, width, height) {
+ var newX = x;
+ var newY = y;
+
+ if (newX < 0) {
+ newX = 4;
+ } else if (newX + width > window.innerWidth) {
+ newX -= newX + width - window.innerWidth;
+ }
+
+ if (newY < 0) {
+ newY = 4;
+ } else if (newY + height > window.innerHeight + $(window).scrollTop) {
+ newY -= newY + height - window.innerHeight;
+ }
+
+ return {x: newX, y: newY};
+ };
+
+ $(document).ready(function(){
+ $('.tooltipped').tooltip();
+ });
+}( jQuery ));
+;/*!
+ * Waves v0.6.4
+ * http://fian.my.id/Waves
+ *
+ * Copyright 2014 Alfiana E. Sibuea and other contributors
+ * Released under the MIT license
+ * https://github.com/fians/Waves/blob/master/LICENSE
+ */
+
+;(function(window) {
+ 'use strict';
+
+ var Waves = Waves || {};
+ var $$ = document.querySelectorAll.bind(document);
+
+ // Find exact position of element
+ function isWindow(obj) {
+ return obj !== null && obj === obj.window;
+ }
+
+ function getWindow(elem) {
+ return isWindow(elem) ? elem : elem.nodeType === 9 && elem.defaultView;
+ }
+
+ function offset(elem) {
+ var docElem, win,
+ box = {top: 0, left: 0},
+ doc = elem && elem.ownerDocument;
+
+ docElem = doc.documentElement;
+
+ if (typeof elem.getBoundingClientRect !== typeof undefined) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow(doc);
+ return {
+ top: box.top + win.pageYOffset - docElem.clientTop,
+ left: box.left + win.pageXOffset - docElem.clientLeft
+ };
+ }
+
+ function convertStyle(obj) {
+ var style = '';
+
+ for (var a in obj) {
+ if (obj.hasOwnProperty(a)) {
+ style += (a + ':' + obj[a] + ';');
+ }
+ }
+
+ return style;
+ }
+
+ var Effect = {
+
+ // Effect delay
+ duration: 750,
+
+ show: function(e, element) {
+
+ // Disable right click
+ if (e.button === 2) {
+ return false;
+ }
+
+ var el = element || this;
+
+ // Create ripple
+ var ripple = document.createElement('div');
+ ripple.className = 'waves-ripple';
+ el.appendChild(ripple);
+
+ // Get click coordinate and element witdh
+ var pos = offset(el);
+ var relativeY = (e.pageY - pos.top);
+ var relativeX = (e.pageX - pos.left);
+ var scale = 'scale('+((el.clientWidth / 100) * 10)+')';
+
+ // Support for touch devices
+ if ('touches' in e) {
+ relativeY = (e.touches[0].pageY - pos.top);
+ relativeX = (e.touches[0].pageX - pos.left);
+ }
+
+ // Attach data to element
+ ripple.setAttribute('data-hold', Date.now());
+ ripple.setAttribute('data-scale', scale);
+ ripple.setAttribute('data-x', relativeX);
+ ripple.setAttribute('data-y', relativeY);
+
+ // Set ripple position
+ var rippleStyle = {
+ 'top': relativeY+'px',
+ 'left': relativeX+'px'
+ };
+
+ ripple.className = ripple.className + ' waves-notransition';
+ ripple.setAttribute('style', convertStyle(rippleStyle));
+ ripple.className = ripple.className.replace('waves-notransition', '');
+
+ // Scale the ripple
+ rippleStyle['-webkit-transform'] = scale;
+ rippleStyle['-moz-transform'] = scale;
+ rippleStyle['-ms-transform'] = scale;
+ rippleStyle['-o-transform'] = scale;
+ rippleStyle.transform = scale;
+ rippleStyle.opacity = '1';
+
+ rippleStyle['-webkit-transition-duration'] = Effect.duration + 'ms';
+ rippleStyle['-moz-transition-duration'] = Effect.duration + 'ms';
+ rippleStyle['-o-transition-duration'] = Effect.duration + 'ms';
+ rippleStyle['transition-duration'] = Effect.duration + 'ms';
+
+ rippleStyle['-webkit-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
+ rippleStyle['-moz-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
+ rippleStyle['-o-transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
+ rippleStyle['transition-timing-function'] = 'cubic-bezier(0.250, 0.460, 0.450, 0.940)';
+
+ ripple.setAttribute('style', convertStyle(rippleStyle));
+ },
+
+ hide: function(e) {
+ TouchHandler.touchup(e);
+
+ var el = this;
+ var width = el.clientWidth * 1.4;
+
+ // Get first ripple
+ var ripple = null;
+ var ripples = el.getElementsByClassName('waves-ripple');
+ if (ripples.length > 0) {
+ ripple = ripples[ripples.length - 1];
+ } else {
+ return false;
+ }
+
+ var relativeX = ripple.getAttribute('data-x');
+ var relativeY = ripple.getAttribute('data-y');
+ var scale = ripple.getAttribute('data-scale');
+
+ // Get delay beetween mousedown and mouse leave
+ var diff = Date.now() - Number(ripple.getAttribute('data-hold'));
+ var delay = 350 - diff;
+
+ if (delay < 0) {
+ delay = 0;
+ }
+
+ // Fade out ripple after delay
+ setTimeout(function() {
+ var style = {
+ 'top': relativeY+'px',
+ 'left': relativeX+'px',
+ 'opacity': '0',
+
+ // Duration
+ '-webkit-transition-duration': Effect.duration + 'ms',
+ '-moz-transition-duration': Effect.duration + 'ms',
+ '-o-transition-duration': Effect.duration + 'ms',
+ 'transition-duration': Effect.duration + 'ms',
+ '-webkit-transform': scale,
+ '-moz-transform': scale,
+ '-ms-transform': scale,
+ '-o-transform': scale,
+ 'transform': scale,
+ };
+
+ ripple.setAttribute('style', convertStyle(style));
+
+ setTimeout(function() {
+ try {
+ el.removeChild(ripple);
+ } catch(e) {
+ return false;
+ }
+ }, Effect.duration);
+ }, delay);
+ },
+
+ // Little hack to make <input> can perform waves effect
+ wrapInput: function(elements) {
+ for (var a = 0; a < elements.length; a++) {
+ var el = elements[a];
+
+ if (el.tagName.toLowerCase() === 'input') {
+ var parent = el.parentNode;
+
+ // If input already have parent just pass through
+ if (parent.tagName.toLowerCase() === 'i' && parent.className.indexOf('waves-effect') !== -1) {
+ continue;
+ }
+
+ // Put element class and style to the specified parent
+ var wrapper = document.createElement('i');
+ wrapper.className = el.className + ' waves-input-wrapper';
+
+ var elementStyle = el.getAttribute('style');
+
+ if (!elementStyle) {
+ elementStyle = '';
+ }
+
+ wrapper.setAttribute('style', elementStyle);
+
+ el.className = 'waves-button-input';
+ el.removeAttribute('style');
+
+ // Put element as child
+ parent.replaceChild(wrapper, el);
+ wrapper.appendChild(el);
+ }
+ }
+ }
+ };
+
+
+ /**
+ * Disable mousedown event for 500ms during and after touch
+ */
+ var TouchHandler = {
+ /* uses an integer rather than bool so there's no issues with
+ * needing to clear timeouts if another touch event occurred
+ * within the 500ms. Cannot mouseup between touchstart and
+ * touchend, nor in the 500ms after touchend. */
+ touches: 0,
+ allowEvent: function(e) {
+ var allow = true;
+
+ if (e.type === 'touchstart') {
+ TouchHandler.touches += 1; //push
+ } else if (e.type === 'touchend' || e.type === 'touchcancel') {
+ setTimeout(function() {
+ if (TouchHandler.touches > 0) {
+ TouchHandler.touches -= 1; //pop after 500ms
+ }
+ }, 500);
+ } else if (e.type === 'mousedown' && TouchHandler.touches > 0) {
+ allow = false;
+ }
+
+ return allow;
+ },
+ touchup: function(e) {
+ TouchHandler.allowEvent(e);
+ }
+ };
+
+
+ /**
+ * Delegated click handler for .waves-effect element.
+ * returns null when .waves-effect element not in "click tree"
+ */
+ function getWavesEffectElement(e) {
+ if (TouchHandler.allowEvent(e) === false) {
+ return null;
+ }
+
+ var element = null;
+ var target = e.target || e.srcElement;
+
+ while (target.parentElement !== null) {
+ if (!(target instanceof SVGElement) && target.className.indexOf('waves-effect') !== -1) {
+ element = target;
+ break;
+ } else if (target.classList.contains('waves-effect')) {
+ element = target;
+ break;
+ }
+ target = target.parentElement;
+ }
+
+ return element;
+ }
+
+ /**
+ * Bubble the click and show effect if .waves-effect elem was found
+ */
+ function showEffect(e) {
+ var element = getWavesEffectElement(e);
+
+ if (element !== null) {
+ Effect.show(e, element);
+
+ if ('ontouchstart' in window) {
+ element.addEventListener('touchend', Effect.hide, false);
+ element.addEventListener('touchcancel', Effect.hide, false);
+ }
+
+ element.addEventListener('mouseup', Effect.hide, false);
+ element.addEventListener('mouseleave', Effect.hide, false);
+ }
+ }
+
+ Waves.displayEffect = function(options) {
+ options = options || {};
+
+ if ('duration' in options) {
+ Effect.duration = options.duration;
+ }
+
+ //Wrap input inside <i> tag
+ Effect.wrapInput($$('.waves-effect'));
+
+ if ('ontouchstart' in window) {
+ document.body.addEventListener('touchstart', showEffect, false);
+ }
+
+ document.body.addEventListener('mousedown', showEffect, false);
+ };
+
+ /**
+ * Attach Waves to an input element (or any element which doesn't
+ * bubble mouseup/mousedown events).
+ * Intended to be used with dynamically loaded forms/inputs, or
+ * where the user doesn't want a delegated click handler.
+ */
+ Waves.attach = function(element) {
+ //FUTURE: automatically add waves classes and allow users
+ // to specify them with an options param? Eg. light/classic/button
+ if (element.tagName.toLowerCase() === 'input') {
+ Effect.wrapInput([element]);
+ element = element.parentElement;
+ }
+
+ if ('ontouchstart' in window) {
+ element.addEventListener('touchstart', showEffect, false);
+ }
+
+ element.addEventListener('mousedown', showEffect, false);
+ };
+
+ window.Waves = Waves;
+
+ document.addEventListener('DOMContentLoaded', function() {
+ Waves.displayEffect();
+ }, false);
+
+})(window);
+;Materialize.toast = function (message, displayLength, className, completeCallback) {
+ className = className || "";
+
+ var container = document.getElementById('toast-container');
+
+ // Create toast container if it does not exist
+ if (container === null) {
+ // create notification container
+ container = document.createElement('div');
+ container.id = 'toast-container';
+ document.body.appendChild(container);
+ }
+
+ // Select and append toast
+ var newToast = createToast(message);
+
+ // only append toast if message is not undefined
+ if(message){
+ container.appendChild(newToast);
+ }
+
+ newToast.style.opacity = 0;
+
+ // Animate toast in
+ Vel(newToast, {translateY: '-35px', opacity: 1 }, {duration: 300,
+ easing: 'easeOutCubic',
+ queue: false});
+
+ // Allows timer to be pause while being panned
+ var timeLeft = displayLength;
+ var counterInterval;
+ if (timeLeft != null) {
+ counterInterval = setInterval (function(){
+ if (newToast.parentNode === null)
+ window.clearInterval(counterInterval);
+
+ // If toast is not being dragged, decrease its time remaining
+ if (!newToast.classList.contains('panning')) {
+ timeLeft -= 20;
+ }
+
+ if (timeLeft <= 0) {
+ // Animate toast out
+ Vel(newToast, {"opacity": 0, marginTop: '-40px'}, { duration: 375,
+ easing: 'easeOutExpo',
+ queue: false,
+ complete: function(){
+ // Call the optional callback
+ if(typeof(completeCallback) === "function")
+ completeCallback();
+ // Remove toast after it times out
+ this[0].parentNode.removeChild(this[0]);
+ }
+ });
+ window.clearInterval(counterInterval);
+ }
+ }, 20);
+ }
+
+
+
+ function createToast(html) {
+
+ // Create toast
+ var toast = document.createElement('div');
+ toast.classList.add('toast');
+ if (className) {
+ var classes = className.split(' ');
+
+ for (var i = 0, count = classes.length; i < count; i++) {
+ toast.classList.add(classes[i]);
+ }
+ }
+ // If type of parameter is HTML Element
+ if ( typeof HTMLElement === "object" ? html instanceof HTMLElement : html && typeof html === "object" && html !== null && html.nodeType === 1 && typeof html.nodeName==="string"
+) {
+ toast.appendChild(html);
+ }
+ else if (html instanceof jQuery) {
+ // Check if it is jQuery object
+ toast.appendChild(html[0]);
+ }
+ else {
+ // Insert as text;
+ toast.innerHTML = html;
+ }
+ // Bind hammer
+ var hammerHandler = new Hammer(toast, {prevent_default: false});
+ hammerHandler.on('pan', function(e) {
+ var deltaX = e.deltaX;
+ var activationDistance = 80;
+
+ // Change toast state
+ if (!toast.classList.contains('panning')){
+ toast.classList.add('panning');
+ }
+
+ var opacityPercent = 1-Math.abs(deltaX / activationDistance);
+ if (opacityPercent < 0)
+ opacityPercent = 0;
+
+ Vel(toast, {left: deltaX, opacity: opacityPercent }, {duration: 50, queue: false, easing: 'easeOutQuad'});
+
+ });
+
+ hammerHandler.on('panend', function(e) {
+ var deltaX = e.deltaX;
+ var activationDistance = 80;
+
+ // If toast dragged past activation point
+ if (Math.abs(deltaX) > activationDistance) {
+ Vel(toast, {marginTop: '-40px'}, { duration: 375,
+ easing: 'easeOutExpo',
+ queue: false,
+ complete: function(){
+ if(typeof(completeCallback) === "function") {
+ completeCallback();
+ }
+ toast.parentNode.removeChild(toast);
+ }
+ });
+
+ } else {
+ toast.classList.remove('panning');
+ // Put toast back into original position
+ Vel(toast, { left: 0, opacity: 1 }, { duration: 300,
+ easing: 'easeOutExpo',
+ queue: false
+ });
+
+ }
+ });
+
+ return toast;
+ }
+};
+;(function ($) {
+
+ var methods = {
+ init : function(options) {
+ var defaults = {
+ menuWidth: 300,
+ edge: 'left',
+ closeOnClick: false,
+ draggable: true
+ };
+ options = $.extend(defaults, options);
+
+ $(this).each(function(){
+ var $this = $(this);
+ var menuId = $this.attr('data-activates');
+ var menu = $("#"+ menuId);
+
+ // Set to width
+ if (options.menuWidth != 300) {
+ menu.css('width', options.menuWidth);
+ }
+
+ // Add Touch Area
+ var $dragTarget = $('.drag-target[data-sidenav="' + menuId + '"]');
+ if (options.draggable) {
+ // Regenerate dragTarget
+ if ($dragTarget.length) {
+ $dragTarget.remove();
+ }
+
+ $dragTarget = $('<div class="drag-target"></div>').attr('data-sidenav', menuId);
+ $('body').append($dragTarget);
+ } else {
+ $dragTarget = $();
+ }
+
+ if (options.edge == 'left') {
+ menu.css('transform', 'translateX(-100%)');
+ $dragTarget.css({'left': 0}); // Add Touch Area
+ }
+ else {
+ menu.addClass('right-aligned') // Change text-alignment to right
+ .css('transform', 'translateX(100%)');
+ $dragTarget.css({'right': 0}); // Add Touch Area
+ }
+
+ // If fixed sidenav, bring menu out
+ if (menu.hasClass('fixed')) {
+ if (window.innerWidth > 992) {
+ menu.css('transform', 'translateX(0)');
+ }
+ }
+
+ // Window resize to reset on large screens fixed
+ if (menu.hasClass('fixed')) {
+ $(window).resize( function() {
+ if (window.innerWidth > 992) {
+ // Close menu if window is resized bigger than 992 and user has fixed sidenav
+ if ($('#sidenav-overlay').length !== 0 && menuOut) {
+ removeMenu(true);
+ }
+ else {
+ // menu.removeAttr('style');
+ menu.css('transform', 'translateX(0%)');
+ // menu.css('width', options.menuWidth);
+ }
+ }
+ else if (menuOut === false){
+ if (options.edge === 'left') {
+ menu.css('transform', 'translateX(-100%)');
+ } else {
+ menu.css('transform', 'translateX(100%)');
+ }
+
+ }
+
+ });
+ }
+
+ // if closeOnClick, then add close event for all a tags in side sideNav
+ if (options.closeOnClick === true) {
+ menu.on("click.itemclick", "a:not(.collapsible-header)", function(){
+ removeMenu();
+ });
+ }
+
+ var removeMenu = function(restoreNav) {
+ panning = false;
+ menuOut = false;
+ // Reenable scrolling
+ $('body').css({
+ overflow: '',
+ width: ''
+ });
+
+ $('#sidenav-overlay').velocity({opacity: 0}, {duration: 200,
+ queue: false, easing: 'easeOutQuad',
+ complete: function() {
+ $(this).remove();
+ } });
+ if (options.edge === 'left') {
+ // Reset phantom div
+ $dragTarget.css({width: '', right: '', left: '0'});
+ menu.velocity(
+ {'translateX': '-100%'},
+ { duration: 200,
+ queue: false,
+ easing: 'easeOutCubic',
+ complete: function() {
+ if (restoreNav === true) {
+ // Restore Fixed sidenav
+ menu.removeAttr('style');
+ menu.css('width', options.menuWidth);
+ }
+ }
+
+ });
+ }
+ else {
+ // Reset phantom div
+ $dragTarget.css({width: '', right: '0', left: ''});
+ menu.velocity(
+ {'translateX': '100%'},
+ { duration: 200,
+ queue: false,
+ easing: 'easeOutCubic',
+ complete: function() {
+ if (restoreNav === true) {
+ // Restore Fixed sidenav
+ menu.removeAttr('style');
+ menu.css('width', options.menuWidth);
+ }
+ }
+ });
+ }
+ };
+
+
+
+ // Touch Event
+ var panning = false;
+ var menuOut = false;
+
+ if (options.draggable) {
+ $dragTarget.on('click', function(){
+ if (menuOut) {
+ removeMenu();
+ }
+ });
+
+ $dragTarget.hammer({
+ prevent_default: false
+ }).bind('pan', function(e) {
+
+ if (e.gesture.pointerType == "touch") {
+
+ var direction = e.gesture.direction;
+ var x = e.gesture.center.x;
+ var y = e.gesture.center.y;
+ var velocityX = e.gesture.velocityX;
+
+ // Disable Scrolling
+ var $body = $('body');
+ var $overlay = $('#sidenav-overlay');
+ var oldWidth = $body.innerWidth();
+ $body.css('overflow', 'hidden');
+ $body.width(oldWidth);
+
+ // If overlay does not exist, create one and if it is clicked, close menu
+ if ($overlay.length === 0) {
+ $overlay = $('<div id="sidenav-overlay"></div>');
+ $overlay.css('opacity', 0).click( function(){
+ removeMenu();
+ });
+ $('body').append($overlay);
+ }
+
+ // Keep within boundaries
+ if (options.edge === 'left') {
+ if (x > options.menuWidth) { x = options.menuWidth; }
+ else if (x < 0) { x = 0; }
+ }
+
+ if (options.edge === 'left') {
+ // Left Direction
+ if (x < (options.menuWidth / 2)) { menuOut = false; }
+ // Right Direction
+ else if (x >= (options.menuWidth / 2)) { menuOut = true; }
+ menu.css('transform', 'translateX(' + (x - options.menuWidth) + 'px)');
+ }
+ else {
+ // Left Direction
+ if (x < (window.innerWidth - options.menuWidth / 2)) {
+ menuOut = true;
+ }
+ // Right Direction
+ else if (x >= (window.innerWidth - options.menuWidth / 2)) {
+ menuOut = false;
+ }
+ var rightPos = (x - options.menuWidth / 2);
+ if (rightPos < 0) {
+ rightPos = 0;
+ }
+
+ menu.css('transform', 'translateX(' + rightPos + 'px)');
+ }
+
+
+ // Percentage overlay
+ var overlayPerc;
+ if (options.edge === 'left') {
+ overlayPerc = x / options.menuWidth;
+ $overlay.velocity({opacity: overlayPerc }, {duration: 10, queue: false, easing: 'easeOutQuad'});
+ }
+ else {
+ overlayPerc = Math.abs((x - window.innerWidth) / options.menuWidth);
+ $overlay.velocity({opacity: overlayPerc }, {duration: 10, queue: false, easing: 'easeOutQuad'});
+ }
+ }
+
+ }).bind('panend', function(e) {
+
+ if (e.gesture.pointerType == "touch") {
+ var $overlay = $('<div id="sidenav-overlay"></div>');
+ var velocityX = e.gesture.velocityX;
+ var x = e.gesture.center.x;
+ var leftPos = x - options.menuWidth;
+ var rightPos = x - options.menuWidth / 2;
+ if (leftPos > 0 ) {
+ leftPos = 0;
+ }
+ if (rightPos < 0) {
+ rightPos = 0;
+ }
+ panning = false;
+
+ if (options.edge === 'left') {
+ // If velocityX <= 0.3 then the user is flinging the menu closed so ignore menuOut
+ if ((menuOut && velocityX <= 0.3) || velocityX < -0.5) {
+ // Return menu to open
+ if (leftPos !== 0) {
+ menu.velocity({'translateX': [0, leftPos]}, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+
+ $overlay.velocity({opacity: 1 }, {duration: 50, queue: false, easing: 'easeOutQuad'});
+ $dragTarget.css({width: '50%', right: 0, left: ''});
+ menuOut = true;
+ }
+ else if (!menuOut || velocityX > 0.3) {
+ // Enable Scrolling
+ $('body').css({
+ overflow: '',
+ width: ''
+ });
+ // Slide menu closed
+ menu.velocity({'translateX': [-1 * options.menuWidth - 10, leftPos]}, {duration: 200, queue: false, easing: 'easeOutQuad'});
+ $overlay.velocity({opacity: 0 }, {duration: 200, queue: false, easing: 'easeOutQuad',
+ complete: function () {
+ $(this).remove();
+ }});
+ $dragTarget.css({width: '10px', right: '', left: 0});
+ }
+ }
+ else {
+ if ((menuOut && velocityX >= -0.3) || velocityX > 0.5) {
+ // Return menu to open
+ if (rightPos !== 0) {
+ menu.velocity({'translateX': [0, rightPos]}, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+
+ $overlay.velocity({opacity: 1 }, {duration: 50, queue: false, easing: 'easeOutQuad'});
+ $dragTarget.css({width: '50%', right: '', left: 0});
+ menuOut = true;
+ }
+ else if (!menuOut || velocityX < -0.3) {
+ // Enable Scrolling
+ $('body').css({
+ overflow: '',
+ width: ''
+ });
+
+ // Slide menu closed
+ menu.velocity({'translateX': [options.menuWidth + 10, rightPos]}, {duration: 200, queue: false, easing: 'easeOutQuad'});
+ $overlay.velocity({opacity: 0 }, {duration: 200, queue: false, easing: 'easeOutQuad',
+ complete: function () {
+ $(this).remove();
+ }});
+ $dragTarget.css({width: '10px', right: 0, left: ''});
+ }
+ }
+
+ }
+ });
+ }
+
+ $this.off('click.sidenav').on('click.sidenav', function() {
+ if (menuOut === true) {
+ menuOut = false;
+ panning = false;
+ removeMenu();
+ }
+ else {
+
+ // Disable Scrolling
+ var $body = $('body');
+ var $overlay = $('<div id="sidenav-overlay"></div>');
+ var oldWidth = $body.innerWidth();
+ $body.css('overflow', 'hidden');
+ $body.width(oldWidth);
+
+ // Push current drag target on top of DOM tree
+ $('body').append($dragTarget);
+
+ if (options.edge === 'left') {
+ $dragTarget.css({width: '50%', right: 0, left: ''});
+ menu.velocity({'translateX': [0, -1 * options.menuWidth]}, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+ else {
+ $dragTarget.css({width: '50%', right: '', left: 0});
+ menu.velocity({'translateX': [0, options.menuWidth]}, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+
+ $overlay.css('opacity', 0)
+ .click(function(){
+ menuOut = false;
+ panning = false;
+ removeMenu();
+ $overlay.velocity({opacity: 0}, {duration: 300, queue: false, easing: 'easeOutQuad',
+ complete: function() {
+ $(this).remove();
+ } });
+
+ });
+ $('body').append($overlay);
+ $overlay.velocity({opacity: 1}, {duration: 300, queue: false, easing: 'easeOutQuad',
+ complete: function () {
+ menuOut = true;
+ panning = false;
+ }
+ });
+ }
+
+ return false;
+ });
+ });
+
+
+ },
+ destroy: function () {
+ var $overlay = $('#sidenav-overlay');
+ var $dragTarget = $('.drag-target[data-sidenav="' + $(this).attr('data-activates') + '"]');
+ $overlay.trigger('click');
+ $dragTarget.remove();
+ $(this).off('click');
+ $overlay.remove();
+ },
+ show : function() {
+ this.trigger('click');
+ },
+ hide : function() {
+ $('#sidenav-overlay').trigger('click');
+ }
+ };
+
+
+ $.fn.sideNav = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.sideNav' );
+ }
+ }; // Plugin end
+}( jQuery ));
+;/**
+ * Extend jquery with a scrollspy plugin.
+ * This watches the window scroll and fires events when elements are scrolled into viewport.
+ *
+ * throttle() and getTime() taken from Underscore.js
+ * https://github.com/jashkenas/underscore
+ *
+ * @author Copyright 2013 John Smart
+ * @license https://raw.github.com/thesmart/jquery-scrollspy/master/LICENSE
+ * @see https://github.com/thesmart
+ * @version 0.1.2
+ */
+(function($) {
+
+ var jWindow = $(window);
+ var elements = [];
+ var elementsInView = [];
+ var isSpying = false;
+ var ticks = 0;
+ var unique_id = 1;
+ var offset = {
+ top : 0,
+ right : 0,
+ bottom : 0,
+ left : 0,
+ }
+
+ /**
+ * Find elements that are within the boundary
+ * @param {number} top
+ * @param {number} right
+ * @param {number} bottom
+ * @param {number} left
+ * @return {jQuery} A collection of elements
+ */
+ function findElements(top, right, bottom, left) {
+ var hits = $();
+ $.each(elements, function(i, element) {
+ if (element.height() > 0) {
+ var elTop = element.offset().top,
+ elLeft = element.offset().left,
+ elRight = elLeft + element.width(),
+ elBottom = elTop + element.height();
+
+ var isIntersect = !(elLeft > right ||
+ elRight < left ||
+ elTop > bottom ||
+ elBottom < top);
+
+ if (isIntersect) {
+ hits.push(element);
+ }
+ }
+ });
+
+ return hits;
+ }
+
+
+ /**
+ * Called when the user scrolls the window
+ */
+ function onScroll(scrollOffset) {
+ // unique tick id
+ ++ticks;
+
+ // viewport rectangle
+ var top = jWindow.scrollTop(),
+ left = jWindow.scrollLeft(),
+ right = left + jWindow.width(),
+ bottom = top + jWindow.height();
+
+ // determine which elements are in view
+ var intersections = findElements(top+offset.top + scrollOffset || 200, right+offset.right, bottom+offset.bottom, left+offset.left);
+ $.each(intersections, function(i, element) {
+
+ var lastTick = element.data('scrollSpy:ticks');
+ if (typeof lastTick != 'number') {
+ // entered into view
+ element.triggerHandler('scrollSpy:enter');
+ }
+
+ // update tick id
+ element.data('scrollSpy:ticks', ticks);
+ });
+
+ // determine which elements are no longer in view
+ $.each(elementsInView, function(i, element) {
+ var lastTick = element.data('scrollSpy:ticks');
+ if (typeof lastTick == 'number' && lastTick !== ticks) {
+ // exited from view
+ element.triggerHandler('scrollSpy:exit');
+ element.data('scrollSpy:ticks', null);
+ }
+ });
+
+ // remember elements in view for next tick
+ elementsInView = intersections;
+ }
+
+ /**
+ * Called when window is resized
+ */
+ function onWinSize() {
+ jWindow.trigger('scrollSpy:winSize');
+ }
+
+
+ /**
+ * Enables ScrollSpy using a selector
+ * @param {jQuery|string} selector The elements collection, or a selector
+ * @param {Object=} options Optional.
+ throttle : number -> scrollspy throttling. Default: 100 ms
+ offsetTop : number -> offset from top. Default: 0
+ offsetRight : number -> offset from right. Default: 0
+ offsetBottom : number -> offset from bottom. Default: 0
+ offsetLeft : number -> offset from left. Default: 0
+ * @returns {jQuery}
+ */
+ $.scrollSpy = function(selector, options) {
+ var defaults = {
+ throttle: 100,
+ scrollOffset: 200 // offset - 200 allows elements near bottom of page to scroll
+ };
+ options = $.extend(defaults, options);
+
+ var visible = [];
+ selector = $(selector);
+ selector.each(function(i, element) {
+ elements.push($(element));
+ $(element).data("scrollSpy:id", i);
+ // Smooth scroll to section
+ $('a[href="#' + $(element).attr('id') + '"]').click(function(e) {
+ e.preventDefault();
+ var offset = $(Materialize.escapeHash(this.hash)).offset().top + 1;
+ $('html, body').animate({ scrollTop: offset - options.scrollOffset }, {duration: 400, queue: false, easing: 'easeOutCubic'});
+ });
+ });
+
+ offset.top = options.offsetTop || 0;
+ offset.right = options.offsetRight || 0;
+ offset.bottom = options.offsetBottom || 0;
+ offset.left = options.offsetLeft || 0;
+
+ var throttledScroll = Materialize.throttle(function() {
+ onScroll(options.scrollOffset);
+ }, options.throttle || 100);
+ var readyScroll = function(){
+ $(document).ready(throttledScroll);
+ };
+
+ if (!isSpying) {
+ jWindow.on('scroll', readyScroll);
+ jWindow.on('resize', readyScroll);
+ isSpying = true;
+ }
+
+ // perform a scan once, after current execution context, and after dom is ready
+ setTimeout(readyScroll, 0);
+
+
+ selector.on('scrollSpy:enter', function() {
+ visible = $.grep(visible, function(value) {
+ return value.height() != 0;
+ });
+
+ var $this = $(this);
+
+ if (visible[0]) {
+ $('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
+ if ($this.data('scrollSpy:id') < visible[0].data('scrollSpy:id')) {
+ visible.unshift($(this));
+ }
+ else {
+ visible.push($(this));
+ }
+ }
+ else {
+ visible.push($(this));
+ }
+
+
+ $('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
+ });
+ selector.on('scrollSpy:exit', function() {
+ visible = $.grep(visible, function(value) {
+ return value.height() != 0;
+ });
+
+ if (visible[0]) {
+ $('a[href="#' + visible[0].attr('id') + '"]').removeClass('active');
+ var $this = $(this);
+ visible = $.grep(visible, function(value) {
+ return value.attr('id') != $this.attr('id');
+ });
+ if (visible[0]) { // Check if empty
+ $('a[href="#' + visible[0].attr('id') + '"]').addClass('active');
+ }
+ }
+ });
+
+ return selector;
+ };
+
+ /**
+ * Listen for window resize events
+ * @param {Object=} options Optional. Set { throttle: number } to change throttling. Default: 100 ms
+ * @returns {jQuery} $(window)
+ */
+ $.winSizeSpy = function(options) {
+ $.winSizeSpy = function() { return jWindow; }; // lock from multiple calls
+ options = options || {
+ throttle: 100
+ };
+ return jWindow.on('resize', Materialize.throttle(onWinSize, options.throttle || 100));
+ };
+
+ /**
+ * Enables ScrollSpy on a collection of elements
+ * e.g. $('.scrollSpy').scrollSpy()
+ * @param {Object=} options Optional.
+ throttle : number -> scrollspy throttling. Default: 100 ms
+ offsetTop : number -> offset from top. Default: 0
+ offsetRight : number -> offset from right. Default: 0
+ offsetBottom : number -> offset from bottom. Default: 0
+ offsetLeft : number -> offset from left. Default: 0
+ * @returns {jQuery}
+ */
+ $.fn.scrollSpy = function(options) {
+ return $.scrollSpy($(this), options);
+ };
+
+})(jQuery);
+;(function ($) {
+ $(document).ready(function() {
+
+ // Function to update labels of text fields
+ Materialize.updateTextFields = function() {
+ var input_selector = 'input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], textarea';
+ $(input_selector).each(function(index, element) {
+ var $this = $(this);
+ if ($(element).val().length > 0 || element.autofocus || $this.attr('placeholder') !== undefined) {
+ $this.siblings('label').addClass('active');
+ } else if ($(element)[0].validity) {
+ $this.siblings('label').toggleClass('active', $(element)[0].validity.badInput === true);
+ } else {
+ $this.siblings('label').removeClass('active');
+ }
+ });
+ };
+
+ // Text based inputs
+ var input_selector = 'input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], textarea';
+
+ // Add active if form auto complete
+ $(document).on('change', input_selector, function () {
+ if($(this).val().length !== 0 || $(this).attr('placeholder') !== undefined) {
+ $(this).siblings('label').addClass('active');
+ }
+ validate_field($(this));
+ });
+
+ // Add active if input element has been pre-populated on document ready
+ $(document).ready(function() {
+ Materialize.updateTextFields();
+ });
+
+ // HTML DOM FORM RESET handling
+ $(document).on('reset', function(e) {
+ var formReset = $(e.target);
+ if (formReset.is('form')) {
+ formReset.find(input_selector).removeClass('valid').removeClass('invalid');
+ formReset.find(input_selector).each(function () {
+ if ($(this).attr('value') === '') {
+ $(this).siblings('label').removeClass('active');
+ }
+ });
+
+ // Reset select
+ formReset.find('select.initialized').each(function () {
+ var reset_text = formReset.find('option[selected]').text();
+ formReset.siblings('input.select-dropdown').val(reset_text);
+ });
+ }
+ });
+
+ // Add active when element has focus
+ $(document).on('focus', input_selector, function () {
+ $(this).siblings('label, .prefix').addClass('active');
+ });
+
+ $(document).on('blur', input_selector, function () {
+ var $inputElement = $(this);
+ var selector = ".prefix";
+
+ if ($inputElement.val().length === 0 && $inputElement[0].validity.badInput !== true && $inputElement.attr('placeholder') === undefined) {
+ selector += ", label";
+ }
+
+ $inputElement.siblings(selector).removeClass('active');
+
+ validate_field($inputElement);
+ });
+
+ window.validate_field = function(object) {
+ var hasLength = object.attr('data-length') !== undefined;
+ var lenAttr = parseInt(object.attr('data-length'));
+ var len = object.val().length;
+
+ if (object.val().length === 0 && object[0].validity.badInput === false) {
+ if (object.hasClass('validate')) {
+ object.removeClass('valid');
+ object.removeClass('invalid');
+ }
+ }
+ else {
+ if (object.hasClass('validate')) {
+ // Check for character counter attributes
+ if ((object.is(':valid') && hasLength && (len <= lenAttr)) || (object.is(':valid') && !hasLength)) {
+ object.removeClass('invalid');
+ object.addClass('valid');
+ }
+ else {
+ object.removeClass('valid');
+ object.addClass('invalid');
+ }
+ }
+ }
+ };
+
+ // Radio and Checkbox focus class
+ var radio_checkbox = 'input[type=radio], input[type=checkbox]';
+ $(document).on('keyup.radio', radio_checkbox, function(e) {
+ // TAB, check if tabbing to radio or checkbox.
+ if (e.which === 9) {
+ $(this).addClass('tabbed');
+ var $this = $(this);
+ $this.one('blur', function(e) {
+
+ $(this).removeClass('tabbed');
+ });
+ return;
+ }
+ });
+
+ // Textarea Auto Resize
+ var hiddenDiv = $('.hiddendiv').first();
+ if (!hiddenDiv.length) {
+ hiddenDiv = $('<div class="hiddendiv common"></div>');
+ $('body').append(hiddenDiv);
+ }
+ var text_area_selector = '.materialize-textarea';
+
+ function textareaAutoResize($textarea) {
+ // Set font properties of hiddenDiv
+
+ var fontFamily = $textarea.css('font-family');
+ var fontSize = $textarea.css('font-size');
+ var lineHeight = $textarea.css('line-height');
+
+ if (fontSize) { hiddenDiv.css('font-size', fontSize); }
+ if (fontFamily) { hiddenDiv.css('font-family', fontFamily); }
+ if (lineHeight) { hiddenDiv.css('line-height', lineHeight); }
+
+ if ($textarea.attr('wrap') === "off") {
+ hiddenDiv.css('overflow-wrap', "normal")
+ .css('white-space', "pre");
+ }
+
+ hiddenDiv.text($textarea.val() + '\n');
+ var content = hiddenDiv.html().replace(/\n/g, '<br>');
+ hiddenDiv.html(content);
+
+
+ // When textarea is hidden, width goes crazy.
+ // Approximate with half of window size
+
+ if ($textarea.is(':visible')) {
+ hiddenDiv.css('width', $textarea.width());
+ }
+ else {
+ hiddenDiv.css('width', $(window).width()/2);
+ }
+
+ $textarea.css('height', hiddenDiv.height());
+ }
+
+ $(text_area_selector).each(function () {
+ var $textarea = $(this);
+ if ($textarea.val().length) {
+ textareaAutoResize($textarea);
+ }
+ });
+
+ $('body').on('keyup keydown autoresize', text_area_selector, function () {
+ textareaAutoResize($(this));
+ });
+
+ // File Input Path
+ $(document).on('change', '.file-field input[type="file"]', function () {
+ var file_field = $(this).closest('.file-field');
+ var path_input = file_field.find('input.file-path');
+ var files = $(this)[0].files;
+ var file_names = [];
+ for (var i = 0; i < files.length; i++) {
+ file_names.push(files[i].name);
+ }
+ path_input.val(file_names.join(", "));
+ path_input.trigger('change');
+ });
+
+ /****************
+ * Range Input *
+ ****************/
+
+ var range_type = 'input[type=range]';
+ var range_mousedown = false;
+ var left;
+
+ $(range_type).each(function () {
+ var thumb = $('<span class="thumb"><span class="value"></span></span>');
+ $(this).after(thumb);
+ });
+
+ var range_wrapper = '.range-field';
+ $(document).on('change', range_type, function(e) {
+ var thumb = $(this).siblings('.thumb');
+ thumb.find('.value').html($(this).val());
+ });
+
+ $(document).on('input mousedown touchstart', range_type, function(e) {
+ var thumb = $(this).siblings('.thumb');
+ var width = $(this).outerWidth();
+
+ // If thumb indicator does not exist yet, create it
+ if (thumb.length <= 0) {
+ thumb = $('<span class="thumb"><span class="value"></span></span>');
+ $(this).after(thumb);
+ }
+
+ // Set indicator value
+ thumb.find('.value').html($(this).val());
+
+ range_mousedown = true;
+ $(this).addClass('active');
+
+ if (!thumb.hasClass('active')) {
+ thumb.velocity({ height: "30px", width: "30px", top: "-20px", marginLeft: "-15px"}, { duration: 300, easing: 'easeOutExpo' });
+ }
+
+ if (e.type !== 'input') {
+ if(e.pageX === undefined || e.pageX === null){//mobile
+ left = e.originalEvent.touches[0].pageX - $(this).offset().left;
+ }
+ else{ // desktop
+ left = e.pageX - $(this).offset().left;
+ }
+ if (left < 0) {
+ left = 0;
+ }
+ else if (left > width) {
+ left = width;
+ }
+ thumb.addClass('active').css('left', left);
+ }
+
+ thumb.find('.value').html($(this).val());
+ });
+
+ $(document).on('mouseup touchend', range_wrapper, function() {
+ range_mousedown = false;
+ $(this).removeClass('active');
+ });
+
+ $(document).on('mousemove touchmove', range_wrapper, function(e) {
+ var thumb = $(this).children('.thumb');
+ var left;
+ if (range_mousedown) {
+ if (!thumb.hasClass('active')) {
+ thumb.velocity({ height: '30px', width: '30px', top: '-20px', marginLeft: '-15px'}, { duration: 300, easing: 'easeOutExpo' });
+ }
+ if (e.pageX === undefined || e.pageX === null) { //mobile
+ left = e.originalEvent.touches[0].pageX - $(this).offset().left;
+ }
+ else{ // desktop
+ left = e.pageX - $(this).offset().left;
+ }
+ var width = $(this).outerWidth();
+
+ if (left < 0) {
+ left = 0;
+ }
+ else if (left > width) {
+ left = width;
+ }
+ thumb.addClass('active').css('left', left);
+ thumb.find('.value').html(thumb.siblings(range_type).val());
+ }
+ });
+
+ $(document).on('mouseout touchleave', range_wrapper, function() {
+ if (!range_mousedown) {
+
+ var thumb = $(this).children('.thumb');
+
+ if (thumb.hasClass('active')) {
+ thumb.velocity({ height: '0', width: '0', top: '10px', marginLeft: '-6px'}, { duration: 100 });
+ }
+ thumb.removeClass('active');
+ }
+ });
+
+ /**************************
+ * Auto complete plugin *
+ *************************/
+ $.fn.autocomplete = function (options) {
+ // Defaults
+ var defaults = {
+ data: {},
+ limit: Infinity,
+ onAutocomplete: null
+ };
+
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+ var $input = $(this);
+ var data = options.data,
+ count = 0,
+ activeIndex = 0,
+ oldVal,
+ $inputDiv = $input.closest('.input-field'); // Div to append on
+
+ // Check if data isn't empty
+ if (!$.isEmptyObject(data)) {
+ var $autocomplete = $('<ul class="autocomplete-content dropdown-content"></ul>');
+ var $oldAutocomplete;
+
+ // Append autocomplete element.
+ // Prevent double structure init.
+ if ($inputDiv.length) {
+ $oldAutocomplete = $inputDiv.children('.autocomplete-content.dropdown-content').first();
+ if (!$oldAutocomplete.length) {
+ $inputDiv.append($autocomplete); // Set ul in body
+ }
+ } else {
+ $oldAutocomplete = $input.next('.autocomplete-content.dropdown-content');
+ if (!$oldAutocomplete.length) {
+ $input.after($autocomplete);
+ }
+ }
+ if ($oldAutocomplete.length) {
+ $autocomplete = $oldAutocomplete;
+ }
+
+ // Highlight partial match.
+ var highlight = function(string, $el) {
+ var img = $el.find('img');
+ var matchStart = $el.text().toLowerCase().indexOf("" + string.toLowerCase() + ""),
+ matchEnd = matchStart + string.length - 1,
+ beforeMatch = $el.text().slice(0, matchStart),
+ matchText = $el.text().slice(matchStart, matchEnd + 1),
+ afterMatch = $el.text().slice(matchEnd + 1);
+ $el.html("<span>" + beforeMatch + "<span class='highlight'>" + matchText + "</span>" + afterMatch + "</span>");
+ if (img.length) {
+ $el.prepend(img);
+ }
+ };
+
+ // Reset current element position
+ var resetCurrentElement = function() {
+ activeIndex = 0;
+ $autocomplete.find('.active').removeClass('active');
+ }
+
+ // Perform search
+ $input.off('keyup.autocomplete').on('keyup.autocomplete', function (e) {
+ // Reset count.
+ count = 0;
+
+ // Don't capture enter or arrow key usage.
+ if (e.which === 13 ||
+ e.which === 38 ||
+ e.which === 40) {
+ return;
+ }
+
+ var val = $input.val().toLowerCase();
+
+ // Check if the input isn't empty
+ if (oldVal !== val) {
+ $autocomplete.empty();
+ resetCurrentElement();
+
+ if (val !== '') {
+ for(var key in data) {
+ if (data.hasOwnProperty(key) &&
+ key.toLowerCase().indexOf(val) !== -1 &&
+ key.toLowerCase() !== val) {
+ // Break if past limit
+ if (count >= options.limit) {
+ break;
+ }
+
+ var autocompleteOption = $('<li></li>');
+ if (!!data[key]) {
+ autocompleteOption.append('<img src="'+ data[key] +'" class="right circle"><span>'+ key +'</span>');
+ } else {
+ autocompleteOption.append('<span>'+ key +'</span>');
+ }
+
+ $autocomplete.append(autocompleteOption);
+ highlight(val, autocompleteOption);
+ count++;
+ }
+ }
+ }
+ }
+
+ // Update oldVal
+ oldVal = val;
+ });
+
+ $input.off('keydown.autocomplete').on('keydown.autocomplete', function (e) {
+ // Arrow keys and enter key usage
+ var keyCode = e.which,
+ liElement,
+ numItems = $autocomplete.children('li').length,
+ $active = $autocomplete.children('.active').first();
+
+ // select element on Enter
+ if (keyCode === 13) {
+ liElement = $autocomplete.children('li').eq(activeIndex);
+ if (liElement.length) {
+ liElement.click();
+ e.preventDefault();
+ }
+ return;
+ }
+
+ // Capture up and down key
+ if ( keyCode === 38 || keyCode === 40 ) {
+ e.preventDefault();
+
+ if (keyCode === 38 &&
+ activeIndex > 0) {
+ activeIndex--;
+ }
+
+ if (keyCode === 40 &&
+ activeIndex < (numItems - 1) &&
+ $active.length) {
+ activeIndex++;
+ }
+
+ $active.removeClass('active');
+ $autocomplete.children('li').eq(activeIndex).addClass('active');
+ }
+ });
+
+ // Set input value
+ $autocomplete.on('click', 'li', function () {
+ var text = $(this).text().trim();
+ $input.val(text);
+ $input.trigger('change');
+ $autocomplete.empty();
+ resetCurrentElement();
+
+ // Handle onAutocomplete callback.
+ if (typeof(options.onAutocomplete) === "function") {
+ options.onAutocomplete.call(this, text);
+ }
+ });
+ }
+ });
+ };
+
+ }); // End of $(document).ready
+
+ /*******************
+ * Select Plugin *
+ ******************/
+ $.fn.material_select = function (callback) {
+ $(this).each(function(){
+ var $select = $(this);
+
+ if ($select.hasClass('browser-default')) {
+ return; // Continue to next (return false breaks out of entire loop)
+ }
+
+ var multiple = $select.attr('multiple') ? true : false,
+ lastID = $select.data('select-id'); // Tear down structure if Select needs to be rebuilt
+
+ if (lastID) {
+ $select.parent().find('span.caret').remove();
+ $select.parent().find('input').remove();
+
+ $select.unwrap();
+ $('ul#select-options-'+lastID).remove();
+ }
+
+ // If destroying the select, remove the selelct-id and reset it to it's uninitialized state.
+ if(callback === 'destroy') {
+ $select.data('select-id', null).removeClass('initialized');
+ return;
+ }
+
+ var uniqueID = Materialize.guid();
+ $select.data('select-id', uniqueID);
+ var wrapper = $('<div class="select-wrapper"></div>');
+ wrapper.addClass($select.attr('class'));
+ var options = $('<ul id="select-options-' + uniqueID +'" class="dropdown-content select-dropdown ' + (multiple ? 'multiple-select-dropdown' : '') + '"></ul>'),
+ selectChildren = $select.children('option, optgroup'),
+ valuesSelected = [],
+ optionsHover = false;
+
+ var label = $select.find('option:selected').html() || $select.find('option:first').html() || "";
+
+ // Function that renders and appends the option taking into
+ // account type and possible image icon.
+ var appendOptionWithIcon = function(select, option, type) {
+ // Add disabled attr if disabled
+ var disabledClass = (option.is(':disabled')) ? 'disabled ' : '';
+ var optgroupClass = (type === 'optgroup-option') ? 'optgroup-option ' : '';
+
+ // add icons
+ var icon_url = option.data('icon');
+ var classes = option.attr('class');
+ if (!!icon_url) {
+ var classString = '';
+ if (!!classes) classString = ' class="' + classes + '"';
+
+ // Check for multiple type.
+ if (type === 'multiple') {
+ options.append($('<li class="' + disabledClass + '"><img alt="" src="' + icon_url + '"' + classString + '><span><input type="checkbox"' + disabledClass + '/><label></label>' + option.html() + '</span></li>'));
+ } else {
+ options.append($('<li class="' + disabledClass + optgroupClass + '"><img alt="" src="' + icon_url + '"' + classString + '><span>' + option.html() + '</span></li>'));
+ }
+ return true;
+ }
+
+ // Check for multiple type.
+ if (type === 'multiple') {
+ options.append($('<li class="' + disabledClass + '"><span><input type="checkbox"' + disabledClass + '/><label></label>' + option.html() + '</span></li>'));
+ } else {
+ options.append($('<li class="' + disabledClass + optgroupClass + '"><span>' + option.html() + '</span></li>'));
+ }
+ };
+
+ /* Create dropdown structure. */
+ if (selectChildren.length) {
+ selectChildren.each(function() {
+ if ($(this).is('option')) {
+ // Direct descendant option.
+ if (multiple) {
+ appendOptionWithIcon($select, $(this), 'multiple');
+
+ } else {
+ appendOptionWithIcon($select, $(this));
+ }
+ } else if ($(this).is('optgroup')) {
+ // Optgroup.
+ var selectOptions = $(this).children('option');
+ options.append($('<li class="optgroup"><span>' + $(this).attr('label') + '</span></li>'));
+
+ selectOptions.each(function() {
+ appendOptionWithIcon($select, $(this), 'optgroup-option');
+ });
+ }
+ });
+ }
+
+ options.find('li:not(.optgroup)').each(function (i) {
+ $(this).click(function (e) {
+ // Check if option element is disabled
+ if (!$(this).hasClass('disabled') && !$(this).hasClass('optgroup')) {
+ var selected = true;
+
+ if (multiple) {
+ $('input[type="checkbox"]', this).prop('checked', function(i, v) { return !v; });
+ selected = toggleEntryFromArray(valuesSelected, $(this).index(), $select);
+ $newSelect.trigger('focus');
+ } else {
+ options.find('li').removeClass('active');
+ $(this).toggleClass('active');
+ $newSelect.val($(this).text());
+ }
+
+ activateOption(options, $(this));
+ $select.find('option').eq(i).prop('selected', selected);
+ // Trigger onchange() event
+ $select.trigger('change');
+ if (typeof callback !== 'undefined') callback();
+ }
+
+ e.stopPropagation();
+ });
+ });
+
+ // Wrap Elements
+ $select.wrap(wrapper);
+ // Add Select Display Element
+ var dropdownIcon = $('<span class="caret">&#9660;</span>');
+ if ($select.is(':disabled'))
+ dropdownIcon.addClass('disabled');
+
+ // escape double quotes
+ var sanitizedLabelHtml = label.replace(/"/g, '&quot;');
+
+ var $newSelect = $('<input type="text" class="select-dropdown" readonly="true" ' + (($select.is(':disabled')) ? 'disabled' : '') + ' data-activates="select-options-' + uniqueID +'" value="'+ sanitizedLabelHtml +'"/>');
+ $select.before($newSelect);
+ $newSelect.before(dropdownIcon);
+
+ $newSelect.after(options);
+ // Check if section element is disabled
+ if (!$select.is(':disabled')) {
+ $newSelect.dropdown({'hover': false, 'closeOnClick': false});
+ }
+
+ // Copy tabindex
+ if ($select.attr('tabindex')) {
+ $($newSelect[0]).attr('tabindex', $select.attr('tabindex'));
+ }
+
+ $select.addClass('initialized');
+
+ $newSelect.on({
+ 'focus': function (){
+ if ($('ul.select-dropdown').not(options[0]).is(':visible')) {
+ $('input.select-dropdown').trigger('close');
+ }
+ if (!options.is(':visible')) {
+ $(this).trigger('open', ['focus']);
+ var label = $(this).val();
+ if (multiple && label.indexOf(',') >= 0) {
+ label = label.split(',')[0];
+ }
+
+ var selectedOption = options.find('li').filter(function() {
+ return $(this).text().toLowerCase() === label.toLowerCase();
+ })[0];
+ activateOption(options, selectedOption, true);
+ }
+ },
+ 'click': function (e){
+ e.stopPropagation();
+ }
+ });
+
+ $newSelect.on('blur', function() {
+ if (!multiple) {
+ $(this).trigger('close');
+ }
+ options.find('li.selected').removeClass('selected');
+ });
+
+ options.hover(function() {
+ optionsHover = true;
+ }, function () {
+ optionsHover = false;
+ });
+
+ $(window).on({
+ 'click': function () {
+ multiple && (optionsHover || $newSelect.trigger('close'));
+ }
+ });
+
+ // Add initial multiple selections.
+ if (multiple) {
+ $select.find("option:selected:not(:disabled)").each(function () {
+ var index = $(this).index();
+
+ toggleEntryFromArray(valuesSelected, index, $select);
+ options.find("li").eq(index).find(":checkbox").prop("checked", true);
+ });
+ }
+
+ /**
+ * Make option as selected and scroll to selected position
+ * @param {jQuery} collection Select options jQuery element
+ * @param {Element} newOption element of the new option
+ * @param {Boolean} firstActivation If on first activation of select
+ */
+ var activateOption = function(collection, newOption, firstActivation) {
+ if (newOption) {
+ collection.find('li.selected').removeClass('selected');
+ var option = $(newOption);
+ option.addClass('selected');
+ if (!multiple || !!firstActivation) {
+ options.scrollTo(option);
+ }
+ }
+ };
+
+ // Allow user to search by typing
+ // this array is cleared after 1 second
+ var filterQuery = [],
+ onKeyDown = function(e){
+ // TAB - switch to another input
+ if(e.which == 9){
+ $newSelect.trigger('close');
+ return;
+ }
+
+ // ARROW DOWN WHEN SELECT IS CLOSED - open select options
+ if(e.which == 40 && !options.is(':visible')){
+ $newSelect.trigger('open');
+ return;
+ }
+
+ // ENTER WHEN SELECT IS CLOSED - submit form
+ if(e.which == 13 && !options.is(':visible')){
+ return;
+ }
+
+ e.preventDefault();
+
+ // CASE WHEN USER TYPE LETTERS
+ var letter = String.fromCharCode(e.which).toLowerCase(),
+ nonLetters = [9,13,27,38,40];
+ if (letter && (nonLetters.indexOf(e.which) === -1)) {
+ filterQuery.push(letter);
+
+ var string = filterQuery.join(''),
+ newOption = options.find('li').filter(function() {
+ return $(this).text().toLowerCase().indexOf(string) === 0;
+ })[0];
+
+ if (newOption) {
+ activateOption(options, newOption);
+ }
+ }
+
+ // ENTER - select option and close when select options are opened
+ if (e.which == 13) {
+ var activeOption = options.find('li.selected:not(.disabled)')[0];
+ if(activeOption){
+ $(activeOption).trigger('click');
+ if (!multiple) {
+ $newSelect.trigger('close');
+ }
+ }
+ }
+
+ // ARROW DOWN - move to next not disabled option
+ if (e.which == 40) {
+ if (options.find('li.selected').length) {
+ newOption = options.find('li.selected').next('li:not(.disabled)')[0];
+ } else {
+ newOption = options.find('li:not(.disabled)')[0];
+ }
+ activateOption(options, newOption);
+ }
+
+ // ESC - close options
+ if (e.which == 27) {
+ $newSelect.trigger('close');
+ }
+
+ // ARROW UP - move to previous not disabled option
+ if (e.which == 38) {
+ newOption = options.find('li.selected').prev('li:not(.disabled)')[0];
+ if(newOption)
+ activateOption(options, newOption);
+ }
+
+ // Automaticaly clean filter query so user can search again by starting letters
+ setTimeout(function(){ filterQuery = []; }, 1000);
+ };
+
+ $newSelect.on('keydown', onKeyDown);
+ });
+
+ function toggleEntryFromArray(entriesArray, entryIndex, select) {
+ var index = entriesArray.indexOf(entryIndex),
+ notAdded = index === -1;
+
+ if (notAdded) {
+ entriesArray.push(entryIndex);
+ } else {
+ entriesArray.splice(index, 1);
+ }
+
+ select.siblings('ul.dropdown-content').find('li').eq(entryIndex).toggleClass('active');
+
+ // use notAdded instead of true (to detect if the option is selected or not)
+ select.find('option').eq(entryIndex).prop('selected', notAdded);
+ setValueToInput(entriesArray, select);
+
+ return notAdded;
+ }
+
+ function setValueToInput(entriesArray, select) {
+ var value = '';
+
+ for (var i = 0, count = entriesArray.length; i < count; i++) {
+ var text = select.find('option').eq(entriesArray[i]).text();
+
+ i === 0 ? value += text : value += ', ' + text;
+ }
+
+ if (value === '') {
+ value = select.find('option:disabled').eq(0).text();
+ }
+
+ select.siblings('input.select-dropdown').val(value);
+ }
+ };
+
+}( jQuery ));
+;(function ($) {
+
+ var methods = {
+
+ init : function(options) {
+ var defaults = {
+ indicators: true,
+ height: 400,
+ transition: 500,
+ interval: 6000
+ };
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+
+ // For each slider, we want to keep track of
+ // which slide is active and its associated content
+ var $this = $(this);
+ var $slider = $this.find('ul.slides').first();
+ var $slides = $slider.find('> li');
+ var $active_index = $slider.find('.active').index();
+ var $active, $indicators, $interval;
+ if ($active_index != -1) { $active = $slides.eq($active_index); }
+
+ // Transitions the caption depending on alignment
+ function captionTransition(caption, duration) {
+ if (caption.hasClass("center-align")) {
+ caption.velocity({opacity: 0, translateY: -100}, {duration: duration, queue: false});
+ }
+ else if (caption.hasClass("right-align")) {
+ caption.velocity({opacity: 0, translateX: 100}, {duration: duration, queue: false});
+ }
+ else if (caption.hasClass("left-align")) {
+ caption.velocity({opacity: 0, translateX: -100}, {duration: duration, queue: false});
+ }
+ }
+
+ // This function will transition the slide to any index of the next slide
+ function moveToSlide(index) {
+ // Wrap around indices.
+ if (index >= $slides.length) index = 0;
+ else if (index < 0) index = $slides.length -1;
+
+ $active_index = $slider.find('.active').index();
+
+ // Only do if index changes
+ if ($active_index != index) {
+ $active = $slides.eq($active_index);
+ $caption = $active.find('.caption');
+
+ $active.removeClass('active');
+ $active.velocity({opacity: 0}, {duration: options.transition, queue: false, easing: 'easeOutQuad',
+ complete: function() {
+ $slides.not('.active').velocity({opacity: 0, translateX: 0, translateY: 0}, {duration: 0, queue: false});
+ } });
+ captionTransition($caption, options.transition);
+
+
+ // Update indicators
+ if (options.indicators) {
+ $indicators.eq($active_index).removeClass('active');
+ }
+
+ $slides.eq(index).velocity({opacity: 1}, {duration: options.transition, queue: false, easing: 'easeOutQuad'});
+ $slides.eq(index).find('.caption').velocity({opacity: 1, translateX: 0, translateY: 0}, {duration: options.transition, delay: options.transition, queue: false, easing: 'easeOutQuad'});
+ $slides.eq(index).addClass('active');
+
+
+ // Update indicators
+ if (options.indicators) {
+ $indicators.eq(index).addClass('active');
+ }
+ }
+ }
+
+ // Set height of slider
+ // If fullscreen, do nothing
+ if (!$this.hasClass('fullscreen')) {
+ if (options.indicators) {
+ // Add height if indicators are present
+ $this.height(options.height + 40);
+ }
+ else {
+ $this.height(options.height);
+ }
+ $slider.height(options.height);
+ }
+
+
+ // Set initial positions of captions
+ $slides.find('.caption').each(function () {
+ captionTransition($(this), 0);
+ });
+
+ // Move img src into background-image
+ $slides.find('img').each(function () {
+ var placeholderBase64 = 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+ if ($(this).attr('src') !== placeholderBase64) {
+ $(this).css('background-image', 'url(' + $(this).attr('src') + ')' );
+ $(this).attr('src', placeholderBase64);
+ }
+ });
+
+ // dynamically add indicators
+ if (options.indicators) {
+ $indicators = $('<ul class="indicators"></ul>');
+ $slides.each(function( index ) {
+ var $indicator = $('<li class="indicator-item"></li>');
+
+ // Handle clicks on indicators
+ $indicator.click(function () {
+ var $parent = $slider.parent();
+ var curr_index = $parent.find($(this)).index();
+ moveToSlide(curr_index);
+
+ // reset interval
+ clearInterval($interval);
+ $interval = setInterval(
+ function(){
+ $active_index = $slider.find('.active').index();
+ if ($slides.length == $active_index + 1) $active_index = 0; // loop to start
+ else $active_index += 1;
+
+ moveToSlide($active_index);
+
+ }, options.transition + options.interval
+ );
+ });
+ $indicators.append($indicator);
+ });
+ $this.append($indicators);
+ $indicators = $this.find('ul.indicators').find('li.indicator-item');
+ }
+
+ if ($active) {
+ $active.show();
+ }
+ else {
+ $slides.first().addClass('active').velocity({opacity: 1}, {duration: options.transition, queue: false, easing: 'easeOutQuad'});
+
+ $active_index = 0;
+ $active = $slides.eq($active_index);
+
+ // Update indicators
+ if (options.indicators) {
+ $indicators.eq($active_index).addClass('active');
+ }
+ }
+
+ // Adjust height to current slide
+ $active.find('img').each(function() {
+ $active.find('.caption').velocity({opacity: 1, translateX: 0, translateY: 0}, {duration: options.transition, queue: false, easing: 'easeOutQuad'});
+ });
+
+ // auto scroll
+ $interval = setInterval(
+ function(){
+ $active_index = $slider.find('.active').index();
+ moveToSlide($active_index + 1);
+
+ }, options.transition + options.interval
+ );
+
+
+ // HammerJS, Swipe navigation
+
+ // Touch Event
+ var panning = false;
+ var swipeLeft = false;
+ var swipeRight = false;
+
+ $this.hammer({
+ prevent_default: false
+ }).bind('pan', function(e) {
+ if (e.gesture.pointerType === "touch") {
+
+ // reset interval
+ clearInterval($interval);
+
+ var direction = e.gesture.direction;
+ var x = e.gesture.deltaX;
+ var velocityX = e.gesture.velocityX;
+ var velocityY = e.gesture.velocityY;
+
+ $curr_slide = $slider.find('.active');
+ if (Math.abs(velocityX) > Math.abs(velocityY)) {
+ $curr_slide.velocity({ translateX: x
+ }, {duration: 50, queue: false, easing: 'easeOutQuad'});
+ }
+
+ // Swipe Left
+ if (direction === 4 && (x > ($this.innerWidth() / 2) || velocityX < -0.65)) {
+ swipeRight = true;
+ }
+ // Swipe Right
+ else if (direction === 2 && (x < (-1 * $this.innerWidth() / 2) || velocityX > 0.65)) {
+ swipeLeft = true;
+ }
+
+ // Make Slide Behind active slide visible
+ var next_slide;
+ if (swipeLeft) {
+ next_slide = $curr_slide.next();
+ if (next_slide.length === 0) {
+ next_slide = $slides.first();
+ }
+ next_slide.velocity({ opacity: 1
+ }, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+ if (swipeRight) {
+ next_slide = $curr_slide.prev();
+ if (next_slide.length === 0) {
+ next_slide = $slides.last();
+ }
+ next_slide.velocity({ opacity: 1
+ }, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+
+
+ }
+
+ }).bind('panend', function(e) {
+ if (e.gesture.pointerType === "touch") {
+
+ $curr_slide = $slider.find('.active');
+ panning = false;
+ curr_index = $slider.find('.active').index();
+
+ if (!swipeRight && !swipeLeft || $slides.length <=1) {
+ // Return to original spot
+ $curr_slide.velocity({ translateX: 0
+ }, {duration: 300, queue: false, easing: 'easeOutQuad'});
+ }
+ else if (swipeLeft) {
+ moveToSlide(curr_index + 1);
+ $curr_slide.velocity({translateX: -1 * $this.innerWidth() }, {duration: 300, queue: false, easing: 'easeOutQuad',
+ complete: function() {
+ $curr_slide.velocity({opacity: 0, translateX: 0}, {duration: 0, queue: false});
+ } });
+ }
+ else if (swipeRight) {
+ moveToSlide(curr_index - 1);
+ $curr_slide.velocity({translateX: $this.innerWidth() }, {duration: 300, queue: false, easing: 'easeOutQuad',
+ complete: function() {
+ $curr_slide.velocity({opacity: 0, translateX: 0}, {duration: 0, queue: false});
+ } });
+ }
+ swipeLeft = false;
+ swipeRight = false;
+
+ // Restart interval
+ clearInterval($interval);
+ $interval = setInterval(
+ function(){
+ $active_index = $slider.find('.active').index();
+ if ($slides.length == $active_index + 1) $active_index = 0; // loop to start
+ else $active_index += 1;
+
+ moveToSlide($active_index);
+
+ }, options.transition + options.interval
+ );
+ }
+ });
+
+ $this.on('sliderPause', function() {
+ clearInterval($interval);
+ });
+
+ $this.on('sliderStart', function() {
+ clearInterval($interval);
+ $interval = setInterval(
+ function(){
+ $active_index = $slider.find('.active').index();
+ if ($slides.length == $active_index + 1) $active_index = 0; // loop to start
+ else $active_index += 1;
+
+ moveToSlide($active_index);
+
+ }, options.transition + options.interval
+ );
+ });
+
+ $this.on('sliderNext', function() {
+ $active_index = $slider.find('.active').index();
+ moveToSlide($active_index + 1);
+ });
+
+ $this.on('sliderPrev', function() {
+ $active_index = $slider.find('.active').index();
+ moveToSlide($active_index - 1);
+ });
+
+ });
+
+
+
+ },
+ pause : function() {
+ $(this).trigger('sliderPause');
+ },
+ start : function() {
+ $(this).trigger('sliderStart');
+ },
+ next : function() {
+ $(this).trigger('sliderNext');
+ },
+ prev : function() {
+ $(this).trigger('sliderPrev');
+ }
+ };
+
+
+ $.fn.slider = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
+ }
+ }; // Plugin end
+}( jQuery ));
+;(function ($) {
+ $(document).ready(function() {
+
+ $(document).on('click.card', '.card', function (e) {
+ if ($(this).find('> .card-reveal').length) {
+ if ($(e.target).is($('.card-reveal .card-title')) || $(e.target).is($('.card-reveal .card-title i'))) {
+ // Make Reveal animate down and display none
+ $(this).find('.card-reveal').velocity(
+ {translateY: 0}, {
+ duration: 225,
+ queue: false,
+ easing: 'easeInOutQuad',
+ complete: function() { $(this).css({ display: 'none'}); }
+ }
+ );
+ }
+ else if ($(e.target).is($('.card .activator')) ||
+ $(e.target).is($('.card .activator i')) ) {
+ $(e.target).closest('.card').css('overflow', 'hidden');
+ $(this).find('.card-reveal').css({ display: 'block'}).velocity("stop", false).velocity({translateY: '-100%'}, {duration: 300, queue: false, easing: 'easeInOutQuad'});
+ }
+ }
+ });
+
+ });
+}( jQuery ));;(function ($) {
+ var materialChipsDefaults = {
+ data: [],
+ placeholder: '',
+ secondaryPlaceholder: '',
+ autocompleteData: {},
+ autocompleteLimit: Infinity,
+ };
+
+ $(document).ready(function() {
+ // Handle removal of static chips.
+ $(document).on('click', '.chip .close', function(e){
+ var $chips = $(this).closest('.chips');
+ if ($chips.attr('data-initialized')) {
+ return;
+ }
+ $(this).closest('.chip').remove();
+ });
+ });
+
+ $.fn.material_chip = function (options) {
+ var self = this;
+ this.$el = $(this);
+ this.$document = $(document);
+ this.SELS = {
+ CHIPS: '.chips',
+ CHIP: '.chip',
+ INPUT: 'input',
+ DELETE: '.material-icons',
+ SELECTED_CHIP: '.selected',
+ };
+
+ if ('data' === options) {
+ return this.$el.data('chips');
+ }
+
+ var curr_options = $.extend({}, materialChipsDefaults, options);
+ self.hasAutocomplete = !$.isEmptyObject(curr_options.autocompleteData);
+
+ // Initialize
+ this.init = function() {
+ var i = 0;
+ var chips;
+ self.$el.each(function(){
+ var $chips = $(this);
+ var chipId = Materialize.guid();
+ self.chipId = chipId;
+
+ if (!curr_options.data || !(curr_options.data instanceof Array)) {
+ curr_options.data = [];
+ }
+ $chips.data('chips', curr_options.data);
+ $chips.attr('data-index', i);
+ $chips.attr('data-initialized', true);
+
+ if (!$chips.hasClass(self.SELS.CHIPS)) {
+ $chips.addClass('chips');
+ }
+
+ self.chips($chips, chipId);
+ i++;
+ });
+ };
+
+ this.handleEvents = function() {
+ var SELS = self.SELS;
+
+ self.$document.off('click.chips-focus', SELS.CHIPS).on('click.chips-focus', SELS.CHIPS, function(e){
+ $(e.target).find(SELS.INPUT).focus();
+ });
+
+ self.$document.off('click.chips-select', SELS.CHIP).on('click.chips-select', SELS.CHIP, function(e){
+ var $chip = $(e.target);
+ if ($chip.length) {
+ var wasSelected = $chip.hasClass('selected');
+ var $chips = $chip.closest(SELS.CHIPS);
+ $(SELS.CHIP).removeClass('selected');
+
+ if (!wasSelected) {
+ self.selectChip($chip.index(), $chips);
+ }
+ }
+ });
+
+ self.$document.off('keydown.chips').on('keydown.chips', function(e){
+ if ($(e.target).is('input, textarea')) {
+ return;
+ }
+
+ // delete
+ var $chip = self.$document.find(SELS.CHIP + SELS.SELECTED_CHIP);
+ var $chips = $chip.closest(SELS.CHIPS);
+ var length = $chip.siblings(SELS.CHIP).length;
+ var index;
+
+ if (!$chip.length) {
+ return;
+ }
+
+ if (e.which === 8 || e.which === 46) {
+ e.preventDefault();
+
+ index = $chip.index();
+ self.deleteChip(index, $chips);
+
+ var selectIndex = null;
+ if ((index + 1) < length) {
+ selectIndex = index;
+ } else if (index === length || (index + 1) === length) {
+ selectIndex = length - 1;
+ }
+
+ if (selectIndex < 0) selectIndex = null;
+
+ if (null !== selectIndex) {
+ self.selectChip(selectIndex, $chips);
+ }
+ if (!length) $chips.find('input').focus();
+
+ // left
+ } else if (e.which === 37) {
+ index = $chip.index() - 1;
+ if (index < 0) {
+ return;
+ }
+ $(SELS.CHIP).removeClass('selected');
+ self.selectChip(index, $chips);
+
+ // right
+ } else if (e.which === 39) {
+ index = $chip.index() + 1;
+ $(SELS.CHIP).removeClass('selected');
+ if (index > length) {
+ $chips.find('input').focus();
+ return;
+ }
+ self.selectChip(index, $chips);
+ }
+ });
+
+ self.$document.off('focusin.chips', SELS.CHIPS + ' ' + SELS.INPUT).on('focusin.chips', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
+ var $currChips = $(e.target).closest(SELS.CHIPS);
+ $currChips.addClass('focus');
+ $currChips.siblings('label, .prefix').addClass('active');
+ $(SELS.CHIP).removeClass('selected');
+ });
+
+ self.$document.off('focusout.chips', SELS.CHIPS + ' ' + SELS.INPUT).on('focusout.chips', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
+ var $currChips = $(e.target).closest(SELS.CHIPS);
+ $currChips.removeClass('focus');
+
+ // Remove active if empty
+ if (!$currChips.data('chips').length) {
+ $currChips.siblings('label').removeClass('active');
+ }
+ $currChips.siblings('.prefix').removeClass('active');
+ });
+
+ self.$document.off('keydown.chips-add', SELS.CHIPS + ' ' + SELS.INPUT).on('keydown.chips-add', SELS.CHIPS + ' ' + SELS.INPUT, function(e){
+ var $target = $(e.target);
+ var $chips = $target.closest(SELS.CHIPS);
+ var chipsLength = $chips.children(SELS.CHIP).length;
+
+ // enter
+ if (13 === e.which) {
+ // Override enter if autocompleting.
+ if (self.hasAutocomplete &&
+ $chips.find('.autocomplete-content.dropdown-content').length &&
+ $chips.find('.autocomplete-content.dropdown-content').children().length) {
+ return;
+ }
+
+ e.preventDefault();
+ self.addChip({tag: $target.val()}, $chips);
+ $target.val('');
+ return;
+ }
+
+ // delete or left
+ if ((8 === e.keyCode || 37 === e.keyCode) && '' === $target.val() && chipsLength) {
+ e.preventDefault();
+ self.selectChip(chipsLength - 1, $chips);
+ $target.blur();
+ return;
+ }
+ });
+
+ // Click on delete icon in chip.
+ self.$document.off('click.chips-delete', SELS.CHIPS + ' ' + SELS.DELETE).on('click.chips-delete', SELS.CHIPS + ' ' + SELS.DELETE, function(e) {
+ var $target = $(e.target);
+ var $chips = $target.closest(SELS.CHIPS);
+ var $chip = $target.closest(SELS.CHIP);
+ e.stopPropagation();
+ self.deleteChip($chip.index(), $chips);
+ $chips.find('input').focus();
+ });
+ };
+
+ this.chips = function($chips, chipId) {
+ var html = '';
+ $chips.data('chips').forEach(function(elem){
+ html += self.renderChip(elem);
+ });
+ html += '<input id="' + chipId +'" class="input" placeholder="">';
+ $chips.html(html);
+ self.setPlaceholder($chips);
+
+ // Set for attribute for label
+ var label = $chips.next('label');
+ if (label.length) {
+ label.attr('for', chipId);
+
+ if ($chips.data('chips').length) {
+ label.addClass('active');
+ }
+ }
+
+ // Setup autocomplete if needed.
+ var input = $('#' + chipId);
+ if (self.hasAutocomplete) {
+ input.autocomplete({
+ data: curr_options.autocompleteData,
+ limit: curr_options.autocompleteLimit,
+ onAutocomplete: function(val) {
+ self.addChip({tag: val}, $chips);
+ input.val('');
+ input.focus();
+ },
+ })
+ }
+ };
+
+ this.renderChip = function(elem) {
+ if (!elem.tag) return;
+
+ var html = '<div class="chip">' + elem.tag;
+ if (elem.image) {
+ html += ' <img src="' + elem.image + '"> ';
+ }
+ html += '<i class="material-icons close">close</i>';
+ html += '</div>';
+ return html;
+ };
+
+ this.setPlaceholder = function($chips) {
+ if ($chips.data('chips').length && curr_options.placeholder) {
+ $chips.find('input').prop('placeholder', curr_options.placeholder);
+
+ } else if (!$chips.data('chips').length && curr_options.secondaryPlaceholder) {
+ $chips.find('input').prop('placeholder', curr_options.secondaryPlaceholder);
+ }
+ };
+
+ this.isValid = function($chips, elem) {
+ var chips = $chips.data('chips');
+ var exists = false;
+ for (var i=0; i < chips.length; i++) {
+ if (chips[i].tag === elem.tag) {
+ exists = true;
+ return;
+ }
+ }
+ return '' !== elem.tag && !exists;
+ };
+
+ this.addChip = function(elem, $chips) {
+ if (!self.isValid($chips, elem)) {
+ return;
+ }
+ var chipHtml = self.renderChip(elem);
+ var newData = [];
+ var oldData = $chips.data('chips');
+ for (var i = 0; i < oldData.length; i++) {
+ newData.push(oldData[i]);
+ }
+ newData.push(elem);
+
+ $chips.data('chips', newData);
+ $(chipHtml).insertBefore($chips.find('input'));
+ $chips.trigger('chip.add', elem);
+ self.setPlaceholder($chips);
+ };
+
+ this.deleteChip = function(chipIndex, $chips) {
+ var chip = $chips.data('chips')[chipIndex];
+ $chips.find('.chip').eq(chipIndex).remove();
+
+ var newData = [];
+ var oldData = $chips.data('chips');
+ for (var i = 0; i < oldData.length; i++) {
+ if (i !== chipIndex) {
+ newData.push(oldData[i]);
+ }
+ }
+
+ $chips.data('chips', newData);
+ $chips.trigger('chip.delete', chip);
+ self.setPlaceholder($chips);
+ };
+
+ this.selectChip = function(chipIndex, $chips) {
+ var $chip = $chips.find('.chip').eq(chipIndex);
+ if ($chip && false === $chip.hasClass('selected')) {
+ $chip.addClass('selected');
+ $chips.trigger('chip.select', $chips.data('chips')[chipIndex]);
+ }
+ };
+
+ this.getChipsElement = function(index, $chips) {
+ return $chips.eq(index);
+ };
+
+ // init
+ this.init();
+
+ this.handleEvents();
+ };
+}( jQuery ));
+;(function ($) {
+ $.fn.pushpin = function (options) {
+ // Defaults
+ var defaults = {
+ top: 0,
+ bottom: Infinity,
+ offset: 0
+ };
+
+ // Remove pushpin event and classes
+ if (options === "remove") {
+ this.each(function () {
+ if (id = $(this).data('pushpin-id')) {
+ $(window).off('scroll.' + id);
+ $(this).removeData('pushpin-id').removeClass('pin-top pinned pin-bottom').removeAttr('style');
+ }
+ });
+ return false;
+ }
+
+ options = $.extend(defaults, options);
+
+
+ $index = 0;
+ return this.each(function() {
+ var $uniqueId = Materialize.guid(),
+ $this = $(this),
+ $original_offset = $(this).offset().top;
+
+ function removePinClasses(object) {
+ object.removeClass('pin-top');
+ object.removeClass('pinned');
+ object.removeClass('pin-bottom');
+ }
+
+ function updateElements(objects, scrolled) {
+ objects.each(function () {
+ // Add position fixed (because its between top and bottom)
+ if (options.top <= scrolled && options.bottom >= scrolled && !$(this).hasClass('pinned')) {
+ removePinClasses($(this));
+ $(this).css('top', options.offset);
+ $(this).addClass('pinned');
+ }
+
+ // Add pin-top (when scrolled position is above top)
+ if (scrolled < options.top && !$(this).hasClass('pin-top')) {
+ removePinClasses($(this));
+ $(this).css('top', 0);
+ $(this).addClass('pin-top');
+ }
+
+ // Add pin-bottom (when scrolled position is below bottom)
+ if (scrolled > options.bottom && !$(this).hasClass('pin-bottom')) {
+ removePinClasses($(this));
+ $(this).addClass('pin-bottom');
+ $(this).css('top', options.bottom - $original_offset);
+ }
+ });
+ }
+
+ $(this).data('pushpin-id', $uniqueId);
+ updateElements($this, $(window).scrollTop());
+ $(window).on('scroll.' + $uniqueId, function () {
+ var $scrolled = $(window).scrollTop() + options.offset;
+ updateElements($this, $scrolled);
+ });
+
+ });
+
+ };
+}( jQuery ));;(function ($) {
+ $(document).ready(function() {
+
+ // jQuery reverse
+ $.fn.reverse = [].reverse;
+
+ // Hover behaviour: make sure this doesn't work on .click-to-toggle FABs!
+ $(document).on('mouseenter.fixedActionBtn', '.fixed-action-btn:not(.click-to-toggle):not(.toolbar)', function(e) {
+ var $this = $(this);
+ openFABMenu($this);
+ });
+ $(document).on('mouseleave.fixedActionBtn', '.fixed-action-btn:not(.click-to-toggle):not(.toolbar)', function(e) {
+ var $this = $(this);
+ closeFABMenu($this);
+ });
+
+ // Toggle-on-click behaviour.
+ $(document).on('click.fabClickToggle', '.fixed-action-btn.click-to-toggle > a', function(e) {
+ var $this = $(this);
+ var $menu = $this.parent();
+ if ($menu.hasClass('active')) {
+ closeFABMenu($menu);
+ } else {
+ openFABMenu($menu);
+ }
+ });
+
+ // Toolbar transition behaviour.
+ $(document).on('click.fabToolbar', '.fixed-action-btn.toolbar > a', function(e) {
+ var $this = $(this);
+ var $menu = $this.parent();
+ FABtoToolbar($menu);
+ });
+
+ });
+
+ $.fn.extend({
+ openFAB: function() {
+ openFABMenu($(this));
+ },
+ closeFAB: function() {
+ closeFABMenu($(this));
+ },
+ openToolbar: function() {
+ FABtoToolbar($(this));
+ },
+ closeToolbar: function() {
+ toolbarToFAB($(this));
+ }
+ });
+
+
+ var openFABMenu = function (btn) {
+ var $this = btn;
+ if ($this.hasClass('active') === false) {
+
+ // Get direction option
+ var horizontal = $this.hasClass('horizontal');
+ var offsetY, offsetX;
+
+ if (horizontal === true) {
+ offsetX = 40;
+ } else {
+ offsetY = 40;
+ }
+
+ $this.addClass('active');
+ $this.find('ul .btn-floating').velocity(
+ { scaleY: ".4", scaleX: ".4", translateY: offsetY + 'px', translateX: offsetX + 'px'},
+ { duration: 0 });
+
+ var time = 0;
+ $this.find('ul .btn-floating').reverse().each( function () {
+ $(this).velocity(
+ { opacity: "1", scaleX: "1", scaleY: "1", translateY: "0", translateX: '0'},
+ { duration: 80, delay: time });
+ time += 40;
+ });
+ }
+ };
+
+ var closeFABMenu = function (btn) {
+ var $this = btn;
+ // Get direction option
+ var horizontal = $this.hasClass('horizontal');
+ var offsetY, offsetX;
+
+ if (horizontal === true) {
+ offsetX = 40;
+ } else {
+ offsetY = 40;
+ }
+
+ $this.removeClass('active');
+ var time = 0;
+ $this.find('ul .btn-floating').velocity("stop", true);
+ $this.find('ul .btn-floating').velocity(
+ { opacity: "0", scaleX: ".4", scaleY: ".4", translateY: offsetY + 'px', translateX: offsetX + 'px'},
+ { duration: 80 }
+ );
+ };
+
+
+ /**
+ * Transform FAB into toolbar
+ * @param {Object} object jQuery object
+ */
+ var FABtoToolbar = function(btn) {
+ if (btn.attr('data-open') === "true") {
+ return;
+ }
+
+ var offsetX, offsetY, scaleFactor;
+ var windowWidth = window.innerWidth;
+ var windowHeight = window.innerHeight;
+ var btnRect = btn[0].getBoundingClientRect();
+ var anchor = btn.find('> a').first();
+ var menu = btn.find('> ul').first();
+ var backdrop = $('<div class="fab-backdrop"></div>');
+ var fabColor = anchor.css('background-color');
+ anchor.append(backdrop);
+
+ offsetX = btnRect.left - (windowWidth / 2) + (btnRect.width / 2);
+ offsetY = windowHeight - btnRect.bottom;
+ scaleFactor = windowWidth / backdrop.width();
+ btn.attr('data-origin-bottom', btnRect.bottom);
+ btn.attr('data-origin-left', btnRect.left);
+ btn.attr('data-origin-width', btnRect.width);
+
+ // Set initial state
+ btn.addClass('active');
+ btn.attr('data-open', true);
+ btn.css({
+ 'text-align': 'center',
+ width: '100%',
+ bottom: 0,
+ left: 0,
+ transform: 'translateX(' + offsetX + 'px)',
+ transition: 'none'
+ });
+ anchor.css({
+ transform: 'translateY(' + -offsetY + 'px)',
+ transition: 'none'
+ });
+ backdrop.css({
+ 'background-color': fabColor
+ });
+
+
+ setTimeout(function() {
+ btn.css({
+ transform: '',
+ transition: 'transform .2s cubic-bezier(0.550, 0.085, 0.680, 0.530), background-color 0s linear .2s'
+ });
+ anchor.css({
+ overflow: 'visible',
+ transform: '',
+ transition: 'transform .2s'
+ });
+
+ setTimeout(function() {
+ btn.css({
+ overflow: 'hidden',
+ 'background-color': fabColor
+ });
+ backdrop.css({
+ transform: 'scale(' + scaleFactor + ')',
+ transition: 'transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)'
+ });
+ menu.find('> li > a').css({
+ opacity: 1
+ });
+
+ // Scroll to close.
+ $(window).on('scroll.fabToolbarClose', function() {
+ toolbarToFAB(btn);
+ $(window).off('scroll.fabToolbarClose');
+ $(document).off('click.fabToolbarClose');
+ });
+
+ $(document).on('click.fabToolbarClose', function(e) {
+ if (!$(e.target).closest(menu).length) {
+ toolbarToFAB(btn);
+ $(window).off('scroll.fabToolbarClose');
+ $(document).off('click.fabToolbarClose');
+ }
+ });
+ }, 100);
+ }, 0);
+ };
+
+ /**
+ * Transform toolbar back into FAB
+ * @param {Object} object jQuery object
+ */
+ var toolbarToFAB = function(btn) {
+ if (btn.attr('data-open') !== "true") {
+ return;
+ }
+
+ var offsetX, offsetY, scaleFactor;
+ var windowWidth = window.innerWidth;
+ var windowHeight = window.innerHeight;
+ var btnWidth = btn.attr('data-origin-width');
+ var btnBottom = btn.attr('data-origin-bottom');
+ var btnLeft = btn.attr('data-origin-left');
+ var anchor = btn.find('> .btn-floating').first();
+ var menu = btn.find('> ul').first();
+ var backdrop = btn.find('.fab-backdrop');
+ var fabColor = anchor.css('background-color');
+
+ offsetX = btnLeft - (windowWidth / 2) + (btnWidth / 2);
+ offsetY = windowHeight - btnBottom;
+ scaleFactor = windowWidth / backdrop.width();
+
+
+ // Hide backdrop
+ btn.removeClass('active');
+ btn.attr('data-open', false);
+ btn.css({
+ 'background-color': 'transparent',
+ transition: 'none'
+ });
+ anchor.css({
+ transition: 'none'
+ });
+ backdrop.css({
+ transform: 'scale(0)',
+ 'background-color': fabColor
+ });
+ menu.find('> li > a').css({
+ opacity: ''
+ });
+
+ setTimeout(function() {
+ backdrop.remove();
+
+ // Set initial state.
+ btn.css({
+ 'text-align': '',
+ width: '',
+ bottom: '',
+ left: '',
+ overflow: '',
+ 'background-color': '',
+ transform: 'translate3d(' + -offsetX + 'px,0,0)'
+ });
+ anchor.css({
+ overflow: '',
+ transform: 'translate3d(0,' + offsetY + 'px,0)'
+ });
+
+ setTimeout(function() {
+ btn.css({
+ transform: 'translate3d(0,0,0)',
+ transition: 'transform .2s'
+ });
+ anchor.css({
+ transform: 'translate3d(0,0,0)',
+ transition: 'transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)'
+ });
+ }, 20);
+ }, 200);
+ };
+
+
+}( jQuery ));
+;(function ($) {
+ // Image transition function
+ Materialize.fadeInImage = function(selectorOrEl) {
+ var element;
+ if (typeof(selectorOrEl) === 'string') {
+ element = $(selectorOrEl);
+ } else if (typeof(selectorOrEl) === 'object') {
+ element = selectorOrEl;
+ } else {
+ return;
+ }
+ element.css({opacity: 0});
+ $(element).velocity({opacity: 1}, {
+ duration: 650,
+ queue: false,
+ easing: 'easeOutSine'
+ });
+ $(element).velocity({opacity: 1}, {
+ duration: 1300,
+ queue: false,
+ easing: 'swing',
+ step: function(now, fx) {
+ fx.start = 100;
+ var grayscale_setting = now/100;
+ var brightness_setting = 150 - (100 - now)/1.75;
+
+ if (brightness_setting < 100) {
+ brightness_setting = 100;
+ }
+ if (now >= 0) {
+ $(this).css({
+ "-webkit-filter": "grayscale("+grayscale_setting+")" + "brightness("+brightness_setting+"%)",
+ "filter": "grayscale("+grayscale_setting+")" + "brightness("+brightness_setting+"%)"
+ });
+ }
+ }
+ });
+ };
+
+ // Horizontal staggered list
+ Materialize.showStaggeredList = function(selectorOrEl) {
+ var element;
+ if (typeof(selectorOrEl) === 'string') {
+ element = $(selectorOrEl);
+ } else if (typeof(selectorOrEl) === 'object') {
+ element = selectorOrEl;
+ } else {
+ return;
+ }
+ var time = 0;
+ element.find('li').velocity(
+ { translateX: "-100px"},
+ { duration: 0 });
+
+ element.find('li').each(function() {
+ $(this).velocity(
+ { opacity: "1", translateX: "0"},
+ { duration: 800, delay: time, easing: [60, 10] });
+ time += 120;
+ });
+ };
+
+
+ $(document).ready(function() {
+ // Hardcoded .staggered-list scrollFire
+ // var staggeredListOptions = [];
+ // $('ul.staggered-list').each(function (i) {
+
+ // var label = 'scrollFire-' + i;
+ // $(this).addClass(label);
+ // staggeredListOptions.push(
+ // {selector: 'ul.staggered-list.' + label,
+ // offset: 200,
+ // callback: 'showStaggeredList("ul.staggered-list.' + label + '")'});
+ // });
+ // scrollFire(staggeredListOptions);
+
+ // HammerJS, Swipe navigation
+
+ // Touch Event
+ var swipeLeft = false;
+ var swipeRight = false;
+
+
+ // Dismissible Collections
+ $('.dismissable').each(function() {
+ $(this).hammer({
+ prevent_default: false
+ }).bind('pan', function(e) {
+ if (e.gesture.pointerType === "touch") {
+ var $this = $(this);
+ var direction = e.gesture.direction;
+ var x = e.gesture.deltaX;
+ var velocityX = e.gesture.velocityX;
+
+ $this.velocity({ translateX: x
+ }, {duration: 50, queue: false, easing: 'easeOutQuad'});
+
+ // Swipe Left
+ if (direction === 4 && (x > ($this.innerWidth() / 2) || velocityX < -0.75)) {
+ swipeLeft = true;
+ }
+
+ // Swipe Right
+ if (direction === 2 && (x < (-1 * $this.innerWidth() / 2) || velocityX > 0.75)) {
+ swipeRight = true;
+ }
+ }
+ }).bind('panend', function(e) {
+ // Reset if collection is moved back into original position
+ if (Math.abs(e.gesture.deltaX) < ($(this).innerWidth() / 2)) {
+ swipeRight = false;
+ swipeLeft = false;
+ }
+
+ if (e.gesture.pointerType === "touch") {
+ var $this = $(this);
+ if (swipeLeft || swipeRight) {
+ var fullWidth;
+ if (swipeLeft) { fullWidth = $this.innerWidth(); }
+ else { fullWidth = -1 * $this.innerWidth(); }
+
+ $this.velocity({ translateX: fullWidth,
+ }, {duration: 100, queue: false, easing: 'easeOutQuad', complete:
+ function() {
+ $this.css('border', 'none');
+ $this.velocity({ height: 0, padding: 0,
+ }, {duration: 200, queue: false, easing: 'easeOutQuad', complete:
+ function() { $this.remove(); }
+ });
+ }
+ });
+ }
+ else {
+ $this.velocity({ translateX: 0,
+ }, {duration: 100, queue: false, easing: 'easeOutQuad'});
+ }
+ swipeLeft = false;
+ swipeRight = false;
+ }
+ });
+
+ });
+
+
+ // time = 0
+ // // Vertical Staggered list
+ // $('ul.staggered-list.vertical li').velocity(
+ // { translateY: "100px"},
+ // { duration: 0 });
+
+ // $('ul.staggered-list.vertical li').each(function() {
+ // $(this).velocity(
+ // { opacity: "1", translateY: "0"},
+ // { duration: 800, delay: time, easing: [60, 25] });
+ // time += 120;
+ // });
+
+ // // Fade in and Scale
+ // $('.fade-in.scale').velocity(
+ // { scaleX: .4, scaleY: .4, translateX: -600},
+ // { duration: 0});
+ // $('.fade-in').each(function() {
+ // $(this).velocity(
+ // { opacity: "1", scaleX: 1, scaleY: 1, translateX: 0},
+ // { duration: 800, easing: [60, 10] });
+ // });
+ });
+}( jQuery ));
+;(function($) {
+
+ var scrollFireEventsHandled = false;
+
+ // Input: Array of JSON objects {selector, offset, callback}
+ Materialize.scrollFire = function(options) {
+ var onScroll = function() {
+ var windowScroll = window.pageYOffset + window.innerHeight;
+
+ for (var i = 0 ; i < options.length; i++) {
+ // Get options from each line
+ var value = options[i];
+ var selector = value.selector,
+ offset = value.offset,
+ callback = value.callback;
+
+ var currentElement = document.querySelector(selector);
+ if ( currentElement !== null) {
+ var elementOffset = currentElement.getBoundingClientRect().top + window.pageYOffset;
+
+ if (windowScroll > (elementOffset + offset)) {
+ if (value.done !== true) {
+ if (typeof(callback) === 'function') {
+ callback.call(this, currentElement);
+ } else if (typeof(callback) === 'string') {
+ var callbackFunc = new Function(callback);
+ callbackFunc(currentElement);
+ }
+ value.done = true;
+ }
+ }
+ }
+ }
+ };
+
+
+ var throttledScroll = Materialize.throttle(function() {
+ onScroll();
+ }, options.throttle || 100);
+
+ if (!scrollFireEventsHandled) {
+ window.addEventListener("scroll", throttledScroll);
+ window.addEventListener("resize", throttledScroll);
+ scrollFireEventsHandled = true;
+ }
+
+ // perform a scan once, after current execution context, and after dom is ready
+ setTimeout(throttledScroll, 0);
+ };
+
+})(jQuery);
+;/*!
+ * pickadate.js v3.5.0, 2014/04/13
+ * By Amsul, http://amsul.ca
+ * Hosted on http://amsul.github.io/pickadate.js
+ * Licensed under MIT
+ */
+
+(function ( factory ) {
+
+ // AMD.
+ if ( typeof define == 'function' && define.amd )
+ define( 'picker', ['jquery'], factory )
+
+ // Node.js/browserify.
+ else if ( typeof exports == 'object' )
+ module.exports = factory( require('jquery') )
+
+ // Browser globals.
+ else this.Picker = factory( jQuery )
+
+}(function( $ ) {
+
+var $window = $( window )
+var $document = $( document )
+var $html = $( document.documentElement )
+
+
+/**
+ * The picker constructor that creates a blank picker.
+ */
+function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) {
+
+ // If there’s no element, return the picker constructor.
+ if ( !ELEMENT ) return PickerConstructor
+
+
+ var
+ IS_DEFAULT_THEME = false,
+
+
+ // The state of the picker.
+ STATE = {
+ id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) )
+ },
+
+
+ // Merge the defaults and options passed.
+ SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {},
+
+
+ // Merge the default classes with the settings classes.
+ CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ),
+
+
+ // The element node wrapper into a jQuery object.
+ $ELEMENT = $( ELEMENT ),
+
+
+ // Pseudo picker constructor.
+ PickerInstance = function() {
+ return this.start()
+ },
+
+
+ // The picker prototype.
+ P = PickerInstance.prototype = {
+
+ constructor: PickerInstance,
+
+ $node: $ELEMENT,
+
+
+ /**
+ * Initialize everything
+ */
+ start: function() {
+
+ // If it’s already started, do nothing.
+ if ( STATE && STATE.start ) return P
+
+
+ // Update the picker states.
+ STATE.methods = {}
+ STATE.start = true
+ STATE.open = false
+ STATE.type = ELEMENT.type
+
+
+ // Confirm focus state, convert into text input to remove UA stylings,
+ // and set as readonly to prevent keyboard popup.
+ ELEMENT.autofocus = ELEMENT == getActiveElement()
+ ELEMENT.readOnly = !SETTINGS.editable
+ ELEMENT.id = ELEMENT.id || STATE.id
+ if ( ELEMENT.type != 'text' ) {
+ ELEMENT.type = 'text'
+ }
+
+
+ // Create a new picker component with the settings.
+ P.component = new COMPONENT(P, SETTINGS)
+
+
+ // Create the picker root with a holder and then prepare it.
+ P.$root = $( PickerConstructor._.node('div', createWrappedComponent(), CLASSES.picker, 'id="' + ELEMENT.id + '_root" tabindex="0"') )
+ prepareElementRoot()
+
+
+ // If there’s a format for the hidden input element, create the element.
+ if ( SETTINGS.formatSubmit ) {
+ prepareElementHidden()
+ }
+
+
+ // Prepare the input element.
+ prepareElement()
+
+
+ // Insert the root as specified in the settings.
+ if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root )
+ else $ELEMENT.after( P.$root )
+
+
+ // Bind the default component and settings events.
+ P.on({
+ start: P.component.onStart,
+ render: P.component.onRender,
+ stop: P.component.onStop,
+ open: P.component.onOpen,
+ close: P.component.onClose,
+ set: P.component.onSet
+ }).on({
+ start: SETTINGS.onStart,
+ render: SETTINGS.onRender,
+ stop: SETTINGS.onStop,
+ open: SETTINGS.onOpen,
+ close: SETTINGS.onClose,
+ set: SETTINGS.onSet
+ })
+
+
+ // Once we’re all set, check the theme in use.
+ IS_DEFAULT_THEME = isUsingDefaultTheme( P.$root.children()[ 0 ] )
+
+
+ // If the element has autofocus, open the picker.
+ if ( ELEMENT.autofocus ) {
+ P.open()
+ }
+
+
+ // Trigger queued the “start” and “render” events.
+ return P.trigger( 'start' ).trigger( 'render' )
+ }, //start
+
+
+ /**
+ * Render a new picker
+ */
+ render: function( entireComponent ) {
+
+ // Insert a new component holder in the root or box.
+ if ( entireComponent ) P.$root.html( createWrappedComponent() )
+ else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) )
+
+ // Trigger the queued “render” events.
+ return P.trigger( 'render' )
+ }, //render
+
+
+ /**
+ * Destroy everything
+ */
+ stop: function() {
+
+ // If it’s already stopped, do nothing.
+ if ( !STATE.start ) return P
+
+ // Then close the picker.
+ P.close()
+
+ // Remove the hidden field.
+ if ( P._hidden ) {
+ P._hidden.parentNode.removeChild( P._hidden )
+ }
+
+ // Remove the root.
+ P.$root.remove()
+
+ // Remove the input class, remove the stored data, and unbind
+ // the events (after a tick for IE - see `P.close`).
+ $ELEMENT.removeClass( CLASSES.input ).removeData( NAME )
+ setTimeout( function() {
+ $ELEMENT.off( '.' + STATE.id )
+ }, 0)
+
+ // Restore the element state
+ ELEMENT.type = STATE.type
+ ELEMENT.readOnly = false
+
+ // Trigger the queued “stop” events.
+ P.trigger( 'stop' )
+
+ // Reset the picker states.
+ STATE.methods = {}
+ STATE.start = false
+
+ return P
+ }, //stop
+
+
+ /**
+ * Open up the picker
+ */
+ open: function( dontGiveFocus ) {
+
+ // If it’s already open, do nothing.
+ if ( STATE.open ) return P
+
+ // Add the “active” class.
+ $ELEMENT.addClass( CLASSES.active )
+ aria( ELEMENT, 'expanded', true )
+
+ // * A Firefox bug, when `html` has `overflow:hidden`, results in
+ // killing transitions :(. So add the “opened” state on the next tick.
+ // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
+ setTimeout( function() {
+
+ // Add the “opened” class to the picker root.
+ P.$root.addClass( CLASSES.opened )
+ aria( P.$root[0], 'hidden', false )
+
+ }, 0 )
+
+ // If we have to give focus, bind the element and doc events.
+ if ( dontGiveFocus !== false ) {
+
+ // Set it as open.
+ STATE.open = true
+
+ // Prevent the page from scrolling.
+ if ( IS_DEFAULT_THEME ) {
+ $html.
+ css( 'overflow', 'hidden' ).
+ css( 'padding-right', '+=' + getScrollbarWidth() )
+ }
+
+ // Pass focus to the root element’s jQuery object.
+ // * Workaround for iOS8 to bring the picker’s root into view.
+ P.$root.eq(0).focus()
+
+ // Bind the document events.
+ $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) {
+
+ var target = event.target
+
+ // If the target of the event is not the element, close the picker picker.
+ // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
+ // Also, for Firefox, a click on an `option` element bubbles up directly
+ // to the doc. So make sure the target wasn't the doc.
+ // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
+ // which causes the picker to unexpectedly close when right-clicking it. So make
+ // sure the event wasn’t a right-click.
+ if ( target != ELEMENT && target != document && event.which != 3 ) {
+
+ // If the target was the holder that covers the screen,
+ // keep the element focused to maintain tabindex.
+ P.close( target === P.$root.children()[0] )
+ }
+
+ }).on( 'keydown.' + STATE.id, function( event ) {
+
+ var
+ // Get the keycode.
+ keycode = event.keyCode,
+
+ // Translate that to a selection change.
+ keycodeToMove = P.component.key[ keycode ],
+
+ // Grab the target.
+ target = event.target
+
+
+ // On escape, close the picker and give focus.
+ if ( keycode == 27 ) {
+ P.close( true )
+ }
+
+
+ // Check if there is a key movement or “enter” keypress on the element.
+ else if ( target == P.$root[0] && ( keycodeToMove || keycode == 13 ) ) {
+
+ // Prevent the default action to stop page movement.
+ event.preventDefault()
+
+ // Trigger the key movement action.
+ if ( keycodeToMove ) {
+ PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] )
+ }
+
+ // On “enter”, if the highlighted item isn’t disabled, set the value and close.
+ else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) {
+ P.set( 'select', P.component.item.highlight ).close()
+ }
+ }
+
+
+ // If the target is within the root and “enter” is pressed,
+ // prevent the default action and trigger a click on the target instead.
+ else if ( $.contains( P.$root[0], target ) && keycode == 13 ) {
+ event.preventDefault()
+ target.click()
+ }
+ })
+ }
+
+ // Trigger the queued “open” events.
+ return P.trigger( 'open' )
+ }, //open
+
+
+ /**
+ * Close the picker
+ */
+ close: function( giveFocus ) {
+
+ // If we need to give focus, do it before changing states.
+ if ( giveFocus ) {
+ // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
+ // The focus is triggered *after* the close has completed - causing it
+ // to open again. So unbind and rebind the event at the next tick.
+ P.$root.off( 'focus.toOpen' ).eq(0).focus()
+ setTimeout( function() {
+ P.$root.on( 'focus.toOpen', handleFocusToOpenEvent )
+ }, 0 )
+ }
+
+ // Remove the “active” class.
+ $ELEMENT.removeClass( CLASSES.active )
+ aria( ELEMENT, 'expanded', false )
+
+ // * A Firefox bug, when `html` has `overflow:hidden`, results in
+ // killing transitions :(. So remove the “opened” state on the next tick.
+ // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
+ setTimeout( function() {
+
+ // Remove the “opened” and “focused” class from the picker root.
+ P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused )
+ aria( P.$root[0], 'hidden', true )
+
+ }, 0 )
+
+ // If it’s already closed, do nothing more.
+ if ( !STATE.open ) return P
+
+ // Set it as closed.
+ STATE.open = false
+
+ // Allow the page to scroll.
+ if ( IS_DEFAULT_THEME ) {
+ $html.
+ css( 'overflow', '' ).
+ css( 'padding-right', '-=' + getScrollbarWidth() )
+ }
+
+ // Unbind the document events.
+ $document.off( '.' + STATE.id )
+
+ // Trigger the queued “close” events.
+ return P.trigger( 'close' )
+ }, //close
+
+
+ /**
+ * Clear the values
+ */
+ clear: function( options ) {
+ return P.set( 'clear', null, options )
+ }, //clear
+
+
+ /**
+ * Set something
+ */
+ set: function( thing, value, options ) {
+
+ var thingItem, thingValue,
+ thingIsObject = $.isPlainObject( thing ),
+ thingObject = thingIsObject ? thing : {}
+
+ // Make sure we have usable options.
+ options = thingIsObject && $.isPlainObject( value ) ? value : options || {}
+
+ if ( thing ) {
+
+ // If the thing isn’t an object, make it one.
+ if ( !thingIsObject ) {
+ thingObject[ thing ] = value
+ }
+
+ // Go through the things of items to set.
+ for ( thingItem in thingObject ) {
+
+ // Grab the value of the thing.
+ thingValue = thingObject[ thingItem ]
+
+ // First, if the item exists and there’s a value, set it.
+ if ( thingItem in P.component.item ) {
+ if ( thingValue === undefined ) thingValue = null
+ P.component.set( thingItem, thingValue, options )
+ }
+
+ // Then, check to update the element value and broadcast a change.
+ if ( thingItem == 'select' || thingItem == 'clear' ) {
+ $ELEMENT.
+ val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ).
+ trigger( 'change' )
+ }
+ }
+
+ // Render a new picker.
+ P.render()
+ }
+
+ // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`.
+ return options.muted ? P : P.trigger( 'set', thingObject )
+ }, //set
+
+
+ /**
+ * Get something
+ */
+ get: function( thing, format ) {
+
+ // Make sure there’s something to get.
+ thing = thing || 'value'
+
+ // If a picker state exists, return that.
+ if ( STATE[ thing ] != null ) {
+ return STATE[ thing ]
+ }
+
+ // Return the submission value, if that.
+ if ( thing == 'valueSubmit' ) {
+ if ( P._hidden ) {
+ return P._hidden.value
+ }
+ thing = 'value'
+ }
+
+ // Return the value, if that.
+ if ( thing == 'value' ) {
+ return ELEMENT.value
+ }
+
+ // Check if a component item exists, return that.
+ if ( thing in P.component.item ) {
+ if ( typeof format == 'string' ) {
+ var thingValue = P.component.get( thing )
+ return thingValue ?
+ PickerConstructor._.trigger(
+ P.component.formats.toString,
+ P.component,
+ [ format, thingValue ]
+ ) : ''
+ }
+ return P.component.get( thing )
+ }
+ }, //get
+
+
+
+ /**
+ * Bind events on the things.
+ */
+ on: function( thing, method, internal ) {
+
+ var thingName, thingMethod,
+ thingIsObject = $.isPlainObject( thing ),
+ thingObject = thingIsObject ? thing : {}
+
+ if ( thing ) {
+
+ // If the thing isn’t an object, make it one.
+ if ( !thingIsObject ) {
+ thingObject[ thing ] = method
+ }
+
+ // Go through the things to bind to.
+ for ( thingName in thingObject ) {
+
+ // Grab the method of the thing.
+ thingMethod = thingObject[ thingName ]
+
+ // If it was an internal binding, prefix it.
+ if ( internal ) {
+ thingName = '_' + thingName
+ }
+
+ // Make sure the thing methods collection exists.
+ STATE.methods[ thingName ] = STATE.methods[ thingName ] || []
+
+ // Add the method to the relative method collection.
+ STATE.methods[ thingName ].push( thingMethod )
+ }
+ }
+
+ return P
+ }, //on
+
+
+
+ /**
+ * Unbind events on the things.
+ */
+ off: function() {
+ var i, thingName,
+ names = arguments;
+ for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) {
+ thingName = names[i]
+ if ( thingName in STATE.methods ) {
+ delete STATE.methods[thingName]
+ }
+ }
+ return P
+ },
+
+
+ /**
+ * Fire off method events.
+ */
+ trigger: function( name, data ) {
+ var _trigger = function( name ) {
+ var methodList = STATE.methods[ name ]
+ if ( methodList ) {
+ methodList.map( function( method ) {
+ PickerConstructor._.trigger( method, P, [ data ] )
+ })
+ }
+ }
+ _trigger( '_' + name )
+ _trigger( name )
+ return P
+ } //trigger
+ } //PickerInstance.prototype
+
+
+ /**
+ * Wrap the picker holder components together.
+ */
+ function createWrappedComponent() {
+
+ // Create a picker wrapper holder
+ return PickerConstructor._.node( 'div',
+
+ // Create a picker wrapper node
+ PickerConstructor._.node( 'div',
+
+ // Create a picker frame
+ PickerConstructor._.node( 'div',
+
+ // Create a picker box node
+ PickerConstructor._.node( 'div',
+
+ // Create the components nodes.
+ P.component.nodes( STATE.open ),
+
+ // The picker box class
+ CLASSES.box
+ ),
+
+ // Picker wrap class
+ CLASSES.wrap
+ ),
+
+ // Picker frame class
+ CLASSES.frame
+ ),
+
+ // Picker holder class
+ CLASSES.holder
+ ) //endreturn
+ } //createWrappedComponent
+
+
+
+ /**
+ * Prepare the input element with all bindings.
+ */
+ function prepareElement() {
+
+ $ELEMENT.
+
+ // Store the picker data by component name.
+ data(NAME, P).
+
+ // Add the “input” class name.
+ addClass(CLASSES.input).
+
+ // Remove the tabindex.
+ attr('tabindex', -1).
+
+ // If there’s a `data-value`, update the value of the element.
+ val( $ELEMENT.data('value') ?
+ P.get('select', SETTINGS.format) :
+ ELEMENT.value
+ )
+
+
+ // Only bind keydown events if the element isn’t editable.
+ if ( !SETTINGS.editable ) {
+
+ $ELEMENT.
+
+ // On focus/click, focus onto the root to open it up.
+ on( 'focus.' + STATE.id + ' click.' + STATE.id, function( event ) {
+ event.preventDefault()
+ P.$root.eq(0).focus()
+ }).
+
+ // Handle keyboard event based on the picker being opened or not.
+ on( 'keydown.' + STATE.id, handleKeydownEvent )
+ }
+
+
+ // Update the aria attributes.
+ aria(ELEMENT, {
+ haspopup: true,
+ expanded: false,
+ readonly: false,
+ owns: ELEMENT.id + '_root'
+ })
+ }
+
+
+ /**
+ * Prepare the root picker element with all bindings.
+ */
+ function prepareElementRoot() {
+
+ P.$root.
+
+ on({
+
+ // For iOS8.
+ keydown: handleKeydownEvent,
+
+ // When something within the root is focused, stop from bubbling
+ // to the doc and remove the “focused” state from the root.
+ focusin: function( event ) {
+ P.$root.removeClass( CLASSES.focused )
+ event.stopPropagation()
+ },
+
+ // When something within the root holder is clicked, stop it
+ // from bubbling to the doc.
+ 'mousedown click': function( event ) {
+
+ var target = event.target
+
+ // Make sure the target isn’t the root holder so it can bubble up.
+ if ( target != P.$root.children()[ 0 ] ) {
+
+ event.stopPropagation()
+
+ // * For mousedown events, cancel the default action in order to
+ // prevent cases where focus is shifted onto external elements
+ // when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
+ // Also, for Firefox, don’t prevent action on the `option` element.
+ if ( event.type == 'mousedown' && !$( target ).is( 'input, select, textarea, button, option' )) {
+
+ event.preventDefault()
+
+ // Re-focus onto the root so that users can click away
+ // from elements focused within the picker.
+ P.$root.eq(0).focus()
+ }
+ }
+ }
+ }).
+
+ // Add/remove the “target” class on focus and blur.
+ on({
+ focus: function() {
+ $ELEMENT.addClass( CLASSES.target )
+ },
+ blur: function() {
+ $ELEMENT.removeClass( CLASSES.target )
+ }
+ }).
+
+ // Open the picker and adjust the root “focused” state
+ on( 'focus.toOpen', handleFocusToOpenEvent ).
+
+ // If there’s a click on an actionable element, carry out the actions.
+ on( 'click', '[data-pick], [data-nav], [data-clear], [data-close]', function() {
+
+ var $target = $( this ),
+ targetData = $target.data(),
+ targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ),
+
+ // * For IE, non-focusable elements can be active elements as well
+ // (http://stackoverflow.com/a/2684561).
+ activeElement = getActiveElement()
+ activeElement = activeElement && ( activeElement.type || activeElement.href )
+
+ // If it’s disabled or nothing inside is actively focused, re-focus the element.
+ if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) {
+ P.$root.eq(0).focus()
+ }
+
+ // If something is superficially changed, update the `highlight` based on the `nav`.
+ if ( !targetDisabled && targetData.nav ) {
+ P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } )
+ }
+
+ // If something is picked, set `select` then close with focus.
+ else if ( !targetDisabled && 'pick' in targetData ) {
+ P.set( 'select', targetData.pick )
+ }
+
+ // If a “clear” button is pressed, empty the values and close with focus.
+ else if ( targetData.clear ) {
+ P.clear().close( true )
+ }
+
+ else if ( targetData.close ) {
+ P.close( true )
+ }
+
+ }) //P.$root
+
+ aria( P.$root[0], 'hidden', true )
+ }
+
+
+ /**
+ * Prepare the hidden input element along with all bindings.
+ */
+ function prepareElementHidden() {
+
+ var name
+
+ if ( SETTINGS.hiddenName === true ) {
+ name = ELEMENT.name
+ ELEMENT.name = ''
+ }
+ else {
+ name = [
+ typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
+ typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
+ ]
+ name = name[0] + ELEMENT.name + name[1]
+ }
+
+ P._hidden = $(
+ '<input ' +
+ 'type=hidden ' +
+
+ // Create the name using the original input’s with a prefix and suffix.
+ 'name="' + name + '"' +
+
+ // If the element has a value, set the hidden value as well.
+ (
+ $ELEMENT.data('value') || ELEMENT.value ?
+ ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
+ ''
+ ) +
+ '>'
+ )[0]
+
+ $ELEMENT.
+
+ // If the value changes, update the hidden input with the correct format.
+ on('change.' + STATE.id, function() {
+ P._hidden.value = ELEMENT.value ?
+ P.get('select', SETTINGS.formatSubmit) :
+ ''
+ })
+
+
+ // Insert the hidden input as specified in the settings.
+ if ( SETTINGS.container ) $( SETTINGS.container ).append( P._hidden )
+ else $ELEMENT.after( P._hidden )
+ }
+
+
+ // For iOS8.
+ function handleKeydownEvent( event ) {
+
+ var keycode = event.keyCode,
+
+ // Check if one of the delete keys was pressed.
+ isKeycodeDelete = /^(8|46)$/.test(keycode)
+
+ // For some reason IE clears the input value on “escape”.
+ if ( keycode == 27 ) {
+ P.close()
+ return false
+ }
+
+ // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
+ if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) {
+
+ // Prevent it from moving the page and bubbling to doc.
+ event.preventDefault()
+ event.stopPropagation()
+
+ // If `delete` was pressed, clear the values and close the picker.
+ // Otherwise open the picker.
+ if ( isKeycodeDelete ) { P.clear().close() }
+ else { P.open() }
+ }
+ }
+
+
+ // Separated for IE
+ function handleFocusToOpenEvent( event ) {
+
+ // Stop the event from propagating to the doc.
+ event.stopPropagation()
+
+ // If it’s a focus event, add the “focused” class to the root.
+ if ( event.type == 'focus' ) {
+ P.$root.addClass( CLASSES.focused )
+ }
+
+ // And then finally open the picker.
+ P.open()
+ }
+
+
+ // Return a new picker instance.
+ return new PickerInstance()
+} //PickerConstructor
+
+
+
+/**
+ * The default classes and prefix to use for the HTML classes.
+ */
+PickerConstructor.klasses = function( prefix ) {
+ prefix = prefix || 'picker'
+ return {
+
+ picker: prefix,
+ opened: prefix + '--opened',
+ focused: prefix + '--focused',
+
+ input: prefix + '__input',
+ active: prefix + '__input--active',
+ target: prefix + '__input--target',
+
+ holder: prefix + '__holder',
+
+ frame: prefix + '__frame',
+ wrap: prefix + '__wrap',
+
+ box: prefix + '__box'
+ }
+} //PickerConstructor.klasses
+
+
+
+/**
+ * Check if the default theme is being used.
+ */
+function isUsingDefaultTheme( element ) {
+
+ var theme,
+ prop = 'position'
+
+ // For IE.
+ if ( element.currentStyle ) {
+ theme = element.currentStyle[prop]
+ }
+
+ // For normal browsers.
+ else if ( window.getComputedStyle ) {
+ theme = getComputedStyle( element )[prop]
+ }
+
+ return theme == 'fixed'
+}
+
+
+
+/**
+ * Get the width of the browser’s scrollbar.
+ * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
+ */
+function getScrollbarWidth() {
+
+ if ( $html.height() <= $window.height() ) {
+ return 0
+ }
+
+ var $outer = $( '<div style="visibility:hidden;width:100px" />' ).
+ appendTo( 'body' )
+
+ // Get the width without scrollbars.
+ var widthWithoutScroll = $outer[0].offsetWidth
+
+ // Force adding scrollbars.
+ $outer.css( 'overflow', 'scroll' )
+
+ // Add the inner div.
+ var $inner = $( '<div style="width:100%" />' ).appendTo( $outer )
+
+ // Get the width with scrollbars.
+ var widthWithScroll = $inner[0].offsetWidth
+
+ // Remove the divs.
+ $outer.remove()
+
+ // Return the difference between the widths.
+ return widthWithoutScroll - widthWithScroll
+}
+
+
+
+/**
+ * PickerConstructor helper methods.
+ */
+PickerConstructor._ = {
+
+ /**
+ * Create a group of nodes. Expects:
+ * `
+ {
+ min: {Integer},
+ max: {Integer},
+ i: {Integer},
+ node: {String},
+ item: {Function}
+ }
+ * `
+ */
+ group: function( groupObject ) {
+
+ var
+ // Scope for the looped object
+ loopObjectScope,
+
+ // Create the nodes list
+ nodesList = '',
+
+ // The counter starts from the `min`
+ counter = PickerConstructor._.trigger( groupObject.min, groupObject )
+
+
+ // Loop from the `min` to `max`, incrementing by `i`
+ for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) {
+
+ // Trigger the `item` function within scope of the object
+ loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] )
+
+ // Splice the subgroup and create nodes out of the sub nodes
+ nodesList += PickerConstructor._.node(
+ groupObject.node,
+ loopObjectScope[ 0 ], // the node
+ loopObjectScope[ 1 ], // the classes
+ loopObjectScope[ 2 ] // the attributes
+ )
+ }
+
+ // Return the list of nodes
+ return nodesList
+ }, //group
+
+
+ /**
+ * Create a dom node string
+ */
+ node: function( wrapper, item, klass, attribute ) {
+
+ // If the item is false-y, just return an empty string
+ if ( !item ) return ''
+
+ // If the item is an array, do a join
+ item = $.isArray( item ) ? item.join( '' ) : item
+
+ // Check for the class
+ klass = klass ? ' class="' + klass + '"' : ''
+
+ // Check for any attributes
+ attribute = attribute ? ' ' + attribute : ''
+
+ // Return the wrapped item
+ return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
+ }, //node
+
+
+ /**
+ * Lead numbers below 10 with a zero.
+ */
+ lead: function( number ) {
+ return ( number < 10 ? '0': '' ) + number
+ },
+
+
+ /**
+ * Trigger a function otherwise return the value.
+ */
+ trigger: function( callback, scope, args ) {
+ return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback
+ },
+
+
+ /**
+ * If the second character is a digit, length is 2 otherwise 1.
+ */
+ digits: function( string ) {
+ return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1
+ },
+
+
+ /**
+ * Tell if something is a date object.
+ */
+ isDate: function( value ) {
+ return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() )
+ },
+
+
+ /**
+ * Tell if something is an integer.
+ */
+ isInteger: function( value ) {
+ return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0
+ },
+
+
+ /**
+ * Create ARIA attribute strings.
+ */
+ ariaAttr: ariaAttr
+} //PickerConstructor._
+
+
+
+/**
+ * Extend the picker with a component and defaults.
+ */
+PickerConstructor.extend = function( name, Component ) {
+
+ // Extend jQuery.
+ $.fn[ name ] = function( options, action ) {
+
+ // Grab the component data.
+ var componentData = this.data( name )
+
+ // If the picker is requested, return the data object.
+ if ( options == 'picker' ) {
+ return componentData
+ }
+
+ // If the component data exists and `options` is a string, carry out the action.
+ if ( componentData && typeof options == 'string' ) {
+ return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] )
+ }
+
+ // Otherwise go through each matched element and if the component
+ // doesn’t exist, create a new picker using `this` element
+ // and merging the defaults and options with a deep copy.
+ return this.each( function() {
+ var $this = $( this )
+ if ( !$this.data( name ) ) {
+ new PickerConstructor( this, name, Component, options )
+ }
+ })
+ }
+
+ // Set the defaults.
+ $.fn[ name ].defaults = Component.defaults
+} //PickerConstructor.extend
+
+
+
+function aria(element, attribute, value) {
+ if ( $.isPlainObject(attribute) ) {
+ for ( var key in attribute ) {
+ ariaSet(element, key, attribute[key])
+ }
+ }
+ else {
+ ariaSet(element, attribute, value)
+ }
+}
+function ariaSet(element, attribute, value) {
+ element.setAttribute(
+ (attribute == 'role' ? '' : 'aria-') + attribute,
+ value
+ )
+}
+function ariaAttr(attribute, data) {
+ if ( !$.isPlainObject(attribute) ) {
+ attribute = { attribute: data }
+ }
+ data = ''
+ for ( var key in attribute ) {
+ var attr = (key == 'role' ? '' : 'aria-') + key,
+ attrVal = attribute[key]
+ data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
+ }
+ return data
+}
+
+// IE8 bug throws an error for activeElements within iframes.
+function getActiveElement() {
+ try {
+ return document.activeElement
+ } catch ( err ) { }
+}
+
+
+
+// Expose the picker constructor.
+return PickerConstructor
+
+
+}));
+
+
+;/*!
+ * Date picker for pickadate.js v3.5.0
+ * http://amsul.github.io/pickadate.js/date.htm
+ */
+
+(function ( factory ) {
+
+ // AMD.
+ if ( typeof define == 'function' && define.amd )
+ define( ['picker', 'jquery'], factory )
+
+ // Node.js/browserify.
+ else if ( typeof exports == 'object' )
+ module.exports = factory( require('./picker.js'), require('jquery') )
+
+ // Browser globals.
+ else factory( Picker, jQuery )
+
+}(function( Picker, $ ) {
+
+
+/**
+ * Globals and constants
+ */
+var DAYS_IN_WEEK = 7,
+ WEEKS_IN_CALENDAR = 6,
+ _ = Picker._
+
+
+
+/**
+ * The date picker constructor
+ */
+function DatePicker( picker, settings ) {
+
+ var calendar = this,
+ element = picker.$node[ 0 ],
+ elementValue = element.value,
+ elementDataValue = picker.$node.data( 'value' ),
+ valueString = elementDataValue || elementValue,
+ formatString = elementDataValue ? settings.formatSubmit : settings.format,
+ isRTL = function() {
+
+ return element.currentStyle ?
+
+ // For IE.
+ element.currentStyle.direction == 'rtl' :
+
+ // For normal browsers.
+ getComputedStyle( picker.$root[0] ).direction == 'rtl'
+ }
+
+ calendar.settings = settings
+ calendar.$node = picker.$node
+
+ // The queue of methods that will be used to build item objects.
+ calendar.queue = {
+ min: 'measure create',
+ max: 'measure create',
+ now: 'now create',
+ select: 'parse create validate',
+ highlight: 'parse navigate create validate',
+ view: 'parse create validate viewset',
+ disable: 'deactivate',
+ enable: 'activate'
+ }
+
+ // The component's item object.
+ calendar.item = {}
+
+ calendar.item.clear = null
+ calendar.item.disable = ( settings.disable || [] ).slice( 0 )
+ calendar.item.enable = -(function( collectionDisabled ) {
+ return collectionDisabled[ 0 ] === true ? collectionDisabled.shift() : -1
+ })( calendar.item.disable )
+
+ calendar.
+ set( 'min', settings.min ).
+ set( 'max', settings.max ).
+ set( 'now' )
+
+ // When there’s a value, set the `select`, which in turn
+ // also sets the `highlight` and `view`.
+ if ( valueString ) {
+ calendar.set( 'select', valueString, { format: formatString })
+ }
+
+ // If there’s no value, default to highlighting “today”.
+ else {
+ calendar.
+ set( 'select', null ).
+ set( 'highlight', calendar.item.now )
+ }
+
+
+ // The keycode to movement mapping.
+ calendar.key = {
+ 40: 7, // Down
+ 38: -7, // Up
+ 39: function() { return isRTL() ? -1 : 1 }, // Right
+ 37: function() { return isRTL() ? 1 : -1 }, // Left
+ go: function( timeChange ) {
+ var highlightedObject = calendar.item.highlight,
+ targetDate = new Date( highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange )
+ calendar.set(
+ 'highlight',
+ targetDate,
+ { interval: timeChange }
+ )
+ this.render()
+ }
+ }
+
+
+ // Bind some picker events.
+ picker.
+ on( 'render', function() {
+ picker.$root.find( '.' + settings.klass.selectMonth ).on( 'change', function() {
+ var value = this.value
+ if ( value ) {
+ picker.set( 'highlight', [ picker.get( 'view' ).year, value, picker.get( 'highlight' ).date ] )
+ picker.$root.find( '.' + settings.klass.selectMonth ).trigger( 'focus' )
+ }
+ })
+ picker.$root.find( '.' + settings.klass.selectYear ).on( 'change', function() {
+ var value = this.value
+ if ( value ) {
+ picker.set( 'highlight', [ value, picker.get( 'view' ).month, picker.get( 'highlight' ).date ] )
+ picker.$root.find( '.' + settings.klass.selectYear ).trigger( 'focus' )
+ }
+ })
+ }, 1 ).
+ on( 'open', function() {
+ var includeToday = ''
+ if ( calendar.disabled( calendar.get('now') ) ) {
+ includeToday = ':not(.' + settings.klass.buttonToday + ')'
+ }
+ picker.$root.find( 'button' + includeToday + ', select' ).attr( 'disabled', false )
+ }, 1 ).
+ on( 'close', function() {
+ picker.$root.find( 'button, select' ).attr( 'disabled', true )
+ }, 1 )
+
+} //DatePicker
+
+
+/**
+ * Set a datepicker item object.
+ */
+DatePicker.prototype.set = function( type, value, options ) {
+
+ var calendar = this,
+ calendarItem = calendar.item
+
+ // If the value is `null` just set it immediately.
+ if ( value === null ) {
+ if ( type == 'clear' ) type = 'select'
+ calendarItem[ type ] = value
+ return calendar
+ }
+
+ // Otherwise go through the queue of methods, and invoke the functions.
+ // Update this as the time unit, and set the final value as this item.
+ // * In the case of `enable`, keep the queue but set `disable` instead.
+ // And in the case of `flip`, keep the queue but set `enable` instead.
+ calendarItem[ ( type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type ) ] = calendar.queue[ type ].split( ' ' ).map( function( method ) {
+ value = calendar[ method ]( type, value, options )
+ return value
+ }).pop()
+
+ // Check if we need to cascade through more updates.
+ if ( type == 'select' ) {
+ calendar.set( 'highlight', calendarItem.select, options )
+ }
+ else if ( type == 'highlight' ) {
+ calendar.set( 'view', calendarItem.highlight, options )
+ }
+ else if ( type.match( /^(flip|min|max|disable|enable)$/ ) ) {
+ if ( calendarItem.select && calendar.disabled( calendarItem.select ) ) {
+ calendar.set( 'select', calendarItem.select, options )
+ }
+ if ( calendarItem.highlight && calendar.disabled( calendarItem.highlight ) ) {
+ calendar.set( 'highlight', calendarItem.highlight, options )
+ }
+ }
+
+ return calendar
+} //DatePicker.prototype.set
+
+
+/**
+ * Get a datepicker item object.
+ */
+DatePicker.prototype.get = function( type ) {
+ return this.item[ type ]
+} //DatePicker.prototype.get
+
+
+/**
+ * Create a picker date object.
+ */
+DatePicker.prototype.create = function( type, value, options ) {
+
+ var isInfiniteValue,
+ calendar = this
+
+ // If there’s no value, use the type as the value.
+ value = value === undefined ? type : value
+
+
+ // If it’s infinity, update the value.
+ if ( value == -Infinity || value == Infinity ) {
+ isInfiniteValue = value
+ }
+
+ // If it’s an object, use the native date object.
+ else if ( $.isPlainObject( value ) && _.isInteger( value.pick ) ) {
+ value = value.obj
+ }
+
+ // If it’s an array, convert it into a date and make sure
+ // that it’s a valid date – otherwise default to today.
+ else if ( $.isArray( value ) ) {
+ value = new Date( value[ 0 ], value[ 1 ], value[ 2 ] )
+ value = _.isDate( value ) ? value : calendar.create().obj
+ }
+
+ // If it’s a number or date object, make a normalized date.
+ else if ( _.isInteger( value ) || _.isDate( value ) ) {
+ value = calendar.normalize( new Date( value ), options )
+ }
+
+ // If it’s a literal true or any other case, set it to now.
+ else /*if ( value === true )*/ {
+ value = calendar.now( type, value, options )
+ }
+
+ // Return the compiled object.
+ return {
+ year: isInfiniteValue || value.getFullYear(),
+ month: isInfiniteValue || value.getMonth(),
+ date: isInfiniteValue || value.getDate(),
+ day: isInfiniteValue || value.getDay(),
+ obj: isInfiniteValue || value,
+ pick: isInfiniteValue || value.getTime()
+ }
+} //DatePicker.prototype.create
+
+
+/**
+ * Create a range limit object using an array, date object,
+ * literal “true”, or integer relative to another time.
+ */
+DatePicker.prototype.createRange = function( from, to ) {
+
+ var calendar = this,
+ createDate = function( date ) {
+ if ( date === true || $.isArray( date ) || _.isDate( date ) ) {
+ return calendar.create( date )
+ }
+ return date
+ }
+
+ // Create objects if possible.
+ if ( !_.isInteger( from ) ) {
+ from = createDate( from )
+ }
+ if ( !_.isInteger( to ) ) {
+ to = createDate( to )
+ }
+
+ // Create relative dates.
+ if ( _.isInteger( from ) && $.isPlainObject( to ) ) {
+ from = [ to.year, to.month, to.date + from ];
+ }
+ else if ( _.isInteger( to ) && $.isPlainObject( from ) ) {
+ to = [ from.year, from.month, from.date + to ];
+ }
+
+ return {
+ from: createDate( from ),
+ to: createDate( to )
+ }
+} //DatePicker.prototype.createRange
+
+
+/**
+ * Check if a date unit falls within a date range object.
+ */
+DatePicker.prototype.withinRange = function( range, dateUnit ) {
+ range = this.createRange(range.from, range.to)
+ return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick
+}
+
+
+/**
+ * Check if two date range objects overlap.
+ */
+DatePicker.prototype.overlapRanges = function( one, two ) {
+
+ var calendar = this
+
+ // Convert the ranges into comparable dates.
+ one = calendar.createRange( one.from, one.to )
+ two = calendar.createRange( two.from, two.to )
+
+ return calendar.withinRange( one, two.from ) || calendar.withinRange( one, two.to ) ||
+ calendar.withinRange( two, one.from ) || calendar.withinRange( two, one.to )
+}
+
+
+/**
+ * Get the date today.
+ */
+DatePicker.prototype.now = function( type, value, options ) {
+ value = new Date()
+ if ( options && options.rel ) {
+ value.setDate( value.getDate() + options.rel )
+ }
+ return this.normalize( value, options )
+}
+
+
+/**
+ * Navigate to next/prev month.
+ */
+DatePicker.prototype.navigate = function( type, value, options ) {
+
+ var targetDateObject,
+ targetYear,
+ targetMonth,
+ targetDate,
+ isTargetArray = $.isArray( value ),
+ isTargetObject = $.isPlainObject( value ),
+ viewsetObject = this.item.view/*,
+ safety = 100*/
+
+
+ if ( isTargetArray || isTargetObject ) {
+
+ if ( isTargetObject ) {
+ targetYear = value.year
+ targetMonth = value.month
+ targetDate = value.date
+ }
+ else {
+ targetYear = +value[0]
+ targetMonth = +value[1]
+ targetDate = +value[2]
+ }
+
+ // If we’re navigating months but the view is in a different
+ // month, navigate to the view’s year and month.
+ if ( options && options.nav && viewsetObject && viewsetObject.month !== targetMonth ) {
+ targetYear = viewsetObject.year
+ targetMonth = viewsetObject.month
+ }
+
+ // Figure out the expected target year and month.
+ targetDateObject = new Date( targetYear, targetMonth + ( options && options.nav ? options.nav : 0 ), 1 )
+ targetYear = targetDateObject.getFullYear()
+ targetMonth = targetDateObject.getMonth()
+
+ // If the month we’re going to doesn’t have enough days,
+ // keep decreasing the date until we reach the month’s last date.
+ while ( /*safety &&*/ new Date( targetYear, targetMonth, targetDate ).getMonth() !== targetMonth ) {
+ targetDate -= 1
+ /*safety -= 1
+ if ( !safety ) {
+ throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
+ }*/
+ }
+
+ value = [ targetYear, targetMonth, targetDate ]
+ }
+
+ return value
+} //DatePicker.prototype.navigate
+
+
+/**
+ * Normalize a date by setting the hours to midnight.
+ */
+DatePicker.prototype.normalize = function( value/*, options*/ ) {
+ value.setHours( 0, 0, 0, 0 )
+ return value
+}
+
+
+/**
+ * Measure the range of dates.
+ */
+DatePicker.prototype.measure = function( type, value/*, options*/ ) {
+
+ var calendar = this
+
+ // If it’s anything false-y, remove the limits.
+ if ( !value ) {
+ value = type == 'min' ? -Infinity : Infinity
+ }
+
+ // If it’s a string, parse it.
+ else if ( typeof value == 'string' ) {
+ value = calendar.parse( type, value )
+ }
+
+ // If it's an integer, get a date relative to today.
+ else if ( _.isInteger( value ) ) {
+ value = calendar.now( type, value, { rel: value } )
+ }
+
+ return value
+} ///DatePicker.prototype.measure
+
+
+/**
+ * Create a viewset object based on navigation.
+ */
+DatePicker.prototype.viewset = function( type, dateObject/*, options*/ ) {
+ return this.create([ dateObject.year, dateObject.month, 1 ])
+}
+
+
+/**
+ * Validate a date as enabled and shift if needed.
+ */
+DatePicker.prototype.validate = function( type, dateObject, options ) {
+
+ var calendar = this,
+
+ // Keep a reference to the original date.
+ originalDateObject = dateObject,
+
+ // Make sure we have an interval.
+ interval = options && options.interval ? options.interval : 1,
+
+ // Check if the calendar enabled dates are inverted.
+ isFlippedBase = calendar.item.enable === -1,
+
+ // Check if we have any enabled dates after/before now.
+ hasEnabledBeforeTarget, hasEnabledAfterTarget,
+
+ // The min & max limits.
+ minLimitObject = calendar.item.min,
+ maxLimitObject = calendar.item.max,
+
+ // Check if we’ve reached the limit during shifting.
+ reachedMin, reachedMax,
+
+ // Check if the calendar is inverted and at least one weekday is enabled.
+ hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter( function( value ) {
+
+ // If there’s a date, check where it is relative to the target.
+ if ( $.isArray( value ) ) {
+ var dateTime = calendar.create( value ).pick
+ if ( dateTime < dateObject.pick ) hasEnabledBeforeTarget = true
+ else if ( dateTime > dateObject.pick ) hasEnabledAfterTarget = true
+ }
+
+ // Return only integers for enabled weekdays.
+ return _.isInteger( value )
+ }).length/*,
+
+ safety = 100*/
+
+
+
+ // Cases to validate for:
+ // [1] Not inverted and date disabled.
+ // [2] Inverted and some dates enabled.
+ // [3] Not inverted and out of range.
+ //
+ // Cases to **not** validate for:
+ // • Navigating months.
+ // • Not inverted and date enabled.
+ // • Inverted and all dates disabled.
+ // • ..and anything else.
+ if ( !options || !options.nav ) if (
+ /* 1 */ ( !isFlippedBase && calendar.disabled( dateObject ) ) ||
+ /* 2 */ ( isFlippedBase && calendar.disabled( dateObject ) && ( hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget ) ) ||
+ /* 3 */ ( !isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick) )
+ ) {
+
+
+ // When inverted, flip the direction if there aren’t any enabled weekdays
+ // and there are no enabled dates in the direction of the interval.
+ if ( isFlippedBase && !hasEnabledWeekdays && ( ( !hasEnabledAfterTarget && interval > 0 ) || ( !hasEnabledBeforeTarget && interval < 0 ) ) ) {
+ interval *= -1
+ }
+
+
+ // Keep looping until we reach an enabled date.
+ while ( /*safety &&*/ calendar.disabled( dateObject ) ) {
+
+ /*safety -= 1
+ if ( !safety ) {
+ throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
+ }*/
+
+
+ // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
+ if ( Math.abs( interval ) > 1 && ( dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month ) ) {
+ dateObject = originalDateObject
+ interval = interval > 0 ? 1 : -1
+ }
+
+
+ // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
+ if ( dateObject.pick <= minLimitObject.pick ) {
+ reachedMin = true
+ interval = 1
+ dateObject = calendar.create([
+ minLimitObject.year,
+ minLimitObject.month,
+ minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)
+ ])
+ }
+ else if ( dateObject.pick >= maxLimitObject.pick ) {
+ reachedMax = true
+ interval = -1
+ dateObject = calendar.create([
+ maxLimitObject.year,
+ maxLimitObject.month,
+ maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)
+ ])
+ }
+
+
+ // If we’ve reached both limits, just break out of the loop.
+ if ( reachedMin && reachedMax ) {
+ break
+ }
+
+
+ // Finally, create the shifted date using the interval and keep looping.
+ dateObject = calendar.create([ dateObject.year, dateObject.month, dateObject.date + interval ])
+ }
+
+ } //endif
+
+
+ // Return the date object settled on.
+ return dateObject
+} //DatePicker.prototype.validate
+
+
+/**
+ * Check if a date is disabled.
+ */
+DatePicker.prototype.disabled = function( dateToVerify ) {
+
+ var
+ calendar = this,
+
+ // Filter through the disabled dates to check if this is one.
+ isDisabledMatch = calendar.item.disable.filter( function( dateToDisable ) {
+
+ // If the date is a number, match the weekday with 0index and `firstDay` check.
+ if ( _.isInteger( dateToDisable ) ) {
+ return dateToVerify.day === ( calendar.settings.firstDay ? dateToDisable : dateToDisable - 1 ) % 7
+ }
+
+ // If it’s an array or a native JS date, create and match the exact date.
+ if ( $.isArray( dateToDisable ) || _.isDate( dateToDisable ) ) {
+ return dateToVerify.pick === calendar.create( dateToDisable ).pick
+ }
+
+ // If it’s an object, match a date within the “from” and “to” range.
+ if ( $.isPlainObject( dateToDisable ) ) {
+ return calendar.withinRange( dateToDisable, dateToVerify )
+ }
+ })
+
+ // If this date matches a disabled date, confirm it’s not inverted.
+ isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function( dateToDisable ) {
+ return $.isArray( dateToDisable ) && dateToDisable[3] == 'inverted' ||
+ $.isPlainObject( dateToDisable ) && dateToDisable.inverted
+ }).length
+
+ // Check the calendar “enabled” flag and respectively flip the
+ // disabled state. Then also check if it’s beyond the min/max limits.
+ return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
+ dateToVerify.pick < calendar.item.min.pick ||
+ dateToVerify.pick > calendar.item.max.pick
+
+} //DatePicker.prototype.disabled
+
+
+/**
+ * Parse a string into a usable type.
+ */
+DatePicker.prototype.parse = function( type, value, options ) {
+
+ var calendar = this,
+ parsingObject = {}
+
+ // If it’s already parsed, we’re good.
+ if ( !value || typeof value != 'string' ) {
+ return value
+ }
+
+ // We need a `.format` to parse the value with.
+ if ( !( options && options.format ) ) {
+ options = options || {}
+ options.format = calendar.settings.format
+ }
+
+ // Convert the format into an array and then map through it.
+ calendar.formats.toArray( options.format ).map( function( label ) {
+
+ var
+ // Grab the formatting label.
+ formattingLabel = calendar.formats[ label ],
+
+ // The format length is from the formatting label function or the
+ // label length without the escaping exclamation (!) mark.
+ formatLength = formattingLabel ? _.trigger( formattingLabel, calendar, [ value, parsingObject ] ) : label.replace( /^!/, '' ).length
+
+ // If there's a format label, split the value up to the format length.
+ // Then add it to the parsing object with appropriate label.
+ if ( formattingLabel ) {
+ parsingObject[ label ] = value.substr( 0, formatLength )
+ }
+
+ // Update the value as the substring from format length to end.
+ value = value.substr( formatLength )
+ })
+
+ // Compensate for month 0index.
+ return [
+ parsingObject.yyyy || parsingObject.yy,
+ +( parsingObject.mm || parsingObject.m ) - 1,
+ parsingObject.dd || parsingObject.d
+ ]
+} //DatePicker.prototype.parse
+
+
+/**
+ * Various formats to display the object in.
+ */
+DatePicker.prototype.formats = (function() {
+
+ // Return the length of the first word in a collection.
+ function getWordLengthFromCollection( string, collection, dateObject ) {
+
+ // Grab the first word from the string.
+ var word = string.match( /\w+/ )[ 0 ]
+
+ // If there's no month index, add it to the date object
+ if ( !dateObject.mm && !dateObject.m ) {
+ dateObject.m = collection.indexOf( word ) + 1
+ }
+
+ // Return the length of the word.
+ return word.length
+ }
+
+ // Get the length of the first word in a string.
+ function getFirstWordLength( string ) {
+ return string.match( /\w+/ )[ 0 ].length
+ }
+
+ return {
+
+ d: function( string, dateObject ) {
+
+ // If there's string, then get the digits length.
+ // Otherwise return the selected date.
+ return string ? _.digits( string ) : dateObject.date
+ },
+ dd: function( string, dateObject ) {
+
+ // If there's a string, then the length is always 2.
+ // Otherwise return the selected date with a leading zero.
+ return string ? 2 : _.lead( dateObject.date )
+ },
+ ddd: function( string, dateObject ) {
+
+ // If there's a string, then get the length of the first word.
+ // Otherwise return the short selected weekday.
+ return string ? getFirstWordLength( string ) : this.settings.weekdaysShort[ dateObject.day ]
+ },
+ dddd: function( string, dateObject ) {
+
+ // If there's a string, then get the length of the first word.
+ // Otherwise return the full selected weekday.
+ return string ? getFirstWordLength( string ) : this.settings.weekdaysFull[ dateObject.day ]
+ },
+ m: function( string, dateObject ) {
+
+ // If there's a string, then get the length of the digits
+ // Otherwise return the selected month with 0index compensation.
+ return string ? _.digits( string ) : dateObject.month + 1
+ },
+ mm: function( string, dateObject ) {
+
+ // If there's a string, then the length is always 2.
+ // Otherwise return the selected month with 0index and leading zero.
+ return string ? 2 : _.lead( dateObject.month + 1 )
+ },
+ mmm: function( string, dateObject ) {
+
+ var collection = this.settings.monthsShort
+
+ // If there's a string, get length of the relevant month from the short
+ // months collection. Otherwise return the selected month from that collection.
+ return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
+ },
+ mmmm: function( string, dateObject ) {
+
+ var collection = this.settings.monthsFull
+
+ // If there's a string, get length of the relevant month from the full
+ // months collection. Otherwise return the selected month from that collection.
+ return string ? getWordLengthFromCollection( string, collection, dateObject ) : collection[ dateObject.month ]
+ },
+ yy: function( string, dateObject ) {
+
+ // If there's a string, then the length is always 2.
+ // Otherwise return the selected year by slicing out the first 2 digits.
+ return string ? 2 : ( '' + dateObject.year ).slice( 2 )
+ },
+ yyyy: function( string, dateObject ) {
+
+ // If there's a string, then the length is always 4.
+ // Otherwise return the selected year.
+ return string ? 4 : dateObject.year
+ },
+
+ // Create an array by splitting the formatting string passed.
+ toArray: function( formatString ) { return formatString.split( /(d{1,4}|m{1,4}|y{4}|yy|!.)/g ) },
+
+ // Format an object into a string using the formatting options.
+ toString: function ( formatString, itemObject ) {
+ var calendar = this
+ return calendar.formats.toArray( formatString ).map( function( label ) {
+ return _.trigger( calendar.formats[ label ], calendar, [ 0, itemObject ] ) || label.replace( /^!/, '' )
+ }).join( '' )
+ }
+ }
+})() //DatePicker.prototype.formats
+
+
+
+
+/**
+ * Check if two date units are the exact.
+ */
+DatePicker.prototype.isDateExact = function( one, two ) {
+
+ var calendar = this
+
+ // When we’re working with weekdays, do a direct comparison.
+ if (
+ ( _.isInteger( one ) && _.isInteger( two ) ) ||
+ ( typeof one == 'boolean' && typeof two == 'boolean' )
+ ) {
+ return one === two
+ }
+
+ // When we’re working with date representations, compare the “pick” value.
+ if (
+ ( _.isDate( one ) || $.isArray( one ) ) &&
+ ( _.isDate( two ) || $.isArray( two ) )
+ ) {
+ return calendar.create( one ).pick === calendar.create( two ).pick
+ }
+
+ // When we’re working with range objects, compare the “from” and “to”.
+ if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
+ return calendar.isDateExact( one.from, two.from ) && calendar.isDateExact( one.to, two.to )
+ }
+
+ return false
+}
+
+
+/**
+ * Check if two date units overlap.
+ */
+DatePicker.prototype.isDateOverlap = function( one, two ) {
+
+ var calendar = this,
+ firstDay = calendar.settings.firstDay ? 1 : 0
+
+ // When we’re working with a weekday index, compare the days.
+ if ( _.isInteger( one ) && ( _.isDate( two ) || $.isArray( two ) ) ) {
+ one = one % 7 + firstDay
+ return one === calendar.create( two ).day + 1
+ }
+ if ( _.isInteger( two ) && ( _.isDate( one ) || $.isArray( one ) ) ) {
+ two = two % 7 + firstDay
+ return two === calendar.create( one ).day + 1
+ }
+
+ // When we’re working with range objects, check if the ranges overlap.
+ if ( $.isPlainObject( one ) && $.isPlainObject( two ) ) {
+ return calendar.overlapRanges( one, two )
+ }
+
+ return false
+}
+
+
+/**
+ * Flip the “enabled” state.
+ */
+DatePicker.prototype.flipEnable = function(val) {
+ var itemObject = this.item
+ itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
+}
+
+
+/**
+ * Mark a collection of dates as “disabled”.
+ */
+DatePicker.prototype.deactivate = function( type, datesToDisable ) {
+
+ var calendar = this,
+ disabledItems = calendar.item.disable.slice(0)
+
+
+ // If we’re flipping, that’s all we need to do.
+ if ( datesToDisable == 'flip' ) {
+ calendar.flipEnable()
+ }
+
+ else if ( datesToDisable === false ) {
+ calendar.flipEnable(1)
+ disabledItems = []
+ }
+
+ else if ( datesToDisable === true ) {
+ calendar.flipEnable(-1)
+ disabledItems = []
+ }
+
+ // Otherwise go through the dates to disable.
+ else {
+
+ datesToDisable.map(function( unitToDisable ) {
+
+ var matchFound
+
+ // When we have disabled items, check for matches.
+ // If something is matched, immediately break out.
+ for ( var index = 0; index < disabledItems.length; index += 1 ) {
+ if ( calendar.isDateExact( unitToDisable, disabledItems[index] ) ) {
+ matchFound = true
+ break
+ }
+ }
+
+ // If nothing was found, add the validated unit to the collection.
+ if ( !matchFound ) {
+ if (
+ _.isInteger( unitToDisable ) ||
+ _.isDate( unitToDisable ) ||
+ $.isArray( unitToDisable ) ||
+ ( $.isPlainObject( unitToDisable ) && unitToDisable.from && unitToDisable.to )
+ ) {
+ disabledItems.push( unitToDisable )
+ }
+ }
+ })
+ }
+
+ // Return the updated collection.
+ return disabledItems
+} //DatePicker.prototype.deactivate
+
+
+/**
+ * Mark a collection of dates as “enabled”.
+ */
+DatePicker.prototype.activate = function( type, datesToEnable ) {
+
+ var calendar = this,
+ disabledItems = calendar.item.disable,
+ disabledItemsCount = disabledItems.length
+
+ // If we’re flipping, that’s all we need to do.
+ if ( datesToEnable == 'flip' ) {
+ calendar.flipEnable()
+ }
+
+ else if ( datesToEnable === true ) {
+ calendar.flipEnable(1)
+ disabledItems = []
+ }
+
+ else if ( datesToEnable === false ) {
+ calendar.flipEnable(-1)
+ disabledItems = []
+ }
+
+ // Otherwise go through the disabled dates.
+ else {
+
+ datesToEnable.map(function( unitToEnable ) {
+
+ var matchFound,
+ disabledUnit,
+ index,
+ isExactRange
+
+ // Go through the disabled items and try to find a match.
+ for ( index = 0; index < disabledItemsCount; index += 1 ) {
+
+ disabledUnit = disabledItems[index]
+
+ // When an exact match is found, remove it from the collection.
+ if ( calendar.isDateExact( disabledUnit, unitToEnable ) ) {
+ matchFound = disabledItems[index] = null
+ isExactRange = true
+ break
+ }
+
+ // When an overlapped match is found, add the “inverted” state to it.
+ else if ( calendar.isDateOverlap( disabledUnit, unitToEnable ) ) {
+ if ( $.isPlainObject( unitToEnable ) ) {
+ unitToEnable.inverted = true
+ matchFound = unitToEnable
+ }
+ else if ( $.isArray( unitToEnable ) ) {
+ matchFound = unitToEnable
+ if ( !matchFound[3] ) matchFound.push( 'inverted' )
+ }
+ else if ( _.isDate( unitToEnable ) ) {
+ matchFound = [ unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted' ]
+ }
+ break
+ }
+ }
+
+ // If a match was found, remove a previous duplicate entry.
+ if ( matchFound ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
+ if ( calendar.isDateExact( disabledItems[index], unitToEnable ) ) {
+ disabledItems[index] = null
+ break
+ }
+ }
+
+ // In the event that we’re dealing with an exact range of dates,
+ // make sure there are no “inverted” dates because of it.
+ if ( isExactRange ) for ( index = 0; index < disabledItemsCount; index += 1 ) {
+ if ( calendar.isDateOverlap( disabledItems[index], unitToEnable ) ) {
+ disabledItems[index] = null
+ break
+ }
+ }
+
+ // If something is still matched, add it into the collection.
+ if ( matchFound ) {
+ disabledItems.push( matchFound )
+ }
+ })
+ }
+
+ // Return the updated collection.
+ return disabledItems.filter(function( val ) { return val != null })
+} //DatePicker.prototype.activate
+
+
+/**
+ * Create a string for the nodes in the picker.
+ */
+DatePicker.prototype.nodes = function( isOpen ) {
+
+ var
+ calendar = this,
+ settings = calendar.settings,
+ calendarItem = calendar.item,
+ nowObject = calendarItem.now,
+ selectedObject = calendarItem.select,
+ highlightedObject = calendarItem.highlight,
+ viewsetObject = calendarItem.view,
+ disabledCollection = calendarItem.disable,
+ minLimitObject = calendarItem.min,
+ maxLimitObject = calendarItem.max,
+
+
+ // Create the calendar table head using a copy of weekday labels collection.
+ // * We do a copy so we don't mutate the original array.
+ tableHead = (function( collection, fullCollection ) {
+
+ // If the first day should be Monday, move Sunday to the end.
+ if ( settings.firstDay ) {
+ collection.push( collection.shift() )
+ fullCollection.push( fullCollection.shift() )
+ }
+
+ // Create and return the table head group.
+ return _.node(
+ 'thead',
+ _.node(
+ 'tr',
+ _.group({
+ min: 0,
+ max: DAYS_IN_WEEK - 1,
+ i: 1,
+ node: 'th',
+ item: function( counter ) {
+ return [
+ collection[ counter ],
+ settings.klass.weekdays,
+ 'scope=col title="' + fullCollection[ counter ] + '"'
+ ]
+ }
+ })
+ )
+ ) //endreturn
+
+ // Materialize modified
+ })( ( settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysLetter ).slice( 0 ), settings.weekdaysFull.slice( 0 ) ), //tableHead
+
+
+ // Create the nav for next/prev month.
+ createMonthNav = function( next ) {
+
+ // Otherwise, return the created month tag.
+ return _.node(
+ 'div',
+ ' ',
+ settings.klass[ 'nav' + ( next ? 'Next' : 'Prev' ) ] + (
+
+ // If the focused month is outside the range, disabled the button.
+ ( next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month ) ||
+ ( !next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month ) ?
+ ' ' + settings.klass.navDisabled : ''
+ ),
+ 'data-nav=' + ( next || -1 ) + ' ' +
+ _.ariaAttr({
+ role: 'button',
+ controls: calendar.$node[0].id + '_table'
+ }) + ' ' +
+ 'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev ) + '"'
+ ) //endreturn
+ }, //createMonthNav
+
+
+ // Create the month label.
+ //Materialize modified
+ createMonthLabel = function(override) {
+
+ var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull
+
+ // Materialize modified
+ if (override == "short_months") {
+ monthsCollection = settings.monthsShort;
+ }
+
+ // If there are months to select, add a dropdown menu.
+ if ( settings.selectMonths && override == undefined) {
+
+ return _.node( 'select',
+ _.group({
+ min: 0,
+ max: 11,
+ i: 1,
+ node: 'option',
+ item: function( loopedMonth ) {
+
+ return [
+
+ // The looped month and no classes.
+ monthsCollection[ loopedMonth ], 0,
+
+ // Set the value and selected index.
+ 'value=' + loopedMonth +
+ ( viewsetObject.month == loopedMonth ? ' selected' : '' ) +
+ (
+ (
+ ( viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month ) ||
+ ( viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month )
+ ) ?
+ ' disabled' : ''
+ )
+ ]
+ }
+ }),
+ settings.klass.selectMonth + ' browser-default',
+ ( isOpen ? '' : 'disabled' ) + ' ' +
+ _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
+ 'title="' + settings.labelMonthSelect + '"'
+ )
+ }
+
+ // Materialize modified
+ if (override == "short_months")
+ if (selectedObject != null)
+ return _.node( 'div', monthsCollection[ selectedObject.month ] );
+ else return _.node( 'div', monthsCollection[ viewsetObject.month ] );
+
+ // If there's a need for a month selector
+ return _.node( 'div', monthsCollection[ viewsetObject.month ], settings.klass.month )
+ }, //createMonthLabel
+
+
+ // Create the year label.
+ // Materialize modified
+ createYearLabel = function(override) {
+
+ var focusedYear = viewsetObject.year,
+
+ // If years selector is set to a literal "true", set it to 5. Otherwise
+ // divide in half to get half before and half after focused year.
+ numberYears = settings.selectYears === true ? 5 : ~~( settings.selectYears / 2 )
+
+ // If there are years to select, add a dropdown menu.
+ if ( numberYears ) {
+
+ var
+ minYear = minLimitObject.year,
+ maxYear = maxLimitObject.year,
+ lowestYear = focusedYear - numberYears,
+ highestYear = focusedYear + numberYears
+
+ // If the min year is greater than the lowest year, increase the highest year
+ // by the difference and set the lowest year to the min year.
+ if ( minYear > lowestYear ) {
+ highestYear += minYear - lowestYear
+ lowestYear = minYear
+ }
+
+ // If the max year is less than the highest year, decrease the lowest year
+ // by the lower of the two: available and needed years. Then set the
+ // highest year to the max year.
+ if ( maxYear < highestYear ) {
+
+ var availableYears = lowestYear - minYear,
+ neededYears = highestYear - maxYear
+
+ lowestYear -= availableYears > neededYears ? neededYears : availableYears
+ highestYear = maxYear
+ }
+
+ if ( settings.selectYears && override == undefined ) {
+ return _.node( 'select',
+ _.group({
+ min: lowestYear,
+ max: highestYear,
+ i: 1,
+ node: 'option',
+ item: function( loopedYear ) {
+ return [
+
+ // The looped year and no classes.
+ loopedYear, 0,
+
+ // Set the value and selected index.
+ 'value=' + loopedYear + ( focusedYear == loopedYear ? ' selected' : '' )
+ ]
+ }
+ }),
+ settings.klass.selectYear + ' browser-default',
+ ( isOpen ? '' : 'disabled' ) + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
+ 'title="' + settings.labelYearSelect + '"'
+ )
+ }
+ }
+
+ // Materialize modified
+ if (override == "raw")
+ return _.node( 'div', focusedYear )
+
+ // Otherwise just return the year focused
+ return _.node( 'div', focusedYear, settings.klass.year )
+ } //createYearLabel
+
+
+ // Materialize modified
+ createDayLabel = function() {
+ if (selectedObject != null)
+ return _.node( 'div', selectedObject.date)
+ else return _.node( 'div', nowObject.date)
+ }
+ createWeekdayLabel = function() {
+ var display_day;
+
+ if (selectedObject != null)
+ display_day = selectedObject.day;
+ else
+ display_day = nowObject.day;
+ var weekday = settings.weekdaysFull[ display_day ]
+ return weekday
+ }
+
+
+ // Create and return the entire calendar.
+return _.node(
+ // Date presentation View
+ 'div',
+ _.node(
+ 'div',
+ createWeekdayLabel(),
+ "picker__weekday-display"
+ )+
+ _.node(
+ // Div for short Month
+ 'div',
+ createMonthLabel("short_months"),
+ settings.klass.month_display
+ )+
+ _.node(
+ // Div for Day
+ 'div',
+ createDayLabel() ,
+ settings.klass.day_display
+ )+
+ _.node(
+ // Div for Year
+ 'div',
+ createYearLabel("raw") ,
+ settings.klass.year_display
+ ),
+ settings.klass.date_display
+ )+
+ // Calendar container
+ _.node('div',
+ _.node('div',
+ ( settings.selectYears ? createMonthLabel() + createYearLabel() : createMonthLabel() + createYearLabel() ) +
+ createMonthNav() + createMonthNav( 1 ),
+ settings.klass.header
+ ) + _.node(
+ 'table',
+ tableHead +
+ _.node(
+ 'tbody',
+ _.group({
+ min: 0,
+ max: WEEKS_IN_CALENDAR - 1,
+ i: 1,
+ node: 'tr',
+ item: function( rowCounter ) {
+
+ // If Monday is the first day and the month starts on Sunday, shift the date back a week.
+ var shiftDateBy = settings.firstDay && calendar.create([ viewsetObject.year, viewsetObject.month, 1 ]).day === 0 ? -7 : 0
+
+ return [
+ _.group({
+ min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
+ max: function() {
+ return this.min + DAYS_IN_WEEK - 1
+ },
+ i: 1,
+ node: 'td',
+ item: function( targetDate ) {
+
+ // Convert the time date from a relative date to a target date.
+ targetDate = calendar.create([ viewsetObject.year, viewsetObject.month, targetDate + ( settings.firstDay ? 1 : 0 ) ])
+
+ var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
+ isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
+ isDisabled = disabledCollection && calendar.disabled( targetDate ) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick,
+ formattedDate = _.trigger( calendar.formats.toString, calendar, [ settings.format, targetDate ] )
+
+ return [
+ _.node(
+ 'div',
+ targetDate.date,
+ (function( klasses ) {
+
+ // Add the `infocus` or `outfocus` classes based on month in view.
+ klasses.push( viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus )
+
+ // Add the `today` class if needed.
+ if ( nowObject.pick == targetDate.pick ) {
+ klasses.push( settings.klass.now )
+ }
+
+ // Add the `selected` class if something's selected and the time matches.
+ if ( isSelected ) {
+ klasses.push( settings.klass.selected )
+ }
+
+ // Add the `highlighted` class if something's highlighted and the time matches.
+ if ( isHighlighted ) {
+ klasses.push( settings.klass.highlighted )
+ }
+
+ // Add the `disabled` class if something's disabled and the object matches.
+ if ( isDisabled ) {
+ klasses.push( settings.klass.disabled )
+ }
+
+ return klasses.join( ' ' )
+ })([ settings.klass.day ]),
+ 'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
+ role: 'gridcell',
+ label: formattedDate,
+ selected: isSelected && calendar.$node.val() === formattedDate ? true : null,
+ activedescendant: isHighlighted ? true : null,
+ disabled: isDisabled ? true : null
+ })
+ ),
+ '',
+ _.ariaAttr({ role: 'presentation' })
+ ] //endreturn
+ }
+ })
+ ] //endreturn
+ }
+ })
+ ),
+ settings.klass.table,
+ 'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
+ role: 'grid',
+ controls: calendar.$node[0].id,
+ readonly: true
+ })
+ )
+ , settings.klass.calendar_container) // end calendar
+
+ +
+
+ // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”.
+ _.node(
+ 'div',
+ _.node( 'button', settings.today, "btn-flat picker__today",
+ 'type=button data-pick=' + nowObject.pick +
+ ( isOpen && !calendar.disabled(nowObject) ? '' : ' disabled' ) + ' ' +
+ _.ariaAttr({ controls: calendar.$node[0].id }) ) +
+ _.node( 'button', settings.clear, "btn-flat picker__clear",
+ 'type=button data-clear=1' +
+ ( isOpen ? '' : ' disabled' ) + ' ' +
+ _.ariaAttr({ controls: calendar.$node[0].id }) ) +
+ _.node('button', settings.close, "btn-flat picker__close",
+ 'type=button data-close=true ' +
+ ( isOpen ? '' : ' disabled' ) + ' ' +
+ _.ariaAttr({ controls: calendar.$node[0].id }) ),
+ settings.klass.footer
+ ) //endreturn
+} //DatePicker.prototype.nodes
+
+
+
+
+/**
+ * The date picker defaults.
+ */
+DatePicker.defaults = (function( prefix ) {
+
+ return {
+
+ // The title label to use for the month nav buttons
+ labelMonthNext: 'Next month',
+ labelMonthPrev: 'Previous month',
+
+ // The title label to use for the dropdown selectors
+ labelMonthSelect: 'Select a month',
+ labelYearSelect: 'Select a year',
+
+ // Months and weekdays
+ monthsFull: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
+ monthsShort: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
+ weekdaysFull: [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
+ weekdaysShort: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
+
+ // Materialize modified
+ weekdaysLetter: [ 'S', 'M', 'T', 'W', 'T', 'F', 'S' ],
+
+ // Today and clear
+ today: 'Today',
+ clear: 'Clear',
+ close: 'Close',
+
+ // The format to show on the `input` element
+ format: 'd mmmm, yyyy',
+
+ // Classes
+ klass: {
+
+ table: prefix + 'table',
+
+ header: prefix + 'header',
+
+
+ // Materialize Added klasses
+ date_display: prefix + 'date-display',
+ day_display: prefix + 'day-display',
+ month_display: prefix + 'month-display',
+ year_display: prefix + 'year-display',
+ calendar_container: prefix + 'calendar-container',
+ // end
+
+
+
+ navPrev: prefix + 'nav--prev',
+ navNext: prefix + 'nav--next',
+ navDisabled: prefix + 'nav--disabled',
+
+ month: prefix + 'month',
+ year: prefix + 'year',
+
+ selectMonth: prefix + 'select--month',
+ selectYear: prefix + 'select--year',
+
+ weekdays: prefix + 'weekday',
+
+ day: prefix + 'day',
+ disabled: prefix + 'day--disabled',
+ selected: prefix + 'day--selected',
+ highlighted: prefix + 'day--highlighted',
+ now: prefix + 'day--today',
+ infocus: prefix + 'day--infocus',
+ outfocus: prefix + 'day--outfocus',
+
+ footer: prefix + 'footer',
+
+ buttonClear: prefix + 'button--clear',
+ buttonToday: prefix + 'button--today',
+ buttonClose: prefix + 'button--close'
+ }
+ }
+})( Picker.klasses().picker + '__' )
+
+
+
+
+
+/**
+ * Extend the picker to add the date picker.
+ */
+Picker.extend( 'pickadate', DatePicker )
+
+
+}));
+
+
+;(function ($) {
+
+ $.fn.characterCounter = function(){
+ return this.each(function(){
+ var $input = $(this);
+ var $counterElement = $input.parent().find('span[class="character-counter"]');
+
+ // character counter has already been added appended to the parent container
+ if ($counterElement.length) {
+ return;
+ }
+
+ var itHasLengthAttribute = $input.attr('data-length') !== undefined;
+
+ if(itHasLengthAttribute){
+ $input.on('input', updateCounter);
+ $input.on('focus', updateCounter);
+ $input.on('blur', removeCounterElement);
+
+ addCounterElement($input);
+ }
+
+ });
+ };
+
+ function updateCounter(){
+ var maxLength = +$(this).attr('data-length'),
+ actualLength = +$(this).val().length,
+ isValidLength = actualLength <= maxLength;
+
+ $(this).parent().find('span[class="character-counter"]')
+ .html( actualLength + '/' + maxLength);
+
+ addInputStyle(isValidLength, $(this));
+ }
+
+ function addCounterElement($input) {
+ var $counterElement = $input.parent().find('span[class="character-counter"]');
+
+ if ($counterElement.length) {
+ return;
+ }
+
+ $counterElement = $('<span/>')
+ .addClass('character-counter')
+ .css('float','right')
+ .css('font-size','12px')
+ .css('height', 1);
+
+ $input.parent().append($counterElement);
+ }
+
+ function removeCounterElement(){
+ $(this).parent().find('span[class="character-counter"]').html('');
+ }
+
+ function addInputStyle(isValidLength, $input){
+ var inputHasInvalidClass = $input.hasClass('invalid');
+ if (isValidLength && inputHasInvalidClass) {
+ $input.removeClass('invalid');
+ }
+ else if(!isValidLength && !inputHasInvalidClass){
+ $input.removeClass('valid');
+ $input.addClass('invalid');
+ }
+ }
+
+ $(document).ready(function(){
+ $('input, textarea').characterCounter();
+ });
+
+}( jQuery ));
+;(function ($) {
+
+ var methods = {
+
+ init : function(options) {
+ var defaults = {
+ duration: 200, // ms
+ dist: -100, // zoom scale TODO: make this more intuitive as an option
+ shift: 0, // spacing for center image
+ padding: 0, // Padding between non center items
+ fullWidth: false, // Change to full width styles
+ indicators: false, // Toggle indicators
+ noWrap: false, // Don't wrap around and cycle through items.
+ onCycleTo: null // Callback for when a new slide is cycled to.
+ };
+ options = $.extend(defaults, options);
+
+ return this.each(function() {
+
+ var images, item_width, item_height, offset, center, pressed, dim, count,
+ reference, referenceY, amplitude, target, velocity,
+ xform, frame, timestamp, ticker, dragged, vertical_dragged;
+ var $indicators = $('<ul class="indicators"></ul>');
+
+
+ // Initialize
+ var view = $(this);
+ var showIndicators = view.attr('data-indicators') || options.indicators;
+
+ // Don't double initialize.
+ if (view.hasClass('initialized')) {
+ // Redraw carousel.
+ $(this).trigger('carouselNext', [0.000001]);
+ return true;
+ }
+
+
+ // Options
+ if (options.fullWidth) {
+ options.dist = 0;
+ var firstImage = view.find('.carousel-item img').first();
+ if (firstImage.length) {
+ imageHeight = firstImage.on('load', function(){
+ view.css('height', $(this).height());
+ });
+ } else {
+ imageHeight = view.find('.carousel-item').first().height();
+ view.css('height', imageHeight);
+ }
+
+ // Offset fixed items when indicators.
+ if (showIndicators) {
+ view.find('.carousel-fixed-item').addClass('with-indicators');
+ }
+ }
+
+
+ view.addClass('initialized');
+ pressed = false;
+ offset = target = 0;
+ images = [];
+ item_width = view.find('.carousel-item').first().innerWidth();
+ item_height = view.find('.carousel-item').first().innerHeight();
+ dim = item_width * 2 + options.padding;
+
+ view.find('.carousel-item').each(function (i) {
+ images.push($(this)[0]);
+ if (showIndicators) {
+ var $indicator = $('<li class="indicator-item"></li>');
+
+ // Add active to first by default.
+ if (i === 0) {
+ $indicator.addClass('active');
+ }
+
+ // Handle clicks on indicators.
+ $indicator.click(function (e) {
+ e.stopPropagation();
+
+ var index = $(this).index();
+ cycleTo(index);
+ });
+ $indicators.append($indicator);
+ }
+ });
+
+ if (showIndicators) {
+ view.append($indicators);
+ }
+ count = images.length;
+
+
+ function setupEvents() {
+ if (typeof window.ontouchstart !== 'undefined') {
+ view[0].addEventListener('touchstart', tap);
+ view[0].addEventListener('touchmove', drag);
+ view[0].addEventListener('touchend', release);
+ }
+ view[0].addEventListener('mousedown', tap);
+ view[0].addEventListener('mousemove', drag);
+ view[0].addEventListener('mouseup', release);
+ view[0].addEventListener('mouseleave', release);
+ view[0].addEventListener('click', click);
+ }
+
+ function xpos(e) {
+ // touch event
+ if (e.targetTouches && (e.targetTouches.length >= 1)) {
+ return e.targetTouches[0].clientX;
+ }
+
+ // mouse event
+ return e.clientX;
+ }
+
+ function ypos(e) {
+ // touch event
+ if (e.targetTouches && (e.targetTouches.length >= 1)) {
+ return e.targetTouches[0].clientY;
+ }
+
+ // mouse event
+ return e.clientY;
+ }
+
+ function wrap(x) {
+ return (x >= count) ? (x % count) : (x < 0) ? wrap(count + (x % count)) : x;
+ }
+
+ function scroll(x) {
+ var i, half, delta, dir, tween, el, alignment, xTranslation;
+ var lastCenter = center;
+
+ offset = (typeof x === 'number') ? x : offset;
+ center = Math.floor((offset + dim / 2) / dim);
+ delta = offset - center * dim;
+ dir = (delta < 0) ? 1 : -1;
+ tween = -dir * delta * 2 / dim;
+ half = count >> 1;
+
+ if (!options.fullWidth) {
+ alignment = 'translateX(' + (view[0].clientWidth - item_width) / 2 + 'px) ';
+ alignment += 'translateY(' + (view[0].clientHeight - item_height) / 2 + 'px)';
+ } else {
+ alignment = 'translateX(0)';
+ }
+
+ // Set indicator active
+ if (showIndicators) {
+ var diff = (center % count);
+ var activeIndicator = $indicators.find('.indicator-item.active');
+ if (activeIndicator.index() !== diff) {
+ activeIndicator.removeClass('active');
+ $indicators.find('.indicator-item').eq(diff).addClass('active');
+ }
+ }
+
+ // center
+ // Don't show wrapped items.
+ if (!options.noWrap || (center >= 0 && center < count)) {
+ el = images[wrap(center)];
+
+ // Add active class to center item.
+ if (!$(el).hasClass('active')) {
+ view.find('.carousel-item').removeClass('active');
+ $(el).addClass('active');
+ }
+ el.style[xform] = alignment +
+ ' translateX(' + (-delta / 2) + 'px)' +
+ ' translateX(' + (dir * options.shift * tween * i) + 'px)' +
+ ' translateZ(' + (options.dist * tween) + 'px)';
+ el.style.zIndex = 0;
+ if (options.fullWidth) { tweenedOpacity = 1; }
+ else { tweenedOpacity = 1 - 0.2 * tween; }
+ el.style.opacity = tweenedOpacity;
+ el.style.display = 'block';
+ }
+
+ for (i = 1; i <= half; ++i) {
+ // right side
+ if (options.fullWidth) {
+ zTranslation = options.dist;
+ tweenedOpacity = (i === half && delta < 0) ? 1 - tween : 1;
+ } else {
+ zTranslation = options.dist * (i * 2 + tween * dir);
+ tweenedOpacity = 1 - 0.2 * (i * 2 + tween * dir);
+ }
+ // Don't show wrapped items.
+ if (!options.noWrap || center + i < count) {
+ el = images[wrap(center + i)];
+ el.style[xform] = alignment +
+ ' translateX(' + (options.shift + (dim * i - delta) / 2) + 'px)' +
+ ' translateZ(' + zTranslation + 'px)';
+ el.style.zIndex = -i;
+ el.style.opacity = tweenedOpacity;
+ el.style.display = 'block';
+ }
+
+
+ // left side
+ if (options.fullWidth) {
+ zTranslation = options.dist;
+ tweenedOpacity = (i === half && delta > 0) ? 1 - tween : 1;
+ } else {
+ zTranslation = options.dist * (i * 2 - tween * dir);
+ tweenedOpacity = 1 - 0.2 * (i * 2 - tween * dir);
+ }
+ // Don't show wrapped items.
+ if (!options.noWrap || center - i >= 0) {
+ el = images[wrap(center - i)];
+ el.style[xform] = alignment +
+ ' translateX(' + (-options.shift + (-dim * i - delta) / 2) + 'px)' +
+ ' translateZ(' + zTranslation + 'px)';
+ el.style.zIndex = -i;
+ el.style.opacity = tweenedOpacity;
+ el.style.display = 'block';
+ }
+ }
+
+ // center
+ // Don't show wrapped items.
+ if (!options.noWrap || (center >= 0 && center < count)) {
+ el = images[wrap(center)];
+ el.style[xform] = alignment +
+ ' translateX(' + (-delta / 2) + 'px)' +
+ ' translateX(' + (dir * options.shift * tween) + 'px)' +
+ ' translateZ(' + (options.dist * tween) + 'px)';
+ el.style.zIndex = 0;
+ if (options.fullWidth) { tweenedOpacity = 1; }
+ else { tweenedOpacity = 1 - 0.2 * tween; }
+ el.style.opacity = tweenedOpacity;
+ el.style.display = 'block';
+ }
+
+ // onCycleTo callback
+ if (lastCenter !== center &&
+ typeof(options.onCycleTo) === "function") {
+ var $curr_item = view.find('.carousel-item').eq(wrap(center));
+ options.onCycleTo.call(this, $curr_item, dragged);
+ }
+ }
+
+ function track() {
+ var now, elapsed, delta, v;
+
+ now = Date.now();
+ elapsed = now - timestamp;
+ timestamp = now;
+ delta = offset - frame;
+ frame = offset;
+
+ v = 1000 * delta / (1 + elapsed);
+ velocity = 0.8 * v + 0.2 * velocity;
+ }
+
+ function autoScroll() {
+ var elapsed, delta;
+
+ if (amplitude) {
+ elapsed = Date.now() - timestamp;
+ delta = amplitude * Math.exp(-elapsed / options.duration);
+ if (delta > 2 || delta < -2) {
+ scroll(target - delta);
+ requestAnimationFrame(autoScroll);
+ } else {
+ scroll(target);
+ }
+ }
+ }
+
+ function click(e) {
+ // Disable clicks if carousel was dragged.
+ if (dragged) {
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+
+ } else if (!options.fullWidth) {
+ var clickedIndex = $(e.target).closest('.carousel-item').index();
+ var diff = (center % count) - clickedIndex;
+
+ // Disable clicks if carousel was shifted by click
+ if (diff !== 0) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ cycleTo(clickedIndex);
+ }
+ }
+
+ function cycleTo(n) {
+ var diff = (center % count) - n;
+
+ // Account for wraparound.
+ if (!options.noWrap) {
+ if (diff < 0) {
+ if (Math.abs(diff + count) < Math.abs(diff)) { diff += count; }
+
+ } else if (diff > 0) {
+ if (Math.abs(diff - count) < diff) { diff -= count; }
+ }
+ }
+
+ // Call prev or next accordingly.
+ if (diff < 0) {
+ view.trigger('carouselNext', [Math.abs(diff)]);
+
+ } else if (diff > 0) {
+ view.trigger('carouselPrev', [diff]);
+ }
+ }
+
+ function tap(e) {
+ pressed = true;
+ dragged = false;
+ vertical_dragged = false;
+ reference = xpos(e);
+ referenceY = ypos(e);
+
+ velocity = amplitude = 0;
+ frame = offset;
+ timestamp = Date.now();
+ clearInterval(ticker);
+ ticker = setInterval(track, 100);
+
+ }
+
+ function drag(e) {
+ var x, delta, deltaY;
+ if (pressed) {
+ x = xpos(e);
+ y = ypos(e);
+ delta = reference - x;
+ deltaY = Math.abs(referenceY - y);
+ if (deltaY < 30 && !vertical_dragged) {
+ // If vertical scrolling don't allow dragging.
+ if (delta > 2 || delta < -2) {
+ dragged = true;
+ reference = x;
+ scroll(offset + delta);
+ }
+
+ } else if (dragged) {
+ // If dragging don't allow vertical scroll.
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+
+ } else {
+ // Vertical scrolling.
+ vertical_dragged = true;
+ }
+ }
+
+ if (dragged) {
+ // If dragging don't allow vertical scroll.
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ }
+
+ function release(e) {
+ if (pressed) {
+ pressed = false;
+ } else {
+ return;
+ }
+
+ clearInterval(ticker);
+ target = offset;
+ if (velocity > 10 || velocity < -10) {
+ amplitude = 0.9 * velocity;
+ target = offset + amplitude;
+ }
+ target = Math.round(target / dim) * dim;
+
+ // No wrap of items.
+ if (options.noWrap) {
+ if (target >= dim * (count - 1)) {
+ target = dim * (count - 1);
+ } else if (target < 0) {
+ target = 0;
+ }
+ }
+ amplitude = target - offset;
+ timestamp = Date.now();
+ requestAnimationFrame(autoScroll);
+
+ if (dragged) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ return false;
+ }
+
+ xform = 'transform';
+ ['webkit', 'Moz', 'O', 'ms'].every(function (prefix) {
+ var e = prefix + 'Transform';
+ if (typeof document.body.style[e] !== 'undefined') {
+ xform = e;
+ return false;
+ }
+ return true;
+ });
+
+
+ $(window).on('resize.carousel', function() {
+ if (options.fullWidth) {
+ item_width = view.find('.carousel-item').first().innerWidth();
+ item_height = view.find('.carousel-item').first().innerHeight();
+ dim = item_width * 2 + options.padding;
+ offset = center * 2 * item_width;
+ target = offset;
+ } else {
+ scroll();
+ }
+ });
+
+ setupEvents();
+ scroll(offset);
+
+ $(this).on('carouselNext', function(e, n) {
+ if (n === undefined) {
+ n = 1;
+ }
+ target = (dim * Math.round(offset / dim)) + (dim * n);
+ if (offset !== target) {
+ amplitude = target - offset;
+ timestamp = Date.now();
+ requestAnimationFrame(autoScroll);
+ }
+ });
+
+ $(this).on('carouselPrev', function(e, n) {
+ if (n === undefined) {
+ n = 1;
+ }
+ target = (dim * Math.round(offset / dim)) - (dim * n);
+ if (offset !== target) {
+ amplitude = target - offset;
+ timestamp = Date.now();
+ requestAnimationFrame(autoScroll);
+ }
+ });
+
+ $(this).on('carouselSet', function(e, n) {
+ if (n === undefined) {
+ n = 0;
+ }
+ cycleTo(n);
+ });
+
+ });
+
+
+
+ },
+ next : function(n) {
+ $(this).trigger('carouselNext', [n]);
+ },
+ prev : function(n) {
+ $(this).trigger('carouselPrev', [n]);
+ },
+ set : function(n) {
+ $(this).trigger('carouselSet', [n]);
+ }
+ };
+
+
+ $.fn.carousel = function(methodOrOptions) {
+ if ( methods[methodOrOptions] ) {
+ return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
+ // Default to "init"
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.carousel' );
+ }
+ }; // Plugin end
+}( jQuery )); \ No newline at end of file
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.min.js b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.min.js
new file mode 100644
index 000000000..00c0d5f85
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/3rd_party/materialize/js/materialize.min.js
@@ -0,0 +1,10 @@
+/*!
+ * Materialize v0.98.0 (http://materializecss.com)
+ * Copyright 2014-2015 Materialize
+ * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
+ */
+if("undefined"==typeof jQuery){var jQuery;jQuery="function"==typeof require?$=require("jquery"):$}jQuery.easing.jswing=jQuery.easing.swing,jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(a,b,c,d,e){return jQuery.easing[jQuery.easing.def](a,b,c,d,e)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b+c:-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b+c:d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b*b+c:-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b*b*b*b+c:d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return 0==b?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){return 0==b?c:b==e?c+d:(b/=e/2)<1?d/2*Math.pow(2,10*(b-1))+c:d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){return(b/=e/2)<1?-d/2*(Math.sqrt(1-b*b)-1)+c:d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(0==b)return c;if(1==(b/=e))return c+d;if(g||(g=.3*e),h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*(2*Math.PI)/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(0==b)return c;if(1==(b/=e))return c+d;if(g||(g=.3*e),h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*(2*Math.PI)/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(0==b)return c;if(2==(b/=e/2))return c+d;if(g||(g=e*(.3*1.5)),h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return b<1?-.5*(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*(2*Math.PI)/g))+c:h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*(2*Math.PI)/g)*.5+d+c},easeInBack:function(a,b,c,d,e,f){return void 0==f&&(f=1.70158),d*(b/=e)*b*((f+1)*b-f)+c},easeOutBack:function(a,b,c,d,e,f){return void 0==f&&(f=1.70158),d*((b=b/e-1)*b*((f+1)*b+f)+1)+c},easeInOutBack:function(a,b,c,d,e,f){return void 0==f&&(f=1.70158),(b/=e/2)<1?d/2*(b*b*(((f*=1.525)+1)*b-f))+c:d/2*((b-=2)*b*(((f*=1.525)+1)*b+f)+2)+c},easeInBounce:function(a,b,c,d,e){return d-jQuery.easing.easeOutBounce(a,e-b,0,d,e)+c},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*(7.5625*b*b)+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(a,b,c,d,e){return b<e/2?.5*jQuery.easing.easeInBounce(a,2*b,0,d,e)+c:.5*jQuery.easing.easeOutBounce(a,2*b-e,0,d,e)+.5*d+c}}),jQuery.extend(jQuery.easing,{easeInOutMaterial:function(a,b,c,d,e){return(b/=e/2)<1?d/2*b*b+c:d/4*((b-=2)*b*b+2)+c}}),jQuery.Velocity?console.log("Velocity is already loaded. You may be needlessly importing Velocity again; note that Materialize includes Velocity."):(!function(a){function b(a){var b=a.length,d=c.type(a);return"function"!==d&&!c.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===d||0===b||"number"==typeof b&&b>0&&b-1 in a))}if(!a.jQuery){var c=function(a,b){return new c.fn.init(a,b)};c.isWindow=function(a){return null!=a&&a==a.window},c.type=function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?e[g.call(a)]||"object":typeof a},c.isArray=Array.isArray||function(a){return"array"===c.type(a)},c.isPlainObject=function(a){var b;if(!a||"object"!==c.type(a)||a.nodeType||c.isWindow(a))return!1;try{if(a.constructor&&!f.call(a,"constructor")&&!f.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(d){return!1}for(b in a);return void 0===b||f.call(a,b)},c.each=function(a,c,d){var e,f=0,g=a.length,h=b(a);if(d){if(h)for(;g>f&&(e=c.apply(a[f],d),e!==!1);f++);else for(f in a)if(e=c.apply(a[f],d),e===!1)break}else if(h)for(;g>f&&(e=c.call(a[f],f,a[f]),e!==!1);f++);else for(f in a)if(e=c.call(a[f],f,a[f]),e===!1)break;return a},c.data=function(a,b,e){if(void 0===e){var f=a[c.expando],g=f&&d[f];if(void 0===b)return g;if(g&&b in g)return g[b]}else if(void 0!==b){var f=a[c.expando]||(a[c.expando]=++c.uuid);return d[f]=d[f]||{},d[f][b]=e,e}},c.removeData=function(a,b){var e=a[c.expando],f=e&&d[e];f&&c.each(b,function(a,b){delete f[b]})},c.extend=function(){var a,b,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;for("boolean"==typeof h&&(k=h,h=arguments[i]||{},i++),"object"!=typeof h&&"function"!==c.type(h)&&(h={}),i===j&&(h=this,i--);j>i;i++)if(null!=(f=arguments[i]))for(e in f)a=h[e],d=f[e],h!==d&&(k&&d&&(c.isPlainObject(d)||(b=c.isArray(d)))?(b?(b=!1,g=a&&c.isArray(a)?a:[]):g=a&&c.isPlainObject(a)?a:{},h[e]=c.extend(k,g,d)):void 0!==d&&(h[e]=d));return h},c.queue=function(a,d,e){function f(a,c){var d=c||[];return null!=a&&(b(Object(a))?!function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;)a[e++]=b[d++];if(c!==c)for(;void 0!==b[d];)a[e++]=b[d++];return a.length=e,a}(d,"string"==typeof a?[a]:a):[].push.call(d,a)),d}if(a){d=(d||"fx")+"queue";var g=c.data(a,d);return e?(!g||c.isArray(e)?g=c.data(a,d,f(e)):g.push(e),g):g||[]}},c.dequeue=function(a,b){c.each(a.nodeType?[a]:a,function(a,d){b=b||"fx";var e=c.queue(d,b),f=e.shift();"inprogress"===f&&(f=e.shift()),f&&("fx"===b&&e.unshift("inprogress"),f.call(d,function(){c.dequeue(d,b)}))})},c.fn=c.prototype={init:function(a){if(a.nodeType)return this[0]=a,this;throw new Error("Not a DOM node.")},offset:function(){var b=this[0].getBoundingClientRect?this[0].getBoundingClientRect():{top:0,left:0};return{top:b.top+(a.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:b.left+(a.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function a(){for(var a=this.offsetParent||document;a&&"html"===!a.nodeType.toLowerCase&&"static"===a.style.position;)a=a.offsetParent;return a||document}var b=this[0],a=a.apply(b),d=this.offset(),e=/^(?:body|html)$/i.test(a.nodeName)?{top:0,left:0}:c(a).offset();return d.top-=parseFloat(b.style.marginTop)||0,d.left-=parseFloat(b.style.marginLeft)||0,a.style&&(e.top+=parseFloat(a.style.borderTopWidth)||0,e.left+=parseFloat(a.style.borderLeftWidth)||0),{top:d.top-e.top,left:d.left-e.left}}};var d={};c.expando="velocity"+(new Date).getTime(),c.uuid=0;for(var e={},f=e.hasOwnProperty,g=e.toString,h="Boolean Number String Function Array Date RegExp Object Error".split(" "),i=0;i<h.length;i++)e["[object "+h[i]+"]"]=h[i].toLowerCase();c.fn.init.prototype=c.fn,a.Velocity={Utilities:c}}}(window),function(a){"object"==typeof module&&"object"==typeof module.exports?module.exports=a():"function"==typeof define&&define.amd?define(a):a()}(function(){return function(a,b,c,d){function e(a){for(var b=-1,c=a?a.length:0,d=[];++b<c;){var e=a[b];e&&d.push(e)}return d}function f(a){return p.isWrapped(a)?a=[].slice.call(a):p.isNode(a)&&(a=[a]),a}function g(a){var b=m.data(a,"velocity");return null===b?d:b}function h(a){return function(b){return Math.round(b*a)*(1/a)}}function i(a,c,d,e){function f(a,b){return 1-3*b+3*a}function g(a,b){return 3*b-6*a}function h(a){return 3*a}function i(a,b,c){return((f(b,c)*a+g(b,c))*a+h(b))*a}function j(a,b,c){return 3*f(b,c)*a*a+2*g(b,c)*a+h(b)}function k(b,c){for(var e=0;p>e;++e){var f=j(c,a,d);if(0===f)return c;var g=i(c,a,d)-b;c-=g/f}return c}function l(){for(var b=0;t>b;++b)x[b]=i(b*u,a,d)}function m(b,c,e){var f,g,h=0;do g=c+(e-c)/2,f=i(g,a,d)-b,f>0?e=g:c=g;while(Math.abs(f)>r&&++h<s);return g}function n(b){for(var c=0,e=1,f=t-1;e!=f&&x[e]<=b;++e)c+=u;--e;var g=(b-x[e])/(x[e+1]-x[e]),h=c+g*u,i=j(h,a,d);return i>=q?k(b,h):0==i?h:m(b,c,c+u)}function o(){y=!0,(a!=c||d!=e)&&l()}var p=4,q=.001,r=1e-7,s=10,t=11,u=1/(t-1),v="Float32Array"in b;if(4!==arguments.length)return!1;for(var w=0;4>w;++w)if("number"!=typeof arguments[w]||isNaN(arguments[w])||!isFinite(arguments[w]))return!1;a=Math.min(a,1),d=Math.min(d,1),a=Math.max(a,0),d=Math.max(d,0);var x=v?new Float32Array(t):new Array(t),y=!1,z=function(b){return y||o(),a===c&&d===e?b:0===b?0:1===b?1:i(n(b),c,e)};z.getControlPoints=function(){return[{x:a,y:c},{x:d,y:e}]};var A="generateBezier("+[a,c,d,e]+")";return z.toString=function(){return A},z}function j(a,b){var c=a;return p.isString(a)?t.Easings[a]||(c=!1):c=p.isArray(a)&&1===a.length?h.apply(null,a):p.isArray(a)&&2===a.length?u.apply(null,a.concat([b])):!(!p.isArray(a)||4!==a.length)&&i.apply(null,a),c===!1&&(c=t.Easings[t.defaults.easing]?t.defaults.easing:s),c}function k(a){if(a){var b=(new Date).getTime(),c=t.State.calls.length;c>1e4&&(t.State.calls=e(t.State.calls));for(var f=0;c>f;f++)if(t.State.calls[f]){var h=t.State.calls[f],i=h[0],j=h[2],n=h[3],o=!!n,q=null;n||(n=t.State.calls[f][3]=b-16);for(var r=Math.min((b-n)/j.duration,1),s=0,u=i.length;u>s;s++){var w=i[s],y=w.element;if(g(y)){var z=!1;if(j.display!==d&&null!==j.display&&"none"!==j.display){if("flex"===j.display){var A=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];m.each(A,function(a,b){v.setPropertyValue(y,"display",b)})}v.setPropertyValue(y,"display",j.display)}j.visibility!==d&&"hidden"!==j.visibility&&v.setPropertyValue(y,"visibility",j.visibility);for(var B in w)if("element"!==B){var C,D=w[B],E=p.isString(D.easing)?t.Easings[D.easing]:D.easing;if(1===r)C=D.endValue;else{var F=D.endValue-D.startValue;if(C=D.startValue+F*E(r,j,F),!o&&C===D.currentValue)continue}if(D.currentValue=C,"tween"===B)q=C;else{if(v.Hooks.registered[B]){var G=v.Hooks.getRoot(B),H=g(y).rootPropertyValueCache[G];H&&(D.rootPropertyValue=H)}var I=v.setPropertyValue(y,B,D.currentValue+(0===parseFloat(C)?"":D.unitType),D.rootPropertyValue,D.scrollData);v.Hooks.registered[B]&&(g(y).rootPropertyValueCache[G]=v.Normalizations.registered[G]?v.Normalizations.registered[G]("extract",null,I[1]):I[1]),"transform"===I[0]&&(z=!0)}}j.mobileHA&&g(y).transformCache.translate3d===d&&(g(y).transformCache.translate3d="(0px, 0px, 0px)",z=!0),z&&v.flushTransformCache(y)}}j.display!==d&&"none"!==j.display&&(t.State.calls[f][2].display=!1),j.visibility!==d&&"hidden"!==j.visibility&&(t.State.calls[f][2].visibility=!1),j.progress&&j.progress.call(h[1],h[1],r,Math.max(0,n+j.duration-b),n,q),1===r&&l(f)}}t.State.isTicking&&x(k)}function l(a,b){if(!t.State.calls[a])return!1;for(var c=t.State.calls[a][0],e=t.State.calls[a][1],f=t.State.calls[a][2],h=t.State.calls[a][4],i=!1,j=0,k=c.length;k>j;j++){var l=c[j].element;if(b||f.loop||("none"===f.display&&v.setPropertyValue(l,"display",f.display),"hidden"===f.visibility&&v.setPropertyValue(l,"visibility",f.visibility)),f.loop!==!0&&(m.queue(l)[1]===d||!/\.velocityQueueEntryFlag/i.test(m.queue(l)[1]))&&g(l)){g(l).isAnimating=!1,g(l).rootPropertyValueCache={};var n=!1;m.each(v.Lists.transforms3D,function(a,b){var c=/^scale/.test(b)?1:0,e=g(l).transformCache[b];g(l).transformCache[b]!==d&&new RegExp("^\\("+c+"[^.]").test(e)&&(n=!0,delete g(l).transformCache[b])}),f.mobileHA&&(n=!0,delete g(l).transformCache.translate3d),n&&v.flushTransformCache(l),v.Values.removeClass(l,"velocity-animating")}if(!b&&f.complete&&!f.loop&&j===k-1)try{f.complete.call(e,e)}catch(o){setTimeout(function(){throw o},1)}h&&f.loop!==!0&&h(e),g(l)&&f.loop===!0&&!b&&(m.each(g(l).tweensContainer,function(a,b){/^rotate/.test(a)&&360===parseFloat(b.endValue)&&(b.endValue=0,b.startValue=360),/^backgroundPosition/.test(a)&&100===parseFloat(b.endValue)&&"%"===b.unitType&&(b.endValue=0,b.startValue=100)}),t(l,"reverse",{loop:!0,delay:f.delay})),f.queue!==!1&&m.dequeue(l,f.queue)}t.State.calls[a]=!1;for(var p=0,q=t.State.calls.length;q>p;p++)if(t.State.calls[p]!==!1){i=!0;break}i===!1&&(t.State.isTicking=!1,delete t.State.calls,t.State.calls=[])}var m,n=function(){if(c.documentMode)return c.documentMode;for(var a=7;a>4;a--){var b=c.createElement("div");if(b.innerHTML="<!--[if IE "+a+"]><span></span><![endif]-->",b.getElementsByTagName("span").length)return b=null,a}return d}(),o=function(){var a=0;return b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame||function(b){var c,d=(new Date).getTime();return c=Math.max(0,16-(d-a)),a=d+c,setTimeout(function(){b(d+c)},c)}}(),p={isString:function(a){return"string"==typeof a},isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},isFunction:function(a){return"[object Function]"===Object.prototype.toString.call(a)},isNode:function(a){return a&&a.nodeType},isNodeList:function(a){return"object"==typeof a&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(a))&&a.length!==d&&(0===a.length||"object"==typeof a[0]&&a[0].nodeType>0)},isWrapped:function(a){return a&&(a.jquery||b.Zepto&&b.Zepto.zepto.isZ(a))},isSVG:function(a){return b.SVGElement&&a instanceof b.SVGElement},isEmptyObject:function(a){for(var b in a)return!1;return!0}},q=!1;if(a.fn&&a.fn.jquery?(m=a,q=!0):m=b.Velocity.Utilities,8>=n&&!q)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=n)return void(jQuery.fn.velocity=jQuery.fn.animate);var r=400,s="swing",t={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:b.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:c.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:m,Redirects:{},Easings:{},Promise:b.Promise,defaults:{queue:"",duration:r,easing:s,begin:d,complete:d,progress:d,display:d,visibility:d,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(a){m.data(a,"velocity",{isSVG:p.isSVG(a),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:2,patch:2},debug:!1};b.pageYOffset!==d?(t.State.scrollAnchor=b,t.State.scrollPropertyLeft="pageXOffset",t.State.scrollPropertyTop="pageYOffset"):(t.State.scrollAnchor=c.documentElement||c.body.parentNode||c.body,t.State.scrollPropertyLeft="scrollLeft",t.State.scrollPropertyTop="scrollTop");var u=function(){function a(a){return-a.tension*a.x-a.friction*a.v}function b(b,c,d){var e={x:b.x+d.dx*c,v:b.v+d.dv*c,tension:b.tension,friction:b.friction};return{dx:e.v,dv:a(e)}}function c(c,d){var e={dx:c.v,dv:a(c)},f=b(c,.5*d,e),g=b(c,.5*d,f),h=b(c,d,g),i=1/6*(e.dx+2*(f.dx+g.dx)+h.dx),j=1/6*(e.dv+2*(f.dv+g.dv)+h.dv);return c.x=c.x+i*d,c.v=c.v+j*d,c}return function d(a,b,e){var f,g,h,i={x:-1,v:0,tension:null,friction:null},j=[0],k=0,l=1e-4,m=.016;for(a=parseFloat(a)||500,b=parseFloat(b)||20,e=e||null,i.tension=a,i.friction=b,f=null!==e,f?(k=d(a,b),g=k/e*m):g=m;h=c(h||i,g),j.push(1+h.x),k+=16,Math.abs(h.x)>l&&Math.abs(h.v)>l;);return f?function(a){return j[a*(j.length-1)|0]}:k}}();t.Easings={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},spring:function(a){return 1-Math.cos(4.5*a*Math.PI)*Math.exp(6*-a)}},m.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(a,b){t.Easings[b[0]]=i.apply(null,b[1])});var v=t.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var a=0;a<v.Lists.colors.length;a++){var b="color"===v.Lists.colors[a]?"0 0 0 1":"255 255 255 1";v.Hooks.templates[v.Lists.colors[a]]=["Red Green Blue Alpha",b]}var c,d,e;if(n)for(c in v.Hooks.templates){d=v.Hooks.templates[c],e=d[0].split(" ");var f=d[1].match(v.RegEx.valueSplit);"Color"===e[0]&&(e.push(e.shift()),f.push(f.shift()),v.Hooks.templates[c]=[e.join(" "),f.join(" ")])}for(c in v.Hooks.templates){d=v.Hooks.templates[c],e=d[0].split(" ");for(var a in e){var g=c+e[a],h=a;v.Hooks.registered[g]=[c,h]}}},getRoot:function(a){var b=v.Hooks.registered[a];return b?b[0]:a},cleanRootPropertyValue:function(a,b){return v.RegEx.valueUnwrap.test(b)&&(b=b.match(v.RegEx.valueUnwrap)[1]),v.Values.isCSSNullValue(b)&&(b=v.Hooks.templates[a][1]),b},extractValue:function(a,b){var c=v.Hooks.registered[a];if(c){var d=c[0],e=c[1];return b=v.Hooks.cleanRootPropertyValue(d,b),b.toString().match(v.RegEx.valueSplit)[e]}return b},injectValue:function(a,b,c){var d=v.Hooks.registered[a];if(d){var e,f,g=d[0],h=d[1];return c=v.Hooks.cleanRootPropertyValue(g,c),e=c.toString().match(v.RegEx.valueSplit),e[h]=b,f=e.join(" ")}return c}},Normalizations:{registered:{clip:function(a,b,c){switch(a){case"name":return"clip";case"extract":var d;return v.RegEx.wrappedValueAlreadyExtracted.test(c)?d=c:(d=c.toString().match(v.RegEx.valueUnwrap),d=d?d[1].replace(/,(\s+)?/g," "):c),d;case"inject":return"rect("+c+")"}},blur:function(a,b,c){switch(a){case"name":return t.State.isFirefox?"filter":"-webkit-filter";case"extract":var d=parseFloat(c);if(!d&&0!==d){var e=c.toString().match(/blur\(([0-9]+[A-z]+)\)/i);d=e?e[1]:0}return d;case"inject":return parseFloat(c)?"blur("+c+")":"none"}},opacity:function(a,b,c){if(8>=n)switch(a){case"name":return"filter";case"extract":var d=c.toString().match(/alpha\(opacity=(.*)\)/i);return c=d?d[1]/100:1;case"inject":return b.style.zoom=1,parseFloat(c)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(c),10)+")"}else switch(a){case"name":return"opacity";case"extract":return c;case"inject":return c}}},register:function(){9>=n||t.State.isGingerbread||(v.Lists.transformsBase=v.Lists.transformsBase.concat(v.Lists.transforms3D));for(var a=0;a<v.Lists.transformsBase.length;a++)!function(){var b=v.Lists.transformsBase[a];v.Normalizations.registered[b]=function(a,c,e){switch(a){case"name":return"transform";case"extract":return g(c)===d||g(c).transformCache[b]===d?/^scale/i.test(b)?1:0:g(c).transformCache[b].replace(/[()]/g,"");case"inject":var f=!1;switch(b.substr(0,b.length-1)){case"translate":f=!/(%|px|em|rem|vw|vh|\d)$/i.test(e);break;case"scal":case"scale":t.State.isAndroid&&g(c).transformCache[b]===d&&1>e&&(e=1),f=!/(\d)$/i.test(e);break;case"skew":f=!/(deg|\d)$/i.test(e);break;case"rotate":f=!/(deg|\d)$/i.test(e)}return f||(g(c).transformCache[b]="("+e+")"),g(c).transformCache[b]}}}();for(var a=0;a<v.Lists.colors.length;a++)!function(){var b=v.Lists.colors[a];v.Normalizations.registered[b]=function(a,c,e){switch(a){case"name":return b;case"extract":var f;if(v.RegEx.wrappedValueAlreadyExtracted.test(e))f=e;else{var g,h={black:"rgb(0, 0, 0)",blue:"rgb(0, 0, 255)",gray:"rgb(128, 128, 128)",green:"rgb(0, 128, 0)",red:"rgb(255, 0, 0)",white:"rgb(255, 255, 255)"};/^[A-z]+$/i.test(e)?g=h[e]!==d?h[e]:h.black:v.RegEx.isHex.test(e)?g="rgb("+v.Values.hexToRgb(e).join(" ")+")":/^rgba?\(/i.test(e)||(g=h.black),f=(g||e).toString().match(v.RegEx.valueUnwrap)[1].replace(/,(\s+)?/g," ")}return 8>=n||3!==f.split(" ").length||(f+=" 1"),f;case"inject":return 8>=n?4===e.split(" ").length&&(e=e.split(/\s+/).slice(0,3).join(" ")):3===e.split(" ").length&&(e+=" 1"),(8>=n?"rgb":"rgba")+"("+e.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(a){return a.replace(/-(\w)/g,function(a,b){return b.toUpperCase()})},SVGAttribute:function(a){var b="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(n||t.State.isAndroid&&!t.State.isChrome)&&(b+="|transform"),new RegExp("^("+b+")$","i").test(a)},prefixCheck:function(a){if(t.State.prefixMatches[a])return[t.State.prefixMatches[a],!0];for(var b=["","Webkit","Moz","ms","O"],c=0,d=b.length;d>c;c++){var e;if(e=0===c?a:b[c]+a.replace(/^\w/,function(a){return a.toUpperCase()}),p.isString(t.State.prefixElement.style[e]))return t.State.prefixMatches[a]=e,[e,!0]}return[a,!1]}},Values:{hexToRgb:function(a){var b,c=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,d=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;return a=a.replace(c,function(a,b,c,d){return b+b+c+c+d+d}),b=d.exec(a),b?[parseInt(b[1],16),parseInt(b[2],16),parseInt(b[3],16)]:[0,0,0]},isCSSNullValue:function(a){return 0==a||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(a)},getUnitType:function(a){return/^(rotate|skew)/i.test(a)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(a)?"":"px"},getDisplayType:function(a){var b=a&&a.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(b)?"inline":/^(li)$/i.test(b)?"list-item":/^(tr)$/i.test(b)?"table-row":/^(table)$/i.test(b)?"table":/^(tbody)$/i.test(b)?"table-row-group":"block"},addClass:function(a,b){a.classList?a.classList.add(b):a.className+=(a.className.length?" ":"")+b},removeClass:function(a,b){a.classList?a.classList.remove(b):a.className=a.className.toString().replace(new RegExp("(^|\\s)"+b.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(a,c,e,f){function h(a,c){function e(){j&&v.setPropertyValue(a,"display","none")}var i=0;if(8>=n)i=m.css(a,c);else{var j=!1;if(/^(width|height)$/.test(c)&&0===v.getPropertyValue(a,"display")&&(j=!0,v.setPropertyValue(a,"display",v.Values.getDisplayType(a))),!f){if("height"===c&&"border-box"!==v.getPropertyValue(a,"boxSizing").toString().toLowerCase()){var k=a.offsetHeight-(parseFloat(v.getPropertyValue(a,"borderTopWidth"))||0)-(parseFloat(v.getPropertyValue(a,"borderBottomWidth"))||0)-(parseFloat(v.getPropertyValue(a,"paddingTop"))||0)-(parseFloat(v.getPropertyValue(a,"paddingBottom"))||0);return e(),k}if("width"===c&&"border-box"!==v.getPropertyValue(a,"boxSizing").toString().toLowerCase()){var l=a.offsetWidth-(parseFloat(v.getPropertyValue(a,"borderLeftWidth"))||0)-(parseFloat(v.getPropertyValue(a,"borderRightWidth"))||0)-(parseFloat(v.getPropertyValue(a,"paddingLeft"))||0)-(parseFloat(v.getPropertyValue(a,"paddingRight"))||0);return e(),l}}var o;o=g(a)===d?b.getComputedStyle(a,null):g(a).computedStyle?g(a).computedStyle:g(a).computedStyle=b.getComputedStyle(a,null),"borderColor"===c&&(c="borderTopColor"),i=9===n&&"filter"===c?o.getPropertyValue(c):o[c],(""===i||null===i)&&(i=a.style[c]),e()}if("auto"===i&&/^(top|right|bottom|left)$/i.test(c)){var p=h(a,"position");("fixed"===p||"absolute"===p&&/top|left/i.test(c))&&(i=m(a).position()[c]+"px")}return i}var i;if(v.Hooks.registered[c]){var j=c,k=v.Hooks.getRoot(j);e===d&&(e=v.getPropertyValue(a,v.Names.prefixCheck(k)[0])),v.Normalizations.registered[k]&&(e=v.Normalizations.registered[k]("extract",a,e)),i=v.Hooks.extractValue(j,e)}else if(v.Normalizations.registered[c]){var l,o;l=v.Normalizations.registered[c]("name",a),"transform"!==l&&(o=h(a,v.Names.prefixCheck(l)[0]),v.Values.isCSSNullValue(o)&&v.Hooks.templates[c]&&(o=v.Hooks.templates[c][1])),i=v.Normalizations.registered[c]("extract",a,o)}if(!/^[\d-]/.test(i))if(g(a)&&g(a).isSVG&&v.Names.SVGAttribute(c))if(/^(height|width)$/i.test(c))try{i=a.getBBox()[c]}catch(p){i=0}else i=a.getAttribute(c);else i=h(a,v.Names.prefixCheck(c)[0]);return v.Values.isCSSNullValue(i)&&(i=0),t.debug>=2&&console.log("Get "+c+": "+i),i},setPropertyValue:function(a,c,d,e,f){var h=c;if("scroll"===c)f.container?f.container["scroll"+f.direction]=d:"Left"===f.direction?b.scrollTo(d,f.alternateValue):b.scrollTo(f.alternateValue,d);else if(v.Normalizations.registered[c]&&"transform"===v.Normalizations.registered[c]("name",a))v.Normalizations.registered[c]("inject",a,d),h="transform",d=g(a).transformCache[c];else{if(v.Hooks.registered[c]){var i=c,j=v.Hooks.getRoot(c);e=e||v.getPropertyValue(a,j),d=v.Hooks.injectValue(i,d,e),c=j}if(v.Normalizations.registered[c]&&(d=v.Normalizations.registered[c]("inject",a,d),c=v.Normalizations.registered[c]("name",a)),h=v.Names.prefixCheck(c)[0],8>=n)try{a.style[h]=d}catch(k){t.debug&&console.log("Browser does not support ["+d+"] for ["+h+"]")}else g(a)&&g(a).isSVG&&v.Names.SVGAttribute(c)?a.setAttribute(c,d):a.style[h]=d;t.debug>=2&&console.log("Set "+c+" ("+h+"): "+d)}return[h,d]},flushTransformCache:function(a){function b(b){return parseFloat(v.getPropertyValue(a,b))}var c="";if((n||t.State.isAndroid&&!t.State.isChrome)&&g(a).isSVG){var d={translate:[b("translateX"),b("translateY")],skewX:[b("skewX")],skewY:[b("skewY")],scale:1!==b("scale")?[b("scale"),b("scale")]:[b("scaleX"),b("scaleY")],rotate:[b("rotateZ"),0,0]};m.each(g(a).transformCache,function(a){/^translate/i.test(a)?a="translate":/^scale/i.test(a)?a="scale":/^rotate/i.test(a)&&(a="rotate"),d[a]&&(c+=a+"("+d[a].join(" ")+") ",delete d[a])})}else{var e,f;m.each(g(a).transformCache,function(b){return e=g(a).transformCache[b],"transformPerspective"===b?(f=e,!0):(9===n&&"rotateZ"===b&&(b="rotate"),void(c+=b+e+" "))}),f&&(c="perspective"+f+" "+c)}v.setPropertyValue(a,"transform",c)}};v.Hooks.register(),v.Normalizations.register(),t.hook=function(a,b,c){var e=d;return a=f(a),m.each(a,function(a,f){if(g(f)===d&&t.init(f),c===d)e===d&&(e=t.CSS.getPropertyValue(f,b));else{var h=t.CSS.setPropertyValue(f,b,c);"transform"===h[0]&&t.CSS.flushTransformCache(f),e=h}}),e};var w=function(){function a(){return h?B.promise||null:i}function e(){function a(a){function l(a,b){var c=d,e=d,g=d;return p.isArray(a)?(c=a[0],!p.isArray(a[1])&&/^[\d-]/.test(a[1])||p.isFunction(a[1])||v.RegEx.isHex.test(a[1])?g=a[1]:(p.isString(a[1])&&!v.RegEx.isHex.test(a[1])||p.isArray(a[1]))&&(e=b?a[1]:j(a[1],h.duration),a[2]!==d&&(g=a[2]))):c=a,b||(e=e||h.easing),p.isFunction(c)&&(c=c.call(f,y,x)),p.isFunction(g)&&(g=g.call(f,y,x)),[c||0,e,g]}function n(a,b){var c,d;return d=(b||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(a){return c=a,""}),c||(c=v.Values.getUnitType(a)),[d,c]}function r(){var a={myParent:f.parentNode||c.body,position:v.getPropertyValue(f,"position"),fontSize:v.getPropertyValue(f,"fontSize")},d=a.position===I.lastPosition&&a.myParent===I.lastParent,e=a.fontSize===I.lastFontSize;I.lastParent=a.myParent,I.lastPosition=a.position,I.lastFontSize=a.fontSize;var h=100,i={};if(e&&d)i.emToPx=I.lastEmToPx,i.percentToPxWidth=I.lastPercentToPxWidth,i.percentToPxHeight=I.lastPercentToPxHeight;else{var j=g(f).isSVG?c.createElementNS("http://www.w3.org/2000/svg","rect"):c.createElement("div");t.init(j),a.myParent.appendChild(j),m.each(["overflow","overflowX","overflowY"],function(a,b){t.CSS.setPropertyValue(j,b,"hidden")}),t.CSS.setPropertyValue(j,"position",a.position),t.CSS.setPropertyValue(j,"fontSize",a.fontSize),t.CSS.setPropertyValue(j,"boxSizing","content-box"),m.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(a,b){t.CSS.setPropertyValue(j,b,h+"%")}),t.CSS.setPropertyValue(j,"paddingLeft",h+"em"),i.percentToPxWidth=I.lastPercentToPxWidth=(parseFloat(v.getPropertyValue(j,"width",null,!0))||1)/h,i.percentToPxHeight=I.lastPercentToPxHeight=(parseFloat(v.getPropertyValue(j,"height",null,!0))||1)/h,i.emToPx=I.lastEmToPx=(parseFloat(v.getPropertyValue(j,"paddingLeft"))||1)/h,a.myParent.removeChild(j)}return null===I.remToPx&&(I.remToPx=parseFloat(v.getPropertyValue(c.body,"fontSize"))||16),null===I.vwToPx&&(I.vwToPx=parseFloat(b.innerWidth)/100,I.vhToPx=parseFloat(b.innerHeight)/100),i.remToPx=I.remToPx,i.vwToPx=I.vwToPx,i.vhToPx=I.vhToPx,t.debug>=1&&console.log("Unit ratios: "+JSON.stringify(i),f),i}if(h.begin&&0===y)try{h.begin.call(o,o)}catch(u){setTimeout(function(){throw u},1)}if("scroll"===C){var w,z,A,D=/^x$/i.test(h.axis)?"Left":"Top",E=parseFloat(h.offset)||0;h.container?p.isWrapped(h.container)||p.isNode(h.container)?(h.container=h.container[0]||h.container,w=h.container["scroll"+D],A=w+m(f).position()[D.toLowerCase()]+E):h.container=null:(w=t.State.scrollAnchor[t.State["scrollProperty"+D]],z=t.State.scrollAnchor[t.State["scrollProperty"+("Left"===D?"Top":"Left")]],A=m(f).offset()[D.toLowerCase()]+E),i={scroll:{rootPropertyValue:!1,startValue:w,currentValue:w,endValue:A,unitType:"",easing:h.easing,scrollData:{container:h.container,direction:D,alternateValue:z}},element:f},t.debug&&console.log("tweensContainer (scroll): ",i.scroll,f)}else if("reverse"===C){if(!g(f).tweensContainer)return void m.dequeue(f,h.queue);"none"===g(f).opts.display&&(g(f).opts.display="auto"),"hidden"===g(f).opts.visibility&&(g(f).opts.visibility="visible"),g(f).opts.loop=!1,g(f).opts.begin=null,g(f).opts.complete=null,s.easing||delete h.easing,s.duration||delete h.duration,h=m.extend({},g(f).opts,h);var F=m.extend(!0,{},g(f).tweensContainer);for(var G in F)if("element"!==G){var H=F[G].startValue;F[G].startValue=F[G].currentValue=F[G].endValue,F[G].endValue=H,p.isEmptyObject(s)||(F[G].easing=h.easing),t.debug&&console.log("reverse tweensContainer ("+G+"): "+JSON.stringify(F[G]),f)}i=F}else if("start"===C){var F;g(f).tweensContainer&&g(f).isAnimating===!0&&(F=g(f).tweensContainer),m.each(q,function(a,b){if(RegExp("^"+v.Lists.colors.join("$|^")+"$").test(a)){var c=l(b,!0),e=c[0],f=c[1],g=c[2];if(v.RegEx.isHex.test(e)){for(var h=["Red","Green","Blue"],i=v.Values.hexToRgb(e),j=g?v.Values.hexToRgb(g):d,k=0;k<h.length;k++){var m=[i[k]];f&&m.push(f),j!==d&&m.push(j[k]),q[a+h[k]]=m}delete q[a]}}});for(var K in q){var L=l(q[K]),M=L[0],N=L[1],O=L[2];K=v.Names.camelCase(K);var P=v.Hooks.getRoot(K),Q=!1;if(g(f).isSVG||"tween"===P||v.Names.prefixCheck(P)[1]!==!1||v.Normalizations.registered[P]!==d){(h.display!==d&&null!==h.display&&"none"!==h.display||h.visibility!==d&&"hidden"!==h.visibility)&&/opacity|filter/.test(K)&&!O&&0!==M&&(O=0),h._cacheValues&&F&&F[K]?(O===d&&(O=F[K].endValue+F[K].unitType),Q=g(f).rootPropertyValueCache[P]):v.Hooks.registered[K]?O===d?(Q=v.getPropertyValue(f,P),O=v.getPropertyValue(f,K,Q)):Q=v.Hooks.templates[P][1]:O===d&&(O=v.getPropertyValue(f,K));var R,S,T,U=!1;if(R=n(K,O),O=R[0],T=R[1],R=n(K,M),M=R[0].replace(/^([+-\/*])=/,function(a,b){return U=b,""}),S=R[1],O=parseFloat(O)||0,M=parseFloat(M)||0,"%"===S&&(/^(fontSize|lineHeight)$/.test(K)?(M/=100,S="em"):/^scale/.test(K)?(M/=100,S=""):/(Red|Green|Blue)$/i.test(K)&&(M=M/100*255,S="")),/[\/*]/.test(U))S=T;else if(T!==S&&0!==O)if(0===M)S=T;else{e=e||r();var V=/margin|padding|left|right|width|text|word|letter/i.test(K)||/X$/.test(K)||"x"===K?"x":"y";
+switch(T){case"%":O*="x"===V?e.percentToPxWidth:e.percentToPxHeight;break;case"px":break;default:O*=e[T+"ToPx"]}switch(S){case"%":O*=1/("x"===V?e.percentToPxWidth:e.percentToPxHeight);break;case"px":break;default:O*=1/e[S+"ToPx"]}}switch(U){case"+":M=O+M;break;case"-":M=O-M;break;case"*":M=O*M;break;case"/":M=O/M}i[K]={rootPropertyValue:Q,startValue:O,currentValue:O,endValue:M,unitType:S,easing:N},t.debug&&console.log("tweensContainer ("+K+"): "+JSON.stringify(i[K]),f)}else t.debug&&console.log("Skipping ["+P+"] due to a lack of browser support.")}i.element=f}i.element&&(v.Values.addClass(f,"velocity-animating"),J.push(i),""===h.queue&&(g(f).tweensContainer=i,g(f).opts=h),g(f).isAnimating=!0,y===x-1?(t.State.calls.push([J,o,h,null,B.resolver]),t.State.isTicking===!1&&(t.State.isTicking=!0,k())):y++)}var e,f=this,h=m.extend({},t.defaults,s),i={};switch(g(f)===d&&t.init(f),parseFloat(h.delay)&&h.queue!==!1&&m.queue(f,h.queue,function(a){t.velocityQueueEntryFlag=!0,g(f).delayTimer={setTimeout:setTimeout(a,parseFloat(h.delay)),next:a}}),h.duration.toString().toLowerCase()){case"fast":h.duration=200;break;case"normal":h.duration=r;break;case"slow":h.duration=600;break;default:h.duration=parseFloat(h.duration)||1}t.mock!==!1&&(t.mock===!0?h.duration=h.delay=1:(h.duration*=parseFloat(t.mock)||1,h.delay*=parseFloat(t.mock)||1)),h.easing=j(h.easing,h.duration),h.begin&&!p.isFunction(h.begin)&&(h.begin=null),h.progress&&!p.isFunction(h.progress)&&(h.progress=null),h.complete&&!p.isFunction(h.complete)&&(h.complete=null),h.display!==d&&null!==h.display&&(h.display=h.display.toString().toLowerCase(),"auto"===h.display&&(h.display=t.CSS.Values.getDisplayType(f))),h.visibility!==d&&null!==h.visibility&&(h.visibility=h.visibility.toString().toLowerCase()),h.mobileHA=h.mobileHA&&t.State.isMobile&&!t.State.isGingerbread,h.queue===!1?h.delay?setTimeout(a,h.delay):a():m.queue(f,h.queue,function(b,c){return c===!0?(B.promise&&B.resolver(o),!0):(t.velocityQueueEntryFlag=!0,void a(b))}),""!==h.queue&&"fx"!==h.queue||"inprogress"===m.queue(f)[0]||m.dequeue(f)}var h,i,n,o,q,s,u=arguments[0]&&(arguments[0].p||m.isPlainObject(arguments[0].properties)&&!arguments[0].properties.names||p.isString(arguments[0].properties));if(p.isWrapped(this)?(h=!1,n=0,o=this,i=this):(h=!0,n=1,o=u?arguments[0].elements||arguments[0].e:arguments[0]),o=f(o)){u?(q=arguments[0].properties||arguments[0].p,s=arguments[0].options||arguments[0].o):(q=arguments[n],s=arguments[n+1]);var x=o.length,y=0;if(!/^(stop|finish)$/i.test(q)&&!m.isPlainObject(s)){var z=n+1;s={};for(var A=z;A<arguments.length;A++)p.isArray(arguments[A])||!/^(fast|normal|slow)$/i.test(arguments[A])&&!/^\d/.test(arguments[A])?p.isString(arguments[A])||p.isArray(arguments[A])?s.easing=arguments[A]:p.isFunction(arguments[A])&&(s.complete=arguments[A]):s.duration=arguments[A]}var B={promise:null,resolver:null,rejecter:null};h&&t.Promise&&(B.promise=new t.Promise(function(a,b){B.resolver=a,B.rejecter=b}));var C;switch(q){case"scroll":C="scroll";break;case"reverse":C="reverse";break;case"finish":case"stop":m.each(o,function(a,b){g(b)&&g(b).delayTimer&&(clearTimeout(g(b).delayTimer.setTimeout),g(b).delayTimer.next&&g(b).delayTimer.next(),delete g(b).delayTimer)});var D=[];return m.each(t.State.calls,function(a,b){b&&m.each(b[1],function(c,e){var f=s===d?"":s;return f!==!0&&b[2].queue!==f&&(s!==d||b[2].queue!==!1)||void m.each(o,function(c,d){d===e&&((s===!0||p.isString(s))&&(m.each(m.queue(d,p.isString(s)?s:""),function(a,b){p.isFunction(b)&&b(null,!0)}),m.queue(d,p.isString(s)?s:"",[])),"stop"===q?(g(d)&&g(d).tweensContainer&&f!==!1&&m.each(g(d).tweensContainer,function(a,b){b.endValue=b.currentValue}),D.push(a)):"finish"===q&&(b[2].duration=1))})})}),"stop"===q&&(m.each(D,function(a,b){l(b,!0)}),B.promise&&B.resolver(o)),a();default:if(!m.isPlainObject(q)||p.isEmptyObject(q)){if(p.isString(q)&&t.Redirects[q]){var E=m.extend({},s),F=E.duration,G=E.delay||0;return E.backwards===!0&&(o=m.extend(!0,[],o).reverse()),m.each(o,function(a,b){parseFloat(E.stagger)?E.delay=G+parseFloat(E.stagger)*a:p.isFunction(E.stagger)&&(E.delay=G+E.stagger.call(b,a,x)),E.drag&&(E.duration=parseFloat(F)||(/^(callout|transition)/.test(q)?1e3:r),E.duration=Math.max(E.duration*(E.backwards?1-a/x:(a+1)/x),.75*E.duration,200)),t.Redirects[q].call(b,b,E||{},a,x,o,B.promise?B:d)}),a()}var H="Velocity: First argument ("+q+") was not a property map, a known action, or a registered redirect. Aborting.";return B.promise?B.rejecter(new Error(H)):console.log(H),a()}C="start"}var I={lastParent:null,lastPosition:null,lastFontSize:null,lastPercentToPxWidth:null,lastPercentToPxHeight:null,lastEmToPx:null,remToPx:null,vwToPx:null,vhToPx:null},J=[];m.each(o,function(a,b){p.isNode(b)&&e.call(b)});var K,E=m.extend({},t.defaults,s);if(E.loop=parseInt(E.loop),K=2*E.loop-1,E.loop)for(var L=0;K>L;L++){var M={delay:E.delay,progress:E.progress};L===K-1&&(M.display=E.display,M.visibility=E.visibility,M.complete=E.complete),w(o,"reverse",M)}return a()}};t=m.extend(w,t),t.animate=w;var x=b.requestAnimationFrame||o;return t.State.isMobile||c.hidden===d||c.addEventListener("visibilitychange",function(){c.hidden?(x=function(a){return setTimeout(function(){a(!0)},16)},k()):x=b.requestAnimationFrame||o}),a.Velocity=t,a!==b&&(a.fn.velocity=w,a.fn.velocity.defaults=t.defaults),m.each(["Down","Up"],function(a,b){t.Redirects["slide"+b]=function(a,c,e,f,g,h){var i=m.extend({},c),j=i.begin,k=i.complete,l={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},n={};i.display===d&&(i.display="Down"===b?"inline"===t.CSS.Values.getDisplayType(a)?"inline-block":"block":"none"),i.begin=function(){j&&j.call(g,g);for(var c in l){n[c]=a.style[c];var d=t.CSS.getPropertyValue(a,c);l[c]="Down"===b?[d,0]:[0,d]}n.overflow=a.style.overflow,a.style.overflow="hidden"},i.complete=function(){for(var b in n)a.style[b]=n[b];k&&k.call(g,g),h&&h.resolver(g)},t(a,l,i)}}),m.each(["In","Out"],function(a,b){t.Redirects["fade"+b]=function(a,c,e,f,g,h){var i=m.extend({},c),j={opacity:"In"===b?1:0},k=i.complete;i.complete=e!==f-1?i.begin=null:function(){k&&k.call(g,g),h&&h.resolver(g)},i.display===d&&(i.display="In"===b?"auto":"none"),t(this,j,i)}}),t}(window.jQuery||window.Zepto||window,window,document)})),!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(k(a,c),b)}function f(a,b,c){return!!Array.isArray(a)&&(g(a,c[b],c),!0)}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e<a.length;)b.call(c,a[e],e,a),e++;else for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)}function h(a,b,c){for(var e=Object.keys(b),f=0;f<e.length;)(!c||c&&a[e[f]]===d)&&(a[e[f]]=b[e[f]]),f++;return a}function i(a,b){return h(a,b,!0)}function j(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&h(d,c)}function k(a,b){return function(){return a.apply(b,arguments)}}function l(a,b){return typeof a==ka?a.apply(b?b[0]||d:d,b):a}function m(a,b){return a===d?b:a}function n(a,b,c){g(r(b),function(b){a.addEventListener(b,c,!1)})}function o(a,b,c){g(r(b),function(b){a.removeEventListener(b,c,!1)})}function p(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function q(a,b){return a.indexOf(b)>-1}function r(a){return a.trim().split(/\s+/g)}function s(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;d<a.length;){if(c&&a[d][c]==b||!c&&a[d]===b)return d;d++}return-1}function t(a){return Array.prototype.slice.call(a,0)}function u(a,b,c){for(var d=[],e=[],f=0;f<a.length;){var g=b?a[f][b]:a[f];s(e,g)<0&&d.push(a[f]),e[f]=g,f++}return c&&(d=b?d.sort(function(a,c){return a[b]>c[b]}):d.sort()),d}function v(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g<ia.length;){if(c=ia[g],e=c?c+f:b,e in a)return e;g++}return d}function w(){return oa++}function x(a){var b=a.ownerDocument;return b.defaultView||b.parentWindow}function y(a,b){var c=this;this.manager=a,this.callback=b,this.element=a.element,this.target=a.options.inputTarget,this.domHandler=function(b){l(a.options.enable,[a])&&c.handler(b)},this.init()}function z(a){var b,c=a.options.inputClass;return new(b=c?c:ra?N:sa?Q:qa?S:M)(a,A)}function A(a,b,c){var d=c.pointers.length,e=c.changedPointers.length,f=b&ya&&0===d-e,g=b&(Aa|Ba)&&0===d-e;c.isFirst=!!f,c.isFinal=!!g,f&&(a.session={}),c.eventType=b,B(a,c),a.emit("hammer.input",c),a.recognize(c),a.session.prevInput=c}function B(a,b){var c=a.session,d=b.pointers,e=d.length;c.firstInput||(c.firstInput=E(b)),e>1&&!c.firstMultiple?c.firstMultiple=E(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=F(d);b.timeStamp=na(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=J(h,i),b.distance=I(h,i),C(c,b),b.offsetDirection=H(b.deltaX,b.deltaY),b.scale=g?L(g.pointers,d):1,b.rotation=g?K(g.pointers,d):0,D(c,b);var j=a.element;p(b.srcEvent.target,j)&&(j=b.srcEvent.target),b.target=j}function C(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};(b.eventType===ya||f.eventType===Aa)&&(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function D(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ba&&(i>xa||h.velocity===d)){var j=h.deltaX-b.deltaX,k=h.deltaY-b.deltaY,l=G(i,j,k);e=l.x,f=l.y,c=ma(l.x)>ma(l.y)?l.x:l.y,g=H(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function E(a){for(var b=[],c=0;c<a.pointers.length;)b[c]={clientX:la(a.pointers[c].clientX),clientY:la(a.pointers[c].clientY)},c++;return{timeStamp:na(),pointers:b,center:F(b),deltaX:a.deltaX,deltaY:a.deltaY}}function F(a){var b=a.length;if(1===b)return{x:la(a[0].clientX),y:la(a[0].clientY)};for(var c=0,d=0,e=0;b>e;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:la(c/b),y:la(d/b)}}function G(a,b,c){return{x:b/a||0,y:c/a||0}}function H(a,b){return a===b?Ca:ma(a)>=ma(b)?a>0?Da:Ea:b>0?Fa:Ga}function I(a,b,c){c||(c=Ka);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function J(a,b,c){c||(c=Ka);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function K(a,b){return J(b[1],b[0],La)-J(a[1],a[0],La)}function L(a,b){return I(b[0],b[1],La)/I(a[0],a[1],La)}function M(){this.evEl=Na,this.evWin=Oa,this.allow=!0,this.pressed=!1,y.apply(this,arguments)}function N(){this.evEl=Ra,this.evWin=Sa,y.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function O(){this.evTarget=Ua,this.evWin=Va,this.started=!1,y.apply(this,arguments)}function P(a,b){var c=t(a.touches),d=t(a.changedTouches);return b&(Aa|Ba)&&(c=u(c.concat(d),"identifier",!0)),[c,d]}function Q(){this.evTarget=Xa,this.targetIds={},y.apply(this,arguments)}function R(a,b){var c=t(a.touches),d=this.targetIds;if(b&(ya|za)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=t(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return p(a.target,i)}),b===ya)for(e=0;e<f.length;)d[f[e].identifier]=!0,e++;for(e=0;e<g.length;)d[g[e].identifier]&&h.push(g[e]),b&(Aa|Ba)&&delete d[g[e].identifier],e++;return h.length?[u(f.concat(h),"identifier",!0),h]:void 0}function S(){y.apply(this,arguments);var a=k(this.handler,this);this.touch=new Q(this.manager,a),this.mouse=new M(this.manager,a)}function T(a,b){this.manager=a,this.set(b)}function U(a){if(q(a,bb))return bb;var b=q(a,cb),c=q(a,db);return b&&c?cb+" "+db:b||c?b?cb:db:q(a,ab)?ab:_a}function V(a){this.id=w(),this.manager=null,this.options=i(a||{},this.defaults),this.options.enable=m(this.options.enable,!0),this.state=eb,this.simultaneous={},this.requireFail=[]}function W(a){return a&jb?"cancel":a&hb?"end":a&gb?"move":a&fb?"start":""}function X(a){return a==Ga?"down":a==Fa?"up":a==Da?"left":a==Ea?"right":""}function Y(a,b){var c=b.manager;return c?c.get(a):a}function Z(){V.apply(this,arguments)}function $(){Z.apply(this,arguments),this.pX=null,this.pY=null}function _(){Z.apply(this,arguments)}function aa(){V.apply(this,arguments),this._timer=null,this._input=null}function ba(){Z.apply(this,arguments)}function ca(){Z.apply(this,arguments)}function da(){V.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ea(a,b){return b=b||{},b.recognizers=m(b.recognizers,ea.defaults.preset),new fa(a,b)}function fa(a,b){b=b||{},this.options=i(b,ea.defaults),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.element=a,this.input=z(this),this.touchAction=new T(this,this.options.touchAction),ga(this,!0),g(b.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ga(a,b){var c=a.element;g(a.options.cssProps,function(a,d){c.style[v(c.style,d)]=b?a:""})}function ha(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var ia=["","webkit","moz","MS","ms","o"],ja=b.createElement("div"),ka="function",la=Math.round,ma=Math.abs,na=Date.now,oa=1,pa=/mobile|tablet|ip(ad|hone|od)|android/i,qa="ontouchstart"in a,ra=v(a,"PointerEvent")!==d,sa=qa&&pa.test(navigator.userAgent),ta="touch",ua="pen",va="mouse",wa="kinect",xa=25,ya=1,za=2,Aa=4,Ba=8,Ca=1,Da=2,Ea=4,Fa=8,Ga=16,Ha=Da|Ea,Ia=Fa|Ga,Ja=Ha|Ia,Ka=["x","y"],La=["clientX","clientY"];y.prototype={handler:function(){},init:function(){this.evEl&&n(this.element,this.evEl,this.domHandler),this.evTarget&&n(this.target,this.evTarget,this.domHandler),this.evWin&&n(x(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&o(this.element,this.evEl,this.domHandler),this.evTarget&&o(this.target,this.evTarget,this.domHandler),this.evWin&&o(x(this.element),this.evWin,this.domHandler)}};var Ma={mousedown:ya,mousemove:za,mouseup:Aa},Na="mousedown",Oa="mousemove mouseup";j(M,y,{handler:function(a){var b=Ma[a.type];b&ya&&0===a.button&&(this.pressed=!0),b&za&&1!==a.which&&(b=Aa),this.pressed&&this.allow&&(b&Aa&&(this.pressed=!1),this.callback(this.manager,b,{pointers:[a],changedPointers:[a],pointerType:va,srcEvent:a}))}});var Pa={pointerdown:ya,pointermove:za,pointerup:Aa,pointercancel:Ba,pointerout:Ba},Qa={2:ta,3:ua,4:va,5:wa},Ra="pointerdown",Sa="pointermove pointerup pointercancel";a.MSPointerEvent&&(Ra="MSPointerDown",Sa="MSPointerMove MSPointerUp MSPointerCancel"),j(N,y,{handler:function(a){var b=this.store,c=!1,d=a.type.toLowerCase().replace("ms",""),e=Pa[d],f=Qa[a.pointerType]||a.pointerType,g=f==ta,h=s(b,a.pointerId,"pointerId");e&ya&&(0===a.button||g)?0>h&&(b.push(a),h=b.length-1):e&(Aa|Ba)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Ta={touchstart:ya,touchmove:za,touchend:Aa,touchcancel:Ba},Ua="touchstart",Va="touchstart touchmove touchend touchcancel";j(O,y,{handler:function(a){var b=Ta[a.type];if(b===ya&&(this.started=!0),this.started){var c=P.call(this,a,b);b&(Aa|Ba)&&0===c[0].length-c[1].length&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:ta,srcEvent:a})}}});var Wa={touchstart:ya,touchmove:za,touchend:Aa,touchcancel:Ba},Xa="touchstart touchmove touchend touchcancel";j(Q,y,{handler:function(a){var b=Wa[a.type],c=R.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:ta,srcEvent:a})}}),j(S,y,{handler:function(a,b,c){var d=c.pointerType==ta,e=c.pointerType==va;if(d)this.mouse.allow=!1;else if(e&&!this.mouse.allow)return;b&(Aa|Ba)&&(this.mouse.allow=!0),this.callback(a,b,c)},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var Ya=v(ja.style,"touchAction"),Za=Ya!==d,$a="compute",_a="auto",ab="manipulation",bb="none",cb="pan-x",db="pan-y";T.prototype={set:function(a){a==$a&&(a=this.compute()),Za&&(this.manager.element.style[Ya]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){l(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),U(a.join(" "))},preventDefaults:function(a){if(!Za){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=q(d,bb),f=q(d,db),g=q(d,cb);return e||f&&c&Ha||g&&c&Ia?this.preventSrc(b):void 0}},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var eb=1,fb=2,gb=4,hb=8,ib=hb,jb=16,kb=32;V.prototype={defaults:{},set:function(a){return h(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=Y(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=Y(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=Y(a,this),-1===s(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=Y(a,this);var b=s(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(c.options.event+(b?W(d):""),a)}var c=this,d=this.state;hb>d&&b(!0),b(),d>=hb&&b(!0)},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=kb)},canEmit:function(){for(var a=0;a<this.requireFail.length;){if(!(this.requireFail[a].state&(kb|eb)))return!1;a++}return!0},recognize:function(a){var b=h({},a);return l(this.options.enable,[this,b])?(this.state&(ib|jb|kb)&&(this.state=eb),this.state=this.process(b),void(this.state&(fb|gb|hb|jb)&&this.tryEmit(b))):(this.reset(),void(this.state=kb))},process:function(){},getTouchAction:function(){},reset:function(){}},j(Z,V,{defaults:{pointers:1},attrTest:function(a){var b=this.options.pointers;return 0===b||a.pointers.length===b},process:function(a){var b=this.state,c=a.eventType,d=b&(fb|gb),e=this.attrTest(a);return d&&(c&Ba||!e)?b|jb:d||e?c&Aa?b|hb:b&fb?b|gb:fb:kb}}),j($,Z,{defaults:{event:"pan",threshold:10,pointers:1,direction:Ja},getTouchAction:function(){var a=this.options.direction,b=[];return a&Ha&&b.push(db),a&Ia&&b.push(cb),b},directionTest:function(a){var b=this.options,c=!0,d=a.distance,e=a.direction,f=a.deltaX,g=a.deltaY;return e&b.direction||(b.direction&Ha?(e=0===f?Ca:0>f?Da:Ea,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ca:0>g?Fa:Ga,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return Z.prototype.attrTest.call(this,a)&&(this.state&fb||!(this.state&fb)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=X(a.direction);b&&this.manager.emit(this.options.event+b,a),this._super.emit.call(this,a)}}),j(_,Z,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[bb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&fb)},emit:function(a){if(this._super.emit.call(this,a),1!==a.scale){var b=a.scale<1?"in":"out";this.manager.emit(this.options.event+b,a)}}}),j(aa,V,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return[_a]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime>b.time;if(this._input=a,!d||!c||a.eventType&(Aa|Ba)&&!f)this.reset();else if(a.eventType&ya)this.reset(),this._timer=e(function(){this.state=ib,this.tryEmit()},b.time,this);else if(a.eventType&Aa)return ib;return kb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===ib&&(a&&a.eventType&Aa?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=na(),this.manager.emit(this.options.event,this._input)))}}),j(ba,Z,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[bb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&fb)}}),j(ca,Z,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:Ha|Ia,pointers:1},getTouchAction:function(){return $.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Ha|Ia)?b=a.velocity:c&Ha?b=a.velocityX:c&Ia&&(b=a.velocityY),this._super.attrTest.call(this,a)&&c&a.direction&&a.distance>this.options.threshold&&ma(b)>this.options.velocity&&a.eventType&Aa},emit:function(a){var b=X(a.direction);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),j(da,V,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[ab]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime<b.time;if(this.reset(),a.eventType&ya&&0===this.count)return this.failTimeout();if(d&&f&&c){if(a.eventType!=Aa)return this.failTimeout();var g=!this.pTime||a.timeStamp-this.pTime<b.interval,h=!this.pCenter||I(this.pCenter,a.center)<b.posThreshold;this.pTime=a.timeStamp,this.pCenter=a.center,h&&g?this.count+=1:this.count=1,this._input=a;var i=this.count%b.taps;if(0===i)return this.hasRequireFailures()?(this._timer=e(function(){this.state=ib,this.tryEmit()},b.interval,this),fb):ib}return kb},failTimeout:function(){return this._timer=e(function(){this.state=kb},this.options.interval,this),kb},reset:function(){clearTimeout(this._timer)},emit:function(){this.state==ib&&(this._input.tapCount=this.count,this.manager.emit(this.options.event,this._input))}}),ea.VERSION="2.0.4",ea.defaults={domEvents:!1,touchAction:$a,enable:!0,inputTarget:null,inputClass:null,preset:[[ba,{enable:!1}],[_,{enable:!1},["rotate"]],[ca,{direction:Ha}],[$,{direction:Ha},["swipe"]],[da],[da,{event:"doubletap",taps:2},["tap"]],[aa]],cssProps:{userSelect:"default",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}};var lb=1,mb=2;fa.prototype={set:function(a){return h(this.options,a),a.touchAction&&this.touchAction.update(),a.inputTarget&&(this.input.destroy(),this.input.target=a.inputTarget,this.input.init()),this},stop:function(a){this.session.stopped=a?mb:lb},recognize:function(a){var b=this.session;if(!b.stopped){this.touchAction.preventDefaults(a);var c,d=this.recognizers,e=b.curRecognizer;(!e||e&&e.state&ib)&&(e=b.curRecognizer=null);for(var f=0;f<d.length;)c=d[f],b.stopped===mb||e&&c!=e&&!c.canRecognizeWith(e)?c.reset():c.recognize(a),!e&&c.state&(fb|gb|hb)&&(e=b.curRecognizer=c),f++}},get:function(a){if(a instanceof V)return a;for(var b=this.recognizers,c=0;c<b.length;c++)if(b[c].options.event==a)return b[c];return null},add:function(a){if(f(a,"add",this))return this;var b=this.get(a.options.event);return b&&this.remove(b),this.recognizers.push(a),a.manager=this,this.touchAction.update(),a},remove:function(a){if(f(a,"remove",this))return this;var b=this.recognizers;return a=this.get(a),b.splice(s(b,a),1),this.touchAction.update(),this},on:function(a,b){var c=this.handlers;return g(r(a),function(a){c[a]=c[a]||[],c[a].push(b)}),this},off:function(a,b){var c=this.handlers;return g(r(a),function(a){b?c[a].splice(s(c[a],b),1):delete c[a]}),this},emit:function(a,b){this.options.domEvents&&ha(a,b);var c=this.handlers[a]&&this.handlers[a].slice();if(c&&c.length){b.type=a,b.preventDefault=function(){b.srcEvent.preventDefault()};for(var d=0;d<c.length;)c[d](b),d++}},destroy:function(){this.element&&ga(this,!1),this.handlers={},this.session={},this.input.destroy(),this.element=null}},h(ea,{INPUT_START:ya,INPUT_MOVE:za,INPUT_END:Aa,INPUT_CANCEL:Ba,STATE_POSSIBLE:eb,STATE_BEGAN:fb,STATE_CHANGED:gb,STATE_ENDED:hb,STATE_RECOGNIZED:ib,STATE_CANCELLED:jb,STATE_FAILED:kb,DIRECTION_NONE:Ca,DIRECTION_LEFT:Da,DIRECTION_RIGHT:Ea,DIRECTION_UP:Fa,DIRECTION_DOWN:Ga,DIRECTION_HORIZONTAL:Ha,DIRECTION_VERTICAL:Ia,DIRECTION_ALL:Ja,Manager:fa,Input:y,TouchAction:T,TouchInput:Q,MouseInput:M,PointerEventInput:N,TouchMouseInput:S,SingleTouchInput:O,Recognizer:V,AttrRecognizer:Z,Tap:da,Pan:$,Swipe:ca,Pinch:_,Rotate:ba,Press:aa,on:n,off:o,each:g,merge:i,extend:h,inherit:j,bindFn:k,prefixed:v}),typeof define==ka&&define.amd?define(function(){return ea}):"undefined"!=typeof module&&module.exports?module.exports=ea:a[c]=ea}(window,document,"Hammer"),function(a){"function"==typeof define&&define.amd?define(["jquery","hammerjs"],a):"object"==typeof exports?a(require("jquery"),require("hammerjs")):a(jQuery,Hammer)}(function(a,b){function c(c,d){var e=a(c);e.data("hammer")||e.data("hammer",new b(e[0],d))}a.fn.hammer=function(a){return this.each(function(){c(this,a)})},b.Manager.prototype.emit=function(b){return function(c,d){b.call(this,c,d),a(this.element).trigger({type:c,gesture:d})}}(b.Manager.prototype.emit)}),function(a){a.Package?Materialize={}:a.Materialize={}}(window),function(a){for(var b=0,c=["webkit","moz"],d=a.requestAnimationFrame,e=a.cancelAnimationFrame,f=c.length;--f>=0&&!d;)d=a[c[f]+"RequestAnimationFrame"],e=a[c[f]+"CancelRequestAnimationFrame"];d&&e||(d=function(a){var c=+Date.now(),d=Math.max(b+16,c);return setTimeout(function(){a(b=d)},d-c)},e=clearTimeout),a.requestAnimationFrame=d,a.cancelAnimationFrame=e}(window),Materialize.guid=function(){function a(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)}return function(){return a()+a()+"-"+a()+"-"+a()+"-"+a()+"-"+a()+a()+a()}}(),Materialize.escapeHash=function(a){return a.replace(/(:|\.|\[|\]|,|=)/g,"\\$1")},Materialize.elementOrParentIsFixed=function(a){var b=$(a),c=b.add(b.parents()),d=!1;return c.each(function(){if("fixed"===$(this).css("position"))return d=!0,!1}),d};var getTime=Date.now||function(){return(new Date).getTime()};Materialize.throttle=function(a,b,c){var d,e,f,g=null,h=0;c||(c={});var i=function(){h=c.leading===!1?0:getTime(),g=null,f=a.apply(d,e),d=e=null};return function(){var j=getTime();h||c.leading!==!1||(h=j);var k=b-(j-h);return d=this,e=arguments,k<=0?(clearTimeout(g),g=null,h=j,f=a.apply(d,e),d=e=null):g||c.trailing===!1||(g=setTimeout(i,k)),f}};var Vel;Vel=jQuery?jQuery.Velocity:$?$.Velocity:Velocity,function(a){a.fn.collapsible=function(b){var c={accordion:void 0,onOpen:void 0,onClose:void 0};return b=a.extend(c,b),this.each(function(){function c(b){j=i.find("> li > .collapsible-header"),b.hasClass("active")?b.parent().addClass("active"):b.parent().removeClass("active"),b.parent().hasClass("active")?b.siblings(".collapsible-body").stop(!0,!1).slideDown({duration:350,easing:"easeOutQuart",queue:!1,complete:function(){a(this).css("height","")}}):b.siblings(".collapsible-body").stop(!0,!1).slideUp({duration:350,easing:"easeOutQuart",queue:!1,complete:function(){a(this).css("height","")}}),j.not(b).removeClass("active").parent().removeClass("active"),j.not(b).parent().children(".collapsible-body").stop(!0,!1).each(function(){a(this).is(":visible")&&a(this).slideUp({duration:350,easing:"easeOutQuart",queue:!1,complete:function(){a(this).css("height",""),f(a(this).siblings(".collapsible-header"))}})})}function d(b){b.hasClass("active")?b.parent().addClass("active"):b.parent().removeClass("active"),b.parent().hasClass("active")?b.siblings(".collapsible-body").stop(!0,!1).slideDown({duration:350,easing:"easeOutQuart",queue:!1,complete:function(){a(this).css("height","")}}):b.siblings(".collapsible-body").stop(!0,!1).slideUp({duration:350,easing:"easeOutQuart",queue:!1,complete:function(){a(this).css("height","")}})}function e(a){b.accordion||"accordion"===k||void 0===k?c(a):d(a),f(a)}function f(a){a.hasClass("active")?"function"==typeof b.onOpen&&b.onOpen.call(this,a.parent()):"function"==typeof b.onClose&&b.onClose.call(this,a.parent())}function g(a){var b=h(a);return b.length>0}function h(a){return a.closest("li > .collapsible-header")}var i=a(this),j=a(this).find("> li > .collapsible-header"),k=i.data("collapsible");i.off("click.collapse","> li > .collapsible-header"),j.off("click.collapse"),i.on("click.collapse","> li > .collapsible-header",function(b){var c=a(b.target);g(c)&&(c=h(c)),c.toggleClass("active"),e(c)}),b.accordion||"accordion"===k||void 0===k?e(j.filter(".active").first()):j.filter(".active").each(function(){e(a(this))})})},a(document).ready(function(){a(".collapsible").collapsible()})}(jQuery),function(a){a.fn.scrollTo=function(b){return a(this).scrollTop(a(this).scrollTop()-a(this).offset().top+a(b).offset().top),this},a.fn.dropdown=function(b){var c={inDuration:300,outDuration:225,constrainWidth:!0,hover:!1,gutter:0,belowOrigin:!1,alignment:"left",stopPropagation:!1};return"open"===b?(this.each(function(){a(this).trigger("open")}),!1):"close"===b?(this.each(function(){a(this).trigger("close")}),!1):void this.each(function(){function d(){void 0!==g.data("induration")&&(h.inDuration=g.data("induration")),void 0!==g.data("outduration")&&(h.outDuration=g.data("outduration")),void 0!==g.data("constrainwidth")&&(h.constrainWidth=g.data("constrainwidth")),void 0!==g.data("hover")&&(h.hover=g.data("hover")),void 0!==g.data("gutter")&&(h.gutter=g.data("gutter")),void 0!==g.data("beloworigin")&&(h.belowOrigin=g.data("beloworigin")),void 0!==g.data("alignment")&&(h.alignment=g.data("alignment")),void 0!==g.data("stoppropagation")&&(h.stopPropagation=g.data("stoppropagation"))}function e(b){"focus"===b&&(i=!0),d(),j.addClass("active"),g.addClass("active"),h.constrainWidth===!0?j.css("width",g.outerWidth()):j.css("white-space","nowrap");var c=window.innerHeight,e=g.innerHeight(),k=g.offset().left,l=g.offset().top-a(window).scrollTop(),m=h.alignment,n=0,o=0,p=0;h.belowOrigin===!0&&(p=e);var q=0,r=0,s=g.parent();if(s.is("body")||(s[0].scrollHeight>s[0].clientHeight&&(q=s[0].scrollTop),s[0].scrollWidth>s[0].clientWidth&&(r=s[0].scrollLeft)),k+j.innerWidth()>a(window).width()?m="right":k-j.innerWidth()+g.innerWidth()<0&&(m="left"),l+j.innerHeight()>c)if(l+e-j.innerHeight()<0){var t=c-l-p;j.css("max-height",t)}else p||(p+=e),p-=j.innerHeight();if("left"===m)n=h.gutter,o=g.position().left+n;else if("right"===m){var u=g.position().left+g.outerWidth()-j.outerWidth();n=-h.gutter,o=u+n}j.css({position:"absolute",top:g.position().top+p+q,left:o+r}),j.stop(!0,!0).css("opacity",0).slideDown({queue:!1,duration:h.inDuration,easing:"easeOutCubic",complete:function(){a(this).css("height","")}}).animate({opacity:1},{queue:!1,duration:h.inDuration,easing:"easeOutSine"}),a(document).bind("click."+j.attr("id")+" touchstart."+j.attr("id"),function(b){j.is(b.target)||g.is(b.target)||g.find(b.target).length||(f(),a(document).unbind("click."+j.attr("id")+" touchstart."+j.attr("id")))})}function f(){i=!1,j.fadeOut(h.outDuration),j.removeClass("active"),g.removeClass("active"),a(document).unbind("click."+j.attr("id")+" touchstart."+j.attr("id")),setTimeout(function(){j.css("max-height","")},h.outDuration)}var g=a(this),h=a.extend({},c,b),i=!1,j=a("#"+g.attr("data-activates"));if(d(),g.after(j),h.hover){var k=!1;g.unbind("click."+g.attr("id")),g.on("mouseenter",function(a){k===!1&&(e(),k=!0)}),g.on("mouseleave",function(b){var c=b.toElement||b.relatedTarget;a(c).closest(".dropdown-content").is(j)||(j.stop(!0,!0),f(),k=!1)}),j.on("mouseleave",function(b){var c=b.toElement||b.relatedTarget;a(c).closest(".dropdown-button").is(g)||(j.stop(!0,!0),f(),k=!1)})}else g.unbind("click."+g.attr("id")),g.bind("click."+g.attr("id"),function(b){i||(g[0]!=b.currentTarget||g.hasClass("active")||0!==a(b.target).closest(".dropdown-content").length?g.hasClass("active")&&(f(),a(document).unbind("click."+j.attr("id")+" touchstart."+j.attr("id"))):(b.preventDefault(),h.stopPropagation&&b.stopPropagation(),e("click")))});g.on("open",function(a,b){e(b)}),g.on("close",f)})},a(document).ready(function(){a(".dropdown-button").dropdown()})}(jQuery),function(a){var b=0,c=0,d=function(){return c++,"materialize-modal-overlay-"+c},e={init:function(c){var e={opacity:.5,inDuration:350,outDuration:250,ready:void 0,
+complete:void 0,dismissible:!0,startingTop:"4%",endingTop:"10%"};return c=a.extend(e,c),this.each(function(){var e=a(this),f=a(this).attr("id")||"#"+a(this).data("target"),g=function(){var d=e.data("overlay-id"),f=a("#"+d);e.removeClass("open"),a("body").css({overflow:"",width:""}),e.find(".modal-close").off("click.close"),a(document).off("keyup.modal"+d),f.velocity({opacity:0},{duration:c.outDuration,queue:!1,ease:"easeOutQuart"});var g={duration:c.outDuration,queue:!1,ease:"easeOutCubic",complete:function(){a(this).css({display:"none"}),"function"==typeof c.complete&&c.complete.call(this,e),f.remove(),b--}};e.hasClass("bottom-sheet")?e.velocity({bottom:"-100%",opacity:0},g):e.velocity({top:c.startingTop,opacity:0,scaleX:.7},g)},h=function(f){var h=a("body"),i=h.innerWidth();if(h.css("overflow","hidden"),h.width(i),!e.hasClass("open")){var j=d(),k=a('<div class="modal-overlay"></div>');lStack=++b,k.attr("id",j).css("z-index",1e3+2*lStack),e.data("overlay-id",j).css("z-index",1e3+2*lStack+1),e.addClass("open"),a("body").append(k),c.dismissible&&(k.click(function(){g()}),a(document).on("keyup.modal"+j,function(a){27===a.keyCode&&g()})),e.find(".modal-close").on("click.close",function(a){g()}),k.css({display:"block",opacity:0}),e.css({display:"block",opacity:0}),k.velocity({opacity:c.opacity},{duration:c.inDuration,queue:!1,ease:"easeOutCubic"}),e.data("associated-overlay",k[0]);var l={duration:c.inDuration,queue:!1,ease:"easeOutCubic",complete:function(){"function"==typeof c.ready&&c.ready.call(this,e,f)}};e.hasClass("bottom-sheet")?e.velocity({bottom:"0",opacity:1},l):(a.Velocity.hook(e,"scaleX",.7),e.css({top:c.startingTop}),e.velocity({top:c.endingTop,opacity:1,scaleX:"1"},l))}};a(document).off("click.modalTrigger",'a[href="#'+f+'"], [data-target="'+f+'"]'),a(this).off("openModal"),a(this).off("closeModal"),a(document).on("click.modalTrigger",'a[href="#'+f+'"], [data-target="'+f+'"]',function(b){c.startingTop=(a(this).offset().top-a(window).scrollTop())/1.15,h(a(this)),b.preventDefault()}),a(this).on("openModal",function(){a(this).attr("href")||"#"+a(this).data("target");h()}),a(this).on("closeModal",function(){g()})})},open:function(){a(this).trigger("openModal")},close:function(){a(this).trigger("closeModal")}};a.fn.modal=function(b){return e[b]?e[b].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof b&&b?void a.error("Method "+b+" does not exist on jQuery.modal"):e.init.apply(this,arguments)}}(jQuery),function(a){a.fn.materialbox=function(){return this.each(function(){function b(){f=!1;var b=i.parent(".material-placeholder"),d=(window.innerWidth,window.innerHeight,i.data("width")),g=i.data("height");i.velocity("stop",!0),a("#materialbox-overlay").velocity("stop",!0),a(".materialbox-caption").velocity("stop",!0),a("#materialbox-overlay").velocity({opacity:0},{duration:h,queue:!1,easing:"easeOutQuad",complete:function(){e=!1,a(this).remove()}}),i.velocity({width:d,height:g,left:0,top:0},{duration:h,queue:!1,easing:"easeOutQuad"}),a(".materialbox-caption").velocity({opacity:0},{duration:h,queue:!1,easing:"easeOutQuad",complete:function(){b.css({height:"",width:"",position:"",top:"",left:""}),i.css({height:"",top:"",left:"",width:"","max-width":"",position:"","z-index":"","will-change":""}),i.removeClass("active"),f=!0,a(this).remove(),c&&c.css("overflow","")}})}if(!a(this).hasClass("initialized")){a(this).addClass("initialized");var c,d,e=!1,f=!0,g=275,h=200,i=a(this),j=a("<div></div>").addClass("material-placeholder");i.wrap(j),i.on("click",function(){var h=i.parent(".material-placeholder"),j=window.innerWidth,k=window.innerHeight,l=i.width(),m=i.height();if(f===!1)return b(),!1;if(e&&f===!0)return b(),!1;f=!1,i.addClass("active"),e=!0,h.css({width:h[0].getBoundingClientRect().width,height:h[0].getBoundingClientRect().height,position:"relative",top:0,left:0}),c=void 0,d=h[0].parentNode;for(;null!==d&&!a(d).is(document);){var n=a(d);"visible"!==n.css("overflow")&&(n.css("overflow","visible"),c=void 0===c?n:c.add(n)),d=d.parentNode}i.css({position:"absolute","z-index":1e3,"will-change":"left, top, width, height"}).data("width",l).data("height",m);var o=a('<div id="materialbox-overlay"></div>').css({opacity:0}).click(function(){f===!0&&b()});i.before(o);var p=o[0].getBoundingClientRect();if(o.css({width:j,height:k,left:-1*p.left,top:-1*p.top}),o.velocity({opacity:1},{duration:g,queue:!1,easing:"easeOutQuad"}),""!==i.data("caption")){var q=a('<div class="materialbox-caption"></div>');q.text(i.data("caption")),a("body").append(q),q.css({display:"inline"}),q.velocity({opacity:1},{duration:g,queue:!1,easing:"easeOutQuad"})}var r=0,s=l/j,t=m/k,u=0,v=0;s>t?(r=m/l,u=.9*j,v=.9*j*r):(r=l/m,u=.9*k*r,v=.9*k),i.hasClass("responsive-img")?i.velocity({"max-width":u,width:l},{duration:0,queue:!1,complete:function(){i.css({left:0,top:0}).velocity({height:v,width:u,left:a(document).scrollLeft()+j/2-i.parent(".material-placeholder").offset().left-u/2,top:a(document).scrollTop()+k/2-i.parent(".material-placeholder").offset().top-v/2},{duration:g,queue:!1,easing:"easeOutQuad",complete:function(){f=!0}})}}):i.css("left",0).css("top",0).velocity({height:v,width:u,left:a(document).scrollLeft()+j/2-i.parent(".material-placeholder").offset().left-u/2,top:a(document).scrollTop()+k/2-i.parent(".material-placeholder").offset().top-v/2},{duration:g,queue:!1,easing:"easeOutQuad",complete:function(){f=!0}})}),a(window).scroll(function(){e&&b()}),a(document).keyup(function(a){27===a.keyCode&&f===!0&&e&&b()})}})},a(document).ready(function(){a(".materialboxed").materialbox()})}(jQuery),function(a){a.fn.parallax=function(){var b=a(window).width();return this.each(function(c){function d(c){var d;d=b<601?e.height()>0?e.height():e.children("img").height():e.height()>0?e.height():500;var f=e.children("img").first(),g=f.height(),h=g-d,i=e.offset().top+d,j=e.offset().top,k=a(window).scrollTop(),l=window.innerHeight,m=k+l,n=(m-j)/(d+l),o=Math.round(h*n);c&&f.css("display","block"),i>k&&j<k+l&&f.css("transform","translate3D(-50%,"+o+"px, 0)")}var e=a(this);e.addClass("parallax"),e.children("img").one("load",function(){d(!0)}).each(function(){this.complete&&a(this).trigger("load")}),a(window).scroll(function(){b=a(window).width(),d(!1)}),a(window).resize(function(){b=a(window).width(),d(!1)})})}}(jQuery),function(a){var b={init:function(b){var c={onShow:null,swipeable:!1,responsiveThreshold:1/0};return b=a.extend(c,b),this.each(function(){var c,d,e,f,g,h=a(this),i=a(window).width(),j=h.find("li.tab a"),k=h.width(),l=a(),m=Math.max(k,h[0].scrollWidth)/j.length,n=prev_index=0,o=!1,p=300,q=function(a){return k-a.position().left-a.outerWidth()-h.scrollLeft()},r=function(a){return a.position().left+h.scrollLeft()},s=function(a){n-a>=0?(f.velocity({right:q(c)},{duration:p,queue:!1,easing:"easeOutQuad"}),f.velocity({left:r(c)},{duration:p,queue:!1,easing:"easeOutQuad",delay:90})):(f.velocity({left:r(c)},{duration:p,queue:!1,easing:"easeOutQuad"}),f.velocity({right:q(c)},{duration:p,queue:!1,easing:"easeOutQuad",delay:90}))};b.swipeable&&i>b.responsiveThreshold&&(b.swipeable=!1),c=a(j.filter('[href="'+location.hash+'"]')),0===c.length&&(c=a(this).find("li.tab a.active").first()),0===c.length&&(c=a(this).find("li.tab a").first()),c.addClass("active"),n=j.index(c),n<0&&(n=0),void 0!==c[0]&&(d=a(c[0].hash),d.addClass("active")),h.find(".indicator").length||h.append('<div class="indicator"></div>'),f=h.find(".indicator"),h.append(f),h.is(":visible")&&setTimeout(function(){f.css({right:q(c)}),f.css({left:r(c)})},0),a(window).resize(function(){k=h.width(),m=Math.max(k,h[0].scrollWidth)/j.length,n<0&&(n=0),0!==m&&0!==k&&(f.css({right:q(c)}),f.css({left:r(c)}))}),b.swipeable?(j.each(function(){var b=a(Materialize.escapeHash(this.hash));b.addClass("carousel-item"),l=l.add(b)}),e=l.wrapAll('<div class="tabs-content carousel"></div>'),l.css("display",""),a(".tabs-content.carousel").carousel({fullWidth:!0,noWrap:!0,onCycleTo:function(a){if(!o){var b=n;n=e.index(a),c=j.eq(n),s(b)}}})):j.not(c).each(function(){a(Materialize.escapeHash(this.hash)).hide()}),h.on("click","a",function(e){if(a(this).parent().hasClass("disabled"))return void e.preventDefault();if(!a(this).attr("target")){o=!0,k=h.width(),m=Math.max(k,h[0].scrollWidth)/j.length,c.removeClass("active");var f=d;c=a(this),d=a(Materialize.escapeHash(this.hash)),j=h.find("li.tab a");c.position();c.addClass("active"),prev_index=n,n=j.index(a(this)),n<0&&(n=0),b.swipeable?l.length&&l.carousel("set",n):(void 0!==d&&(d.show(),d.addClass("active"),"function"==typeof b.onShow&&b.onShow.call(this,d)),void 0===f||f.is(d)||(f.hide(),f.removeClass("active"))),g=setTimeout(function(){o=!1},p),s(prev_index),e.preventDefault()}})})},select_tab:function(a){this.find('a[href="#'+a+'"]').trigger("click")}};a.fn.tabs=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof c&&c?void a.error("Method "+c+" does not exist on jQuery.tabs"):b.init.apply(this,arguments)},a(document).ready(function(){a("ul.tabs").tabs()})}(jQuery),function(a){a.fn.tooltip=function(c){var d=5,e={delay:350,tooltip:"",position:"bottom",html:!1};return"remove"===c?(this.each(function(){a("#"+a(this).attr("data-tooltip-id")).remove(),a(this).off("mouseenter.tooltip mouseleave.tooltip")}),!1):(c=a.extend(e,c),this.each(function(){var e=Materialize.guid(),f=a(this);f.attr("data-tooltip-id")&&a("#"+f.attr("data-tooltip-id")).remove(),f.attr("data-tooltip-id",e);var g,h,i,j,k,l,m=function(){g=f.attr("data-html")?"true"===f.attr("data-html"):c.html,h=f.attr("data-delay"),h=void 0===h||""===h?c.delay:h,i=f.attr("data-position"),i=void 0===i||""===i?c.position:i,j=f.attr("data-tooltip"),j=void 0===j||""===j?c.tooltip:j};m();var n=function(){var b=a('<div class="material-tooltip"></div>');return j=g?a("<span></span>").html(j):a("<span></span>").text(j),b.append(j).appendTo(a("body")).attr("id",e),l=a('<div class="backdrop"></div>'),l.appendTo(b),b};k=n(),f.off("mouseenter.tooltip mouseleave.tooltip");var o,p=!1;f.on({"mouseenter.tooltip":function(a){var c=function(){m(),p=!0,k.velocity("stop"),l.velocity("stop"),k.css({visibility:"visible",left:"0px",top:"0px"});var a,c,e,g=f.outerWidth(),h=f.outerHeight(),j=k.outerHeight(),n=k.outerWidth(),o="0px",q="0px",r=l[0].offsetWidth,s=l[0].offsetHeight,t=8,u=8,v=0;"top"===i?(a=f.offset().top-j-d,c=f.offset().left+g/2-n/2,e=b(c,a,n,j),o="-10px",l.css({bottom:0,left:0,borderRadius:"14px 14px 0 0",transformOrigin:"50% 100%",marginTop:j,marginLeft:n/2-r/2})):"left"===i?(a=f.offset().top+h/2-j/2,c=f.offset().left-n-d,e=b(c,a,n,j),q="-10px",l.css({top:"-7px",right:0,width:"14px",height:"14px",borderRadius:"14px 0 0 14px",transformOrigin:"95% 50%",marginTop:j/2,marginLeft:n})):"right"===i?(a=f.offset().top+h/2-j/2,c=f.offset().left+g+d,e=b(c,a,n,j),q="+10px",l.css({top:"-7px",left:0,width:"14px",height:"14px",borderRadius:"0 14px 14px 0",transformOrigin:"5% 50%",marginTop:j/2,marginLeft:"0px"})):(a=f.offset().top+f.outerHeight()+d,c=f.offset().left+g/2-n/2,e=b(c,a,n,j),o="+10px",l.css({top:0,left:0,marginLeft:n/2-r/2})),k.css({top:e.y,left:e.x}),t=Math.SQRT2*n/parseInt(r),u=Math.SQRT2*j/parseInt(s),v=Math.max(t,u),k.velocity({translateY:o,translateX:q},{duration:350,queue:!1}).velocity({opacity:1},{duration:300,delay:50,queue:!1}),l.css({visibility:"visible"}).velocity({opacity:1},{duration:55,delay:0,queue:!1}).velocity({scaleX:v,scaleY:v},{duration:300,delay:0,queue:!1,easing:"easeInOutQuad"})};o=setTimeout(c,h)},"mouseleave.tooltip":function(){p=!1,clearTimeout(o),setTimeout(function(){p!==!0&&(k.velocity({opacity:0,translateY:0,translateX:0},{duration:225,queue:!1}),l.velocity({opacity:0,scaleX:1,scaleY:1},{duration:225,queue:!1,complete:function(){l.css({visibility:"hidden"}),k.css({visibility:"hidden"}),p=!1}}))},225)}})}))};var b=function(b,c,d,e){var f=b,g=c;return f<0?f=4:f+d>window.innerWidth&&(f-=f+d-window.innerWidth),g<0?g=4:g+e>window.innerHeight+a(window).scrollTop&&(g-=g+e-window.innerHeight),{x:f,y:g}};a(document).ready(function(){a(".tooltipped").tooltip()})}(jQuery),function(a){"use strict";function b(a){return null!==a&&a===a.window}function c(a){return b(a)?a:9===a.nodeType&&a.defaultView}function d(a){var b,d,e={top:0,left:0},f=a&&a.ownerDocument;return b=f.documentElement,"undefined"!=typeof a.getBoundingClientRect&&(e=a.getBoundingClientRect()),d=c(f),{top:e.top+d.pageYOffset-b.clientTop,left:e.left+d.pageXOffset-b.clientLeft}}function e(a){var b="";for(var c in a)a.hasOwnProperty(c)&&(b+=c+":"+a[c]+";");return b}function f(a){if(k.allowEvent(a)===!1)return null;for(var b=null,c=a.target||a.srcElement;null!==c.parentElement;){if(!(c instanceof SVGElement||c.className.indexOf("waves-effect")===-1)){b=c;break}if(c.classList.contains("waves-effect")){b=c;break}c=c.parentElement}return b}function g(b){var c=f(b);null!==c&&(j.show(b,c),"ontouchstart"in a&&(c.addEventListener("touchend",j.hide,!1),c.addEventListener("touchcancel",j.hide,!1)),c.addEventListener("mouseup",j.hide,!1),c.addEventListener("mouseleave",j.hide,!1))}var h=h||{},i=document.querySelectorAll.bind(document),j={duration:750,show:function(a,b){if(2===a.button)return!1;var c=b||this,f=document.createElement("div");f.className="waves-ripple",c.appendChild(f);var g=d(c),h=a.pageY-g.top,i=a.pageX-g.left,k="scale("+c.clientWidth/100*10+")";"touches"in a&&(h=a.touches[0].pageY-g.top,i=a.touches[0].pageX-g.left),f.setAttribute("data-hold",Date.now()),f.setAttribute("data-scale",k),f.setAttribute("data-x",i),f.setAttribute("data-y",h);var l={top:h+"px",left:i+"px"};f.className=f.className+" waves-notransition",f.setAttribute("style",e(l)),f.className=f.className.replace("waves-notransition",""),l["-webkit-transform"]=k,l["-moz-transform"]=k,l["-ms-transform"]=k,l["-o-transform"]=k,l.transform=k,l.opacity="1",l["-webkit-transition-duration"]=j.duration+"ms",l["-moz-transition-duration"]=j.duration+"ms",l["-o-transition-duration"]=j.duration+"ms",l["transition-duration"]=j.duration+"ms",l["-webkit-transition-timing-function"]="cubic-bezier(0.250, 0.460, 0.450, 0.940)",l["-moz-transition-timing-function"]="cubic-bezier(0.250, 0.460, 0.450, 0.940)",l["-o-transition-timing-function"]="cubic-bezier(0.250, 0.460, 0.450, 0.940)",l["transition-timing-function"]="cubic-bezier(0.250, 0.460, 0.450, 0.940)",f.setAttribute("style",e(l))},hide:function(a){k.touchup(a);var b=this,c=(1.4*b.clientWidth,null),d=b.getElementsByClassName("waves-ripple");if(!(d.length>0))return!1;c=d[d.length-1];var f=c.getAttribute("data-x"),g=c.getAttribute("data-y"),h=c.getAttribute("data-scale"),i=Date.now()-Number(c.getAttribute("data-hold")),l=350-i;l<0&&(l=0),setTimeout(function(){var a={top:g+"px",left:f+"px",opacity:"0","-webkit-transition-duration":j.duration+"ms","-moz-transition-duration":j.duration+"ms","-o-transition-duration":j.duration+"ms","transition-duration":j.duration+"ms","-webkit-transform":h,"-moz-transform":h,"-ms-transform":h,"-o-transform":h,transform:h};c.setAttribute("style",e(a)),setTimeout(function(){try{b.removeChild(c)}catch(a){return!1}},j.duration)},l)},wrapInput:function(a){for(var b=0;b<a.length;b++){var c=a[b];if("input"===c.tagName.toLowerCase()){var d=c.parentNode;if("i"===d.tagName.toLowerCase()&&d.className.indexOf("waves-effect")!==-1)continue;var e=document.createElement("i");e.className=c.className+" waves-input-wrapper";var f=c.getAttribute("style");f||(f=""),e.setAttribute("style",f),c.className="waves-button-input",c.removeAttribute("style"),d.replaceChild(e,c),e.appendChild(c)}}}},k={touches:0,allowEvent:function(a){var b=!0;return"touchstart"===a.type?k.touches+=1:"touchend"===a.type||"touchcancel"===a.type?setTimeout(function(){k.touches>0&&(k.touches-=1)},500):"mousedown"===a.type&&k.touches>0&&(b=!1),b},touchup:function(a){k.allowEvent(a)}};h.displayEffect=function(b){b=b||{},"duration"in b&&(j.duration=b.duration),j.wrapInput(i(".waves-effect")),"ontouchstart"in a&&document.body.addEventListener("touchstart",g,!1),document.body.addEventListener("mousedown",g,!1)},h.attach=function(b){"input"===b.tagName.toLowerCase()&&(j.wrapInput([b]),b=b.parentElement),"ontouchstart"in a&&b.addEventListener("touchstart",g,!1),b.addEventListener("mousedown",g,!1)},a.Waves=h,document.addEventListener("DOMContentLoaded",function(){h.displayEffect()},!1)}(window),Materialize.toast=function(a,b,c,d){function e(a){var b=document.createElement("div");if(b.classList.add("toast"),c)for(var e=c.split(" "),f=0,g=e.length;f<g;f++)b.classList.add(e[f]);("object"==typeof HTMLElement?a instanceof HTMLElement:a&&"object"==typeof a&&null!==a&&1===a.nodeType&&"string"==typeof a.nodeName)?b.appendChild(a):a instanceof jQuery?b.appendChild(a[0]):b.innerHTML=a;var h=new Hammer(b,{prevent_default:!1});return h.on("pan",function(a){var c=a.deltaX,d=80;b.classList.contains("panning")||b.classList.add("panning");var e=1-Math.abs(c/d);e<0&&(e=0),Vel(b,{left:c,opacity:e},{duration:50,queue:!1,easing:"easeOutQuad"})}),h.on("panend",function(a){var c=a.deltaX,e=80;Math.abs(c)>e?Vel(b,{marginTop:"-40px"},{duration:375,easing:"easeOutExpo",queue:!1,complete:function(){"function"==typeof d&&d(),b.parentNode.removeChild(b)}}):(b.classList.remove("panning"),Vel(b,{left:0,opacity:1},{duration:300,easing:"easeOutExpo",queue:!1}))}),b}c=c||"";var f=document.getElementById("toast-container");null===f&&(f=document.createElement("div"),f.id="toast-container",document.body.appendChild(f));var g=e(a);a&&f.appendChild(g),g.style.opacity=0,Vel(g,{translateY:"-35px",opacity:1},{duration:300,easing:"easeOutCubic",queue:!1});var h,i=b;null!=i&&(h=setInterval(function(){null===g.parentNode&&window.clearInterval(h),g.classList.contains("panning")||(i-=20),i<=0&&(Vel(g,{opacity:0,marginTop:"-40px"},{duration:375,easing:"easeOutExpo",queue:!1,complete:function(){"function"==typeof d&&d(),this[0].parentNode.removeChild(this[0])}}),window.clearInterval(h))},20))},function(a){var b={init:function(b){var c={menuWidth:300,edge:"left",closeOnClick:!1,draggable:!0};b=a.extend(c,b),a(this).each(function(){var c=a(this),d=c.attr("data-activates"),e=a("#"+d);300!=b.menuWidth&&e.css("width",b.menuWidth);var f=a('.drag-target[data-sidenav="'+d+'"]');b.draggable?(f.length&&f.remove(),f=a('<div class="drag-target"></div>').attr("data-sidenav",d),a("body").append(f)):f=a(),"left"==b.edge?(e.css("transform","translateX(-100%)"),f.css({left:0})):(e.addClass("right-aligned").css("transform","translateX(100%)"),f.css({right:0})),e.hasClass("fixed")&&window.innerWidth>992&&e.css("transform","translateX(0)"),e.hasClass("fixed")&&a(window).resize(function(){window.innerWidth>992?0!==a("#sidenav-overlay").length&&i?g(!0):e.css("transform","translateX(0%)"):i===!1&&("left"===b.edge?e.css("transform","translateX(-100%)"):e.css("transform","translateX(100%)"))}),b.closeOnClick===!0&&e.on("click.itemclick","a:not(.collapsible-header)",function(){g()});var g=function(c){h=!1,i=!1,a("body").css({overflow:"",width:""}),a("#sidenav-overlay").velocity({opacity:0},{duration:200,queue:!1,easing:"easeOutQuad",complete:function(){a(this).remove()}}),"left"===b.edge?(f.css({width:"",right:"",left:"0"}),e.velocity({translateX:"-100%"},{duration:200,queue:!1,easing:"easeOutCubic",complete:function(){c===!0&&(e.removeAttr("style"),e.css("width",b.menuWidth))}})):(f.css({width:"",right:"0",left:""}),e.velocity({translateX:"100%"},{duration:200,queue:!1,easing:"easeOutCubic",complete:function(){c===!0&&(e.removeAttr("style"),e.css("width",b.menuWidth))}}))},h=!1,i=!1;b.draggable&&(f.on("click",function(){i&&g()}),f.hammer({prevent_default:!1}).bind("pan",function(c){if("touch"==c.gesture.pointerType){var d=(c.gesture.direction,c.gesture.center.x),f=(c.gesture.center.y,c.gesture.velocityX,a("body")),h=a("#sidenav-overlay"),j=f.innerWidth();if(f.css("overflow","hidden"),f.width(j),0===h.length&&(h=a('<div id="sidenav-overlay"></div>'),h.css("opacity",0).click(function(){g()}),a("body").append(h)),"left"===b.edge&&(d>b.menuWidth?d=b.menuWidth:d<0&&(d=0)),"left"===b.edge)d<b.menuWidth/2?i=!1:d>=b.menuWidth/2&&(i=!0),e.css("transform","translateX("+(d-b.menuWidth)+"px)");else{d<window.innerWidth-b.menuWidth/2?i=!0:d>=window.innerWidth-b.menuWidth/2&&(i=!1);var k=d-b.menuWidth/2;k<0&&(k=0),e.css("transform","translateX("+k+"px)")}var l;"left"===b.edge?(l=d/b.menuWidth,h.velocity({opacity:l},{duration:10,queue:!1,easing:"easeOutQuad"})):(l=Math.abs((d-window.innerWidth)/b.menuWidth),h.velocity({opacity:l},{duration:10,queue:!1,easing:"easeOutQuad"}))}}).bind("panend",function(c){if("touch"==c.gesture.pointerType){var d=a('<div id="sidenav-overlay"></div>'),g=c.gesture.velocityX,j=c.gesture.center.x,k=j-b.menuWidth,l=j-b.menuWidth/2;k>0&&(k=0),l<0&&(l=0),h=!1,"left"===b.edge?i&&g<=.3||g<-.5?(0!==k&&e.velocity({translateX:[0,k]},{duration:300,queue:!1,easing:"easeOutQuad"}),d.velocity({opacity:1},{duration:50,queue:!1,easing:"easeOutQuad"}),f.css({width:"50%",right:0,left:""}),i=!0):(!i||g>.3)&&(a("body").css({overflow:"",width:""}),e.velocity({translateX:[-1*b.menuWidth-10,k]},{duration:200,queue:!1,easing:"easeOutQuad"}),d.velocity({opacity:0},{duration:200,queue:!1,easing:"easeOutQuad",complete:function(){a(this).remove()}}),f.css({width:"10px",right:"",left:0})):i&&g>=-.3||g>.5?(0!==l&&e.velocity({translateX:[0,l]},{duration:300,queue:!1,easing:"easeOutQuad"}),d.velocity({opacity:1},{duration:50,queue:!1,easing:"easeOutQuad"}),f.css({width:"50%",right:"",left:0}),i=!0):(!i||g<-.3)&&(a("body").css({overflow:"",width:""}),e.velocity({translateX:[b.menuWidth+10,l]},{duration:200,queue:!1,easing:"easeOutQuad"}),d.velocity({opacity:0},{duration:200,queue:!1,easing:"easeOutQuad",complete:function(){a(this).remove()}}),f.css({width:"10px",right:0,left:""}))}})),c.off("click.sidenav").on("click.sidenav",function(){if(i===!0)i=!1,h=!1,g();else{var c=a("body"),d=a('<div id="sidenav-overlay"></div>'),j=c.innerWidth();c.css("overflow","hidden"),c.width(j),a("body").append(f),"left"===b.edge?(f.css({width:"50%",right:0,left:""}),e.velocity({translateX:[0,-1*b.menuWidth]},{duration:300,queue:!1,easing:"easeOutQuad"})):(f.css({width:"50%",right:"",left:0}),e.velocity({translateX:[0,b.menuWidth]},{duration:300,queue:!1,easing:"easeOutQuad"})),d.css("opacity",0).click(function(){i=!1,h=!1,g(),d.velocity({opacity:0},{duration:300,queue:!1,easing:"easeOutQuad",complete:function(){a(this).remove()}})}),a("body").append(d),d.velocity({opacity:1},{duration:300,queue:!1,easing:"easeOutQuad",complete:function(){i=!0,h=!1}})}return!1})})},destroy:function(){var b=a("#sidenav-overlay"),c=a('.drag-target[data-sidenav="'+a(this).attr("data-activates")+'"]');b.trigger("click"),c.remove(),a(this).off("click"),b.remove()},show:function(){this.trigger("click")},hide:function(){a("#sidenav-overlay").trigger("click")}};a.fn.sideNav=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof c&&c?void a.error("Method "+c+" does not exist on jQuery.sideNav"):b.init.apply(this,arguments)}}(jQuery),function(a){function b(b,c,d,e){var g=a();return a.each(f,function(a,f){if(f.height()>0){var h=f.offset().top,i=f.offset().left,j=i+f.width(),k=h+f.height(),l=!(i>c||j<e||h>d||k<b);l&&g.push(f)}}),g}function c(c){++i;var d=e.scrollTop(),f=e.scrollLeft(),h=f+e.width(),k=d+e.height(),l=b(d+j.top+c||200,h+j.right,k+j.bottom,f+j.left);a.each(l,function(a,b){var c=b.data("scrollSpy:ticks");"number"!=typeof c&&b.triggerHandler("scrollSpy:enter"),b.data("scrollSpy:ticks",i)}),a.each(g,function(a,b){var c=b.data("scrollSpy:ticks");"number"==typeof c&&c!==i&&(b.triggerHandler("scrollSpy:exit"),b.data("scrollSpy:ticks",null))}),g=l}function d(){e.trigger("scrollSpy:winSize")}var e=a(window),f=[],g=[],h=!1,i=0,j={top:0,right:0,bottom:0,left:0};a.scrollSpy=function(b,d){var g={throttle:100,scrollOffset:200};d=a.extend(g,d);var i=[];b=a(b),b.each(function(b,c){f.push(a(c)),a(c).data("scrollSpy:id",b),a('a[href="#'+a(c).attr("id")+'"]').click(function(b){b.preventDefault();var c=a(Materialize.escapeHash(this.hash)).offset().top+1;a("html, body").animate({scrollTop:c-d.scrollOffset},{duration:400,queue:!1,easing:"easeOutCubic"})})}),j.top=d.offsetTop||0,j.right=d.offsetRight||0,j.bottom=d.offsetBottom||0,j.left=d.offsetLeft||0;var k=Materialize.throttle(function(){c(d.scrollOffset)},d.throttle||100),l=function(){a(document).ready(k)};return h||(e.on("scroll",l),e.on("resize",l),h=!0),setTimeout(l,0),b.on("scrollSpy:enter",function(){i=a.grep(i,function(a){return 0!=a.height()});var b=a(this);i[0]?(a('a[href="#'+i[0].attr("id")+'"]').removeClass("active"),b.data("scrollSpy:id")<i[0].data("scrollSpy:id")?i.unshift(a(this)):i.push(a(this))):i.push(a(this)),a('a[href="#'+i[0].attr("id")+'"]').addClass("active")}),b.on("scrollSpy:exit",function(){if(i=a.grep(i,function(a){return 0!=a.height()}),i[0]){a('a[href="#'+i[0].attr("id")+'"]').removeClass("active");var b=a(this);i=a.grep(i,function(a){return a.attr("id")!=b.attr("id")}),i[0]&&a('a[href="#'+i[0].attr("id")+'"]').addClass("active")}}),b},a.winSizeSpy=function(b){return a.winSizeSpy=function(){return e},b=b||{throttle:100},e.on("resize",Materialize.throttle(d,b.throttle||100))},a.fn.scrollSpy=function(b){return a.scrollSpy(a(this),b)}}(jQuery),function(a){a(document).ready(function(){function b(b){var c=b.css("font-family"),d=b.css("font-size"),f=b.css("line-height");d&&e.css("font-size",d),c&&e.css("font-family",c),f&&e.css("line-height",f),"off"===b.attr("wrap")&&e.css("overflow-wrap","normal").css("white-space","pre"),e.text(b.val()+"\n");var g=e.html().replace(/\n/g,"<br>");e.html(g),b.is(":visible")?e.css("width",b.width()):e.css("width",a(window).width()/2),b.css("height",e.height())}Materialize.updateTextFields=function(){var b="input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], textarea";a(b).each(function(b,c){var d=a(this);a(c).val().length>0||c.autofocus||void 0!==d.attr("placeholder")?d.siblings("label").addClass("active"):a(c)[0].validity?d.siblings("label").toggleClass("active",a(c)[0].validity.badInput===!0):d.siblings("label").removeClass("active")})};var c="input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], input[type=number], input[type=search], textarea";a(document).on("change",c,function(){0===a(this).val().length&&void 0===a(this).attr("placeholder")||a(this).siblings("label").addClass("active"),validate_field(a(this))}),a(document).ready(function(){Materialize.updateTextFields()}),a(document).on("reset",function(b){var d=a(b.target);d.is("form")&&(d.find(c).removeClass("valid").removeClass("invalid"),d.find(c).each(function(){""===a(this).attr("value")&&a(this).siblings("label").removeClass("active")}),d.find("select.initialized").each(function(){var a=d.find("option[selected]").text();d.siblings("input.select-dropdown").val(a)}))}),a(document).on("focus",c,function(){a(this).siblings("label, .prefix").addClass("active")}),a(document).on("blur",c,function(){var b=a(this),c=".prefix";0===b.val().length&&b[0].validity.badInput!==!0&&void 0===b.attr("placeholder")&&(c+=", label"),b.siblings(c).removeClass("active"),validate_field(b)}),window.validate_field=function(a){var b=void 0!==a.attr("data-length"),c=parseInt(a.attr("data-length")),d=a.val().length;0===a.val().length&&a[0].validity.badInput===!1?a.hasClass("validate")&&(a.removeClass("valid"),a.removeClass("invalid")):a.hasClass("validate")&&(a.is(":valid")&&b&&d<=c||a.is(":valid")&&!b?(a.removeClass("invalid"),a.addClass("valid")):(a.removeClass("valid"),a.addClass("invalid")))};var d="input[type=radio], input[type=checkbox]";a(document).on("keyup.radio",d,function(b){if(9===b.which){a(this).addClass("tabbed");var c=a(this);return void c.one("blur",function(b){a(this).removeClass("tabbed")})}});var e=a(".hiddendiv").first();e.length||(e=a('<div class="hiddendiv common"></div>'),a("body").append(e));var f=".materialize-textarea";a(f).each(function(){var c=a(this);c.val().length&&b(c)}),a("body").on("keyup keydown autoresize",f,function(){b(a(this))}),a(document).on("change",'.file-field input[type="file"]',function(){for(var b=a(this).closest(".file-field"),c=b.find("input.file-path"),d=a(this)[0].files,e=[],f=0;f<d.length;f++)e.push(d[f].name);c.val(e.join(", ")),c.trigger("change")});var g,h="input[type=range]",i=!1;a(h).each(function(){var b=a('<span class="thumb"><span class="value"></span></span>');a(this).after(b)});var j=".range-field";a(document).on("change",h,function(b){var c=a(this).siblings(".thumb");c.find(".value").html(a(this).val())}),a(document).on("input mousedown touchstart",h,function(b){var c=a(this).siblings(".thumb"),d=a(this).outerWidth();c.length<=0&&(c=a('<span class="thumb"><span class="value"></span></span>'),a(this).after(c)),c.find(".value").html(a(this).val()),i=!0,a(this).addClass("active"),c.hasClass("active")||c.velocity({height:"30px",width:"30px",top:"-20px",marginLeft:"-15px"},{duration:300,easing:"easeOutExpo"}),"input"!==b.type&&(g=void 0===b.pageX||null===b.pageX?b.originalEvent.touches[0].pageX-a(this).offset().left:b.pageX-a(this).offset().left,g<0?g=0:g>d&&(g=d),c.addClass("active").css("left",g)),c.find(".value").html(a(this).val())}),a(document).on("mouseup touchend",j,function(){i=!1,a(this).removeClass("active")}),a(document).on("mousemove touchmove",j,function(b){var c,d=a(this).children(".thumb");if(i){d.hasClass("active")||d.velocity({height:"30px",width:"30px",top:"-20px",marginLeft:"-15px"},{duration:300,easing:"easeOutExpo"}),c=void 0===b.pageX||null===b.pageX?b.originalEvent.touches[0].pageX-a(this).offset().left:b.pageX-a(this).offset().left;var e=a(this).outerWidth();c<0?c=0:c>e&&(c=e),d.addClass("active").css("left",c),d.find(".value").html(d.siblings(h).val())}}),a(document).on("mouseout touchleave",j,function(){if(!i){var b=a(this).children(".thumb");b.hasClass("active")&&b.velocity({height:"0",width:"0",top:"10px",marginLeft:"-6px"},{duration:100}),b.removeClass("active")}}),a.fn.autocomplete=function(b){var c={data:{},limit:1/0,onAutocomplete:null};return b=a.extend(c,b),this.each(function(){var c,d=a(this),e=b.data,f=0,g=0,h=d.closest(".input-field");if(!a.isEmptyObject(e)){var i,j=a('<ul class="autocomplete-content dropdown-content"></ul>');h.length?(i=h.children(".autocomplete-content.dropdown-content").first(),i.length||h.append(j)):(i=d.next(".autocomplete-content.dropdown-content"),i.length||d.after(j)),i.length&&(j=i);var k=function(a,b){var c=b.find("img"),d=b.text().toLowerCase().indexOf(""+a.toLowerCase()),e=d+a.length-1,f=b.text().slice(0,d),g=b.text().slice(d,e+1),h=b.text().slice(e+1);b.html("<span>"+f+"<span class='highlight'>"+g+"</span>"+h+"</span>"),c.length&&b.prepend(c)},l=function(){g=0,j.find(".active").removeClass("active")};d.off("keyup.autocomplete").on("keyup.autocomplete",function(g){if(f=0,13!==g.which&&38!==g.which&&40!==g.which){var h=d.val().toLowerCase();if(c!==h&&(j.empty(),l(),""!==h))for(var i in e)if(e.hasOwnProperty(i)&&i.toLowerCase().indexOf(h)!==-1&&i.toLowerCase()!==h){if(f>=b.limit)break;var m=a("<li></li>");e[i]?m.append('<img src="'+e[i]+'" class="right circle"><span>'+i+"</span>"):m.append("<span>"+i+"</span>"),j.append(m),k(h,m),f++}c=h}}),d.off("keydown.autocomplete").on("keydown.autocomplete",function(a){var b,c=a.which,d=j.children("li").length,e=j.children(".active").first();return 13===c?(b=j.children("li").eq(g),void(b.length&&(b.click(),a.preventDefault()))):void(38!==c&&40!==c||(a.preventDefault(),38===c&&g>0&&g--,40===c&&g<d-1&&e.length&&g++,e.removeClass("active"),j.children("li").eq(g).addClass("active")))}),j.on("click","li",function(){var c=a(this).text().trim();d.val(c),d.trigger("change"),j.empty(),l(),"function"==typeof b.onAutocomplete&&b.onAutocomplete.call(this,c)})}})}}),a.fn.material_select=function(b){function c(a,b,c){var e=a.indexOf(b),f=e===-1;return f?a.push(b):a.splice(e,1),c.siblings("ul.dropdown-content").find("li").eq(b).toggleClass("active"),c.find("option").eq(b).prop("selected",f),d(a,c),f}function d(a,b){
+for(var c="",d=0,e=a.length;d<e;d++){var f=b.find("option").eq(a[d]).text();c+=0===d?f:", "+f}""===c&&(c=b.find("option:disabled").eq(0).text()),b.siblings("input.select-dropdown").val(c)}a(this).each(function(){var d=a(this);if(!d.hasClass("browser-default")){var e=!!d.attr("multiple"),f=d.data("select-id");if(f&&(d.parent().find("span.caret").remove(),d.parent().find("input").remove(),d.unwrap(),a("ul#select-options-"+f).remove()),"destroy"===b)return void d.data("select-id",null).removeClass("initialized");var g=Materialize.guid();d.data("select-id",g);var h=a('<div class="select-wrapper"></div>');h.addClass(d.attr("class"));var i=a('<ul id="select-options-'+g+'" class="dropdown-content select-dropdown '+(e?"multiple-select-dropdown":"")+'"></ul>'),j=d.children("option, optgroup"),k=[],l=!1,m=d.find("option:selected").html()||d.find("option:first").html()||"",n=function(b,c,d){var e=c.is(":disabled")?"disabled ":"",f="optgroup-option"===d?"optgroup-option ":"",g=c.data("icon"),h=c.attr("class");if(g){var j="";return h&&(j=' class="'+h+'"'),"multiple"===d?i.append(a('<li class="'+e+'"><img alt="" src="'+g+'"'+j+'><span><input type="checkbox"'+e+"/><label></label>"+c.html()+"</span></li>")):i.append(a('<li class="'+e+f+'"><img alt="" src="'+g+'"'+j+"><span>"+c.html()+"</span></li>")),!0}"multiple"===d?i.append(a('<li class="'+e+'"><span><input type="checkbox"'+e+"/><label></label>"+c.html()+"</span></li>")):i.append(a('<li class="'+e+f+'"><span>'+c.html()+"</span></li>"))};j.length&&j.each(function(){if(a(this).is("option"))e?n(d,a(this),"multiple"):n(d,a(this));else if(a(this).is("optgroup")){var b=a(this).children("option");i.append(a('<li class="optgroup"><span>'+a(this).attr("label")+"</span></li>")),b.each(function(){n(d,a(this),"optgroup-option")})}}),i.find("li:not(.optgroup)").each(function(f){a(this).click(function(g){if(!a(this).hasClass("disabled")&&!a(this).hasClass("optgroup")){var h=!0;e?(a('input[type="checkbox"]',this).prop("checked",function(a,b){return!b}),h=c(k,a(this).index(),d),q.trigger("focus")):(i.find("li").removeClass("active"),a(this).toggleClass("active"),q.val(a(this).text())),r(i,a(this)),d.find("option").eq(f).prop("selected",h),d.trigger("change"),"undefined"!=typeof b&&b()}g.stopPropagation()})}),d.wrap(h);var o=a('<span class="caret">&#9660;</span>');d.is(":disabled")&&o.addClass("disabled");var p=m.replace(/"/g,"&quot;"),q=a('<input type="text" class="select-dropdown" readonly="true" '+(d.is(":disabled")?"disabled":"")+' data-activates="select-options-'+g+'" value="'+p+'"/>');d.before(q),q.before(o),q.after(i),d.is(":disabled")||q.dropdown({hover:!1,closeOnClick:!1}),d.attr("tabindex")&&a(q[0]).attr("tabindex",d.attr("tabindex")),d.addClass("initialized"),q.on({focus:function(){if(a("ul.select-dropdown").not(i[0]).is(":visible")&&a("input.select-dropdown").trigger("close"),!i.is(":visible")){a(this).trigger("open",["focus"]);var b=a(this).val();e&&b.indexOf(",")>=0&&(b=b.split(",")[0]);var c=i.find("li").filter(function(){return a(this).text().toLowerCase()===b.toLowerCase()})[0];r(i,c,!0)}},click:function(a){a.stopPropagation()}}),q.on("blur",function(){e||a(this).trigger("close"),i.find("li.selected").removeClass("selected")}),i.hover(function(){l=!0},function(){l=!1}),a(window).on({click:function(){e&&(l||q.trigger("close"))}}),e&&d.find("option:selected:not(:disabled)").each(function(){var b=a(this).index();c(k,b,d),i.find("li").eq(b).find(":checkbox").prop("checked",!0)});var r=function(b,c,d){if(c){b.find("li.selected").removeClass("selected");var f=a(c);f.addClass("selected"),e&&!d||i.scrollTo(f)}},s=[],t=function(b){if(9==b.which)return void q.trigger("close");if(40==b.which&&!i.is(":visible"))return void q.trigger("open");if(13!=b.which||i.is(":visible")){b.preventDefault();var c=String.fromCharCode(b.which).toLowerCase(),d=[9,13,27,38,40];if(c&&d.indexOf(b.which)===-1){s.push(c);var f=s.join(""),g=i.find("li").filter(function(){return 0===a(this).text().toLowerCase().indexOf(f)})[0];g&&r(i,g)}if(13==b.which){var h=i.find("li.selected:not(.disabled)")[0];h&&(a(h).trigger("click"),e||q.trigger("close"))}40==b.which&&(g=i.find("li.selected").length?i.find("li.selected").next("li:not(.disabled)")[0]:i.find("li:not(.disabled)")[0],r(i,g)),27==b.which&&q.trigger("close"),38==b.which&&(g=i.find("li.selected").prev("li:not(.disabled)")[0],g&&r(i,g)),setTimeout(function(){s=[]},1e3)}};q.on("keydown",t)}})}}(jQuery),function(a){var b={init:function(b){var c={indicators:!0,height:400,transition:500,interval:6e3};return b=a.extend(c,b),this.each(function(){function c(a,b){a.hasClass("center-align")?a.velocity({opacity:0,translateY:-100},{duration:b,queue:!1}):a.hasClass("right-align")?a.velocity({opacity:0,translateX:100},{duration:b,queue:!1}):a.hasClass("left-align")&&a.velocity({opacity:0,translateX:-100},{duration:b,queue:!1})}function d(a){a>=j.length?a=0:a<0&&(a=j.length-1),k=i.find(".active").index(),k!=a&&(e=j.eq(k),$caption=e.find(".caption"),e.removeClass("active"),e.velocity({opacity:0},{duration:b.transition,queue:!1,easing:"easeOutQuad",complete:function(){j.not(".active").velocity({opacity:0,translateX:0,translateY:0},{duration:0,queue:!1})}}),c($caption,b.transition),b.indicators&&f.eq(k).removeClass("active"),j.eq(a).velocity({opacity:1},{duration:b.transition,queue:!1,easing:"easeOutQuad"}),j.eq(a).find(".caption").velocity({opacity:1,translateX:0,translateY:0},{duration:b.transition,delay:b.transition,queue:!1,easing:"easeOutQuad"}),j.eq(a).addClass("active"),b.indicators&&f.eq(a).addClass("active"))}var e,f,g,h=a(this),i=h.find("ul.slides").first(),j=i.find("> li"),k=i.find(".active").index();k!=-1&&(e=j.eq(k)),h.hasClass("fullscreen")||(b.indicators?h.height(b.height+40):h.height(b.height),i.height(b.height)),j.find(".caption").each(function(){c(a(this),0)}),j.find("img").each(function(){var b="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";a(this).attr("src")!==b&&(a(this).css("background-image","url("+a(this).attr("src")+")"),a(this).attr("src",b))}),b.indicators&&(f=a('<ul class="indicators"></ul>'),j.each(function(c){var e=a('<li class="indicator-item"></li>');e.click(function(){var c=i.parent(),e=c.find(a(this)).index();d(e),clearInterval(g),g=setInterval(function(){k=i.find(".active").index(),j.length==k+1?k=0:k+=1,d(k)},b.transition+b.interval)}),f.append(e)}),h.append(f),f=h.find("ul.indicators").find("li.indicator-item")),e?e.show():(j.first().addClass("active").velocity({opacity:1},{duration:b.transition,queue:!1,easing:"easeOutQuad"}),k=0,e=j.eq(k),b.indicators&&f.eq(k).addClass("active")),e.find("img").each(function(){e.find(".caption").velocity({opacity:1,translateX:0,translateY:0},{duration:b.transition,queue:!1,easing:"easeOutQuad"})}),g=setInterval(function(){k=i.find(".active").index(),d(k+1)},b.transition+b.interval);var l=!1,m=!1,n=!1;h.hammer({prevent_default:!1}).bind("pan",function(a){if("touch"===a.gesture.pointerType){clearInterval(g);var b=a.gesture.direction,c=a.gesture.deltaX,d=a.gesture.velocityX,e=a.gesture.velocityY;$curr_slide=i.find(".active"),Math.abs(d)>Math.abs(e)&&$curr_slide.velocity({translateX:c},{duration:50,queue:!1,easing:"easeOutQuad"}),4===b&&(c>h.innerWidth()/2||d<-.65)?n=!0:2===b&&(c<-1*h.innerWidth()/2||d>.65)&&(m=!0);var f;m&&(f=$curr_slide.next(),0===f.length&&(f=j.first()),f.velocity({opacity:1},{duration:300,queue:!1,easing:"easeOutQuad"})),n&&(f=$curr_slide.prev(),0===f.length&&(f=j.last()),f.velocity({opacity:1},{duration:300,queue:!1,easing:"easeOutQuad"}))}}).bind("panend",function(a){"touch"===a.gesture.pointerType&&($curr_slide=i.find(".active"),l=!1,curr_index=i.find(".active").index(),!n&&!m||j.length<=1?$curr_slide.velocity({translateX:0},{duration:300,queue:!1,easing:"easeOutQuad"}):m?(d(curr_index+1),$curr_slide.velocity({translateX:-1*h.innerWidth()},{duration:300,queue:!1,easing:"easeOutQuad",complete:function(){$curr_slide.velocity({opacity:0,translateX:0},{duration:0,queue:!1})}})):n&&(d(curr_index-1),$curr_slide.velocity({translateX:h.innerWidth()},{duration:300,queue:!1,easing:"easeOutQuad",complete:function(){$curr_slide.velocity({opacity:0,translateX:0},{duration:0,queue:!1})}})),m=!1,n=!1,clearInterval(g),g=setInterval(function(){k=i.find(".active").index(),j.length==k+1?k=0:k+=1,d(k)},b.transition+b.interval))}),h.on("sliderPause",function(){clearInterval(g)}),h.on("sliderStart",function(){clearInterval(g),g=setInterval(function(){k=i.find(".active").index(),j.length==k+1?k=0:k+=1,d(k)},b.transition+b.interval)}),h.on("sliderNext",function(){k=i.find(".active").index(),d(k+1)}),h.on("sliderPrev",function(){k=i.find(".active").index(),d(k-1)})})},pause:function(){a(this).trigger("sliderPause")},start:function(){a(this).trigger("sliderStart")},next:function(){a(this).trigger("sliderNext")},prev:function(){a(this).trigger("sliderPrev")}};a.fn.slider=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof c&&c?void a.error("Method "+c+" does not exist on jQuery.tooltip"):b.init.apply(this,arguments)}}(jQuery),function(a){a(document).ready(function(){a(document).on("click.card",".card",function(b){a(this).find("> .card-reveal").length&&(a(b.target).is(a(".card-reveal .card-title"))||a(b.target).is(a(".card-reveal .card-title i"))?a(this).find(".card-reveal").velocity({translateY:0},{duration:225,queue:!1,easing:"easeInOutQuad",complete:function(){a(this).css({display:"none"})}}):(a(b.target).is(a(".card .activator"))||a(b.target).is(a(".card .activator i")))&&(a(b.target).closest(".card").css("overflow","hidden"),a(this).find(".card-reveal").css({display:"block"}).velocity("stop",!1).velocity({translateY:"-100%"},{duration:300,queue:!1,easing:"easeInOutQuad"})))})})}(jQuery),function(a){var b={data:[],placeholder:"",secondaryPlaceholder:"",autocompleteData:{},autocompleteLimit:1/0};a(document).ready(function(){a(document).on("click",".chip .close",function(b){var c=a(this).closest(".chips");c.attr("data-initialized")||a(this).closest(".chip").remove()})}),a.fn.material_chip=function(c){var d=this;if(this.$el=a(this),this.$document=a(document),this.SELS={CHIPS:".chips",CHIP:".chip",INPUT:"input",DELETE:".material-icons",SELECTED_CHIP:".selected"},"data"===c)return this.$el.data("chips");var e=a.extend({},b,c);d.hasAutocomplete=!a.isEmptyObject(e.autocompleteData),this.init=function(){var b=0;d.$el.each(function(){var c=a(this),f=Materialize.guid();d.chipId=f,e.data&&e.data instanceof Array||(e.data=[]),c.data("chips",e.data),c.attr("data-index",b),c.attr("data-initialized",!0),c.hasClass(d.SELS.CHIPS)||c.addClass("chips"),d.chips(c,f),b++})},this.handleEvents=function(){var b=d.SELS;d.$document.off("click.chips-focus",b.CHIPS).on("click.chips-focus",b.CHIPS,function(c){a(c.target).find(b.INPUT).focus()}),d.$document.off("click.chips-select",b.CHIP).on("click.chips-select",b.CHIP,function(c){var e=a(c.target);if(e.length){var f=e.hasClass("selected"),g=e.closest(b.CHIPS);a(b.CHIP).removeClass("selected"),f||d.selectChip(e.index(),g)}}),d.$document.off("keydown.chips").on("keydown.chips",function(c){if(!a(c.target).is("input, textarea")){var e,f=d.$document.find(b.CHIP+b.SELECTED_CHIP),g=f.closest(b.CHIPS),h=f.siblings(b.CHIP).length;if(f.length)if(8===c.which||46===c.which){c.preventDefault(),e=f.index(),d.deleteChip(e,g);var i=null;e+1<h?i=e:e!==h&&e+1!==h||(i=h-1),i<0&&(i=null),null!==i&&d.selectChip(i,g),h||g.find("input").focus()}else if(37===c.which){if(e=f.index()-1,e<0)return;a(b.CHIP).removeClass("selected"),d.selectChip(e,g)}else if(39===c.which){if(e=f.index()+1,a(b.CHIP).removeClass("selected"),e>h)return void g.find("input").focus();d.selectChip(e,g)}}}),d.$document.off("focusin.chips",b.CHIPS+" "+b.INPUT).on("focusin.chips",b.CHIPS+" "+b.INPUT,function(c){var d=a(c.target).closest(b.CHIPS);d.addClass("focus"),d.siblings("label, .prefix").addClass("active"),a(b.CHIP).removeClass("selected")}),d.$document.off("focusout.chips",b.CHIPS+" "+b.INPUT).on("focusout.chips",b.CHIPS+" "+b.INPUT,function(c){var d=a(c.target).closest(b.CHIPS);d.removeClass("focus"),d.data("chips").length||d.siblings("label").removeClass("active"),d.siblings(".prefix").removeClass("active")}),d.$document.off("keydown.chips-add",b.CHIPS+" "+b.INPUT).on("keydown.chips-add",b.CHIPS+" "+b.INPUT,function(c){var e=a(c.target),f=e.closest(b.CHIPS),g=f.children(b.CHIP).length;if(13===c.which){if(d.hasAutocomplete&&f.find(".autocomplete-content.dropdown-content").length&&f.find(".autocomplete-content.dropdown-content").children().length)return;return c.preventDefault(),d.addChip({tag:e.val()},f),void e.val("")}if((8===c.keyCode||37===c.keyCode)&&""===e.val()&&g)return c.preventDefault(),d.selectChip(g-1,f),void e.blur()}),d.$document.off("click.chips-delete",b.CHIPS+" "+b.DELETE).on("click.chips-delete",b.CHIPS+" "+b.DELETE,function(c){var e=a(c.target),f=e.closest(b.CHIPS),g=e.closest(b.CHIP);c.stopPropagation(),d.deleteChip(g.index(),f),f.find("input").focus()})},this.chips=function(b,c){var f="";b.data("chips").forEach(function(a){f+=d.renderChip(a)}),f+='<input id="'+c+'" class="input" placeholder="">',b.html(f),d.setPlaceholder(b);var g=b.next("label");g.length&&(g.attr("for",c),b.data("chips").length&&g.addClass("active"));var h=a("#"+c);d.hasAutocomplete&&h.autocomplete({data:e.autocompleteData,limit:e.autocompleteLimit,onAutocomplete:function(a){d.addChip({tag:a},b),h.val(""),h.focus()}})},this.renderChip=function(a){if(a.tag){var b='<div class="chip">'+a.tag;return a.image&&(b+=' <img src="'+a.image+'"> '),b+='<i class="material-icons close">close</i>',b+="</div>"}},this.setPlaceholder=function(a){a.data("chips").length&&e.placeholder?a.find("input").prop("placeholder",e.placeholder):!a.data("chips").length&&e.secondaryPlaceholder&&a.find("input").prop("placeholder",e.secondaryPlaceholder)},this.isValid=function(a,b){for(var c=a.data("chips"),d=!1,e=0;e<c.length;e++)if(c[e].tag===b.tag)return void(d=!0);return""!==b.tag&&!d},this.addChip=function(b,c){if(d.isValid(c,b)){for(var e=d.renderChip(b),f=[],g=c.data("chips"),h=0;h<g.length;h++)f.push(g[h]);f.push(b),c.data("chips",f),a(e).insertBefore(c.find("input")),c.trigger("chip.add",b),d.setPlaceholder(c)}},this.deleteChip=function(a,b){var c=b.data("chips")[a];b.find(".chip").eq(a).remove();for(var e=[],f=b.data("chips"),g=0;g<f.length;g++)g!==a&&e.push(f[g]);b.data("chips",e),b.trigger("chip.delete",c),d.setPlaceholder(b)},this.selectChip=function(a,b){var c=b.find(".chip").eq(a);c&&!1===c.hasClass("selected")&&(c.addClass("selected"),b.trigger("chip.select",b.data("chips")[a]))},this.getChipsElement=function(a,b){return b.eq(a)},this.init(),this.handleEvents()}}(jQuery),function(a){a.fn.pushpin=function(b){var c={top:0,bottom:1/0,offset:0};return"remove"===b?(this.each(function(){(id=a(this).data("pushpin-id"))&&(a(window).off("scroll."+id),a(this).removeData("pushpin-id").removeClass("pin-top pinned pin-bottom").removeAttr("style"))}),!1):(b=a.extend(c,b),$index=0,this.each(function(){function c(a){a.removeClass("pin-top"),a.removeClass("pinned"),a.removeClass("pin-bottom")}function d(d,e){d.each(function(){b.top<=e&&b.bottom>=e&&!a(this).hasClass("pinned")&&(c(a(this)),a(this).css("top",b.offset),a(this).addClass("pinned")),e<b.top&&!a(this).hasClass("pin-top")&&(c(a(this)),a(this).css("top",0),a(this).addClass("pin-top")),e>b.bottom&&!a(this).hasClass("pin-bottom")&&(c(a(this)),a(this).addClass("pin-bottom"),a(this).css("top",b.bottom-g))})}var e=Materialize.guid(),f=a(this),g=a(this).offset().top;a(this).data("pushpin-id",e),d(f,a(window).scrollTop()),a(window).on("scroll."+e,function(){var c=a(window).scrollTop()+b.offset;d(f,c)})}))}}(jQuery),function(a){a(document).ready(function(){a.fn.reverse=[].reverse,a(document).on("mouseenter.fixedActionBtn",".fixed-action-btn:not(.click-to-toggle):not(.toolbar)",function(c){var d=a(this);b(d)}),a(document).on("mouseleave.fixedActionBtn",".fixed-action-btn:not(.click-to-toggle):not(.toolbar)",function(b){var d=a(this);c(d)}),a(document).on("click.fabClickToggle",".fixed-action-btn.click-to-toggle > a",function(d){var e=a(this),f=e.parent();f.hasClass("active")?c(f):b(f)}),a(document).on("click.fabToolbar",".fixed-action-btn.toolbar > a",function(b){var c=a(this),e=c.parent();d(e)})}),a.fn.extend({openFAB:function(){b(a(this))},closeFAB:function(){c(a(this))},openToolbar:function(){d(a(this))},closeToolbar:function(){e(a(this))}});var b=function(b){var c=b;if(c.hasClass("active")===!1){var d,e,f=c.hasClass("horizontal");f===!0?e=40:d=40,c.addClass("active"),c.find("ul .btn-floating").velocity({scaleY:".4",scaleX:".4",translateY:d+"px",translateX:e+"px"},{duration:0});var g=0;c.find("ul .btn-floating").reverse().each(function(){a(this).velocity({opacity:"1",scaleX:"1",scaleY:"1",translateY:"0",translateX:"0"},{duration:80,delay:g}),g+=40})}},c=function(a){var b,c,d=a,e=d.hasClass("horizontal");e===!0?c=40:b=40,d.removeClass("active");d.find("ul .btn-floating").velocity("stop",!0),d.find("ul .btn-floating").velocity({opacity:"0",scaleX:".4",scaleY:".4",translateY:b+"px",translateX:c+"px"},{duration:80})},d=function(b){if("true"!==b.attr("data-open")){var c,d,f,g=window.innerWidth,h=window.innerHeight,i=b[0].getBoundingClientRect(),j=b.find("> a").first(),k=b.find("> ul").first(),l=a('<div class="fab-backdrop"></div>'),m=j.css("background-color");j.append(l),c=i.left-g/2+i.width/2,d=h-i.bottom,f=g/l.width(),b.attr("data-origin-bottom",i.bottom),b.attr("data-origin-left",i.left),b.attr("data-origin-width",i.width),b.addClass("active"),b.attr("data-open",!0),b.css({"text-align":"center",width:"100%",bottom:0,left:0,transform:"translateX("+c+"px)",transition:"none"}),j.css({transform:"translateY("+-d+"px)",transition:"none"}),l.css({"background-color":m}),setTimeout(function(){b.css({transform:"",transition:"transform .2s cubic-bezier(0.550, 0.085, 0.680, 0.530), background-color 0s linear .2s"}),j.css({overflow:"visible",transform:"",transition:"transform .2s"}),setTimeout(function(){b.css({overflow:"hidden","background-color":m}),l.css({transform:"scale("+f+")",transition:"transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)"}),k.find("> li > a").css({opacity:1}),a(window).on("scroll.fabToolbarClose",function(){e(b),a(window).off("scroll.fabToolbarClose"),a(document).off("click.fabToolbarClose")}),a(document).on("click.fabToolbarClose",function(c){a(c.target).closest(k).length||(e(b),a(window).off("scroll.fabToolbarClose"),a(document).off("click.fabToolbarClose"))})},100)},0)}},e=function(a){if("true"===a.attr("data-open")){var b,c,d,e=window.innerWidth,f=window.innerHeight,g=a.attr("data-origin-width"),h=a.attr("data-origin-bottom"),i=a.attr("data-origin-left"),j=a.find("> .btn-floating").first(),k=a.find("> ul").first(),l=a.find(".fab-backdrop"),m=j.css("background-color");b=i-e/2+g/2,c=f-h,d=e/l.width(),a.removeClass("active"),a.attr("data-open",!1),a.css({"background-color":"transparent",transition:"none"}),j.css({transition:"none"}),l.css({transform:"scale(0)","background-color":m}),k.find("> li > a").css({opacity:""}),setTimeout(function(){l.remove(),a.css({"text-align":"",width:"",bottom:"",left:"",overflow:"","background-color":"",transform:"translate3d("+-b+"px,0,0)"}),j.css({overflow:"",transform:"translate3d(0,"+c+"px,0)"}),setTimeout(function(){a.css({transform:"translate3d(0,0,0)",transition:"transform .2s"}),j.css({transform:"translate3d(0,0,0)",transition:"transform .2s cubic-bezier(0.550, 0.055, 0.675, 0.190)"})},20)},200)}}}(jQuery),function(a){Materialize.fadeInImage=function(b){var c;if("string"==typeof b)c=a(b);else{if("object"!=typeof b)return;c=b}c.css({opacity:0}),a(c).velocity({opacity:1},{duration:650,queue:!1,easing:"easeOutSine"}),a(c).velocity({opacity:1},{duration:1300,queue:!1,easing:"swing",step:function(b,c){c.start=100;var d=b/100,e=150-(100-b)/1.75;e<100&&(e=100),b>=0&&a(this).css({"-webkit-filter":"grayscale("+d+")brightness("+e+"%)",filter:"grayscale("+d+")brightness("+e+"%)"})}})},Materialize.showStaggeredList=function(b){var c;if("string"==typeof b)c=a(b);else{if("object"!=typeof b)return;c=b}var d=0;c.find("li").velocity({translateX:"-100px"},{duration:0}),c.find("li").each(function(){a(this).velocity({opacity:"1",translateX:"0"},{duration:800,delay:d,easing:[60,10]}),d+=120})},a(document).ready(function(){var b=!1,c=!1;a(".dismissable").each(function(){a(this).hammer({prevent_default:!1}).bind("pan",function(d){if("touch"===d.gesture.pointerType){var e=a(this),f=d.gesture.direction,g=d.gesture.deltaX,h=d.gesture.velocityX;e.velocity({translateX:g},{duration:50,queue:!1,easing:"easeOutQuad"}),4===f&&(g>e.innerWidth()/2||h<-.75)&&(b=!0),2===f&&(g<-1*e.innerWidth()/2||h>.75)&&(c=!0)}}).bind("panend",function(d){if(Math.abs(d.gesture.deltaX)<a(this).innerWidth()/2&&(c=!1,b=!1),"touch"===d.gesture.pointerType){var e=a(this);if(b||c){var f;f=b?e.innerWidth():-1*e.innerWidth(),e.velocity({translateX:f},{duration:100,queue:!1,easing:"easeOutQuad",complete:function(){e.css("border","none"),e.velocity({height:0,padding:0},{duration:200,queue:!1,easing:"easeOutQuad",complete:function(){e.remove()}})}})}else e.velocity({translateX:0},{duration:100,queue:!1,easing:"easeOutQuad"});b=!1,c=!1}})})})}(jQuery),function(a){var b=!1;Materialize.scrollFire=function(a){var c=function(){for(var b=window.pageYOffset+window.innerHeight,c=0;c<a.length;c++){var d=a[c],e=d.selector,f=d.offset,g=d.callback,h=document.querySelector(e);if(null!==h){var i=h.getBoundingClientRect().top+window.pageYOffset;if(b>i+f&&d.done!==!0){if("function"==typeof g)g.call(this,h);else if("string"==typeof g){var j=new Function(g);j(h)}d.done=!0}}}},d=Materialize.throttle(function(){c()},a.throttle||100);b||(window.addEventListener("scroll",d),window.addEventListener("resize",d),b=!0),setTimeout(d,0)}}(jQuery),function(a){"function"==typeof define&&define.amd?define("picker",["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):this.Picker=a(jQuery)}(function(a){function b(f,g,i,l){function m(){return b._.node("div",b._.node("div",b._.node("div",b._.node("div",y.component.nodes(t.open),v.box),v.wrap),v.frame),v.holder)}function n(){w.data(g,y).addClass(v.input).attr("tabindex",-1).val(w.data("value")?y.get("select",u.format):f.value),u.editable||w.on("focus."+t.id+" click."+t.id,function(a){a.preventDefault(),y.$root.eq(0).focus()}).on("keydown."+t.id,q),e(f,{haspopup:!0,expanded:!1,readonly:!1,owns:f.id+"_root"})}function o(){y.$root.on({keydown:q,focusin:function(a){y.$root.removeClass(v.focused),a.stopPropagation()},"mousedown click":function(b){var c=b.target;c!=y.$root.children()[0]&&(b.stopPropagation(),"mousedown"!=b.type||a(c).is("input, select, textarea, button, option")||(b.preventDefault(),y.$root.eq(0).focus()))}}).on({focus:function(){w.addClass(v.target)},blur:function(){w.removeClass(v.target)}}).on("focus.toOpen",r).on("click","[data-pick], [data-nav], [data-clear], [data-close]",function(){var b=a(this),c=b.data(),d=b.hasClass(v.navDisabled)||b.hasClass(v.disabled),e=h();e=e&&(e.type||e.href),(d||e&&!a.contains(y.$root[0],e))&&y.$root.eq(0).focus(),!d&&c.nav?y.set("highlight",y.component.item.highlight,{nav:c.nav}):!d&&"pick"in c?y.set("select",c.pick):c.clear?y.clear().close(!0):c.close&&y.close(!0)}),e(y.$root[0],"hidden",!0)}function p(){var b;u.hiddenName===!0?(b=f.name,f.name=""):(b=["string"==typeof u.hiddenPrefix?u.hiddenPrefix:"","string"==typeof u.hiddenSuffix?u.hiddenSuffix:"_submit"],b=b[0]+f.name+b[1]),y._hidden=a('<input type=hidden name="'+b+'"'+(w.data("value")||f.value?' value="'+y.get("select",u.formatSubmit)+'"':"")+">")[0],w.on("change."+t.id,function(){y._hidden.value=f.value?y.get("select",u.formatSubmit):""}),u.container?a(u.container).append(y._hidden):w.after(y._hidden)}function q(a){var b=a.keyCode,c=/^(8|46)$/.test(b);return 27==b?(y.close(),!1):void((32==b||c||!t.open&&y.component.key[b])&&(a.preventDefault(),a.stopPropagation(),c?y.clear().close():y.open()))}function r(a){a.stopPropagation(),"focus"==a.type&&y.$root.addClass(v.focused),y.open()}if(!f)return b;var s=!1,t={id:f.id||"P"+Math.abs(~~(Math.random()*new Date))},u=i?a.extend(!0,{},i.defaults,l):l||{},v=a.extend({},b.klasses(),u.klass),w=a(f),x=function(){return this.start()},y=x.prototype={constructor:x,$node:w,start:function(){return t&&t.start?y:(t.methods={},t.start=!0,t.open=!1,t.type=f.type,f.autofocus=f==h(),f.readOnly=!u.editable,f.id=f.id||t.id,"text"!=f.type&&(f.type="text"),y.component=new i(y,u),y.$root=a(b._.node("div",m(),v.picker,'id="'+f.id+'_root" tabindex="0"')),o(),u.formatSubmit&&p(),n(),u.container?a(u.container).append(y.$root):w.after(y.$root),y.on({start:y.component.onStart,render:y.component.onRender,stop:y.component.onStop,open:y.component.onOpen,close:y.component.onClose,set:y.component.onSet}).on({start:u.onStart,render:u.onRender,stop:u.onStop,open:u.onOpen,close:u.onClose,set:u.onSet}),s=c(y.$root.children()[0]),f.autofocus&&y.open(),y.trigger("start").trigger("render"))},render:function(a){return a?y.$root.html(m()):y.$root.find("."+v.box).html(y.component.nodes(t.open)),y.trigger("render")},stop:function(){return t.start?(y.close(),y._hidden&&y._hidden.parentNode.removeChild(y._hidden),y.$root.remove(),w.removeClass(v.input).removeData(g),setTimeout(function(){w.off("."+t.id)},0),f.type=t.type,f.readOnly=!1,y.trigger("stop"),t.methods={},t.start=!1,y):y},open:function(c){return t.open?y:(w.addClass(v.active),e(f,"expanded",!0),setTimeout(function(){y.$root.addClass(v.opened),e(y.$root[0],"hidden",!1)},0),c!==!1&&(t.open=!0,s&&k.css("overflow","hidden").css("padding-right","+="+d()),y.$root.eq(0).focus(),j.on("click."+t.id+" focusin."+t.id,function(a){var b=a.target;b!=f&&b!=document&&3!=a.which&&y.close(b===y.$root.children()[0])}).on("keydown."+t.id,function(c){var d=c.keyCode,e=y.component.key[d],f=c.target;27==d?y.close(!0):f!=y.$root[0]||!e&&13!=d?a.contains(y.$root[0],f)&&13==d&&(c.preventDefault(),f.click()):(c.preventDefault(),e?b._.trigger(y.component.key.go,y,[b._.trigger(e)]):y.$root.find("."+v.highlighted).hasClass(v.disabled)||y.set("select",y.component.item.highlight).close())})),y.trigger("open"))},close:function(a){return a&&(y.$root.off("focus.toOpen").eq(0).focus(),setTimeout(function(){y.$root.on("focus.toOpen",r)},0)),w.removeClass(v.active),e(f,"expanded",!1),setTimeout(function(){y.$root.removeClass(v.opened+" "+v.focused),e(y.$root[0],"hidden",!0)},0),t.open?(t.open=!1,s&&k.css("overflow","").css("padding-right","-="+d()),j.off("."+t.id),y.trigger("close")):y},clear:function(a){return y.set("clear",null,a)},set:function(b,c,d){var e,f,g=a.isPlainObject(b),h=g?b:{};if(d=g&&a.isPlainObject(c)?c:d||{},b){g||(h[b]=c);for(e in h)f=h[e],e in y.component.item&&(void 0===f&&(f=null),y.component.set(e,f,d)),"select"!=e&&"clear"!=e||w.val("clear"==e?"":y.get(e,u.format)).trigger("change");y.render()}return d.muted?y:y.trigger("set",h)},get:function(a,c){if(a=a||"value",null!=t[a])return t[a];if("valueSubmit"==a){if(y._hidden)return y._hidden.value;a="value"}if("value"==a)return f.value;if(a in y.component.item){if("string"==typeof c){var d=y.component.get(a);return d?b._.trigger(y.component.formats.toString,y.component,[c,d]):""}return y.component.get(a)}},on:function(b,c,d){var e,f,g=a.isPlainObject(b),h=g?b:{};if(b){g||(h[b]=c);for(e in h)f=h[e],d&&(e="_"+e),t.methods[e]=t.methods[e]||[],t.methods[e].push(f)}return y},off:function(){var a,b,c=arguments;for(a=0,namesCount=c.length;a<namesCount;a+=1)b=c[a],b in t.methods&&delete t.methods[b];return y},trigger:function(a,c){var d=function(a){var d=t.methods[a];d&&d.map(function(a){b._.trigger(a,y,[c])})};return d("_"+a),d(a),y}};return new x}function c(a){var b,c="position";return a.currentStyle?b=a.currentStyle[c]:window.getComputedStyle&&(b=getComputedStyle(a)[c]),"fixed"==b}function d(){if(k.height()<=i.height())return 0;var b=a('<div style="visibility:hidden;width:100px" />').appendTo("body"),c=b[0].offsetWidth;b.css("overflow","scroll");var d=a('<div style="width:100%" />').appendTo(b),e=d[0].offsetWidth;return b.remove(),c-e}function e(b,c,d){if(a.isPlainObject(c))for(var e in c)f(b,e,c[e]);else f(b,c,d)}function f(a,b,c){a.setAttribute(("role"==b?"":"aria-")+b,c)}function g(b,c){a.isPlainObject(b)||(b={attribute:c}),c="";for(var d in b){var e=("role"==d?"":"aria-")+d,f=b[d];c+=null==f?"":e+'="'+b[d]+'"'}return c}function h(){try{return document.activeElement}catch(a){}}var i=a(window),j=a(document),k=a(document.documentElement);return b.klasses=function(a){return a=a||"picker",{picker:a,opened:a+"--opened",focused:a+"--focused",input:a+"__input",active:a+"__input--active",target:a+"__input--target",holder:a+"__holder",frame:a+"__frame",wrap:a+"__wrap",box:a+"__box"}},b._={group:function(a){for(var c,d="",e=b._.trigger(a.min,a);e<=b._.trigger(a.max,a,[e]);e+=a.i)c=b._.trigger(a.item,a,[e]),d+=b._.node(a.node,c[0],c[1],c[2]);return d},node:function(b,c,d,e){return c?(c=a.isArray(c)?c.join(""):c,d=d?' class="'+d+'"':"",e=e?" "+e:"","<"+b+d+e+">"+c+"</"+b+">"):""},lead:function(a){return(a<10?"0":"")+a},trigger:function(a,b,c){return"function"==typeof a?a.apply(b,c||[]):a},digits:function(a){return/\d/.test(a[1])?2:1},isDate:function(a){return{}.toString.call(a).indexOf("Date")>-1&&this.isInteger(a.getDate())},isInteger:function(a){return{}.toString.call(a).indexOf("Number")>-1&&a%1===0},ariaAttr:g},b.extend=function(c,d){a.fn[c]=function(e,f){var g=this.data(c);return"picker"==e?g:g&&"string"==typeof e?b._.trigger(g[e],g,[f]):this.each(function(){var f=a(this);f.data(c)||new b(this,c,d,e)})},a.fn[c].defaults=d.defaults},b}),function(a){"function"==typeof define&&define.amd?define(["picker","jquery"],a):"object"==typeof exports?module.exports=a(require("./picker.js"),require("jquery")):a(Picker,jQuery)}(function(a,b){function c(a,b){var c=this,d=a.$node[0],e=d.value,f=a.$node.data("value"),g=f||e,h=f?b.formatSubmit:b.format,i=function(){return d.currentStyle?"rtl"==d.currentStyle.direction:"rtl"==getComputedStyle(a.$root[0]).direction};c.settings=b,c.$node=a.$node,c.queue={min:"measure create",max:"measure create",now:"now create",select:"parse create validate",highlight:"parse navigate create validate",view:"parse create validate viewset",disable:"deactivate",enable:"activate"},c.item={},c.item.clear=null,c.item.disable=(b.disable||[]).slice(0),c.item.enable=-function(a){return a[0]===!0?a.shift():-1}(c.item.disable),c.set("min",b.min).set("max",b.max).set("now"),g?c.set("select",g,{format:h}):c.set("select",null).set("highlight",c.item.now),c.key={40:7,38:-7,39:function(){return i()?-1:1},37:function(){return i()?1:-1},go:function(a){var b=c.item.highlight,d=new Date(b.year,b.month,b.date+a);c.set("highlight",d,{interval:a}),this.render()}},a.on("render",function(){a.$root.find("."+b.klass.selectMonth).on("change",function(){var c=this.value;c&&(a.set("highlight",[a.get("view").year,c,a.get("highlight").date]),a.$root.find("."+b.klass.selectMonth).trigger("focus"))}),a.$root.find("."+b.klass.selectYear).on("change",function(){var c=this.value;c&&(a.set("highlight",[c,a.get("view").month,a.get("highlight").date]),a.$root.find("."+b.klass.selectYear).trigger("focus"))})},1).on("open",function(){var d="";c.disabled(c.get("now"))&&(d=":not(."+b.klass.buttonToday+")"),a.$root.find("button"+d+", select").attr("disabled",!1)},1).on("close",function(){a.$root.find("button, select").attr("disabled",!0)},1)}var d=7,e=6,f=a._;c.prototype.set=function(a,b,c){var d=this,e=d.item;return null===b?("clear"==a&&(a="select"),e[a]=b,d):(e["enable"==a?"disable":"flip"==a?"enable":a]=d.queue[a].split(" ").map(function(e){return b=d[e](a,b,c)}).pop(),"select"==a?d.set("highlight",e.select,c):"highlight"==a?d.set("view",e.highlight,c):a.match(/^(flip|min|max|disable|enable)$/)&&(e.select&&d.disabled(e.select)&&d.set("select",e.select,c),e.highlight&&d.disabled(e.highlight)&&d.set("highlight",e.highlight,c)),d)},c.prototype.get=function(a){return this.item[a]},c.prototype.create=function(a,c,d){var e,g=this;return c=void 0===c?a:c,
+c==-(1/0)||c==1/0?e=c:b.isPlainObject(c)&&f.isInteger(c.pick)?c=c.obj:b.isArray(c)?(c=new Date(c[0],c[1],c[2]),c=f.isDate(c)?c:g.create().obj):c=f.isInteger(c)||f.isDate(c)?g.normalize(new Date(c),d):g.now(a,c,d),{year:e||c.getFullYear(),month:e||c.getMonth(),date:e||c.getDate(),day:e||c.getDay(),obj:e||c,pick:e||c.getTime()}},c.prototype.createRange=function(a,c){var d=this,e=function(a){return a===!0||b.isArray(a)||f.isDate(a)?d.create(a):a};return f.isInteger(a)||(a=e(a)),f.isInteger(c)||(c=e(c)),f.isInteger(a)&&b.isPlainObject(c)?a=[c.year,c.month,c.date+a]:f.isInteger(c)&&b.isPlainObject(a)&&(c=[a.year,a.month,a.date+c]),{from:e(a),to:e(c)}},c.prototype.withinRange=function(a,b){return a=this.createRange(a.from,a.to),b.pick>=a.from.pick&&b.pick<=a.to.pick},c.prototype.overlapRanges=function(a,b){var c=this;return a=c.createRange(a.from,a.to),b=c.createRange(b.from,b.to),c.withinRange(a,b.from)||c.withinRange(a,b.to)||c.withinRange(b,a.from)||c.withinRange(b,a.to)},c.prototype.now=function(a,b,c){return b=new Date,c&&c.rel&&b.setDate(b.getDate()+c.rel),this.normalize(b,c)},c.prototype.navigate=function(a,c,d){var e,f,g,h,i=b.isArray(c),j=b.isPlainObject(c),k=this.item.view;if(i||j){for(j?(f=c.year,g=c.month,h=c.date):(f=+c[0],g=+c[1],h=+c[2]),d&&d.nav&&k&&k.month!==g&&(f=k.year,g=k.month),e=new Date(f,g+(d&&d.nav?d.nav:0),1),f=e.getFullYear(),g=e.getMonth();new Date(f,g,h).getMonth()!==g;)h-=1;c=[f,g,h]}return c},c.prototype.normalize=function(a){return a.setHours(0,0,0,0),a},c.prototype.measure=function(a,b){var c=this;return b?"string"==typeof b?b=c.parse(a,b):f.isInteger(b)&&(b=c.now(a,b,{rel:b})):b="min"==a?-(1/0):1/0,b},c.prototype.viewset=function(a,b){return this.create([b.year,b.month,1])},c.prototype.validate=function(a,c,d){var e,g,h,i,j=this,k=c,l=d&&d.interval?d.interval:1,m=j.item.enable===-1,n=j.item.min,o=j.item.max,p=m&&j.item.disable.filter(function(a){if(b.isArray(a)){var d=j.create(a).pick;d<c.pick?e=!0:d>c.pick&&(g=!0)}return f.isInteger(a)}).length;if((!d||!d.nav)&&(!m&&j.disabled(c)||m&&j.disabled(c)&&(p||e||g)||!m&&(c.pick<=n.pick||c.pick>=o.pick)))for(m&&!p&&(!g&&l>0||!e&&l<0)&&(l*=-1);j.disabled(c)&&(Math.abs(l)>1&&(c.month<k.month||c.month>k.month)&&(c=k,l=l>0?1:-1),c.pick<=n.pick?(h=!0,l=1,c=j.create([n.year,n.month,n.date+(c.pick===n.pick?0:-1)])):c.pick>=o.pick&&(i=!0,l=-1,c=j.create([o.year,o.month,o.date+(c.pick===o.pick?0:1)])),!h||!i);)c=j.create([c.year,c.month,c.date+l]);return c},c.prototype.disabled=function(a){var c=this,d=c.item.disable.filter(function(d){return f.isInteger(d)?a.day===(c.settings.firstDay?d:d-1)%7:b.isArray(d)||f.isDate(d)?a.pick===c.create(d).pick:b.isPlainObject(d)?c.withinRange(d,a):void 0});return d=d.length&&!d.filter(function(a){return b.isArray(a)&&"inverted"==a[3]||b.isPlainObject(a)&&a.inverted}).length,c.item.enable===-1?!d:d||a.pick<c.item.min.pick||a.pick>c.item.max.pick},c.prototype.parse=function(a,b,c){var d=this,e={};return b&&"string"==typeof b?(c&&c.format||(c=c||{},c.format=d.settings.format),d.formats.toArray(c.format).map(function(a){var c=d.formats[a],g=c?f.trigger(c,d,[b,e]):a.replace(/^!/,"").length;c&&(e[a]=b.substr(0,g)),b=b.substr(g)}),[e.yyyy||e.yy,+(e.mm||e.m)-1,e.dd||e.d]):b},c.prototype.formats=function(){function a(a,b,c){var d=a.match(/\w+/)[0];return c.mm||c.m||(c.m=b.indexOf(d)+1),d.length}function b(a){return a.match(/\w+/)[0].length}return{d:function(a,b){return a?f.digits(a):b.date},dd:function(a,b){return a?2:f.lead(b.date)},ddd:function(a,c){return a?b(a):this.settings.weekdaysShort[c.day]},dddd:function(a,c){return a?b(a):this.settings.weekdaysFull[c.day]},m:function(a,b){return a?f.digits(a):b.month+1},mm:function(a,b){return a?2:f.lead(b.month+1)},mmm:function(b,c){var d=this.settings.monthsShort;return b?a(b,d,c):d[c.month]},mmmm:function(b,c){var d=this.settings.monthsFull;return b?a(b,d,c):d[c.month]},yy:function(a,b){return a?2:(""+b.year).slice(2)},yyyy:function(a,b){return a?4:b.year},toArray:function(a){return a.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g)},toString:function(a,b){var c=this;return c.formats.toArray(a).map(function(a){return f.trigger(c.formats[a],c,[0,b])||a.replace(/^!/,"")}).join("")}}}(),c.prototype.isDateExact=function(a,c){var d=this;return f.isInteger(a)&&f.isInteger(c)||"boolean"==typeof a&&"boolean"==typeof c?a===c:(f.isDate(a)||b.isArray(a))&&(f.isDate(c)||b.isArray(c))?d.create(a).pick===d.create(c).pick:!(!b.isPlainObject(a)||!b.isPlainObject(c))&&(d.isDateExact(a.from,c.from)&&d.isDateExact(a.to,c.to))},c.prototype.isDateOverlap=function(a,c){var d=this,e=d.settings.firstDay?1:0;return f.isInteger(a)&&(f.isDate(c)||b.isArray(c))?(a=a%7+e,a===d.create(c).day+1):f.isInteger(c)&&(f.isDate(a)||b.isArray(a))?(c=c%7+e,c===d.create(a).day+1):!(!b.isPlainObject(a)||!b.isPlainObject(c))&&d.overlapRanges(a,c)},c.prototype.flipEnable=function(a){var b=this.item;b.enable=a||(b.enable==-1?1:-1)},c.prototype.deactivate=function(a,c){var d=this,e=d.item.disable.slice(0);return"flip"==c?d.flipEnable():c===!1?(d.flipEnable(1),e=[]):c===!0?(d.flipEnable(-1),e=[]):c.map(function(a){for(var c,g=0;g<e.length;g+=1)if(d.isDateExact(a,e[g])){c=!0;break}c||(f.isInteger(a)||f.isDate(a)||b.isArray(a)||b.isPlainObject(a)&&a.from&&a.to)&&e.push(a)}),e},c.prototype.activate=function(a,c){var d=this,e=d.item.disable,g=e.length;return"flip"==c?d.flipEnable():c===!0?(d.flipEnable(1),e=[]):c===!1?(d.flipEnable(-1),e=[]):c.map(function(a){var c,h,i,j;for(i=0;i<g;i+=1){if(h=e[i],d.isDateExact(h,a)){c=e[i]=null,j=!0;break}if(d.isDateOverlap(h,a)){b.isPlainObject(a)?(a.inverted=!0,c=a):b.isArray(a)?(c=a,c[3]||c.push("inverted")):f.isDate(a)&&(c=[a.getFullYear(),a.getMonth(),a.getDate(),"inverted"]);break}}if(c)for(i=0;i<g;i+=1)if(d.isDateExact(e[i],a)){e[i]=null;break}if(j)for(i=0;i<g;i+=1)if(d.isDateOverlap(e[i],a)){e[i]=null;break}c&&e.push(c)}),e.filter(function(a){return null!=a})},c.prototype.nodes=function(a){var b=this,c=b.settings,g=b.item,h=g.now,i=g.select,j=g.highlight,k=g.view,l=g.disable,m=g.min,n=g.max,o=function(a,b){return c.firstDay&&(a.push(a.shift()),b.push(b.shift())),f.node("thead",f.node("tr",f.group({min:0,max:d-1,i:1,node:"th",item:function(d){return[a[d],c.klass.weekdays,'scope=col title="'+b[d]+'"']}})))}((c.showWeekdaysFull?c.weekdaysFull:c.weekdaysLetter).slice(0),c.weekdaysFull.slice(0)),p=function(a){return f.node("div"," ",c.klass["nav"+(a?"Next":"Prev")]+(a&&k.year>=n.year&&k.month>=n.month||!a&&k.year<=m.year&&k.month<=m.month?" "+c.klass.navDisabled:""),"data-nav="+(a||-1)+" "+f.ariaAttr({role:"button",controls:b.$node[0].id+"_table"})+' title="'+(a?c.labelMonthNext:c.labelMonthPrev)+'"')},q=function(d){var e=c.showMonthsShort?c.monthsShort:c.monthsFull;return"short_months"==d&&(e=c.monthsShort),c.selectMonths&&void 0==d?f.node("select",f.group({min:0,max:11,i:1,node:"option",item:function(a){return[e[a],0,"value="+a+(k.month==a?" selected":"")+(k.year==m.year&&a<m.month||k.year==n.year&&a>n.month?" disabled":"")]}}),c.klass.selectMonth+" browser-default",(a?"":"disabled")+" "+f.ariaAttr({controls:b.$node[0].id+"_table"})+' title="'+c.labelMonthSelect+'"'):"short_months"==d?null!=i?f.node("div",e[i.month]):f.node("div",e[k.month]):f.node("div",e[k.month],c.klass.month)},r=function(d){var e=k.year,g=c.selectYears===!0?5:~~(c.selectYears/2);if(g){var h=m.year,i=n.year,j=e-g,l=e+g;if(h>j&&(l+=h-j,j=h),i<l){var o=j-h,p=l-i;j-=o>p?p:o,l=i}if(c.selectYears&&void 0==d)return f.node("select",f.group({min:j,max:l,i:1,node:"option",item:function(a){return[a,0,"value="+a+(e==a?" selected":"")]}}),c.klass.selectYear+" browser-default",(a?"":"disabled")+" "+f.ariaAttr({controls:b.$node[0].id+"_table"})+' title="'+c.labelYearSelect+'"')}return"raw"==d?f.node("div",e):f.node("div",e,c.klass.year)};return createDayLabel=function(){return null!=i?f.node("div",i.date):f.node("div",h.date)},createWeekdayLabel=function(){var a;a=null!=i?i.day:h.day;var b=c.weekdaysFull[a];return b},f.node("div",f.node("div",createWeekdayLabel(),"picker__weekday-display")+f.node("div",q("short_months"),c.klass.month_display)+f.node("div",createDayLabel(),c.klass.day_display)+f.node("div",r("raw"),c.klass.year_display),c.klass.date_display)+f.node("div",f.node("div",(c.selectYears?q()+r():q()+r())+p()+p(1),c.klass.header)+f.node("table",o+f.node("tbody",f.group({min:0,max:e-1,i:1,node:"tr",item:function(a){var e=c.firstDay&&0===b.create([k.year,k.month,1]).day?-7:0;return[f.group({min:d*a-k.day+e+1,max:function(){return this.min+d-1},i:1,node:"td",item:function(a){a=b.create([k.year,k.month,a+(c.firstDay?1:0)]);var d=i&&i.pick==a.pick,e=j&&j.pick==a.pick,g=l&&b.disabled(a)||a.pick<m.pick||a.pick>n.pick,o=f.trigger(b.formats.toString,b,[c.format,a]);return[f.node("div",a.date,function(b){return b.push(k.month==a.month?c.klass.infocus:c.klass.outfocus),h.pick==a.pick&&b.push(c.klass.now),d&&b.push(c.klass.selected),e&&b.push(c.klass.highlighted),g&&b.push(c.klass.disabled),b.join(" ")}([c.klass.day]),"data-pick="+a.pick+" "+f.ariaAttr({role:"gridcell",label:o,selected:!(!d||b.$node.val()!==o)||null,activedescendant:!!e||null,disabled:!!g||null})),"",f.ariaAttr({role:"presentation"})]}})]}})),c.klass.table,'id="'+b.$node[0].id+'_table" '+f.ariaAttr({role:"grid",controls:b.$node[0].id,readonly:!0})),c.klass.calendar_container)+f.node("div",f.node("button",c.today,"btn-flat picker__today","type=button data-pick="+h.pick+(a&&!b.disabled(h)?"":" disabled")+" "+f.ariaAttr({controls:b.$node[0].id}))+f.node("button",c.clear,"btn-flat picker__clear","type=button data-clear=1"+(a?"":" disabled")+" "+f.ariaAttr({controls:b.$node[0].id}))+f.node("button",c.close,"btn-flat picker__close","type=button data-close=true "+(a?"":" disabled")+" "+f.ariaAttr({controls:b.$node[0].id})),c.klass.footer)},c.defaults=function(a){return{labelMonthNext:"Next month",labelMonthPrev:"Previous month",labelMonthSelect:"Select a month",labelYearSelect:"Select a year",monthsFull:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdaysFull:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],weekdaysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],weekdaysLetter:["S","M","T","W","T","F","S"],today:"Today",clear:"Clear",close:"Close",format:"d mmmm, yyyy",klass:{table:a+"table",header:a+"header",date_display:a+"date-display",day_display:a+"day-display",month_display:a+"month-display",year_display:a+"year-display",calendar_container:a+"calendar-container",navPrev:a+"nav--prev",navNext:a+"nav--next",navDisabled:a+"nav--disabled",month:a+"month",year:a+"year",selectMonth:a+"select--month",selectYear:a+"select--year",weekdays:a+"weekday",day:a+"day",disabled:a+"day--disabled",selected:a+"day--selected",highlighted:a+"day--highlighted",now:a+"day--today",infocus:a+"day--infocus",outfocus:a+"day--outfocus",footer:a+"footer",buttonClear:a+"button--clear",buttonToday:a+"button--today",buttonClose:a+"button--close"}}}(a.klasses().picker+"__"),a.extend("pickadate",c)}),function(a){function b(){var b=+a(this).attr("data-length"),c=+a(this).val().length,d=c<=b;a(this).parent().find('span[class="character-counter"]').html(c+"/"+b),e(d,a(this))}function c(b){var c=b.parent().find('span[class="character-counter"]');c.length||(c=a("<span/>").addClass("character-counter").css("float","right").css("font-size","12px").css("height",1),b.parent().append(c))}function d(){a(this).parent().find('span[class="character-counter"]').html("")}function e(a,b){var c=b.hasClass("invalid");a&&c?b.removeClass("invalid"):a||c||(b.removeClass("valid"),b.addClass("invalid"))}a.fn.characterCounter=function(){return this.each(function(){var e=a(this),f=e.parent().find('span[class="character-counter"]');if(!f.length){var g=void 0!==e.attr("data-length");g&&(e.on("input",b),e.on("focus",b),e.on("blur",d),c(e))}})},a(document).ready(function(){a("input, textarea").characterCounter()})}(jQuery),function(a){var b={init:function(b){var c={duration:200,dist:-100,shift:0,padding:0,fullWidth:!1,indicators:!1,noWrap:!1,onCycleTo:null};return b=a.extend(c,b),this.each(function(){function c(){"undefined"!=typeof window.ontouchstart&&(J[0].addEventListener("touchstart",l),J[0].addEventListener("touchmove",m),J[0].addEventListener("touchend",n)),J[0].addEventListener("mousedown",l),J[0].addEventListener("mousemove",m),J[0].addEventListener("mouseup",n),J[0].addEventListener("mouseleave",n),J[0].addEventListener("click",j)}function d(a){return a.targetTouches&&a.targetTouches.length>=1?a.targetTouches[0].clientX:a.clientX}function e(a){return a.targetTouches&&a.targetTouches.length>=1?a.targetTouches[0].clientY:a.clientY}function f(a){return a>=v?a%v:a<0?f(v+a%v):a}function g(c){var d,e,g,h,i,j,k,l=s;if(r="number"==typeof c?c:r,s=Math.floor((r+u/2)/u),g=r-s*u,h=g<0?1:-1,i=-h*g*2/u,e=v>>1,b.fullWidth?k="translateX(0)":(k="translateX("+(J[0].clientWidth-p)/2+"px) ",k+="translateY("+(J[0].clientHeight-q)/2+"px)"),K){var m=s%v,n=I.find(".indicator-item.active");n.index()!==m&&(n.removeClass("active"),I.find(".indicator-item").eq(m).addClass("active"))}for((!b.noWrap||s>=0&&s<v)&&(j=o[f(s)],a(j).hasClass("active")||(J.find(".carousel-item").removeClass("active"),a(j).addClass("active")),j.style[C]=k+" translateX("+-g/2+"px) translateX("+h*b.shift*i*d+"px) translateZ("+b.dist*i+"px)",j.style.zIndex=0,b.fullWidth?tweenedOpacity=1:tweenedOpacity=1-.2*i,j.style.opacity=tweenedOpacity,j.style.display="block"),d=1;d<=e;++d)b.fullWidth?(zTranslation=b.dist,tweenedOpacity=d===e&&g<0?1-i:1):(zTranslation=b.dist*(2*d+i*h),tweenedOpacity=1-.2*(2*d+i*h)),(!b.noWrap||s+d<v)&&(j=o[f(s+d)],j.style[C]=k+" translateX("+(b.shift+(u*d-g)/2)+"px) translateZ("+zTranslation+"px)",j.style.zIndex=-d,j.style.opacity=tweenedOpacity,j.style.display="block"),b.fullWidth?(zTranslation=b.dist,tweenedOpacity=d===e&&g>0?1-i:1):(zTranslation=b.dist*(2*d-i*h),tweenedOpacity=1-.2*(2*d-i*h)),(!b.noWrap||s-d>=0)&&(j=o[f(s-d)],j.style[C]=k+" translateX("+(-b.shift+(-u*d-g)/2)+"px) translateZ("+zTranslation+"px)",j.style.zIndex=-d,j.style.opacity=tweenedOpacity,j.style.display="block");if((!b.noWrap||s>=0&&s<v)&&(j=o[f(s)],j.style[C]=k+" translateX("+-g/2+"px) translateX("+h*b.shift*i+"px) translateZ("+b.dist*i+"px)",j.style.zIndex=0,b.fullWidth?tweenedOpacity=1:tweenedOpacity=1-.2*i,j.style.opacity=tweenedOpacity,j.style.display="block"),l!==s&&"function"==typeof b.onCycleTo){var t=J.find(".carousel-item").eq(f(s));b.onCycleTo.call(this,t,G)}}function h(){var a,b,c,d;a=Date.now(),b=a-E,E=a,c=r-D,D=r,d=1e3*c/(1+b),B=.8*d+.2*B}function i(){var a,c;z&&(a=Date.now()-E,c=z*Math.exp(-a/b.duration),c>2||c<-2?(g(A-c),requestAnimationFrame(i)):g(A))}function j(c){if(G)return c.preventDefault(),c.stopPropagation(),!1;if(!b.fullWidth){var d=a(c.target).closest(".carousel-item").index(),e=s%v-d;0!==e&&(c.preventDefault(),c.stopPropagation()),k(d)}}function k(a){var c=s%v-a;b.noWrap||(c<0?Math.abs(c+v)<Math.abs(c)&&(c+=v):c>0&&Math.abs(c-v)<c&&(c-=v)),c<0?J.trigger("carouselNext",[Math.abs(c)]):c>0&&J.trigger("carouselPrev",[c])}function l(a){t=!0,G=!1,H=!1,w=d(a),x=e(a),B=z=0,D=r,E=Date.now(),clearInterval(F),F=setInterval(h,100)}function m(a){var b,c,f;if(t)if(b=d(a),y=e(a),c=w-b,f=Math.abs(x-y),f<30&&!H)(c>2||c<-2)&&(G=!0,w=b,g(r+c));else{if(G)return a.preventDefault(),a.stopPropagation(),!1;H=!0}if(G)return a.preventDefault(),a.stopPropagation(),!1}function n(a){if(t)return t=!1,clearInterval(F),A=r,(B>10||B<-10)&&(z=.9*B,A=r+z),A=Math.round(A/u)*u,b.noWrap&&(A>=u*(v-1)?A=u*(v-1):A<0&&(A=0)),z=A-r,E=Date.now(),requestAnimationFrame(i),G&&(a.preventDefault(),a.stopPropagation()),!1}var o,p,q,r,s,t,u,v,w,x,z,A,B,C,D,E,F,G,H,I=a('<ul class="indicators"></ul>'),J=a(this),K=J.attr("data-indicators")||b.indicators;if(J.hasClass("initialized"))return a(this).trigger("carouselNext",[1e-6]),!0;if(b.fullWidth){b.dist=0;var L=J.find(".carousel-item img").first();L.length?imageHeight=L.on("load",function(){J.css("height",a(this).height())}):(imageHeight=J.find(".carousel-item").first().height(),J.css("height",imageHeight)),K&&J.find(".carousel-fixed-item").addClass("with-indicators")}J.addClass("initialized"),t=!1,r=A=0,o=[],p=J.find(".carousel-item").first().innerWidth(),q=J.find(".carousel-item").first().innerHeight(),u=2*p+b.padding,J.find(".carousel-item").each(function(b){if(o.push(a(this)[0]),K){var c=a('<li class="indicator-item"></li>');0===b&&c.addClass("active"),c.click(function(b){b.stopPropagation();var c=a(this).index();k(c)}),I.append(c)}}),K&&J.append(I),v=o.length,C="transform",["webkit","Moz","O","ms"].every(function(a){var b=a+"Transform";return"undefined"==typeof document.body.style[b]||(C=b,!1)}),a(window).on("resize.carousel",function(){b.fullWidth?(p=J.find(".carousel-item").first().innerWidth(),q=J.find(".carousel-item").first().innerHeight(),u=2*p+b.padding,r=2*s*p,A=r):g()}),c(),g(r),a(this).on("carouselNext",function(a,b){void 0===b&&(b=1),A=u*Math.round(r/u)+u*b,r!==A&&(z=A-r,E=Date.now(),requestAnimationFrame(i))}),a(this).on("carouselPrev",function(a,b){void 0===b&&(b=1),A=u*Math.round(r/u)-u*b,r!==A&&(z=A-r,E=Date.now(),requestAnimationFrame(i))}),a(this).on("carouselSet",function(a,b){void 0===b&&(b=0),k(b)})})},next:function(b){a(this).trigger("carouselNext",[b])},prev:function(b){a(this).trigger("carouselPrev",[b])},set:function(b){a(this).trigger("carouselSet",[b])}};a.fn.carousel=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof c&&c?void a.error("Method "+c+" does not exist on jQuery.carousel"):b.init.apply(this,arguments)}}(jQuery); \ No newline at end of file
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/images/3rd_party/commits.png b/utils/test/vnfcatalogue/VNF_Catalogue/public/images/3rd_party/commits.png
new file mode 100644
index 000000000..1247621a7
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/images/3rd_party/commits.png
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/images/logo.png b/utils/test/vnfcatalogue/VNF_Catalogue/public/images/logo.png
new file mode 100644
index 000000000..fe18194ec
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/images/logo.png
Binary files differ
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/javascripts/global.js b/utils/test/vnfcatalogue/VNF_Catalogue/public/javascripts/global.js
new file mode 100644
index 000000000..3ae20d1a9
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/javascripts/global.js
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+$(document).ready( function() {
+ $(".button-collapse").sideNav();
+ $('#Search').click(function() {
+ var tags = $('#Tags').val().toLowerCase().split(/[ ,]+/);
+ window.location.href = '/search_projects?tags=' + tags;
+ return false;
+ });
+ $('#SearchSpan').click(function(){
+ var tags = $('#Tags').val().toLowerCase().split(/[ ,]+/);
+ window.location.href = '/search_projects?tags=' + tags;
+ return false;
+ });
+ $('div.form-group-custom i.material-icons').click(function(e){
+ var tags = $('#Tags').val().toLowerCase().split(/[ ,]+/);
+ window.location.href = '/search_projects?tags=' + tags;
+ return false;
+ });
+});
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/3rd_party/bootstrap.css b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/3rd_party/bootstrap.css
new file mode 100755
index 000000000..6167622ce
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/3rd_party/bootstrap.css
@@ -0,0 +1,6757 @@
+/*!
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
+ * Copyright 2011-2016 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+html {
+ font-family: sans-serif;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+body {
+ margin: 0;
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block;
+}
+audio,
+canvas,
+progress,
+video {
+ display: inline-block;
+ vertical-align: baseline;
+}
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+[hidden],
+template {
+ display: none;
+}
+a {
+ background-color: transparent;
+}
+a:active,
+a:hover {
+ outline: 0;
+}
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+b,
+strong {
+ font-weight: bold;
+}
+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 #c0c0c0;
+}
+legend {
+ padding: 0;
+ border: 0;
+}
+textarea {
+ overflow: auto;
+}
+optgroup {
+ font-weight: bold;
+}
+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 {
+ *,
+ *:before,
+ *:after {
+ color: #000 !important;
+ text-shadow: none !important;
+ background: transparent !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^="#"]:after,
+ a[href^="javascript:"]:after {
+ content: "";
+ }
+ pre,
+ blockquote {
+ border: 1px solid #999;
+
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ img {
+ max-width: 100% !important;
+ }
+ p,
+ h2,
+ h3 {
+ 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 th,
+ .table-bordered td {
+ 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: normal;
+ line-height: 1;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.glyphicon-asterisk:before {
+ content: "\002a";
+}
+.glyphicon-plus:before {
+ content: "\002b";
+}
+.glyphicon-euro:before,
+.glyphicon-eur: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;
+}
+*:before,
+*:after {
+ -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;
+}
+input,
+button,
+select,
+textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+a {
+ color: #337ab7;
+ text-decoration: none;
+}
+a:hover,
+a:focus {
+ color: #23527c;
+ text-decoration: underline;
+}
+a:focus {
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+figure {
+ margin: 0;
+}
+img {
+ vertical-align: middle;
+}
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > 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,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
+ font-weight: normal;
+ line-height: 1;
+ color: #777;
+}
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.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,
+.h4,
+h5,
+.h5,
+h6,
+.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:hover,
+a.text-primary:focus {
+ color: #286090;
+}
+.text-success {
+ color: #3c763d;
+}
+a.text-success:hover,
+a.text-success:focus {
+ color: #2b542c;
+}
+.text-info {
+ color: #31708f;
+}
+a.text-info:hover,
+a.text-info:focus {
+ color: #245269;
+}
+.text-warning {
+ color: #8a6d3b;
+}
+a.text-warning:hover,
+a.text-warning:focus {
+ color: #66512c;
+}
+.text-danger {
+ color: #a94442;
+}
+a.text-danger:hover,
+a.text-danger:focus {
+ color: #843534;
+}
+.bg-primary {
+ color: #fff;
+ background-color: #337ab7;
+}
+a.bg-primary:hover,
+a.bg-primary:focus {
+ background-color: #286090;
+}
+.bg-success {
+ background-color: #dff0d8;
+}
+a.bg-success:hover,
+a.bg-success:focus {
+ background-color: #c1e2b3;
+}
+.bg-info {
+ background-color: #d9edf7;
+}
+a.bg-info:hover,
+a.bg-info:focus {
+ background-color: #afd9ee;
+}
+.bg-warning {
+ background-color: #fcf8e3;
+}
+a.bg-warning:hover,
+a.bg-warning:focus {
+ background-color: #f7ecb5;
+}
+.bg-danger {
+ background-color: #f2dede;
+}
+a.bg-danger:hover,
+a.bg-danger:focus {
+ background-color: #e4b9b9;
+}
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eee;
+}
+ul,
+ol {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+ 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;
+}
+dt,
+dd {
+ line-height: 1.42857143;
+}
+dt {
+ font-weight: bold;
+}
+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[title],
+abbr[data-original-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 p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+ margin-bottom: 0;
+}
+blockquote footer,
+blockquote small,
+blockquote .small {
+ display: block;
+ font-size: 80%;
+ line-height: 1.42857143;
+ color: #777;
+}
+blockquote footer:before,
+blockquote small: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 footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+ content: '';
+}
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small: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: bold;
+ -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-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
+ position: relative;
+ min-height: 1px;
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+ 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-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+ 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-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+ 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-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+ 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 > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+ 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 > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+ border-top: 0;
+}
+.table > tbody + tbody {
+ border-top: 2px solid #ddd;
+}
+.table .table {
+ background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+ padding: 5px;
+}
+.table-bordered {
+ border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+ border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+ 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 > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+ background-color: #f5f5f5;
+}
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+ background-color: #e8e8e8;
+}
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+ background-color: #dff0d8;
+}
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+ background-color: #d0e9c6;
+}
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+ background-color: #d9edf7;
+}
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+ background-color: #c4e3f3;
+}
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+ background-color: #fcf8e3;
+}
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+ background-color: #faf2cc;
+}
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+ background-color: #f2dede;
+}
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+ 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 > thead > tr > th,
+ .table-responsive > .table > tbody > tr > th,
+ .table-responsive > .table > tfoot > tr > th,
+ .table-responsive > .table > thead > tr > td,
+ .table-responsive > .table > tbody > tr > td,
+ .table-responsive > .table > tfoot > tr > td {
+ white-space: nowrap;
+ }
+ .table-responsive > .table-bordered {
+ border: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:first-child,
+ .table-responsive > .table-bordered > tbody > tr > th:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+ .table-responsive > .table-bordered > thead > tr > td:first-child,
+ .table-responsive > .table-bordered > tbody > tr > td:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:last-child,
+ .table-responsive > .table-bordered > tbody > tr > th:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+ .table-responsive > .table-bordered > thead > tr > td:last-child,
+ .table-responsive > .table-bordered > tbody > tr > td:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+ }
+ .table-responsive > .table-bordered > tbody > tr:last-child > th,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+ .table-responsive > .table-bordered > tbody > tr:last-child > td,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ 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: bold;
+}
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+input[type="radio"],
+input[type="checkbox"] {
+ 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="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+output {
+ display: block;
+ padding-top: 7px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+}
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+ -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+}
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {
+ color: #999;
+ opacity: 1;
+}
+.form-control:-ms-input-placeholder {
+ color: #999;
+}
+.form-control::-webkit-input-placeholder {
+ color: #999;
+}
+.form-control::-ms-expand {
+ background-color: transparent;
+ border: 0;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ background-color: #eee;
+ opacity: 1;
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+}
+textarea.form-control {
+ height: auto;
+}
+input[type="search"] {
+ -webkit-appearance: none;
+}
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+ input[type="date"].form-control,
+ input[type="time"].form-control,
+ input[type="datetime-local"].form-control,
+ input[type="month"].form-control {
+ line-height: 34px;
+ }
+ input[type="date"].input-sm,
+ input[type="time"].input-sm,
+ input[type="datetime-local"].input-sm,
+ input[type="month"].input-sm,
+ .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"] {
+ line-height: 30px;
+ }
+ input[type="date"].input-lg,
+ input[type="time"].input-lg,
+ input[type="datetime-local"].input-lg,
+ input[type="month"].input-lg,
+ .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"] {
+ line-height: 46px;
+ }
+}
+.form-group {
+ margin-bottom: 15px;
+}
+.radio,
+.checkbox {
+ position: relative;
+ display: block;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+ min-height: 20px;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ position: absolute;
+ margin-top: 4px \9;
+ margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+ position: relative;
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ vertical-align: middle;
+ cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+ cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox 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;
+}
+textarea.input-sm,
+select[multiple].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 textarea.form-control,
+.form-group-sm select[multiple].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;
+}
+textarea.input-lg,
+select[multiple].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 textarea.form-control,
+.form-group-lg select[multiple].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;
+}
+.input-lg + .form-control-feedback,
+.input-group-lg + .form-control-feedback,
+.form-group-lg .form-control + .form-control-feedback {
+ width: 46px;
+ height: 46px;
+ line-height: 46px;
+}
+.input-sm + .form-control-feedback,
+.input-group-sm + .form-control-feedback,
+.form-group-sm .form-control + .form-control-feedback {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+}
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline,
+.has-success.radio label,
+.has-success.checkbox label,
+.has-success.radio-inline label,
+.has-success.checkbox-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 .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline,
+.has-warning.radio label,
+.has-warning.checkbox label,
+.has-warning.radio-inline label,
+.has-warning.checkbox-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 .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline,
+.has-error.radio label,
+.has-error.checkbox label,
+.has-error.radio-inline label,
+.has-error.checkbox-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 .input-group-addon,
+ .form-inline .input-group .input-group-btn,
+ .form-inline .input-group .form-control {
+ width: auto;
+ }
+ .form-inline .input-group > .form-control {
+ width: 100%;
+ }
+ .form-inline .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio,
+ .form-inline .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio label,
+ .form-inline .checkbox label {
+ padding-left: 0;
+ }
+ .form-inline .radio input[type="radio"],
+ .form-inline .checkbox input[type="checkbox"] {
+ position: relative;
+ margin-left: 0;
+ }
+ .form-inline .has-feedback .form-control-feedback {
+ top: 0;
+ }
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+ padding-top: 7px;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+ min-height: 27px;
+}
+.form-horizontal .form-group {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+@media (min-width: 768px) {
+ .form-horizontal .control-label {
+ padding-top: 7px;
+ margin-bottom: 0;
+ text-align: right;
+ }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+ right: 15px;
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-lg .control-label {
+ padding-top: 11px;
+ font-size: 18px;
+ }
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-sm .control-label {
+ padding-top: 6px;
+ font-size: 12px;
+ }
+}
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: normal;
+ 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:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus,
+.btn.focus {
+ 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:hover,
+.btn-default.active:hover,
+.open > .dropdown-toggle.btn-default:hover,
+.btn-default:active:focus,
+.btn-default.active:focus,
+.open > .dropdown-toggle.btn-default:focus,
+.btn-default:active.focus,
+.btn-default.active.focus,
+.open > .dropdown-toggle.btn-default.focus {
+ 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:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus {
+ 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:hover,
+.btn-primary.active:hover,
+.open > .dropdown-toggle.btn-primary:hover,
+.btn-primary:active:focus,
+.btn-primary.active:focus,
+.open > .dropdown-toggle.btn-primary:focus,
+.btn-primary:active.focus,
+.btn-primary.active.focus,
+.open > .dropdown-toggle.btn-primary.focus {
+ 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:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus {
+ 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:hover,
+.btn-success.active:hover,
+.open > .dropdown-toggle.btn-success:hover,
+.btn-success:active:focus,
+.btn-success.active:focus,
+.open > .dropdown-toggle.btn-success:focus,
+.btn-success:active.focus,
+.btn-success.active.focus,
+.open > .dropdown-toggle.btn-success.focus {
+ 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:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus {
+ 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:hover,
+.btn-info.active:hover,
+.open > .dropdown-toggle.btn-info:hover,
+.btn-info:active:focus,
+.btn-info.active:focus,
+.open > .dropdown-toggle.btn-info:focus,
+.btn-info:active.focus,
+.btn-info.active.focus,
+.open > .dropdown-toggle.btn-info.focus {
+ 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:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus {
+ 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:hover,
+.btn-warning.active:hover,
+.open > .dropdown-toggle.btn-warning:hover,
+.btn-warning:active:focus,
+.btn-warning.active:focus,
+.open > .dropdown-toggle.btn-warning:focus,
+.btn-warning:active.focus,
+.btn-warning.active.focus,
+.open > .dropdown-toggle.btn-warning.focus {
+ 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:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus {
+ 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:hover,
+.btn-danger.active:hover,
+.open > .dropdown-toggle.btn-danger:hover,
+.btn-danger:active:focus,
+.btn-danger.active:focus,
+.open > .dropdown-toggle.btn-danger:focus,
+.btn-danger:active.focus,
+.btn-danger.active.focus,
+.open > .dropdown-toggle.btn-danger.focus {
+ 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:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus {
+ background-color: #d9534f;
+ border-color: #d43f3a;
+}
+.btn-danger .badge {
+ color: #d9534f;
+ background-color: #fff;
+}
+.btn-link {
+ font-weight: normal;
+ 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:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+ color: #23527c;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+ color: #777;
+ text-decoration: none;
+}
+.btn-lg,
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px;
+}
+.btn-sm,
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
+}
+.btn-xs,
+.btn-group-xs > .btn {
+ 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="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].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;
+}
+.dropup,
+.dropdown {
+ 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: normal;
+ line-height: 1.42857143;
+ color: #333;
+ white-space: nowrap;
+}
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ color: #262626;
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+ color: #fff;
+ text-decoration: none;
+ background-color: #337ab7;
+ outline: 0;
+}
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ color: #777;
+}
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+ 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 > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ float: left;
+}
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group-vertical > .btn:focus,
+.btn-group > .btn:active,
+.btn-group-vertical > .btn:active,
+.btn-group > .btn.active,
+.btn-group-vertical > .btn.active {
+ z-index: 2;
+}
+.btn-group .btn + .btn,
+.btn-group .btn + .btn-group,
+.btn-group .btn-group + .btn,
+.btn-group .btn-group + .btn-group {
+ margin-left: -1px;
+}
+.btn-toolbar {
+ margin-left: -5px;
+}
+.btn-toolbar .btn,
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+ float: left;
+}
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
+ margin-left: 5px;
+}
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+.btn-group > .btn:first-child {
+ margin-left: 0;
+}
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group > .btn-group {
+ float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+.btn-group > .btn + .dropdown-toggle {
+ padding-right: 8px;
+ padding-left: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-right: 12px;
+ padding-left: 12px;
+}
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.btn .caret {
+ margin-left: 0;
+}
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0;
+}
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px;
+}
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
+}
+.btn-group-vertical > .btn-group > .btn {
+ float: none;
+}
+.btn-group-vertical > .btn + .btn,
+.btn-group-vertical > .btn + .btn-group,
+.btn-group-vertical > .btn-group + .btn,
+.btn-group-vertical > .btn-group + .btn-group {
+ margin-top: -1px;
+ margin-left: 0;
+}
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn:first-child:not(:last-child) {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn:last-child:not(:first-child) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+}
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
+ display: table-cell;
+ float: none;
+ width: 1%;
+}
+.btn-group-justified > .btn-group .btn {
+ width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+ left: auto;
+}
+[data-toggle="buttons"] > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+}
+.input-group {
+ position: relative;
+ display: table;
+ border-collapse: separate;
+}
+.input-group[class*="col-"] {
+ float: none;
+ padding-right: 0;
+ padding-left: 0;
+}
+.input-group .form-control {
+ position: relative;
+ z-index: 2;
+ float: left;
+ width: 100%;
+ margin-bottom: 0;
+}
+.input-group .form-control:focus {
+ z-index: 3;
+}
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px;
+}
+select.input-group-lg > .form-control,
+select.input-group-lg > .input-group-addon,
+select.input-group-lg > .input-group-btn > .btn {
+ height: 46px;
+ line-height: 46px;
+}
+textarea.input-group-lg > .form-control,
+textarea.input-group-lg > .input-group-addon,
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].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;
+}
+textarea.input-group-sm > .form-control,
+textarea.input-group-sm > .input-group-addon,
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
+ height: auto;
+}
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+ display: table-cell;
+}
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child),
+.input-group .form-control: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: normal;
+ 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="radio"],
+.input-group-addon input[type="checkbox"] {
+ 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:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+ 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:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+ 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:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+ 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:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eee;
+}
+.nav > li.disabled > a {
+ color: #777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+ color: #777;
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+ 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:hover,
+.nav-tabs > li.active > a:focus {
+ 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:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+ 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:hover,
+ .nav-tabs.nav-justified > .active > a:focus {
+ 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:hover,
+.nav-pills > li.active > a:focus {
+ 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:hover,
+.nav-tabs-justified > .active > a:focus {
+ 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:hover,
+ .nav-tabs-justified > .active > a:focus {
+ 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-top .navbar-collapse,
+ .navbar-static-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+ max-height: 340px;
+}
+@media (max-device-width: 480px) and (orientation: landscape) {
+ .navbar-fixed-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ max-height: 200px;
+ }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+@media (min-width: 768px) {
+ .container > .navbar-header,
+ .container-fluid > .navbar-header,
+ .container > .navbar-collapse,
+ .container-fluid > .navbar-collapse {
+ 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-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+@media (min-width: 768px) {
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ 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:hover,
+.navbar-brand:focus {
+ 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 > li > a,
+ .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a {
+ line-height: 20px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-nav .open .dropdown-menu > li > a:focus {
+ 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 .input-group-addon,
+ .navbar-form .input-group .input-group-btn,
+ .navbar-form .input-group .form-control {
+ width: auto;
+ }
+ .navbar-form .input-group > .form-control {
+ width: 100%;
+ }
+ .navbar-form .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio,
+ .navbar-form .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio label,
+ .navbar-form .checkbox label {
+ padding-left: 0;
+ }
+ .navbar-form .radio input[type="radio"],
+ .navbar-form .checkbox input[type="checkbox"] {
+ 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:hover,
+.navbar-default .navbar-brand:focus {
+ 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:hover,
+.navbar-default .navbar-nav > li > a:focus {
+ color: #333;
+ background-color: transparent;
+}
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #555;
+ background-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+ color: #ccc;
+ background-color: transparent;
+}
+.navbar-default .navbar-toggle {
+ border-color: #ddd;
+}
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ 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:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+ 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:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #333;
+ background-color: transparent;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #555;
+ background-color: #e7e7e7;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ 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:hover,
+.navbar-default .btn-link:focus {
+ color: #333;
+}
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+ color: #ccc;
+}
+.navbar-inverse {
+ background-color: #222;
+ border-color: #080808;
+}
+.navbar-inverse .navbar-brand {
+ color: #9d9d9d;
+}
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+ 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:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+ color: #fff;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+ color: #fff;
+ background-color: #080808;
+}
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+ color: #444;
+ background-color: transparent;
+}
+.navbar-inverse .navbar-toggle {
+ border-color: #333;
+}
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+ 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:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ 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:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #fff;
+ background-color: transparent;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #fff;
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ 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:hover,
+.navbar-inverse .btn-link:focus {
+ color: #fff;
+}
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+ 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:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+ z-index: 2;
+ color: #23527c;
+ background-color: #eee;
+ border-color: #ddd;
+}
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+ z-index: 3;
+ color: #fff;
+ cursor: default;
+ background-color: #337ab7;
+ border-color: #337ab7;
+}
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+ 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:hover,
+.pager li > a:focus {
+ 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:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #fff;
+}
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+a.label:hover,
+a.label:focus {
+ 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]:hover,
+.label-default[href]:focus {
+ background-color: #5e5e5e;
+}
+.label-primary {
+ background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #286090;
+}
+.label-success {
+ background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
+}
+.label-info {
+ background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
+}
+.label-warning {
+ background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
+}
+.label-danger {
+ background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
+}
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ 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-xs .badge,
+.btn-group-xs > .btn .badge {
+ top: 0;
+ padding: 1px 5px;
+}
+a.badge:hover,
+a.badge:focus {
+ color: #fff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: #337ab7;
+ background-color: #fff;
+}
+.list-group-item > .badge {
+ float: right;
+}
+.list-group-item > .badge + .badge {
+ margin-right: 5px;
+}
+.nav-pills > li > a > .badge {
+ margin-left: 3px;
+}
+.jumbotron {
+ padding-top: 30px;
+ padding-bottom: 30px;
+ margin-bottom: 30px;
+ color: inherit;
+ background-color: #eee;
+}
+.jumbotron h1,
+.jumbotron .h1 {
+ color: inherit;
+}
+.jumbotron p {
+ margin-bottom: 15px;
+ font-size: 21px;
+ font-weight: 200;
+}
+.jumbotron > hr {
+ border-top-color: #d5d5d5;
+}
+.container .jumbotron,
+.container-fluid .jumbotron {
+ padding-right: 15px;
+ padding-left: 15px;
+ border-radius: 6px;
+}
+.jumbotron .container {
+ max-width: 100%;
+}
+@media screen and (min-width: 768px) {
+ .jumbotron {
+ padding-top: 48px;
+ padding-bottom: 48px;
+ }
+ .container .jumbotron,
+ .container-fluid .jumbotron {
+ padding-right: 60px;
+ padding-left: 60px;
+ }
+ .jumbotron h1,
+ .jumbotron .h1 {
+ font-size: 63px;
+ }
+}
+.thumbnail {
+ display: block;
+ padding: 4px;
+ margin-bottom: 20px;
+ line-height: 1.42857143;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ -webkit-transition: border .2s ease-in-out;
+ -o-transition: border .2s ease-in-out;
+ transition: border .2s ease-in-out;
+}
+.thumbnail > img,
+.thumbnail a > img {
+ margin-right: auto;
+ margin-left: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+ 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: bold;
+}
+.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-striped .progress-bar,
+.progress-bar-striped {
+ 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.active .progress-bar,
+.progress-bar.active {
+ -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-left,
+.media-right,
+.media-body {
+ 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:hover,
+button.list-group-item:hover,
+a.list-group-item:focus,
+button.list-group-item:focus {
+ 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:hover,
+.list-group-item.disabled:focus {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #eee;
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+ color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+ color: #777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
+ z-index: 2;
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #337ab7;
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+ color: inherit;
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .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:hover,
+button.list-group-item-success:hover,
+a.list-group-item-success:focus,
+button.list-group-item-success:focus {
+ color: #3c763d;
+ background-color: #d0e9c6;
+}
+a.list-group-item-success.active,
+button.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+button.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus,
+button.list-group-item-success.active:focus {
+ 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:hover,
+button.list-group-item-info:hover,
+a.list-group-item-info:focus,
+button.list-group-item-info:focus {
+ color: #31708f;
+ background-color: #c4e3f3;
+}
+a.list-group-item-info.active,
+button.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+button.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus,
+button.list-group-item-info.active:focus {
+ 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:hover,
+button.list-group-item-warning:hover,
+a.list-group-item-warning:focus,
+button.list-group-item-warning:focus {
+ color: #8a6d3b;
+ background-color: #faf2cc;
+}
+a.list-group-item-warning.active,
+button.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+button.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus,
+button.list-group-item-warning.active:focus {
+ 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:hover,
+button.list-group-item-danger:hover,
+a.list-group-item-danger:focus,
+button.list-group-item-danger:focus {
+ color: #a94442;
+ background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+button.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+button.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus,
+button.list-group-item-danger.active:focus {
+ 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 > a,
+.panel-title > small,
+.panel-title > .small,
+.panel-title > small > a,
+.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 > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+ margin-bottom: 0;
+}
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.panel > .table:first-child > thead: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-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+}
+.panel > .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 td:first-child,
+.panel > .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 td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th: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 th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+ border-top-left-radius: 3px;
+}
+.panel > .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 td:last-child,
+.panel > .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 td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th: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 th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+ border-top-right-radius: 3px;
+}
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
+}
+.panel > .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 td:first-child,
+.panel > .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 td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+ border-bottom-left-radius: 3px;
+}
+.panel > .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 td:last-child,
+.panel > .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 td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .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 th,
+.panel > .table > tbody:first-child > tr:first-child td {
+ border-top: 0;
+}
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+ border: 0;
+}
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+}
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+}
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+ border-bottom: 0;
+}
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.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 > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+ 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 iframe,
+.embed-responsive embed,
+.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: bold;
+ line-height: 1;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ filter: alpha(opacity=20);
+ opacity: .2;
+}
+.close:hover,
+.close:focus {
+ color: #000;
+ text-decoration: none;
+ cursor: pointer;
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+button.close {
+ -webkit-appearance: none;
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+}
+.modal-open {
+ overflow: hidden;
+}
+.modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1050;
+ display: none;
+ overflow: hidden;
+ -webkit-overflow-scrolling: touch;
+ outline: 0;
+}
+.modal.fade .modal-dialog {
+ -webkit-transition: -webkit-transform .3s ease-out;
+ -o-transition: -o-transform .3s ease-out;
+ transition: transform .3s ease-out;
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ -o-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+}
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ -o-transform: translate(0, 0);
+ transform: translate(0, 0);
+}
+.modal-open .modal {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: 10px;
+}
+.modal-content {
+ position: relative;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 6px;
+ outline: 0;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+}
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000;
+}
+.modal-backdrop.fade {
+ filter: alpha(opacity=0);
+ opacity: 0;
+}
+.modal-backdrop.in {
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+.modal-header {
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
+}
+.modal-header .close {
+ margin-top: -2px;
+}
+.modal-title {
+ margin: 0;
+ line-height: 1.42857143;
+}
+.modal-body {
+ position: relative;
+ padding: 15px;
+}
+.modal-footer {
+ padding: 15px;
+ text-align: right;
+ border-top: 1px solid #e5e5e5;
+}
+.modal-footer .btn + .btn {
+ margin-bottom: 0;
+ margin-left: 5px;
+}
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
+}
+.modal-scrollbar-measure {
+ position: absolute;
+ top: -9999px;
+ width: 50px;
+ height: 50px;
+ overflow: scroll;
+}
+@media (min-width: 768px) {
+ .modal-dialog {
+ width: 600px;
+ margin: 30px auto;
+ }
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+ }
+ .modal-sm {
+ width: 300px;
+ }
+}
+@media (min-width: 992px) {
+ .modal-lg {
+ width: 900px;
+ }
+}
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: normal;
+ 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: normal;
+ 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 > img,
+.carousel-inner > .item > a > 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.next,
+ .carousel-inner > .item.active.right {
+ left: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ }
+ .carousel-inner > .item.prev,
+ .carousel-inner > .item.active.left {
+ left: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ }
+ .carousel-inner > .item.next.left,
+ .carousel-inner > .item.prev.right,
+ .carousel-inner > .item.active {
+ left: 0;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+ }
+}
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
+}
+.carousel-inner > .active {
+ left: 0;
+}
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+.carousel-inner > .next {
+ left: 100%;
+}
+.carousel-inner > .prev {
+ left: -100%;
+}
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
+}
+.carousel-inner > .active.left {
+ left: -100%;
+}
+.carousel-inner > .active.right {
+ left: 100%;
+}
+.carousel-control {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 15%;
+ font-size: 20px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+ background-color: rgba(0, 0, 0, 0);
+ filter: alpha(opacity=50);
+ opacity: .5;
+}
+.carousel-control.left {
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+ background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+ background-repeat: repeat-x;
+}
+.carousel-control.right {
+ right: 0;
+ left: auto;
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+ background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+ background-repeat: repeat-x;
+}
+.carousel-control:hover,
+.carousel-control:focus {
+ color: #fff;
+ text-decoration: none;
+ filter: alpha(opacity=90);
+ outline: 0;
+ opacity: .9;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ z-index: 5;
+ display: inline-block;
+ margin-top: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+ left: 50%;
+ margin-left: -10px;
+}
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+ right: 50%;
+ margin-right: -10px;
+}
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+ 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-prev,
+ .carousel-control .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -10px;
+ font-size: 30px;
+ }
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .icon-prev {
+ margin-left: -10px;
+ }
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-next {
+ margin-right: -10px;
+ }
+ .carousel-caption {
+ right: 20%;
+ left: 20%;
+ padding-bottom: 30px;
+ }
+ .carousel-indicators {
+ bottom: 20px;
+ }
+}
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
+.modal-header:before,
+.modal-header:after,
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ content: " ";
+}
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
+.modal-header:after,
+.modal-footer: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-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+ display: none !important;
+}
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-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;
+ }
+ th.visible-xs,
+ td.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;
+ }
+ th.visible-sm,
+ td.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;
+ }
+ th.visible-md,
+ td.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;
+ }
+ th.visible-lg,
+ td.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;
+ }
+ th.visible-print,
+ td.visible-print {
+ display: table-cell !important;
+ }
+}
+.visible-print-block {
+ display: none !important;
+}
+@media print {
+ .visible-print-block {
+ display: block !important;
+ }
+}
+.visible-print-inline {
+ display: none !important;
+}
+@media print {
+ .visible-print-inline {
+ display: inline !important;
+ }
+}
+.visible-print-inline-block {
+ display: none !important;
+}
+@media print {
+ .visible-print-inline-block {
+ display: inline-block !important;
+ }
+}
+@media print {
+ .hidden-print {
+ display: none !important;
+ }
+}
+/*# sourceMappingURL=bootstrap.css.map */
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/search_projects.css b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/search_projects.css
new file mode 100644
index 000000000..8875f7fe5
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/search_projects.css
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+input[type="text"]:focus:not([readonly]) {
+ transition: all 0s !important;
+ border-radius: 5px;
+ border: 2px solid #333333;
+ box-shadow: 0 0 15px 1px rgba(0,0,0,0.50);
+ color: #333333;
+}
+.gray {
+ background: rgb(249,249,249);
+}
+.material-search-custom {
+ left : -30px;
+
+ padding-left: 10px;
+}
+.form-group-custom {
+ width : 400px;
+ position: relative;
+ float: right;
+ left: -40%;
+}
+.form-control-custom {
+ padding-top: 10px;
+ padding-left: 50px;
+}
+.card-shadow-custom {
+ box-shadow: 0 2px 3px 0 rgba(0,0,0,0.50);
+ border-bottom: 2px solid #8B19A2;
+}
+.row-custom {
+ width: 100%;
+ margin: 1em 1em 1em 1em;
+ padding-right: 20px;
+}
+.card-title-div-custom {
+ margin: 0.5em 0 0.5em 0;
+ text-align: left;
+ display: inline-block;
+}
+.card-title-span-custom {
+ margin: 0 0 0 2em;
+ display: inline-block;
+}
+.card-title-div-custom-right {
+ margin: 0.5em 0em 0.5em 0;
+ text-align: right;
+ display: inline-block;
+}
+.card-image-picture-custom {
+ margin: 1em 0em 0em 3.5em;
+ display: inline-block;
+}
+.collection .collection-item.active {
+ background-color: rgb(255,245,114);
+ text-decoration: none;
+ transition: none;
+}
+.collection a.collection-item:not(.active):hover {
+ background-color: rgb(255,245,114);
+ text-decoration: none;
+ transition: none;
+}
+.collection .collection-item {
+ padding: 1px 25px;
+ border-bottom: 0px;
+ transition: none;
+}
+.chip > a {
+ display: block;
+ text-decoration: none;
+ text-align: center;
+ display:inline-block;
+ font-size:13px;
+ font-weight:500;
+ color:rgba(0, 0, 0, 0.6) !important;
+ margin-right: 15px !important;
+ margin-left: 15px !important;
+ text-transform: none !important;
+}
+.chip > a:hover {
+ background-color: transparent;
+ text-decoration: none;
+ transition: none;
+}
+a.a-custom-more {
+ display: block;
+ text-decoration: none;
+ text-align: center;
+ display:inline-block;
+ font-size: 20px;
+ font-weight:500;
+ color:rgba(0, 0, 0, 0.6) !important;
+ margin-right: 15px !important;
+ text-transform: none !important;
+}
+a.a-custom-more:hover {
+ background-color: transparent;
+}
+.collection-custom {
+ margin: 0em 0em 0em 1em;
+}
+.card .row .card-action-custom {
+ margin-left: 20px;
+}
+.rating {
+ color: rgb(253,225,109) !important;
+}
+.filled-stars .star i {
+ color: rgb(253,225,109) !important;
+}
+.glyphicon-star-empty {
+ color: rgb(221,221,221);
+}
+.star {
+}
+span.glyphicon.glyphicon-search.form-control-feedback,
+ div.form-group-custom i.material-icons {
+ top: 0.5em;
+ left: 16.5em;
+ cursor:pointer;
+ z-index: 30;
+ position: absolute;
+}
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/style.css b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/style.css
new file mode 100644
index 000000000..4769cfcc0
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/public/stylesheets/style.css
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+@import url('https://fonts.googleapis.com/css?family=Muli:300,400,600,700,800');
+*
+{
+ color: #333333;
+ font-family: 'Muli', sans-serif;
+}
+*:focus
+{
+ outline: none;
+}
+html,
+body
+{
+ margin: 0;
+ padding: 0;
+ background: #ffffff;
+ font-family: 'Muli', sans-serif;
+}
+header
+{
+ padding: 10px 35px 0 0px;
+}
+header ul
+{
+ list-style: none;
+ display: inline-block;
+}
+header ul li
+{
+ display: inline-block;
+}
+header .logo
+{
+ background: url(../images/logo.png) no-repeat;
+ background-size: cover;
+
+}
+header .brand-logo-extends
+{
+ background: url(../images/logo.png) no-repeat;
+ background-size: cover;
+ width: 154px;
+ height: 34px;
+ margin-top: 11%;
+ margin-right: 0%;
+ padding: 0;
+ position: relative !important;
+ /*margin-right: 20px;
+ margin-left: 0;
+ float: left;*/
+ /*display: inline-block;
+ */
+}
+
+nav
+{
+ height: 10px;
+}
+
+header ul li.links
+{
+ margin: 7px 10px 0 0;
+}
+header ul li > a,
+.content ul.most-menu li.items a
+{
+ color: #333333;
+ font-weight: 800;
+ font-size: 14px;
+ letter-spacing: 0.6px;
+ font-family: 'Muli', sans-serif;
+}
+header ul.navigation-right
+{
+ float: right;
+ padding-top: 8px;
+}
+header li.signup > a
+{
+ border: 2px solid #333333;
+ border-radius: 4px;
+ font-size: 14px;
+ font-weigt: 700;
+ padding: 2px 10px;
+}
+header li.signin > a
+{
+ border-bottom: 2px solid #333333;
+ font-size: 13px;
+ font-weight: 700;
+ padding: 0px 2px;
+}
+header li.option
+{
+ font-weight: 800;
+ padding: 0 10px;
+}
+header ul li > a:hover,
+header li.signin > a:hover,
+header li.signup > a:hover,
+header ul li > a:focus,
+header li.signin > a:focus,
+header li.signup > a:focus,
+.content ul.most-menu li a:hover,
+.content ul.most-menu li a:focus
+{
+ text-decoration: none;
+ cursor: pointer;
+ color: #333333;
+}
+header li.signup > a:hover
+{
+ background: #333333;
+ color: #ffffff;
+}
+.search-box
+{
+ text-align: center;
+ padding: 100px 0;
+}
+.search-box h1
+{
+ font-size: 30px;
+ letter-spacing: 2px;
+ color: #333333;
+ font-weight: 600;
+}
+form.search-form
+{
+ padding: 10px 20px;
+}
+form.search-form input.search-input
+{
+ font-weight: 400;
+ margin: 30px 0;
+ height: 80px;
+ padding: 10px 30px;
+ max-width: 800px;
+ width: 70%;
+ border-radius: 5px;
+ border: 2px solid #333333;
+ box-shadow: 0 0 15px 1px rgba(0,0,0,0.50);
+ color: #333333;
+ font-size: 22px;
+}
+input.search-input-rest
+{
+ font-weight: 400;
+ margin: 0px 0;
+ height: 30px;
+ padding: 10px 30px;
+ min-width: 90%;
+ max-width: 90%;
+ /*max-width: 500px;
+ */
+ border-radius: 5px;
+ border: 2px solid #333333;
+ box-shadow: 0 0 15px 1px rgba(0,0,0,0.50);
+ color: #333333;
+ font-size: 22px;
+}
+
+
+form.search-form button.search-button
+{
+ padding: 18px 35px;
+ background: #FFF572;
+ border: 0;
+ box-shadow: 0 0 15px 1px #958F40;
+ border-radius: 1px;
+ font-size: 20px;
+ color: #393E41;
+ letter-spacing: 1px;
+ border-radius: 5px;
+ font-weight: 600;
+}
+form.search-form input:focus
+{
+ outline: none;
+}
+form.search-form input::-webkit-input-placeholder
+{
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #333333;
+}
+form.search-form input::-moz-placeholder
+{
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #333333;
+}
+form.search-form input:-moz-placeholder
+{
+ font-weight: 400;
+ letter-spacing: 5px;
+ color: #333333;
+}
+form.search-form input:-ms-input-placeholder
+{
+ font-weight: 400;
+ letter-spacing: 1px;
+ color: #333333;
+}
+.content
+{
+ height: 500px;
+ background: #f9f9f9;
+ padding: 10px 0;
+}
+.content ul.most-menu
+{
+ list-style: none;
+ text-align: center;
+ padding-bottom: 10px;
+}
+.content ul.most-menu li.items
+{
+ display: inline-block;
+ margin-right: 5px;
+ padding: 15px 25px;
+}
+.content ul.most-menu li.active
+{
+ /*background: #FFF572;*/
+}
+.content-box
+{
+ overflow: hidden;
+ padding: 20px 0 50px 0;
+ display: flex;
+ justify-content: center;
+ background: #FFFFFF;
+ box-shadow: 0 2px 3px 0 rgba(0,0,0,0.50);
+ border-bottom: 2px solid #8B19A2;
+ margin-bottom: 30px;
+}
+.content-data
+{
+ align-self: center;
+}
+.content-data h1.content-title
+{
+ font-size: 25px;
+ color: #000000;
+ letter-spacing: 1.2px;
+}
+.content-data .box
+{
+ padding: 10px 0;
+ height: 90px;
+ text-align: center;
+ border: 2px solid #4D4D4D;
+ border-radius: 2px;
+}
+.content-data .commit-icon
+{
+ width: 23px;
+ height: 16px;
+}
+.content-data .box h3.commits
+{
+ text-align: center;
+ font-size: 12px;
+ color: #333333;
+ letter-spacing: 0.03px;
+}
+.content-height-overwrite
+{
+ height: 110px;
+}
+.float-center-magic
+{
+ float: right;
+ position: relative;
+ left: -30%;
+}
+nav ul li:hover, nav ul li.active, nav ul li a.active, nav ul li a:hover {
+ background-color: rgb(255,245,114);
+}
+a:hover, a.active {
+ background-color: rgb(255,245,114);
+}
+footer
+{
+ font-size: 12px;
+ font-weight: 800;
+ color: #333333;
+ text-align: center;
+ padding: 20px;
+}
+.space-10
+{
+ height: 10px;
+}
+.space-30
+{
+ height: 100px;
+}
+input[type="text"]:focus:not([readonly]) {
+ transition: all 0s !important;
+ border-radius: 5px;
+ border: 2px solid #333333;
+ box-shadow: 0 0 15px 1px rgba(0,0,0,0.50);
+ color: #333333;
+}
+.gray {
+ background: rgb(249,249,249);
+}
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/routes/index.js b/utils/test/vnfcatalogue/VNF_Catalogue/routes/index.js
new file mode 100644
index 000000000..950fcd57e
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/routes/index.js
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+var express = require('express');
+var router = express.Router();
+
+/* GET VNF_Catalogue Home Page. */
+router.get('/', function(req, res) {
+ res.render('index', { title: 'Express' });
+});
+
+module.exports = router;
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/routes/search_projects.js b/utils/test/vnfcatalogue/VNF_Catalogue/routes/search_projects.js
new file mode 100644
index 000000000..49fceeb3c
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/routes/search_projects.js
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+var express = require('express');
+var router = express.Router();
+
+router.get('/', function(req, res) {
+ var tags = req.param('tags');
+ console.log(tags);
+ res.render('search_projects', { title: 'Express' });
+});
+
+module.exports = router;
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/views/error.jade b/utils/test/vnfcatalogue/VNF_Catalogue/views/error.jade
new file mode 100644
index 000000000..4f7fbcaeb
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/views/error.jade
@@ -0,0 +1,12 @@
+//
+ Copyright (c) 2017 Kumar Rishabh and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Apache License, Version 2.0
+ which accompanies this distribution, and is available at
+ http://www.apache.org/licenses/LICENSE-2.0
+extends layout
+
+block content
+ h1= message
+ h2= error.status
+ pre #{error.stack}
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/views/index.jade b/utils/test/vnfcatalogue/VNF_Catalogue/views/index.jade
new file mode 100644
index 000000000..e60c3ba26
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/views/index.jade
@@ -0,0 +1,122 @@
+extends layout
+
+//
+ Copyright (c) 2017 Kumar Rishabh and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Apache License, Version 2.0
+ which accompanies this distribution, and is available at
+ http://www.apache.org/licenses/LICENSE-2.0
+block content
+ .search-box
+ h1 VNF Catalogue
+ form.search-form
+ input.search-input(type='text', placeholder='Search...', id='Tags')
+ .space-10
+ button.search-button(type='submit', value='Search', id='Search') Search
+ .content.content-height-overwrite
+ nav.z-depth-0.transparent
+ .nav-wrapper
+ ul#nav-mobile.float-center-magic.hide-on-med-and-down.most-menu
+ li.items
+ a(href='#') Most Popular
+ li.items
+ a(href='#') Most Active
+ li.items
+ a(href='#') Most Active Contributions
+ .content
+ .container
+ .row
+ .box-container
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ .col-md-3
+ .content-box
+ .content-data
+ h1.content-title Beacon
+ .box
+ img.commit-icon(src='/images/3rd_party/commits.png')
+ h3.commits
+ | 4,845
+ br
+ | commits
+ footer
+ | © 2017 XYZ Company
+ style(type='text/css').
+ input[type="text"]:focus:not([readonly]) {
+ transition: all 0s !important;
+ border-radius: 5px;
+ border: 2px solid #333333;
+ box-shadow: 0 0 15px 1px rgba(0,0,0,0.50);
+ color: #333333;
+ }
+ .gray {
+ background: rgb(249,249,249);
+ }
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/views/layout.jade b/utils/test/vnfcatalogue/VNF_Catalogue/views/layout.jade
new file mode 100644
index 000000000..89142f813
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/views/layout.jade
@@ -0,0 +1,53 @@
+doctype html
+//
+ Copyright (c) 2017 Kumar Rishabh and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Apache License, Version 2.0
+ which accompanies this distribution, and is available at
+ http://www.apache.org/licenses/LICENSE-2.0
+html(lang='en')
+html
+ head
+ title= title
+ link(rel='stylesheet', href='/stylesheets/3rd_party/bootstrap.css')
+ link(rel='stylesheet', href='/3rd_party/materialize/css/materialize.css')
+ script(type='text/javascript', src='https://code.jquery.com/jquery-3.1.1.min.js')
+ script(src='/javascripts/global.js')
+ script(src='/3rd_party/materialize/js/materialize.js')
+ link(href='https://fonts.googleapis.com/icon?family=Material+Icons', rel='stylesheet')
+ link(rel='stylesheet', href='/stylesheets/style.css')
+ body
+ header
+ nav.transparent.z-depth-0
+ .nav-wrapper
+ a.button-collapse(href='#', data-activates='mobile-demo')
+ i.material-icons menu
+ ul#nav-mobile.left.hide-on-med-and-down
+ li
+ a(href='#')
+ img.left.brand-logo.brand-logo-extends
+ li
+ a(href='#') Projects
+ li
+ a(href='#') People
+ li
+ a(href='#') About
+ ul#nav-mobile.right.hide-on-med-and-down
+ li.signup
+ a(href='#') Sign up
+ li.option or
+ li.signin
+ a(href='#') Sign in
+ ul#mobile-demo.side-nav
+ li
+ a(href='#') Projects
+ li
+ a(href='#') People
+ li
+ a(href='#') About
+ li.signup
+ a(href='#') Sign up
+ li.signin
+ a(href='#') Sign in
+ block content
+
diff --git a/utils/test/vnfcatalogue/VNF_Catalogue/views/search_projects.jade b/utils/test/vnfcatalogue/VNF_Catalogue/views/search_projects.jade
new file mode 100644
index 000000000..6670d212c
--- /dev/null
+++ b/utils/test/vnfcatalogue/VNF_Catalogue/views/search_projects.jade
@@ -0,0 +1,75 @@
+extends layout
+
+//
+ Copyright (c) 2017 Kumar Rishabh and others.
+ All rights reserved. This program and the accompanying materials
+ are made available under the terms of the Apache License, Version 2.0
+ which accompanies this distribution, and is available at
+ http://www.apache.org/licenses/LICENSE-2.0
+block content
+ .search-box
+ .form-group-custom.form-group.has-feedback
+ input.search-input-rest.form-control(type='text', placeholder='Search...', id='Tags')
+ i.material-icons search
+ .content
+ .container
+ .card.card-shadow-custom.horizontal
+ .row.row-custom
+ .col.s5.card-title-div-custom
+ span.card-title.card-title-span-custom Card Title
+ .col.s5.card-title-div-custom
+ i.material-icons grade
+ span.card-title PenguinScore: 42
+ .col.s2.card-title-div-custom-right
+ form(action='#')
+ input#search_result_1(type='checkbox')
+ label(for='search_result_1') Compare
+ //
+ <div class="card-action">
+ <a href="#">This is a link</a>
+ </div>
+ .col.s4.card-image
+ img.card-image-picture-custom(src='/images/logo.png')
+ .col.s8.card-stacked
+ .card-content
+ p
+ .collection.collection-custom
+ a.collection-item(href='#!')
+ span
+ i.material-icons code
+ | Lines Of Code: 1.03M
+ a.collection-item(href='#!')
+ span
+ i.material-icons code
+ | Lines Of Code: 1.03M
+ a.collection-item(href='#!')
+ span
+ i.material-icons code
+ | Lines Of Code: 1.03M
+ a.collection-item(href='#!')
+ span
+ i.material-icons code
+ | Lines Of Code: 1.03M
+ .card-action
+ | Tags:
+ .chip
+ a.a-custom(href='#!') Tag1
+ .chip
+ a.a-custom(href='#!') Tag2
+ .chip
+ a.a-custom(href='#!') Tag3
+ .chip
+ a.a-custom(href='#!') Tag4
+ .chip
+ a.a-custom(href='#!') Tag5
+ a.a-custom-more(href='#!') more
+ .divider
+ .card-action-custom.col.s12.card-action
+ | License:
+ a(href='#') MIT
+ | Complexity:
+ a(href='#') Atomic
+ footer
+ | © 2017 XYZ Company
+ link(rel='stylesheet', href='/stylesheets/search_projects.css')
+
diff --git a/utils/test/vnfcatalogue/helpers/README.md b/utils/test/vnfcatalogue/helpers/README.md
new file mode 100644
index 000000000..6c0ca78c3
--- /dev/null
+++ b/utils/test/vnfcatalogue/helpers/README.md
@@ -0,0 +1,22 @@
+# Helper Directory
+
+## Helper to migrate database
+
+First make sure nodejs and mysql are installed. Then use
+
+```bash
+npm install bookshelf mysql knex when lodash --save
+```
+
+Create a database named **vnf_catalogue**.
+Enter the mysql credentials in migrate.js.
+
+Then use
+
+```bash
+node migrate
+```
+
+If successful the script will return success message. The current script is
+idempotent is nature, if run twice it will just return error and write nothing.
+
diff --git a/utils/test/vnfcatalogue/helpers/migrate.js b/utils/test/vnfcatalogue/helpers/migrate.js
new file mode 100644
index 000000000..ec209053c
--- /dev/null
+++ b/utils/test/vnfcatalogue/helpers/migrate.js
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh(penguinRaider) and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+
+var knex = require('knex')({
+ client: 'mysql',
+ connection: {
+ host : 'localhost',
+ user : '*',
+ password : '*',
+ database : 'vnf_catalogue',
+ charset : 'utf8'
+ }
+});
+var Schema = require('./schema');
+var sequence = require('when/sequence');
+var _ = require('lodash');
+function createTable(tableName) {
+ return knex.schema.createTable(tableName, function (table) {
+ var column;
+ var columnKeys = _.keys(Schema[tableName]);
+ _.each(columnKeys, function (key) {
+ if (Schema[tableName][key].type === 'text' && Schema[tableName][key].hasOwnProperty('fieldtype')) {
+ column = table[Schema[tableName][key].type](key, Schema[tableName][key].fieldtype);
+ }
+ else if (Schema[tableName][key].type === 'string' && Schema[tableName][key].hasOwnProperty('maxlength')) {
+ column = table[Schema[tableName][key].type](key, Schema[tableName][key].maxlength);
+ }
+ else {
+ column = table[Schema[tableName][key].type](key);
+ }
+ if (Schema[tableName][key].hasOwnProperty('nullable') && Schema[tableName][key].nullable === true) {
+ column.nullable();
+ }
+ else {
+ column.notNullable();
+ }
+ if (Schema[tableName][key].hasOwnProperty('primary') && Schema[tableName][key].primary === true) {
+ column.primary();
+ }
+ if (Schema[tableName][key].hasOwnProperty('unique') && Schema[tableName][key].unique) {
+ column.unique();
+ }
+ if (Schema[tableName][key].hasOwnProperty('unsigned') && Schema[tableName][key].unsigned) {
+ column.unsigned();
+ }
+ if (Schema[tableName][key].hasOwnProperty('references')) {
+ column.references(Schema[tableName][key].references);
+ }
+ if (Schema[tableName][key].hasOwnProperty('defaultTo')) {
+ column.defaultTo(Schema[tableName][key].defaultTo);
+ }
+ });
+ });
+}
+function createTables () {
+ var tables = [];
+ var tableNames = _.keys(Schema);
+ tables = _.map(tableNames, function (tableName) {
+ return function () {
+ return createTable(tableName);
+ };
+ });
+ return sequence(tables);
+}
+createTables()
+.then(function() {
+ console.log('Tables created!!');
+ process.exit(0);
+})
+.catch(function (error) {
+ throw error;
+});
diff --git a/utils/test/vnfcatalogue/helpers/schema.js b/utils/test/vnfcatalogue/helpers/schema.js
new file mode 100644
index 000000000..2aaf99ae2
--- /dev/null
+++ b/utils/test/vnfcatalogue/helpers/schema.js
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Kumar Rishabh(penguinRaider) and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *******************************************************************************/
+var Schema = {
+ photo: {
+ photo_id: {type: 'increments', nullable: false, primary: true},
+ photo_url: {type: 'string', maxlength: 254, nullable: false}
+ },
+ user: {
+ user_id: {type: 'increments', nullable: false, primary: true},
+ user_name: {type: 'string', maxlength: 254, nullable: false},
+ password: {type: 'string', maxlength: 150, nullable: false},
+ email_id: {type: 'string', maxlength: 254, nullable: false, unique: true, validations: {isEmail: true}},
+ photo_id: {type: 'integer', nullable: true, unsigned: true, references: 'photo.photo_id'},
+ company: {type: 'string', maxlength: 254, nullable: false},
+ introduction: {type: 'string', maxlength: 510, nullable: false},
+ last_login: {type: 'dateTime', nullable: true},
+ created_at: {type: 'dateTime', nullable: false},
+ },
+ vnf: {
+ vnf_id: {type: 'increments', nullable: false, primary: true},
+ vnf_name: {type: 'string', maxlength: 254, nullable: false},
+ repo_url: {type: 'string', maxlength: 254, nullable: false},
+ photo_id: {type: 'integer', nullable: true, unsigned: true, references: 'photo.photo_id'},
+ submitter_id: {type: 'integer', nullable: false, unsigned: true, references: 'user.user_id'},
+ lines_of_code: {type: 'integer', nullable: true, unsigned: true},
+ versions: {type: 'integer', nullable: true, unsigned: true},
+ no_of_developers: {type: 'integer', nullable: true, unsigned: true},
+ },
+ tag: {
+ tag_id: {type: 'increments', nullable: false, primary: true},
+ name: {type: 'string', maxlength: 150, nullable: false}
+ },
+ vnf_tags: {
+ vnf_tag_id: {type: 'increments', nullable: false, primary: true},
+ tag_id: {type: 'integer', nullable: false, unsigned: true, references: 'tag.tag_id'},
+ vnf_id: {type: 'integer', nullable: false, unsigned: true, references: 'vnf.vnf_id'},
+ },
+ vnf_contributors: {
+ vnf_contributors_id: {type: 'increments', nullable: false, primary: true},
+ user_id: {type: 'integer', nullable: false, unsigned: true, references: 'user.user_id'},
+ vnf_id: {type: 'integer', nullable: false, unsigned: true, references: 'vnf.vnf_id'},
+ created_at: {type: 'dateTime', nullable: false},
+ }
+};
+module.exports = Schema;